# ast-match
A library to facilitate easier manipulation of Python AST (abstract syntax tree) objects.
This library allows you to write the syntax in an intuitive way, instead of having to write the names of the internal classes in `ast` module.
For manipulating the internal structure, it's recommended to read through https://docs.python.org/3/library/ast.html to know what kind of nodes may appear in an AST.
It's recommended to read through these at least once (as well as linked documentations at the bottom of the page) at least once,
it will save you lots of time later on.
### Limitations
This module does not allow you to manipulate the tree in *all possible* ways. For that you still need to tinker with the AST itself.
### Related packages
https://greentreesnakes.readthedocs.io/en/latest/examples.html#real-projects
### Usage
Import everything (apart from testing purpose it's recommended to avoid `import *`):
```python
>>> from ast_match import *
>>> from pprint import pprint
```
First, note that Python distinguishes between statement and expression, so you need to specify the type explicitly:
```python
>>> expr("a=1")
Traceback (most recent call last):
...
AssertionError
>>> pp(stmt("a=1"))
<ast.AST: a = 1>
```
Here the `pp` function used to "pretty-print" the resulting `ast.AST` object. If you use IPython you may want to refer to the section below for automatic pretty-printing.
The API somewhat resemble `re` module API:
<table>
<tr>
<th>
`re` module
</th>
<th>
`ast_match` module
</th>
</tr>
<tr>
<td>
```python
>>> import re
>>> pattern=re.compile("(?P<last>.*)-1")
>>> match=pattern.fullmatch("7*8-1")
>>> match.groupdict()
{'last': '7*8'}
```
</td>
<td>
```python
>>> from ast_match import *
>>> pattern=compile(expr("_last-1"))
>>> match=pattern.fullmatch(expr("7*8-1"))
>>> match
Match{'last': <ast.AST: 7 * 8>}
```
</td>
</tr>
<tr>
<td>
```python
>>> pattern=re.compile(r"(?P<x>\d+)\*(?P<y>\d+)")
>>> pattern.sub(r"\g<y>*\g<x>", "1*2+3*4")
'2*1+4*3'
```
</td>
<td>
```python
>>> pattern=compile(expr("_x * _y"))
>>> pp(pattern.sub(repl(expr("_y*_x")), expr("1*2+3*4")))
<ast.AST: 2 * 1 + 4 * 3>
```
</td>
</tr>
<tr>
<td>
```python
>>> pattern=re.compile(r"(?P<a>\d+)\*(?P<b>\d+)")
>>> pprint([*pattern.finditer("1*2+3*4")])
[<re.Match object; span=(0, 3), match='1*2'>,
<re.Match object; span=(4, 7), match='3*4'>]
```
</td>
<td>
```python
>>> pattern=compile(expr("_a*_b"))
>>> pprint([*pattern.finditer(expr("1*2+3*4"))])
[Match{'a': <ast.AST: 1>, 'b': <ast.AST: 2>},
Match{'a': <ast.AST: 3>, 'b': <ast.AST: 4>}]
```
</td>
</tr>
</table>
### Note for Vim users
The code inside strings may not be syntax-highlighted as Python code.
To fix, consider adding the following to `.vim/after/syntax/python.vim`:
```vim
syn region pythonSpecialInclude1
\ start=+\(expr\|stmt\)(r\?\z(['"]\)+ end=+\z1)+ keepend
\ contains=pythonSpecialIncludeInner1
syn region pythonSpecialIncludeInner1
\ start=+\z(['"]\)\zs+ end=+\ze\z1+ keepend
\ contained contains=TOP
```
You may want to test on some Python code as follows (the part inside `expr` should be highlighted as Python code instead of string)
```python
expr("lambda x: 1")
expr(r"lambda x: 1")
expr("""
for i in range(5):
pass
""")
expr(r"""
for i in range(5):
pass
""")
expr('lambda x: 1')
stmt('for i in range(5): pass')
```
For functions other than `expr` or `stmt` it should still be highlighted as string:
```python
f('for i in range(5): pass')
f(r'for i in range(5): pass')
```
### Note for IPython users
For usage in IPython, the default display of `ast`-module objects is not very nice:
```python
In [19]: ast.parse('for i in range(10): print(i, i+1)')
Out[19]: <ast.Module at 0x7fc4b17d6110>
```
So it's recommended you use the following.
```python
formatter=get_ipython().display_formatter.formatters["text/plain"]
formatter.for_type(ast.AST, lambda o, p, cycle: p.text(ast.dump(o, indent=2)))
#formatter.for_type(ast.AST, lambda o, p, cycle: p.text(prettyrepr(o))) # alternative, prettier but does not show the internal
#formatter.pop(ast.AST) # revert
```
Then the display will be more readable:
```python
In [21]: ast.parse('for i in range(10): print(i, i+1)')
Out[21]:
Module(
body=[
For(
target=Name(id='i', ctx=Store()),
iter=Call(
func=Name(id='range', ctx=Load()),
args=[
Constant(value=10)],
keywords=[]),
body=[
Expr(
value=Call(
func=Name(id='print', ctx=Load()),
args=[
Name(id='i', ctx=Load()),
BinOp(
left=Name(id='i', ctx=Load()),
op=Add(),
right=Constant(value=1))],
keywords=[]))],
orelse=[])],
type_ignores=[])
```
It may also be desirable to put the code into `.ipython/profile_default/startup/` or similar so that it's run automatically when IPython starts.
Raw data
{
"_id": null,
"home_page": "https://github.com/user202729/ast-match",
"name": "ast-match",
"maintainer": "",
"docs_url": null,
"requires_python": "",
"maintainer_email": "",
"keywords": "",
"author": "user202729",
"author_email": "",
"download_url": "https://files.pythonhosted.org/packages/5e/1d/670342fbfa870724dde610e94390e9f7cf10f4b04e64242a682ef9608ce1/ast-match-0.1.0.tar.gz",
"platform": null,
"description": "# ast-match\n\nA library to facilitate easier manipulation of Python AST (abstract syntax tree) objects.\n\nThis library allows you to write the syntax in an intuitive way, instead of having to write the names of the internal classes in `ast` module.\n\nFor manipulating the internal structure, it's recommended to read through https://docs.python.org/3/library/ast.html to know what kind of nodes may appear in an AST.\n\nIt's recommended to read through these at least once (as well as linked documentations at the bottom of the page) at least once,\nit will save you lots of time later on.\n\n### Limitations\n\nThis module does not allow you to manipulate the tree in *all possible* ways. For that you still need to tinker with the AST itself.\n\n### Related packages\n\nhttps://greentreesnakes.readthedocs.io/en/latest/examples.html#real-projects\n\n### Usage\n\nImport everything (apart from testing purpose it's recommended to avoid `import *`):\n\n```python\n>>> from ast_match import *\n>>> from pprint import pprint\n\n```\n\nFirst, note that Python distinguishes between statement and expression, so you need to specify the type explicitly:\n\n```python\n>>> expr(\"a=1\")\nTraceback (most recent call last):\n ...\nAssertionError\n\n>>> pp(stmt(\"a=1\"))\n<ast.AST: a = 1>\n\n```\n\nHere the `pp` function used to \"pretty-print\" the resulting `ast.AST` object. If you use IPython you may want to refer to the section below for automatic pretty-printing.\n\nThe API somewhat resemble `re` module API:\n\n<table>\n<tr>\n<th>\n\n`re` module\n\n</th>\n<th>\n\n`ast_match` module\n\n</th>\n</tr>\n<tr>\n<td>\n\n```python\n>>> import re\n>>> pattern=re.compile(\"(?P<last>.*)-1\")\n>>> match=pattern.fullmatch(\"7*8-1\")\n>>> match.groupdict()\n{'last': '7*8'}\n\n```\n\n</td>\n<td> \n\n```python\n>>> from ast_match import *\n>>> pattern=compile(expr(\"_last-1\"))\n>>> match=pattern.fullmatch(expr(\"7*8-1\"))\n>>> match\nMatch{'last': <ast.AST: 7 * 8>}\n\n```\n\n</td>\n</tr>\n<tr>\n<td>\n\n```python\n>>> pattern=re.compile(r\"(?P<x>\\d+)\\*(?P<y>\\d+)\")\n>>> pattern.sub(r\"\\g<y>*\\g<x>\", \"1*2+3*4\")\n'2*1+4*3'\n\n```\n\n</td>\n<td> \n\n```python\n>>> pattern=compile(expr(\"_x * _y\"))\n>>> pp(pattern.sub(repl(expr(\"_y*_x\")), expr(\"1*2+3*4\")))\n<ast.AST: 2 * 1 + 4 * 3>\n\n```\n\n</td>\n</tr>\n<tr>\n<td>\n\n```python\n>>> pattern=re.compile(r\"(?P<a>\\d+)\\*(?P<b>\\d+)\")\n>>> pprint([*pattern.finditer(\"1*2+3*4\")])\n[<re.Match object; span=(0, 3), match='1*2'>,\n <re.Match object; span=(4, 7), match='3*4'>]\n\n```\n\n</td>\n<td> \n\n```python\n>>> pattern=compile(expr(\"_a*_b\"))\n>>> pprint([*pattern.finditer(expr(\"1*2+3*4\"))])\n[Match{'a': <ast.AST: 1>, 'b': <ast.AST: 2>},\n Match{'a': <ast.AST: 3>, 'b': <ast.AST: 4>}]\n\n```\n\n</td>\n</tr>\n</table>\n\n### Note for Vim users\n\nThe code inside strings may not be syntax-highlighted as Python code.\n\nTo fix, consider adding the following to `.vim/after/syntax/python.vim`:\n\n```vim\nsyn region pythonSpecialInclude1\n\t\t\t\\ start=+\\(expr\\|stmt\\)(r\\?\\z(['\"]\\)+ end=+\\z1)+ keepend\n\t\t\t\\ contains=pythonSpecialIncludeInner1\n\nsyn region pythonSpecialIncludeInner1\n\t\t\t\\ start=+\\z(['\"]\\)\\zs+ end=+\\ze\\z1+ keepend\n\t\t\t\\ contained contains=TOP\n```\n\nYou may want to test on some Python code as follows (the part inside `expr` should be highlighted as Python code instead of string)\n\n```python\nexpr(\"lambda x: 1\")\nexpr(r\"lambda x: 1\")\nexpr(\"\"\"\nfor i in range(5):\n\tpass\n\"\"\")\nexpr(r\"\"\"\nfor i in range(5):\n\tpass\n\"\"\")\nexpr('lambda x: 1')\nstmt('for i in range(5): pass')\n```\n\nFor functions other than `expr` or `stmt` it should still be highlighted as string:\n\n```python\nf('for i in range(5): pass')\nf(r'for i in range(5): pass')\n```\n\n\n### Note for IPython users\n\nFor usage in IPython, the default display of `ast`-module objects is not very nice:\n\n```python\nIn [19]: ast.parse('for i in range(10): print(i, i+1)')\nOut[19]: <ast.Module at 0x7fc4b17d6110>\n```\n\nSo it's recommended you use the following.\n\n```python\nformatter=get_ipython().display_formatter.formatters[\"text/plain\"]\nformatter.for_type(ast.AST, lambda o, p, cycle: p.text(ast.dump(o, indent=2)))\n#formatter.for_type(ast.AST, lambda o, p, cycle: p.text(prettyrepr(o))) # alternative, prettier but does not show the internal\n#formatter.pop(ast.AST) # revert\n```\n\nThen the display will be more readable:\n\n```python\nIn [21]: ast.parse('for i in range(10): print(i, i+1)')\nOut[21]: \nModule(\n body=[\n For(\n target=Name(id='i', ctx=Store()),\n iter=Call(\n func=Name(id='range', ctx=Load()),\n args=[\n Constant(value=10)],\n keywords=[]),\n body=[\n Expr(\n value=Call(\n func=Name(id='print', ctx=Load()),\n args=[\n Name(id='i', ctx=Load()),\n BinOp(\n left=Name(id='i', ctx=Load()),\n op=Add(),\n right=Constant(value=1))],\n keywords=[]))],\n orelse=[])],\n type_ignores=[])\n```\n\nIt may also be desirable to put the code into `.ipython/profile_default/startup/` or similar so that it's run automatically when IPython starts.\n",
"bugtrack_url": null,
"license": "GNU General Public License v3 or later (GPLv3+)",
"summary": "A library to facilitate easier manipulation of Python AST (abstract syntax tree) objects.",
"version": "0.1.0",
"project_urls": {
"Homepage": "https://github.com/user202729/ast-match"
},
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "36630860513f44ff2259c635aa287e4084a81c411a45d9149b49f036f942cce1",
"md5": "4c1531d03a18fd262e5e28845bd1185c",
"sha256": "936145aff8b1ecf84e8fe0a82760c1e64fbe4203bd7637a682b30179ffab7aec"
},
"downloads": -1,
"filename": "ast_match-0.1.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "4c1531d03a18fd262e5e28845bd1185c",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": null,
"size": 21967,
"upload_time": "2023-06-04T15:27:53",
"upload_time_iso_8601": "2023-06-04T15:27:53.255103Z",
"url": "https://files.pythonhosted.org/packages/36/63/0860513f44ff2259c635aa287e4084a81c411a45d9149b49f036f942cce1/ast_match-0.1.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "5e1d670342fbfa870724dde610e94390e9f7cf10f4b04e64242a682ef9608ce1",
"md5": "e61edeffb943a5dbc9f015bd1602b9eb",
"sha256": "98e532b7ed7d70f32ea0a9104f19eb8a140049419595a60ff994755ebc079ed5"
},
"downloads": -1,
"filename": "ast-match-0.1.0.tar.gz",
"has_sig": false,
"md5_digest": "e61edeffb943a5dbc9f015bd1602b9eb",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 22849,
"upload_time": "2023-06-04T15:27:55",
"upload_time_iso_8601": "2023-06-04T15:27:55.411468Z",
"url": "https://files.pythonhosted.org/packages/5e/1d/670342fbfa870724dde610e94390e9f7cf10f4b04e64242a682ef9608ce1/ast-match-0.1.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-06-04 15:27:55",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "user202729",
"github_project": "ast-match",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "ast-match"
}