# UnEval #
UnEval is a microlibrary for generating python-expressions.
If you ever need to use
[eval](https://docs.python.org/3/library/functions.html#eval),
write [macros](https://en.wikipedia.org/wiki/Macro)
or implement [domain specific languages](https://en.wikipedia.org/wiki/Domain-specific_language),
this library provides a better way to generate python expressions than using [strings](https://docs.python.org/3/library/stdtypes.html#str).
Strings can contain syntax errors, make it harder to deal with parentheses and aren't syntax highlighted.
Expressions look and act a lot like pythoncode, except that they aren't evaluated immediately.
## Installation ##
Make sure to [install pip](https://pip.pypa.io/en/stable/installation/) then run:
```sh
pip install uneval
```
## Usage ##
Firstly, the building blocks can be used to generate expressions.
Secondly, these expressions can be converted.
### Examples ###
```python
import ast
from uneval import quote, to_ast, to_bytecode
# Build expressions
x, y = quote.x, quote.y # Shortcut for quote("x"), quote("y")
z = x * x + y * y
# Convert
print(z) # x * x + y * y
print(ast.dump(to_ast(z))) # BinOp(left=BinOp(left=Name(id='x', ctx=Load()), op=Mult(), right=Name(id='x', ctx=Load())), op=Add(), right=BinOp(left=Name(id='y', ctx=Load()), op=Mult(), right=Name(id='y', ctx=Load())))
print(eval(to_bytecode(z), {"x": 3, "y": 4})) # 25
```
This can be used when working in [pandas](https://pandas.pydata.org/) and you want to use [eval](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.eval.html#pandas.DataFrame.eval) or [query](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.query.html#pandas.DataFrame.query):
```python
from uneval import quote as q
# No syntax highlighting. Syntax checkers won't catch errors.
df.eval("bmi = mass / height**2")
# With syntax highlighting. Syntax checkers can catch errors here.
df.eval(f"bmi = {q.mass / q.height**2}")
```
Expressions can also be converted to [λ](https://docs.python.org/3/glossary.html#term-lambda)-functions:
```python
from uneval import F, quote
x, y = quote.x, quote.y
hello = F("Hello World!")
hello() # => "Hello World!"
plus1 = F.x(x + 1)
plus1(4) # => 5
multiply = F.xy(x * y)
multiply(5, 7) # => 35
```
### Building blocks ###
```python
from uneval import quote as q
```
| Factory | Example | Result |
|---------------|-----------------------------------------------------|------------------------------|
| `quote` | `q('a')` or `q.a` (shortcut) | `a` |
| `if_` | `if_(q.x >= 0, q.x, -q.x)` | `x if x >= 0 else -x` |
| `for_` | `for_(q.x**2, (q.x, q.range(5)))` | `(x**2 for x in range(5))` |
| `lambda_` | `lambda_([q.x], q.x * q.x)` | `lambda x: x * x` |
| `and_`, `or_` | `and_(q.x >= 10, q.x <= 15)` | `x >= 10 and x <= 15` |
| `not_`, `in_` | `not_(in_(q.x, {1, 2, 3}))` | `not x in {1, 2, 3}` |
| `fstr`, `fmt` | `fstr("sin(", q.a, ") is ", fmt(q.sin(q.a), ".3"))` | `f'sin({a}) is {sin(a):.3}'` |
### Converters ###
| Converter | Target | Remark |
|----------------|-------------|----------------------------|
| `str` | String | Convert to readable python |
| `to_ast` | AST-node | Convert to AST-node |
| `to_bytecode` | Code-object | Compile the expression |
| `F.parameters` | Function | Create a λ-function |
## Similar work ##
Libraries that implement something similar:
- [Macropy](https://github.com/lihaoyi/macropy) has [quasiquote](https://macropy3.readthedocs.io/en/latest/reference.html#quasiquote).
- [SymPy](https://www.sympy.org/en/index.html) - Symbolic manipulation, but its representation is different from Python.
- [Polars](https://docs.pola.rs/user-guide/expressions/) - Writing `col.x` creates something like this `Expression`-object.
Other:
- [Fixing lambda](https://stupidpythonideas.blogspot.com/2014/02/fixing-lambda.html) - A blog post about alternative lambda syntaxes.
- [Mini-lambda](https://smarie.github.io/python-mini-lambda/#see-also) - Packages to "fix" lambda.
- [Meta](https://srossross.github.io/Meta/html/) - A few utils to work on AST's.
- [latexify](https://github.com/google/latexify_py) and [pytexit](https://github.com/erwanp/pytexit) - Convert python to LaTeX.
- [numexpr](https://github.com/pydata/numexpr) and [aesara](https://github.com/aesara-devs/aesara) / [pytensor](https://github.com/pymc-devs/pytensor) - Fast evaluation of numerical expressions.
Useful references:
- [python macros use cases](https://stackoverflow.com/questions/764412/python-macros-use-cases) - Stack-overflow discussion.
- [Green tree snakes](https://greentreesnakes.readthedocs.io/en/latest/) - Unofficial documentation of AST nodes.
Raw data
{
"_id": null,
"home_page": null,
"name": "uneval",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": null,
"keywords": "expression, ast, dsl, fexpr, lambda, symbolic",
"author": null,
"author_email": "lverweijen <lauwerund@gmail.com>",
"download_url": null,
"platform": null,
"description": "# UnEval #\r\n\r\nUnEval is a microlibrary for generating python-expressions.\r\n\r\nIf you ever need to use\r\n[eval](https://docs.python.org/3/library/functions.html#eval),\r\nwrite [macros](https://en.wikipedia.org/wiki/Macro)\r\nor implement [domain specific languages](https://en.wikipedia.org/wiki/Domain-specific_language),\r\nthis library provides a better way to generate python expressions than using [strings](https://docs.python.org/3/library/stdtypes.html#str).\r\nStrings can contain syntax errors, make it harder to deal with parentheses and aren't syntax highlighted.\r\nExpressions look and act a lot like pythoncode, except that they aren't evaluated immediately.\r\n\r\n## Installation ##\r\n\r\nMake sure to [install pip](https://pip.pypa.io/en/stable/installation/) then run:\r\n```sh\r\npip install uneval\r\n```\r\n\r\n## Usage ##\r\n\r\nFirstly, the building blocks can be used to generate expressions.\r\nSecondly, these expressions can be converted.\r\n\r\n### Examples ###\r\n\r\n```python\r\nimport ast\r\nfrom uneval import quote, to_ast, to_bytecode\r\n\r\n# Build expressions\r\nx, y = quote.x, quote.y # Shortcut for quote(\"x\"), quote(\"y\")\r\nz = x * x + y * y\r\n\r\n# Convert\r\nprint(z) # x * x + y * y\r\nprint(ast.dump(to_ast(z))) # BinOp(left=BinOp(left=Name(id='x', ctx=Load()), op=Mult(), right=Name(id='x', ctx=Load())), op=Add(), right=BinOp(left=Name(id='y', ctx=Load()), op=Mult(), right=Name(id='y', ctx=Load())))\r\nprint(eval(to_bytecode(z), {\"x\": 3, \"y\": 4})) # 25\r\n```\r\n\r\nThis can be used when working in [pandas](https://pandas.pydata.org/) and you want to use [eval](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.eval.html#pandas.DataFrame.eval) or [query](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.query.html#pandas.DataFrame.query):\r\n\r\n```python\r\nfrom uneval import quote as q\r\n\r\n# No syntax highlighting. Syntax checkers won't catch errors.\r\ndf.eval(\"bmi = mass / height**2\")\r\n\r\n# With syntax highlighting. Syntax checkers can catch errors here.\r\ndf.eval(f\"bmi = {q.mass / q.height**2}\")\r\n```\r\n\r\nExpressions can also be converted to [\u03bb](https://docs.python.org/3/glossary.html#term-lambda)-functions:\r\n\r\n```python\r\nfrom uneval import F, quote\r\n\r\nx, y = quote.x, quote.y\r\n\r\nhello = F(\"Hello World!\")\r\nhello() # => \"Hello World!\"\r\n\r\nplus1 = F.x(x + 1)\r\nplus1(4) # => 5\r\n\r\nmultiply = F.xy(x * y)\r\nmultiply(5, 7) # => 35\r\n```\r\n\r\n### Building blocks ###\r\n\r\n```python\r\nfrom uneval import quote as q\r\n```\r\n\r\n| Factory | Example | Result |\r\n|---------------|-----------------------------------------------------|------------------------------|\r\n| `quote` | `q('a')` or `q.a` (shortcut) | `a` |\r\n| `if_` | `if_(q.x >= 0, q.x, -q.x)` | `x if x >= 0 else -x` |\r\n| `for_` | `for_(q.x**2, (q.x, q.range(5)))` | `(x**2 for x in range(5))` |\r\n| `lambda_` | `lambda_([q.x], q.x * q.x)` | `lambda x: x * x` |\r\n| `and_`, `or_` | `and_(q.x >= 10, q.x <= 15)` | `x >= 10 and x <= 15` |\r\n| `not_`, `in_` | `not_(in_(q.x, {1, 2, 3}))` | `not x in {1, 2, 3}` |\r\n| `fstr`, `fmt` | `fstr(\"sin(\", q.a, \") is \", fmt(q.sin(q.a), \".3\"))` | `f'sin({a}) is {sin(a):.3}'` |\r\n\r\n### Converters ###\r\n\r\n| Converter | Target | Remark |\r\n|----------------|-------------|----------------------------|\r\n| `str` | String | Convert to readable python |\r\n| `to_ast` | AST-node | Convert to AST-node |\r\n| `to_bytecode` | Code-object | Compile the expression |\r\n| `F.parameters` | Function | Create a \u03bb-function |\r\n\r\n## Similar work ##\r\n\r\nLibraries that implement something similar:\r\n- [Macropy](https://github.com/lihaoyi/macropy) has [quasiquote](https://macropy3.readthedocs.io/en/latest/reference.html#quasiquote).\r\n- [SymPy](https://www.sympy.org/en/index.html) - Symbolic manipulation, but its representation is different from Python.\r\n- [Polars](https://docs.pola.rs/user-guide/expressions/) - Writing `col.x` creates something like this `Expression`-object.\r\n\r\nOther:\r\n- [Fixing lambda](https://stupidpythonideas.blogspot.com/2014/02/fixing-lambda.html) - A blog post about alternative lambda syntaxes.\r\n- [Mini-lambda](https://smarie.github.io/python-mini-lambda/#see-also) - Packages to \"fix\" lambda.\r\n- [Meta](https://srossross.github.io/Meta/html/) - A few utils to work on AST's.\r\n- [latexify](https://github.com/google/latexify_py) and [pytexit](https://github.com/erwanp/pytexit) - Convert python to LaTeX.\r\n- [numexpr](https://github.com/pydata/numexpr) and [aesara](https://github.com/aesara-devs/aesara) / [pytensor](https://github.com/pymc-devs/pytensor) - Fast evaluation of numerical expressions.\r\n\r\nUseful references:\r\n- [python macros use cases](https://stackoverflow.com/questions/764412/python-macros-use-cases) - Stack-overflow discussion.\r\n- [Green tree snakes](https://greentreesnakes.readthedocs.io/en/latest/) - Unofficial documentation of AST nodes.\r\n",
"bugtrack_url": null,
"license": "Apache License 2.0",
"summary": "Library for generating python-expressions",
"version": "0.1.2",
"project_urls": {
"Changelog": "https://github.com/lverweijen/python-uneval/blob/main/changes.md",
"Homepage": "https://github.com/lverweijen/python-uneval",
"Issues": "https://github.com/lverweijen/python-uneval/issues",
"Pypi": "https://github.com/lverweijen/python-uneval",
"Repository": "https://github.com/lverweijen/python-uneval"
},
"split_keywords": [
"expression",
" ast",
" dsl",
" fexpr",
" lambda",
" symbolic"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "10199bec16f4f38d41c5f5edf6578064842dfea8ade6aaad0357bde0b0656918",
"md5": "5dedc033303094c6406bc35003789eee",
"sha256": "2b00b0a8b807e00194b00ee8ebc2f6f70373608ee76a7bbb19e422e74b9d9d03"
},
"downloads": -1,
"filename": "uneval-0.1.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "5dedc033303094c6406bc35003789eee",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 12389,
"upload_time": "2024-08-26T11:36:06",
"upload_time_iso_8601": "2024-08-26T11:36:06.080525Z",
"url": "https://files.pythonhosted.org/packages/10/19/9bec16f4f38d41c5f5edf6578064842dfea8ade6aaad0357bde0b0656918/uneval-0.1.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-08-26 11:36:06",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "lverweijen",
"github_project": "python-uneval",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "uneval"
}