Skip to main content

Cute Python codegen

Project description

trolskgen

Ergonomic codegen for Python - pip install trolskgen.

Note on Python 3.14 template strings.

From Python 3.14 upwards, there are template strings, these make trolskgen significantly more succinct.

Where previously you'd do:

name = t("f")
func = t(
    """
    def {name}():
        ...
    """,
    name=name,
)
trolskgen.to_source(func)

As of Python 3.14, you can do:

name = t"f"
func =  t"""
    def {name}():
        ...
"""
trolskgen.to_source(func)

There are some if sys.version_info >= (3, 14) flags around, but it should just work come release date.


trolskgen lets you easily build and compose ast.AST trees, and thereby easily generate source code. It doesn't handle any formatting concerns, just ruff format it afterwards. If you want comments, sorry, instead use a docstring or some Annotated[] wizardry.

Quick example:

import trolskgen
from trolskgen import t

name = t("f")
func = t(
    """
    def {name}():
        ...
    """,
    name=name,
)
trolskgen.to_source(func)
trolskgen.to_ast(func)

Gives you the source str:

def f():
    ...

And the ast.AST:

ast.Module(
    body=[
        ast.FunctionDef(
            name="f",
            args=ast.arguments(...),
            body=[ast.Expr(value=ast.Constant(value=Ellipsis))],
            decorator_list=[],
            type_params=[],
        )
    ],
)

A more complete example:

import datetime as dt

name = t("MySpecialClass")
bases = [int, list]
field_name = t("d")
fields = [
    t(
        "a: {type_}",
        type_=str,
    ),
    t(
        "{field_name}: dt.date = {default}",
        field_name=field_name,
        default=dt.date(2000, 1, 1),
    ),
]
method = t(
    """
    def inc(self) -> None:
        self.{field_name} += dt.timedelta(days=1)
    """,
    field_name=field_name,
)
my_special_class_source = t(
    """
    class {name}({bases:*}, float):
        {fields:*}
        {method}
    """,
    name=name,
    bases=bases,
    fields=fields,
    method=method,
)

trolskgen.to_source(my_special_class_source)

Gives you the source str:

class MySpecialClass(int, list, float):
    a: str
    d: dt.date = dt.date(2000, 1, 1)

    def inc(self) -> None:
        self.d += dt.timedelta(days=1)

API

Building templates
trolskgen.t(s: str, **kwargs: Any) -> trolskgen.templates.Template

Creates source templates. If you use the format string :*, it will splat in place - see above: {bases:*}, {fields:*}

This is redundant as of Python 3.14 - see above.

Converting to AST/source
trolskgen.to_ast(o: Any, *, config: Config) -> ast.AST
trolskgen.to_source(o: Any, *, config: Config) -> str

Try to convert o into an ast.AST/str representation.

The following are special cases for the value of o:

  • ast.AST nodes - these just get passed straight back out.
  • trolskgen.templates.Template or string.templatelib.Template - these get parsed as Python code.

trolskgen will generate sensible ASTs, for the following types:

  • None
  • int
  • float
  • str
  • bool
  • list
  • tuple
  • dict
  • set
  • classes
  • functions
  • dt.datetime
  • dt.date
  • enum.Enum
  • dataclass
  • Annotated, T | U, etc.
  • pydantic.BaseModel

Note that in conjunction with ruff, we have a decent pretty printer that you can use for test diffs etc.

We can add our own classes/overrides using:

Configuring/Overriding
trolskgen.ConvertInterface
trolskgen.Converter
trolskgen.Config
trolskgen.Config().prepend_converter(converter: Converter, *, before: Converter | None) -> Config

If you own the class, you can just add a __trolskgen__ method that satisfies trolskgen.ConvertInterface.

For example:

class MyInterfaceClass:
    def __trolskgen__(self, f: trolskgen.F) -> ast.AST:
        return f(t("MyInterfaceClass({values:*})", values=[1, 2, 3]))

trolskgen.to_source(MyInterfaceClass()) == "MyInterfaceClass(1, 2, 3)"

Note that we use f to recursively call trolskgen.to_ast(...) while preserving the current Config.


If you don't own the class, you can build a trolskgen.Config with a custom Converter function.

For example, if you for some reason wanted to render all ints in the form x + 1, you could:

def custom_int_converter(o: Any, f: trolskgen.F) -> ast.AST | None:
    if not isinstance(o, int):
        return None
    return f(t(f"{o - 1} + 1"))

config = trolskgen.Config().prepend_converter(custom_int_converter)
trolskgen.to_source([6, 9], config=config) == "[5 + 1, 8 + 1]"

Development

uv pip install -e '.[dev]'
mypy .
pytest -vv
uv pip install build twine
python -m build
twine check dist/*
twine upload dist/*

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

trolskgen-0.0.6.tar.gz (14.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

trolskgen-0.0.6-py3-none-any.whl (11.3 kB view details)

Uploaded Python 3

File details

Details for the file trolskgen-0.0.6.tar.gz.

File metadata

  • Download URL: trolskgen-0.0.6.tar.gz
  • Upload date:
  • Size: 14.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.5

File hashes

Hashes for trolskgen-0.0.6.tar.gz
Algorithm Hash digest
SHA256 05722302ca69d2543954cc03ab49080391a02102a77e9a8a6e46c16a086a371d
MD5 e8d72d48621762f7ca75c1ef0a82bc1e
BLAKE2b-256 9eeb8891fb199d1483e1dd96ed8a7937513d5716fc4a539efbb4a7246b3fdaf4

See more details on using hashes here.

File details

Details for the file trolskgen-0.0.6-py3-none-any.whl.

File metadata

  • Download URL: trolskgen-0.0.6-py3-none-any.whl
  • Upload date:
  • Size: 11.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.5

File hashes

Hashes for trolskgen-0.0.6-py3-none-any.whl
Algorithm Hash digest
SHA256 7fe52876141a8dec65b4ed553c06f535c279dcb144673b1b30b7b2e0e41e980d
MD5 f2ab6da42689fdd87f67929dd954f8e5
BLAKE2b-256 46e022ccef35daa4d19d9167b3f0db95107d4a2d3edb0cbbcf8efff8555a7244

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page