Skip to content

guilatrova/ast_selector

Repository files navigation

Query AST elements by using CSS Selector-like syntax

Actions Status PyPI Semantic Release GitHub Downloads Code style: black try/except style: tryceratops Follow guilatrova

"Query AST elements 🌲 by using CSS Selector-like 💅 syntax."

Summary

Installation and usage

Installation

pip install ast-selector

Quick Start

Query all functions that raises at least an exception:

from ast_selector import AstSelector

tree = load_python_code_as_ast_tree()
query = "FunctionDef Raise $FunctionDef"

functions_raising_exceptions = AstSelector(query, tree).all()

Examples

Query by AST type

Simply use the AST type. Note it should have the proper casing.

AstSelector("FunctionDef", tree).all() # Any Ast.FunctionDef
AstSelector("Raise", tree).all() # Any ast.Raise
AstSelector("Expr", tree).all() # Any ast.Expr

Filter AST by property type

You can filter property types by writing like: [Prop is Type].

Condition: Any ast.Expr that contains .value prop and that prop is an instance of ast.Call

Result: List of ast.Expr that fulfills the condition.

AstSelector("Expr[value is Call]", tree).all()

Drill to AST property

You can navigate as you filter the elements you want by using .prop.

Condition: Any ast.Expr that contains .value prop and that prop is an instance of ast.Call, take .value.

Result: List of ast.Call that fulfills the condition.

AstSelector("Expr[value is Call].value", tree).all()

Filter AST by property value

You can filter property values by writing like: [Prop = Value].

Condition: Any ast.FunctionDef, take returns as long as it contains id equals to int.

Result: List of ast.Name that fulfills the condition.

AstSelector("FunctionDef.returns[id=int]", tree).all()

Filter AST by multiple conditions

You can keep appending [Cond1][Cond2][Cond3] as you wish:

Condition: Any ast.Raise that has exc of type ast.Call AND that has cause as None.

Result: List of ast.Raise that fulfills the condition.

AstSelector("Raise[exc is Call][cause is None]", tree).all()

Drill down but take previous reference

You can keep drilling down, but take a previous value as your result by using $[Placeholder] syntax:

Condition: Any ast.FunctionDef, take returns as long as it has id equals to int, then take the original FunctionDef.

Result: List of ast.FunctionDef that fulfills the condition.

AstSelector("FunctionDef.returns[id=int] $FunctionDef", tree).all()

Filter and drill through references

You can keep filtering and drilling references as you would normally.

Drill $Expr and take args as result:

AstSelector("Expr[value is Call].value[func is Attribute].func[attr = exception] $Expr.value.args", tree).all()

Drill $FunctionDef (redundant) and filter functions named main_int as result:

AstSelector("FunctionDef.returns[id=int] $FunctionDef[name=main_int]", tree).all()

Count nodes

AstSelector(query, tree).count()

Take first node only

# Raises exception if None
AstSelector(query, tree).first()

Check if node exists

AstSelector(query, tree).exists()

Contributing

Thank you for considering making AST Selector better for everyone!

Refer to Contributing docs.

Change log

See CHANGELOG.

License

MIT

Credits

It's extremely hard to keep hacking on open source like that while keeping a full-time job. I thank God from the bottom of my heart for both the inspiration and the energy.