asttoolkit
A toolkit for manipulating and transforming Python Abstract Syntax Trees (AST).
Science Score: 26.0%
This score indicates how likely this project is to be science-related based on various indicators:
-
○CITATION.cff file
-
✓codemeta.json file
Found codemeta.json file -
✓.zenodo.json file
Found .zenodo.json file -
○DOI references
-
○Academic publication links
-
○Academic email domains
-
○Institutional organization owner
-
○JOSS paper metadata
-
○Scientific vocabulary similarity
Low similarity (10.0%) to scientific vocabulary
Keywords
Repository
A toolkit for manipulating and transforming Python Abstract Syntax Trees (AST).
Basic Info
Statistics
- Stars: 2
- Watchers: 2
- Forks: 0
- Open Issues: 8
- Releases: 36
Topics
Metadata Files
README.md
astToolkit
Do You Want This Package?
astToolkit provides a powerfully composable system for manipulating Python Abstract Syntax Trees. Use it when:
- You need to programmatically analyze, transform, or generate Python code.
- You want type-safe operations that help prevent AST manipulation errors.
- You prefer working with a consistent, fluent API rather than raw AST nodes.
- You desire the ability to compose complex AST transformations from simple, reusable parts.
Don't use it for simple text-based code manipulation—use regex or string operations instead.
Architecture
astToolkit implements a layered architecture designed for composability and type safety:
Core "Atomic" Classes - The foundation of the system:
Be: Type guards that returnTypeIs[ast.NodeType]for safe type narrowing.DOT: Read-only accessors that retrieve node attributes with proper typing.Grab: Transformation functions that modify specific attributes while preserving node structure.Make: Factory methods that create properly configured AST nodes with consistent interfaces.
Traversal and Transformation - Built on the visitor pattern:
NodeTourist: Extendsast.NodeVisitorto extract information from nodes that match the antecedent (sometimes called "predicate").NodeChanger: Extendsast.NodeTransformerto selectively transform nodes that match antecedents.
Composable APIs - The antecedent-action pattern:
ClassIsAndAttribute: A powerful antecedent constructor: it confirms the class type ofnode, then applies whatever condition check you want to an attribute ofnode. As long as you listen to your type checker, you won't accidentally pair an attribute to a class that doesn't have that attribute. Furthermore, your IDE's hover type hints will tell you which classes are valid for the attribute you are checking.IfThis: Generates predicate functions that identify nodes based on structure, content, or relationships.Then: Creates action functions that specify what to do with matched nodes (extract, replace, modify).
Higher-level Tools - Built from the core components:
_toolkitAST.py: Functions for common operations like extracting function definitions or importing modules.transformationTools.py: Advanced utilities like function inlining and code generation.IngredientsFunctionandIngredientsModule: Containers for holding AST components and their dependencies.
Type System - Over 120 specialized types for AST components:
- Custom type annotations for AST node attributes.
- Union types that accurately model Python's AST structure.
- Type guards that enable static type checkers to understand dynamic type narrowing.
Easy-to-use Tools for Annoying Tasks
- extractClassDef
- extractFunctionDef
- parseLogicalPath2astModule
- parsePathFilename2astModule
Easy-to-use Tools for More Complicated Tasks
- removeUnusedParameters
- write_astModule
The toolFactory
Hypothetically, you could customize every aspect of the classes Be, DOT, GRAB, and Make and more than 100 TypeAlias in the toolFactory directory/package.
Usage
astToolkit provides a comprehensive set of tools for AST manipulation, organized in a layered architecture for composability and type safety. The following examples demonstrate how to use these tools in real-world scenarios.
Core Pattern: Layered AST Manipulation
The astToolkit approach follows a layered pattern:
- Create/Access/Check - Use
Make,DOT, andBeto work with AST nodes - Locate - Use
IfThispredicates to identify nodes of interest - Transform - Use
NodeChangerandThento modify nodes - Extract - Use
NodeTouristto collect information from the AST
Example 1: Extracting Information from AST
This example shows how to extract information from a function's parameters:
```python from astToolkit import Be, DOT, NodeTourist, Then import ast
Parse some Python code into an AST
code = """ def process_data(state: DataClass): result = state.value * 2 return result """ tree = ast.parse(code)
Extract the parameter name from the function
functiondef = tree.body[0] paramname = NodeTourist( Be.arg, # Look for function parameters Then.extractIt(DOT.arg) # Extract the parameter name ).captureLastMatch(function_def)
print(f"Function parameter name: {param_name}") # Outputs: state
Extract the parameter's type annotation
annotation = NodeTourist( Be.arg, # Look for function parameters Then.extractIt(DOT.annotation) # Extract the type annotation ).captureLastMatch(function_def)
if annotation and Be.Name(annotation): annotationname = DOT.id(annotation) print(f"Parameter type: {annotationname}") # Outputs: DataClass ```
Example 2: Transforming AST Nodes
This example demonstrates how to transform a specific node in the AST:
```python from astToolkit import Be, IfThis, Make, NodeChanger, Then import ast
Parse some Python code into an AST
code = """ def double(x): return x * 2 """ tree = ast.parse(code)
Define a predicate to find the multiplication operation
find_mult = Be.Mult
Define a transformation to change multiplication to addition
changetoadd = Then.replaceWith(ast.Add())
Apply the transformation
NodeChanger(findmult, changeto_add).visit(tree)
Now the code is equivalent to:
def double(x):
return x + x
print(ast.unparse(tree)) ```
Example 3: Advanced AST Transformation with Custom Predicates
This example shows a more complex transformation inspired by the mapFolding package:
```python from astToolkit import str, Be, DOT, Grab, IfThis as astToolkit_IfThis, Make, NodeChanger, Then import ast
Define custom predicates by extending IfThis
class IfThis(astToolkit_IfThis): @staticmethod def isAttributeNamespaceIdentifierGreaterThan0( namespace: str, identifier: str ) -> Callable[[ast.AST], TypeIs[ast.Compare] | bool]:
return lambda node: (
Be.Compare(node)
and IfThis.isAttributeNamespaceIdentifier(namespace, identifier)(DOT.left(node))
and Be.Gt(node.ops[0])
and IfThis.isConstant_value(0)(node.comparators[0]))
@staticmethod def isWhileAttributeNamespaceIdentifierGreaterThan0( namespace: str, identifier: str ) -> Callable[[ast.AST], TypeIs[ast.While] | bool]:
return lambda node: (
Be.While(node)
and IfThis.isAttributeNamespaceIdentifierGreaterThan0(namespace, identifier)(DOT.test(node)))
Parse some code
code = """ while claude.counter > 0: result += counter counter -= 1 """ tree = ast.parse(code)
Find the while loop with our custom predicate
findwhileloop = IfThis.isWhileAttributeNamespaceIdentifierGreaterThan0("claude", "counter")
Replace counter > 0 with counter > 1
change_condition = Grab.testAttribute( Grab.comparatorsAttribute( Then.replaceWith([Make.Constant(1)]) ) )
Apply the transformation
NodeChanger(findwhileloop, change_condition).visit(tree)
print(ast.unparse(tree))
Now outputs:
while counter > 1:
result += counter
counter -= 1
```
Example 4: Building Code Generation Systems
The following example shows how to set up a foundation for code generation and transformation systems:
```python from astToolkit import ( Be, DOT, IngredientsFunction, IngredientsModule, LedgerOfImports, Make, NodeTourist, Then, parseLogicalPath2astModule, write_astModule ) import ast
Parse a module to extract a function
moduleast = parseLogicalPath2astModule("mypackage.source_module")
Extract a function and track its imports
functionname = "targetfunction" functiondef = NodeTourist( IfThis.isFunctionDefIdentifier(functionname), Then.extractIt ).captureLastMatch(module_ast)
if functiondef: # Create a self-contained function with tracked imports ingredients = IngredientsFunction( functiondef, LedgerOfImports(module_ast) )
# Rename the function
ingredients.astFunctionDef.name = "optimized_" + function_name
# Add a decorator
decorator = Make.Call(
Make.Name("jit"),
[],
[Make.keyword("cache", Make.Constant(True))]
)
ingredients.astFunctionDef.decorator_list.append(decorator)
# Add required import
ingredients.imports.addImportFrom_asStr("numba", "jit")
# Create a module and write it to disk
module = IngredientsModule(ingredients)
write_astModule(module, "path/to/generated_code.py", "my_package")
```
Example 5: Extending Core Classes
To create specialized patterns for your codebase, extend the core classes:
```python from astToolkit import str, Be, IfThis as astToolkit_IfThis from collections.abc import Callable from typing import TypeIs import ast
class IfThis(astToolkitIfThis): @staticmethod def isAttributeNamespaceIdentifierGreaterThan0( namespace: str, identifier: str ) -> Callable[[ast.AST], TypeIs[ast.Compare] | bool]: """Find comparisons like 'state.counter > 0'""" return lambda node: ( Be.Compare(node) and IfThis.isAttributeNamespaceIdentifier(namespace, identifier)(node.left) and Be.Gt(node.ops[0]) and IfThis.isConstantvalue(0)(node.comparators[0]) )
@staticmethod
def isWhileAttributeNamespaceIdentifierGreaterThan0(
namespace: str,
identifier: str
) -> Callable[[ast.AST], TypeIs[ast.While] | bool]:
"""Find while loops like 'while state.counter > 0:'"""
return lambda node: (
Be.While(node)
and IfThis.isAttributeNamespaceIdentifierGreaterThan0(namespace, identifier)(node.test)
)
```
Real-world Application: Code Transformation Assembly-line
In the mapFolding project, astToolkit is used to build a complete transformation assembly-line that:
- Extracts algorithms from source modules
- Transforms them into optimized variants
- Applies numerical computing decorators
- Handles dataclass management and type systems
- Generates complete modules with proper imports
This pattern enables the separation of readable algorithm implementations from their high-performance variants while ensuring they remain functionally equivalent.
For deeper examples, see the mapFolding/someAssemblyRequired directory.
Installation
bash
pip install astToolkit
My Recovery
How to code
Coding One Step at a Time:
- WRITE CODE.
- Don't write stupid code that's hard to revise.
- Write good code.
- When revising, write better code.
Owner
- Name: Hunter Hogan
- Login: hunterhogan
- Kind: user
- Website: www.hunterthinks.com
- Repositories: 1
- Profile: https://github.com/hunterhogan
GitHub Events
Total
- Create event: 23
- Issues event: 4
- Release event: 24
- Watch event: 2
- Push event: 161
Last Year
- Create event: 23
- Issues event: 4
- Release event: 24
- Watch event: 2
- Push event: 161
Packages
- Total packages: 1
-
Total downloads:
- pypi 440 last-month
- Total dependent packages: 0
- Total dependent repositories: 0
- Total versions: 36
- Total maintainers: 1
pypi.org: asttoolkit
A powerfully composable, type-safe toolkit for Python abstract syntax tree (AST) manipulation, analysis, transformation, and code generation with a layered architecture designed for building sophisticated code processing assembly-lines.
- Homepage: https://github.com/hunterhogan/astToolkit
- Documentation: https://asttoolkit.readthedocs.io/
- License: CC-BY-NC-4.0
-
Latest release: 0.7.1
published 7 months ago
Rankings
Maintainers (1)
Dependencies
- actions/checkout v4 composite
- actions/setup-python v5 composite
- softprops/action-gh-release c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda composite
- actions/checkout v4 composite
- actions/download-artifact v4 composite
- actions/setup-python v5 composite
- actions/upload-artifact v4 composite
- pypa/gh-action-pypi-publish 76f52bc884231f62b9a034ebfe128415bbaabdfc composite
- actions/checkout v4 composite
- actions/setup-python v5 composite
- actions/checkout v4 composite
- actions/setup-python v5 composite
- Z0Z_tools *
- autoflake *
- python_minifier *
- tomli *
- PyGitHub *
- Z0Z_tools *
- mypy *
- pytest *
- pytest-cov *
- pytest-xdist *
- pyupgrade *
- tomli *
- typeshed_client *