Code Monkey home page Code Monkey logo

pyp's Introduction

pyp

Build Status Coverage Status Checked with mypy

Easily run Python at the shell! Magical, but never mysterious.

Installation

Run pip install pypyp (note the extra "yp"!)

pyp requires Python 3.8 or above.

How it works

pyp will statically analyse the input code to detect undefined variables. Based on what it finds, it will proceed to transform the AST of the input code as needed. We then compile and execute the result, or if using --explain, unparse the AST back to source code.

Examples

This section will walk you through the details of using pyp, and hopefully replace your needs for many common shell utilities. For a cheatsheet / tldr, run pyp --help.

pyp can easily be used to apply Python code to each line in the input.

Just use one of the magic variables x, l or line to refer to the current line.

# pyp like cut
ls | pyp 'x[:3]'
ps x | pyp 'line.split()[4]'

pyp can be used to easily apply Python code to the entire input as well.

Use the magic variable lines for a list of rstripped lines or stdin for sys.stdin.

# pyp like wc -c
cat /usr/share/dict/words | pyp 'len(stdin.read())'

# pyp like awk
seq 1 5 | pyp 'sum(map(int, lines))'

pyp will automatically import modules you use.

# pyp like sh
echo echo echo | pyp 'subprocess.run(lines[0], shell=True); pass'

# pyp like jq
curl -s 'https://api.github.com/repos/hauntsaninja/pyp/commits?per_page=1' | pyp 'json.load(stdin)[0]["commit"]["author"]'

# pyp like egrep
cat /usr/share/dict/words | pyp 'x if re.search("(p|m)yth", x) else None'

For collections, math, itertools, pathlib.Path, pprint.pp, pyp will figure it out even if you don't use the qualified name.

# pyp like bc
pyp 'sqrt(5)'

# pyp like ${x##*.}
ls | pyp 'Path(x).suffix'

pyp can give you access to loop indices using the magic variables i, idx or index.

# pyp like line numbers
cat setup.py | pyp 'f"{idx+1: >3} {x}"'

Note so far you haven't had to call print!

By default, pyp will print the last expression in your code โ€” except if it evaluates to None (or the last statement is pass). And you can always explicitly call print yourself, in which case pyp will stay out of your way.

# pyp like grep
cat /usr/share/dict/words | pyp 'x if "python" in x else None'
cat /usr/share/dict/words | pyp 'if "python" in x: print(x); "this will not be printed"'

pyp will attempt to intelligently print dicts and iterables.

This makes the output of pyp easier to compose with shell tools. Again, explicit printing will stop this magic, but pyp makes the function pypprint available if you do want to explicitly opt back in.

# pyp like tail
ls | pyp 'lines[-10:]'

# pyp like sort
ls | pyp 'sorted(lines)'
ls | pyp 'print(f"Sorting {len(lines)} lines"); pypprint(sorted(lines))'

# pyp like sort | uniq
ls | pyp 'sorted(set(lines))'

pyp lets you run snippets of Python before and after processing input.

Note if you run into trouble with semicolons and want a new line (without using a multiline string in your shell), you can just pass another string to pyp. You can also always pipe pyp to pyp!

# pyp like anything!
ps aux | pyp -b 'd = defaultdict(list)' 'user, pid, *_ = x.split()' 'd[user].append(pid)' -a 'del d["root"]' -a 'd'

pyp can be magical, but it doesn't have to be mysterious!

Use --explain or --script and pyp will output a script equivalent to what it would run. This can also serve as a useful starting point for more complex scripts.

pyp --explain -b 'd = defaultdict(list)' 'user, pid, *_ = x.split()' 'd[user].append(pid)' -a 'del d["root"]' -a 'd'
#!/usr/bin/env python3
from collections import defaultdict
from pyp import pypprint
import sys
d = defaultdict(list)
for x in sys.stdin:
    x = x.rstrip('\n')
    (user, pid, *_) = x.split()
    d[user].append(pid)
del d['root']
if d is not None:
    pypprint(d)

And if your command hits an exception, pyp will reconstruct a traceback into the generated code.

pyp is configurable.

Point the environment variable PYP_CONFIG_PATH to a file containing, for example:

import numpy as np
import tensorflow as tf
from pipetools import *

def p95(data):
    return np.percentile(data, 95)

class PotentiallyUsefulClass: ...

When attempting to define undefined names, pyp will statically* analyse this file as a source of possible definitions. This means that if you don't use tf, we won't import tensorflow! And of course, --explain will show you exactly what gets run (and hence what doesn't!):

pyp --explain 'print(p95(list(map(float, stdin))))'
#!/usr/bin/env python3
import sys
import numpy as np

def p95(data):
    return np.percentile(data, 95)
stdin = sys.stdin
print(p95(list(map(float, stdin))))

Note, importing things from libraries like pipetools in your configuration can allow you to achieve high levels of syntax sugar:

seq 1 110 | pyp 'lines > foreach(int) | where(X > 100) | group_by(X % 3) | sort_by(X[0])'

*If you use wildcard imports, we will need to import those modules if there remain undefined names, though we skip this in the happy path. If this matters to you, definitely don't from tensorflow import * in your config!

pyp lets you configure your own magic!

If definitions in your config file depend on magic variables, pyp will substitute them in the way that makes sense. For example, put the following in your config...

n = int(x)
f = x.split()
j = json.load(stdin)

import pandas as pd
csv = pd.read_csv(stdin)

...to make pyp easier than ever for your custom use cases:

ps | pyp 'f[3]'

cat commits.json | pyp 'j[0]["commit"]["author"]'

< cities.csv pyp 'csv.to_string()'

I have questions!

There's additional documentation and examples at FAQ. If that doesn't answer your question, please open an issue!

Related projects

Pyed Piper aka Python Power at the Prompt

pypyp takes inspiration (and the command name!) from here.

Pyed Piper had been dead and Python 2 only for a decade when pypyp was written; it appears it's recently been resurrected. Pyed Piper is further away from normal Python syntax and APIs than pypyp aims to be. In particular, Pyed Piper has an emphasis on piping within Python similar to the way you can combine pypyp with pipetools in the configuration example above.

Pyped is very similar; enough so that I probably wouldn't have written pyp had I known about it. But I'm glad I didn't, since Pyped doesn't do the AST introspection and manipulation that we do. This means:

  • Pyped relies on you to pass in flags to tell it what to do, when pyp can unambiguously infer intention from the input.
  • It doesn't provide easy automatic printing, or smart printing of iterables and dicts.
  • It hardcodes a list of imports and installs some libraries on your system. This project's automatic import will work for any library you use.
  • It doesn't have anything like --explain/--script.

However,

  • It has some conveniences, like regex splitting of input, that you'd have to do for yourself here.
  • It supports Python 2 and early versions of Python 3.
  • It's been around for much longer.

Since writing pyp, it turns out there are more alternatives out there than I thought :-) Some quick notes:

  • Most of them rely on the user passing in flags, like Pyped.
  • Most of them have limitations around automatic printing, like only being able to automatically print single expressions or not handling iterables and dicts well.
  • Some of them have custom syntax for in-process command chaining, which can be convenient.
  • Some of them have specialised support for things like JSON input or running shell commands.
  • Some of them expose the input in interesting ways with custom line / file / stream objects.
  • Some of them have more advanced options for error handling (although none of them have pyp's great tracebacks).
  • None of them have powerful configuration like pyp.
  • None of them have anything like --explain.

For whatever it's worth, I've listed the projects above in approximate order of my personal preference.

mario is a featureful take on shell processing with Python. It doesn't use undefined name detection, instead relying on a pluggable subcommand system. While the subcommands can be more verbose than pyp, mario makes up some ground by automatic application of functions and a custom command chaining syntax. The result can feel a little DSL-like, while pyp tries to feel very close to writing Python.

Consider using mario if:

  • You find yourself stringing together long sequences of pyp commands and want to be able to command chain within a single process out of the box.
  • You find yourself often needing to reuse complex pyp commands or doing a lot of domain specific shell processing that you wish you could reuse with a single command.
  • You want to easily be able to use async functions.

Consider pyp if:

  • You want to minimise keystrokes for things that should be quick and easy.
  • You want something minimal and lightweight that feels very close to Python. You don't want to have to remember commands.
  • You're happy using Python libraries to do domain specific heavy lifting, for easy command chaining or syntax sugar. You don't mind (or want to be able to) fall back to a script via --script to deal with complexity.

xonsh is a shell whose language is a superset of Python; this is more ambitious and pretty different from pyp. pyp is easier to use for the one-liner piping use case, but if you need more Python in your shell, check out xonsh.

If awk works for you, how did you end up here?

pyp's People

Contributors

hauntsaninja avatar ian-h-chamberlain avatar nickodell avatar piec avatar stuartlangridge avatar yuvadm avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pyp's Issues

Tests fail with Python 3.8.11 and 3.9.6

Hi,

I'm preparing a distribution package of pyp for openSUSE, but noticed, that it is failing with some strange error on Python 3.8.11 and 3.9.6:

[    8s] + py.test-3.9 -v
[    8s] ============================= test session starts ==============================
[    8s] platform linux -- Python 3.9.6, pytest-6.2.4, py-1.10.0, pluggy-0.13.1 -- /usr/bin/python3.9
[    8s] cachedir: .pytest_cache
[    8s] rootdir: /home/abuild/rpmbuild/BUILD/pyp-0.3.3
[    8s] collecting ... collected 33 items
[    8s] 
[    8s] tests/test_find_names.py::test_basic PASSED                              [  3%]
[    8s] tests/test_find_names.py::test_builtins PASSED                           [  6%]
[    8s] tests/test_find_names.py::test_loops PASSED                              [  9%]
[    8s] tests/test_find_names.py::test_weird_assignments PASSED                  [ 12%]
[    8s] tests/test_find_names.py::test_more_control_flow PASSED                  [ 15%]
[    8s] tests/test_find_names.py::test_import PASSED                             [ 18%]
[    8s] tests/test_find_names.py::test_walrus PASSED                             [ 21%]
[    8s] tests/test_find_names.py::test_comprehensions PASSED                     [ 24%]
[    8s] tests/test_find_names.py::test_args PASSED                               [ 27%]
[    8s] tests/test_find_names.py::test_definitions PASSED                        [ 30%]
[    8s] tests/test_find_names.py::test_scope PASSED                              [ 33%]
[    8s] tests/test_find_names.py::test_scope_failures XFAIL (do not currentl...) [ 36%]
[    8s] tests/test_find_names.py::test_del XFAIL (do not currently support d...) [ 39%]
[    8s] tests/test_pyp.py::test_examples PASSED                                  [ 42%]
[    8s] tests/test_pyp.py::test_magic_variable_failures PASSED                   [ 45%]
[    8s] tests/test_pyp.py::test_user_error PASSED                                [ 48%]
[    8s] tests/test_pyp.py::test_tracebacks FAILED                                [ 51%]
[    8s] tests/test_pyp.py::test_explain PASSED                                   [ 54%]
[    8s] tests/test_pyp.py::test_disable_automatic_print PASSED                   [ 57%]
[    8s] tests/test_pyp.py::test_automatic_print_inside_statement PASSED          [ 60%]
[    8s] tests/test_pyp.py::test_pypprint_basic PASSED                            [ 63%]
[    8s] tests/test_pyp.py::test_get_valid_name PASSED                            [ 66%]
[    8s] tests/test_pyp.py::test_wildcard_import PASSED                           [ 69%]
[    8s] tests/test_pyp.py::test_config_imports PASSED                            [ 72%]
[    8s] tests/test_pyp.py::test_config_invalid PASSED                            [ 75%]
[    8s] tests/test_pyp.py::test_config_shebang PASSED                            [ 78%]
[    8s] tests/test_pyp.py::test_config_lazy_wildcard_import PASSED               [ 81%]
[    8s] tests/test_pyp.py::test_config_scope PASSED                              [ 84%]
[    8s] tests/test_pyp.py::test_config_shadow PASSED                             [ 87%]
[    8s] tests/test_pyp.py::test_config_recursive PASSED                          [ 90%]
[    8s] tests/test_pyp.py::test_config_conditional PASSED                        [ 93%]
[    8s] tests/test_pyp.py::test_config_conditional_current_shortcoming XFAIL     [ 96%]
[    8s] tests/test_pyp.py::test_config_end_to_end PASSED                         [100%]
[    8s] 
[    8s] =================================== FAILURES ===================================
[    8s] _______________________________ test_tracebacks ________________________________
[    8s] 
[    8s]     def test_tracebacks():
[    8s]         # If our sins against traceback implementation details come back to haunt us, and we can't
[    8s]         # reconstruct a traceback, check that we still output something reasonable
[    8s]         TBE = traceback.TracebackException
[    8s]         with patch("traceback.TracebackException") as mock_tb:
[    8s]             count = 0
[    8s]     
[    8s]             def effect(*args, **kwargs):
[    8s]                 nonlocal count
[    8s]                 if count == 0:
[    8s]                     assert args[0] == ZeroDivisionError
[    8s]                     count += 1
[    8s]                     raise Exception
[    8s]                 return TBE(*args, **kwargs)
[    8s]     
[    8s]             mock_tb.side_effect = effect
[    8s]             pattern = re.compile("Code raised.*ZeroDivisionError", re.DOTALL)
[    8s]             with pytest.raises(pyp.PypError, match=pattern) as e:
[    8s]                 run_pyp("pyp '1 / 0'")
[    8s]             # Make sure that the test works and we couldn't actually reconstruct a traceback
[    8s]             assert "Possible" not in e.value.args[0]
[    8s]     
[    8s]         # Check the entire output, end to end
[    8s]         pyp_error = run_cmd("pyp 'def f(): 1/0' 'f()'", check=False)
[    8s]         message = lambda x, y: (  # noqa
[    8s]             "error: Code raised the following exception, consider using --explain to investigate:\n\n"
[    8s]             "Possible reconstructed traceback (most recent call last):\n"
[    8s]             '  File "<pyp>", in <module>\n'
[    8s]             "    output = f()\n"
[    8s]             '  File "<pyp>", in f\n'
[    8s]             f"    {x}1 / 0{y}\n"
[    8s]             "ZeroDivisionError: division by zero\n"
[    8s]         )
[    8s] >       assert pyp_error == message("(", ")") or pyp_error == message("", "")
[    8s] E       assert ('error: Code ...ion by zero\n' == 'error: Code ...ion by zero\n'
[    8s] E           error: Code raised the following exception, consider using --explain to investigate:
[    8s] E           
[    8s] E           Possible reconstructed traceback (most recent call last):
[    8s] E         + Traceback (most recent call last):
[    8s] E             File "<pyp>", in <module>
[    8s] E               output = f()
[    8s] E             File "<pyp>", in f...
[    8s] E         
[    8s] E         ...Full output truncated (5 lines hidden), use '-vv' to show or 'error: Code ...ion by zero\n' == 'error: Code ...ion by zero\n'
[    8s] E           error: Code raised the following exception, consider using --explain to investigate:
[    8s] E           
[    8s] E           Possible reconstructed traceback (most recent call last):
[    8s] E         + Traceback (most recent call last):
[    8s] E             File "<pyp>", in <module>
[    8s] E               output = f()
[    8s] E             File "<pyp>", in f...
[    8s] E         
[    8s] E         ...Full output truncated (3 lines hidden), use '-vv' to show)
[    8s] 
[    8s] tests/test_pyp.py:209: AssertionError
[    8s] =========================== short test summary info ============================
[    8s] FAILED tests/test_pyp.py::test_tracebacks - assert ('error: Code ...ion by ze...
[    8s] =================== 1 failed, 29 passed, 3 xfailed in 0.32s ====================
[    8s] error: Bad exit status from /var/tmp/rpm-tmp.uU6mEp (%check)

To me, it looks, like the expected message from the traceback changed.
I couldn't locate anything related to this in the non released commits.
Sorry for not providing a PR this time.

Here's the package home:
https://build.opensuse.org/package/live_build_log/home:frispete:python/python-pyp/openSUSE_Tumbleweed/x86_64

Excluding test_tracebacks succeeds, as being done in:
https://build.opensuse.org/package/live_build_log/home:frispete:Tumbleweed/python-pyp/openSUSE_Tumbleweed/x86_64

failure in pipetools example in documentation

I tried this example from the README:
seq 1 110 | pyp 'lines > foreach(int) | where(X > 100) | group_by(X % 3) | sort_by(X[0])'

but I get the error ModuleNotFoundError: No module named 'X'.

I do have from pipetools import * in my config.

Create a man page for pyp

Often when I'm writing on off shell scripts that are piped together, I forget various flags and have to refer to man $PROGRAM. Since I'm now using pyp quite often in my shell scripts, it would be fantastic if I could run man pyp and see its documentation as well.

Print last defined variable if no print statements and no final value

A few feature requests:

  1. If the pyp command ends with an assignment, use the value of that assignment as the value to print.
  2. textwrap.dedent before parsing
  3. Allow import aliases

Note that 3. is different from the discussion we've been having in #5 and #6. If the user explicitly imports something (something they may not want to include in their config file once that exists), that should work.

E.g. I think this should work:

echo 10 | pyp '
  import numpy as np  # Ideally this line isn't needed either
  values = range(1, int(x))
  unnormalized_probabilities = [1/value for value in values]
  total = sum(unnormalized_probabilities)
  probabilities = [p / total for p in unnormalized_probabilities]
  value = np.random.choice(values, p=probabilities)
'

Whereas today this is needed:

echo 10 | pyp '
values = range(1, int(x))
unnormalized_probabilities = [1/value for value in values]
total = sum(unnormalized_probabilities)
probabilities = [p / total for p in unnormalized_probabilities]
value = numpy.random.choice(values, p=probabilities)
value
'

Note the three changes that were needed to address each of the three feature requests.

  1. added value line at end
  2. manually dedented
  3. used numpy in place of import alias

The reason for preferring the former is it makes it relatively easy to reuse code from a real Python file with minimal modifications for pyp.

Wdyt about these requests?

grep equivalence

Hi,
Very useful tool!
What's the grep equivalence in pyp ? For example, how to achieve the function of 'grep "abcdef" -A 1 -B2 file.txt' using pyp ?

Submodule Import Problems

Suppose I have a string of HTML content and would like to extract certain information from it:

pyp "xml.etree.ElementTree.fromstring('<html><head><title>Title</title></head></html>').find('head/title').text"

Even though pyp tried to import xml, there will still be AttributeError: module 'xml' has no attribute 'etree' because of xml.etree.ElementTreeโ€™s submodule structure.

I can explicitly use -b parameter for proper importing:

pyp -b "import xml.etree.ElementTree" "xml.etree.ElementTree.fromstring('<html><head><title>Title</title></head></html>').find('head/title').text"
# Title

However, if I add the same line to the PYP_CONFIG_PATH config file, the same AttributeError happens still.

cat $PYP_CONFIG_PATH
# import xml.etree.ElementTree
pyp "xml.etree.ElementTree.fromstring('<html><head><title>Title</title></head></html>').find('head/title').text"
# AttributeError: module 'xml' has no attribute 'etree'

So, the question is:
What is the correct way to have xml.etree.ElementTree imported automatically?

Can't use automatic variables in before statement

I'm using pyp to parse and munge CSV/TSV files.

Recently, I ran into an issue where I can't use stdin in a before statement.

Here's my code, in Bash:

< pollution.csv python3 pyp.py \
	-b 'input = csv.DictReader(stdin, delimiter=",")' \
	-b 'output = csv.DictWriter(sys.stdout, fieldnames=input.fieldnames)' \
	-b 'output.writeheader()' \
	'for row in input: output.writerow(row); pass'

(This is just a dummy command which doesn't modify the input.)

Here's the error:

Possible reconstructed traceback (most recent call last):
  File "<pyp>", in <module>
    input = csv.DictReader(stdin, delimiter=',')
NameError: name 'stdin' is not defined

Looking at the error, it looks like the code assumes that an automatic variable (eg. lines, x, stdin) will not be used during a before statement. For some of them, this is reasonable, because they don't have a meaning outside of a loop. But lines and stdin have perfectly sensible meanings before the loop.

`AssertionError: The command doesn't process input, but input is present` on `sys.stdin` usage

I was trying to read bytes instead of string from stdin for #27, but got this:

$ echo pyp | pyp "sys.stdin"
error: Code raised the following exception, consider using --explain to investigate:

Possible reconstructed traceback (most recent call last):
  File "<pyp>", in <module>
    assert sys.stdin.isatty() or not sys.stdin.read(), "The command doesn't process input, but input is present"
AssertionError: The command doesn't process input, but input is present
$ echo 1 | pyp "sys.stdin" --explain
#!/usr/bin/env python3
import sys
from pyp import pypprint
assert sys.stdin.isatty() or not sys.stdin.read(), "The command doesn't process input, but input is present"
output = sys.stdin
if output is not None:
    pypprint(output)

Consider first-level support of pipetools

Pipetools (https://0101.github.io/pipetools/doc/pipeutils.html) is a nifty package which allows you to write code like this.

ls | pyp 'lines > pipetools.pipe | sorted | list'

While I wouldn't consider it for production use, it has incredible synergy with pyp. Being able to omit pipetools (like with math, etc) would make it even better.

Alternatively, a configurable list of default-imported modules in something like ~/.pyprc would be arguably even better.

Add if else clause in the code examples

It wasn't so straightforward to figure it out, but here's how it can be done:

last | pyp 'print(x, end="")' 'if "+" in x: hours = x.split()[-1]; hours = re.sub("[()]", "", hours).split("+")[0]' 'else: hours = 0' 'print(" ", hours)'

which gives the following code:

#!/usr/bin/env python3

import re
import sys
for x in sys.stdin:
    x = x.rstrip('\n')
    print(x, end='')
    if ('+' in x):
        hours = x.split()[(- 1)]
        hours = re.sub('[()]', '', hours).split('+')[0]
    else:
        hours = 0
    print(' ', hours)

Hope someone finds it useful ๐Ÿ˜‰

Roadmap

  1. Allow use of magic variables in config
    This would essentially allow user defined magic variables / give pyp more macro like abilities, e.g, you could have j = json.loads(stdin) in your config and then run j[0]["whatever"]. An implementation that mostly works is pretty straightforward. The only tricky part in getting something that works for most use cases is that we wouldn't want to define other need parts of the config after magic variables are defined (e.g., inside a loop). We should also allow config definitions to depend on config definitions that depend on magic variables.
  2. Allow fancy use of magic variables in config
    The suggestion in 1 could be more complicated. Since we're now introducing two locations for config definitions (previously only after missing imports, now also before processing input), we could allow config definitions to depend on --before definitions. This could unlock some cool possibilities.
    It would also be cool to allow custom magic variables that loop over some custom object (e.g., read in CSV and do something for each record). I'm not sure what the syntax for this should be, but since top-level loops aren't really useful in a config, we could maybe invent some special semantics for them. It would be a nice unification if the out of the box magic variables were essentially "default config".
  3. Allow pyp flags to be set in config
    Currently pyp only has --define-pypprint, but we could allow that to be configured in pyp's config. This should be very straightforward.
  4. (not currently merged) Transparently optimise lines to a generator, if possible
    Currently lines is always a list. It's impossible to do this right in general, but one way that could work well is to optimise if we detect it's only mentioned once and that mention is either in a comprehension, loop or the second argument to map.
    Possible implementations were investigated in #23 and #25. The version in #23 is probably better opted in to by using the new user defined magic variables feature implemented in 1; I didn't really see wins from #25 that justified the change. Willing to reconsider if this is an actual user painpoint.
  5. Improve support for conditional definitions in config.
    We have some basic support, but it's broken. I have some patches that fix things, but complicate the code in ways that I'm not sure I want to commit to.
    (edit: conditional definitions are no longer fairly broken and are instead just sometimes surprising in a bad way)
  6. Improve find_names to take scopes into account.
    This could help simplify config parsing, especially regarding the above point, if we broaden our return value to separate out maybe defined variables. Two notes, a) currently we only use self.defined in get_valid_name, which we only use in one place, so that's the only thing defined variables definitely needs to support (i.e., make sure output doesn't end up in a weird scope), and b) we should make sure we're optimistic about user code definitions and pessimistic about config definitions, so we don't weirdly break things.
    We now have scope support in name finding, which has allowed unification with the config parsing code that used to be.
    Things that can be further improved: a) conditional definitions (somewhat done), b) scoping semantics for comprehensions (done), late binding, nonlocal / global

I am open to contributions (maybe other than for 1 and 2), but please ask me first. Also fair warning, for this project, I will likely be a little picky / may refactor completely fine contributions as I see fit.

Make pyp a library that can be used by other tools like VisiData

I think pyp is a very cool tool. I don't actually use it, but I'm impressed by what you've created, and there is a way I would use this code more.

It would be useful if someone created a VisiData plugin that used pyp as the way to evaluate values. Visidata has commands that allow the user to enter Python expressions, which are evaluated to add/modify/select rows of values. If you want to use one of the modules that isn't already imported, you need to manually add the import either via a command or your visidatarc file. For example there is the addcol-expr command:

I think it would be useful if pyp could be used to evaluate these expressions, adding the magic that pyp provides.

I had made a Visidata request, but there is no interest yet from the developers, but I think the users would find it useful:

I don't really think that a Visidata plugin would necessarily make sense for you to create. But I think it would be much easier if pyp could be used as a module in other tools, and used with in a REPL or eval loop like Visisdata does.

Support standard import aliases

In Python there are several standard import aliases.
E.g. import numpy as np, import networkx as nx, import tensorflow as tf, and from matplotlib import pyplot as plt.
I think it would be a great addition to pyp to support these import aliases. This will make using pyp for e.g. numpy processing more pleasant.

To be concrete, currently pyp 'numpy.add(1, 2)' works but pyp 'np.add(1, 2)' does not, and I am suggesting adding support for the latter.

Ability to use pypy?

Firstly thank you for this fantastic project.

I was wondering if pyp was configurable so that I could use pypy instead of the default python?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.