astlab is a Python library that provides an intuitive API for building and manipulating Abstract Syntax Trees (ASTs) to generate Python code. With astlab, you can easily construct Python modules, classes, functions, type aliases, and generics using a fluent API — then render them into valid, executable Python code.
- Easy AST construction: Build Python code using a fluent, structured API.
- Code generation: Generate fully valid, formatted Python source without templates.
- Supports nested scopes & auto imports: Create classes, methods, and nested modules with automatic import resolution.
- Type system support: Define and use type variables, generic classes, and type aliases compatible with Python 3.9–3.14 syntax.
- Highly customizable: Extend the builder model for any Python AST use case.
pip install astlabimport ast
import astlab
with astlab.module("foo") as foo:
with foo.class_def("Bar").dataclass() as bar:
bar.field_def("spam", int)
print(foo.render())
print(ast.dump(foo.build(), indent=4))import builtins
import dataclasses
@dataclasses.dataclass()
class Bar:
spam: builtins.intimport astlab
with astlab.module("foo") as foo:
with foo.class_def("Bar") as bar:
with bar.method_def("do_stuff").arg("spam", int).returns(str) as stuff:
stuff.assign_stmt("result", stuff.call(str).arg(stuff.attr("spam")))
stuff.return_stmt(stuff.attr("result"))
print(foo.render())import builtins
class Bar:
def do_stuff(self, spam: builtins.int) -> builtins.str:
result = builtins.str(spam)
return resultimport astlab
with astlab.package("main") as main:
with main.module("foo") as foo:
with foo.class_def("Bar") as bar:
pass
with main.module("spam") as spam:
with spam.class_def("Eggs").inherits(bar) as eggs:
with eggs.method_def("do_stuff").returns(bar.ref().optional()) as stuff:
pass
print(spam.render())import main.foo
import typing
class Eggs(main.foo.Bar):
def do_stuff(self) -> typing.Optional[main.foo.Bar]:
passastlab supports defining type variables and generic classes.
Both the legacy (typing.TypeVar) and modern (class Node[T: int]) syntaxes are supported depending on Python version.
import astlab
with astlab.module("generic") as mod:
with mod.class_def("Node") as node, node.type_var("T").lower(int) as T:
node.field_def("value", T)
node.field_def("parent", node.ref().type_params(type_var).optional(), mod.none())
print(mod.render())import builtins
import typing
T = typing.TypeVar('T', bound=builtins.int)
class Node(typing.Generic[T]):
value: T
parent: typing.Optional['Node[T]'] = Noneimport builtins
import typing
class Node[T: builtins.int]:
value: T
parent: typing.Optional['Node[T]'] = Noneimport builtins
import typing
class Node[T: builtins.int]:
value: T
parent: typing.Optional[Node[T]] = Noneastlab allows declarative creation of type aliases, including recursive and generic aliases.
It automatically emits valid syntax for both typing.TypeAlias (pre-3.12) and type X = Y (3.12+).
import astlab
from astlab.types import predef
with astlab.module("alias") as mod:
mod.type_alias("MyInt").assign(int)
with mod.type_alias("Json") as json_alias:
json_alias.assign(
json_alias.union_type(
None,
bool,
int,
float,
str,
mod.list_type(json_alias),
mod.dict_type(str, json_alias),
)
)
with (
mod.type_alias("Nested") as nested_alias,
nested_alias.type_var("T") as T,
):
nested_alias.assign(
nested_alias.union_type(
T
nested_alias.sequence_type(nested_alias.type_params(T)),
)
)import builtins
import typing
MyInt: typing.TypeAlias = builtins.int
Json: typing.TypeAlias = typing.Union[
None,
builtins.bool,
builtins.int,
builtins.float,
builtins.str,
builtins.list['Json'],
builtins.dict[builtins.str, 'Json'],
]
T = typing.TypeVar("T")
Nested: typing.TypeAlias = typing.Union[T, typing.Sequence['Nested[T]']]import builtins
import typing
type MyInt = builtins.int
type Json = typing.Union[
None,
builtins.bool,
builtins.int,
builtins.float,
builtins.str,
builtins.list['Json'],
builtins.dict[builtins.str, 'Json'],
]
type Nested[T] = typing.Union[T, typing.Sequence['Nested[T]']]import builtins
import typing
type MyInt = builtins.int
type Json = typing.Union[
None,
builtins.bool,
builtins.int,
builtins.float,
builtins.str,
builtins.list[Json],
builtins.dict[builtins.str, Json],
]
type Nested[T] = typing.Union[T, typing.Sequence[Nested[T]]]