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.


troslkgen 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.4.tar.gz (14.0 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.4-py3-none-any.whl (11.0 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: trolskgen-0.0.4.tar.gz
  • Upload date:
  • Size: 14.0 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.4.tar.gz
Algorithm Hash digest
SHA256 2fe25056b14c1a96f0af4e1112793882a0e70aac07a5bc3e7f0ddbefcfaf714b
MD5 277f2ed1b8f1e5c2f5b73e241dc0c706
BLAKE2b-256 18a16c940bd551a40bb43649bc50479a50d5692277b267cfa3ec07ed3f5a68e4

See more details on using hashes here.

File details

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

File metadata

  • Download URL: trolskgen-0.0.4-py3-none-any.whl
  • Upload date:
  • Size: 11.0 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.4-py3-none-any.whl
Algorithm Hash digest
SHA256 ac921cf99203234e31038daf09661d2b5c65f5955c87ed1e6626c7ab5813865a
MD5 b8004ea169591a337902d157af73c9e7
BLAKE2b-256 aa52d1056bf1d2ed3b92388cc318c8cdf9f5a056a2e8c935538bab622509aba0

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