Skip to content
/ astlab Public

A Python library for building and generating Abstract Syntax Trees (ASTs) with a simple, intuitive API.

License

Notifications You must be signed in to change notification settings

zerlok/astlab

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

58 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

astlab

Latest Version Python Supported Versions MyPy Strict Test Coverage Downloads GitHub stars

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.

Features

  • 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.

Installation

pip install astlab

Usage

Simple Example

import 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))

Output

import builtins
import dataclasses

@dataclasses.dataclass()
class Bar:
    spam: builtins.int

Function Definition & Call Example

import 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())

Output

import builtins

class Bar:

    def do_stuff(self, spam: builtins.int) -> builtins.str:
        result = builtins.str(spam)
        return result

Type Reference Example

import 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())

Output

import main.foo
import typing

class Eggs(main.foo.Bar):

    def do_stuff(self) -> typing.Optional[main.foo.Bar]:
        pass

Generics and Type Variables

astlab 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.

Example

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())

Output (python < 3.12)

import builtins
import typing

T = typing.TypeVar('T', bound=builtins.int)

class Node(typing.Generic[T]):
    value: T
    parent: typing.Optional['Node[T]'] = None

Output (python 3.12, 3.13)

import builtins
import typing

class Node[T: builtins.int]:
    value: T
    parent: typing.Optional['Node[T]'] = None

Output (python ≥ 3.14)

import builtins
import typing

class Node[T: builtins.int]:
    value: T
    parent: typing.Optional[Node[T]] = None

Type Aliases

astlab 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+).

Example

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)),
            )
        )

Output (python < 3.12)

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]']]

Output (python 3.12, 3.13)

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]']]

Output (python ≥ 3.14)

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]]]

About

A Python library for building and generating Abstract Syntax Trees (ASTs) with a simple, intuitive API.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •  

Languages