API reference

Most of the public API is covered in Reference. The symbols below round out the surface for tool authors and contributors.

foundry CLI

Foundry CLI entry point.

The CLI is target-agnostic: every piece of framework-specific behavior comes from a Target discovered via the foundry.targets entry-point group. The foundry CLI loads the config, runs the generic pipeline against the target’s registry/assembler/env, and writes files to disk.

clean_cmd(config, out=PosixPath('.'), target_name=None)[source]

Delete the output directory.

Removes out and its contents. The current working directory is never deleted. --config is parsed so the CLI surfaces config errors consistently, but its contents do not influence what is removed.

Return type:

None

cli_main()[source]

Run the CLI, converting CLIError to a clean exit.

Any CLIError raised inside a command is rendered as {prefix}: {message} on stderr and exits with code 1. Other exceptions propagate with a traceback, because they indicate a bug rather than bad user input.

Return type:

None

generate_cmd(config, out=PosixPath('.'), target_name=None, clean=False, dry_run=False)[source]

Generate files from a config via the selected target.

Return type:

None

main()[source]

Run the generic code-generation CLI.

Return type:

None

targets_list_cmd()[source]

List every target registered under foundry.targets.

Each line is formatted as <name> (<language>) so users can see at a glance which target to pass to --target.

Return type:

None

validate_cmd(config, target_name=None)[source]

Validate a config file without generating anything.

Parses and schema-checks --config using the selected target, then exits. Useful as a pre-commit check or for editor integrations that want fast feedback without running the full pipeline.

Return type:

None

kiln target

Registration of kiln as a foundry target.

Exposes target, the Target instance the foundry CLI picks up via the foundry.targets entry-point group declared in kiln’s pyproject.toml.

Importing this module transitively imports kiln’s renderer and operation modules (via foundry’s entry-point discovery), which populate foundry.render.registry as a side effect.

foundry.config

Load and validate target config files.

Supports .json files directly and .jsonnet files via foundry.jsonnet, which adds prefix-based stdlib imports so targets can ship their own libsonnet helpers under a registered prefix (e.g. import 'kiln/auth/jwt.libsonnet').

class FoundryConfig(**data)[source]

Base class for target-level config schemas.

Targets registering with foundry should subclass this so their config model carries the foundry-recognized meta fields. Today that is just package_prefix; more may be added as foundry grows. Target-specific fields (auth, databases, apps, routes, whatever) are declared by the subclass.

package_prefix

Dotted prefix prepended to the dotted import path of every GeneratedFile (and used as the on-disk directory prefix). Empty string disables the prefix. Targets that generate Python (or another language with package semantics) should override the default to something sensible (kiln uses "_generated").

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

load_config(path, schema, stdlibs=None)[source]

Load and validate a config file against schema.

Parameters:
Return type:

FoundryConfig

Returns:

Validated model instance of schema.

Raises:

ConfigError – If the file is missing, has an unsupported extension, fails to parse, or fails schema validation.

foundry.jsonnet

Thin wrapper around _jsonnet with prefix-based stdlib imports.

Jsonnet delegates import resolution to a host-supplied callback. This module provides a callback that interprets the first path segment of an import as a registered stdlib prefix: given {"kiln": Path(".../jsonnet")}, an import like 'kiln/auth/jwt.libsonnet' resolves under that directory. Imports without a matching prefix fall through to the normal relative-to-importer resolution, so user configs can still import './shared.libsonnet' freely.

The only public entry points are evaluate() (file in, JSON string out) and make_import_callback() (the bare callback, exposed for callers that want to build their own evaluator).

evaluate(path, stdlibs=None)[source]

Evaluate a Jsonnet file to a JSON string.

Parameters:
  • path (Path) – Path to the .jsonnet file.

  • stdlibs (Mapping[str, Path] | None) – Optional mapping of jsonnet import prefix to stdlib directory. {"kiln": Path(".../jsonnet")} makes import 'kiln/...' resolve under that directory.

Return type:

str

Returns:

JSON string produced by the Jsonnet evaluator.

Raises:
make_import_callback(stdlibs)[source]

Build a jsonnet import callback that honours stdlibs.

Imports whose first path segment matches a registered prefix are resolved from the associated stdlib directory; all other imports are resolved relative to the importing file.

Parameters:

stdlibs (Mapping[str, Path]) – Mapping of import prefix to stdlib directory.

Return type:

Callable[[str, str], tuple[str, bytes]]

Returns:

Callable with the _jsonnet import-callback signature (importing_dir, import_path) -> (resolved_path, bytes).

foundry.pipeline

Generic build pipeline: config in, files out.

Runs the Engine over the full config tree in a single pass, then assembles the build store into files using foundry’s shared registry and generic assembler. Reads package_prefix directly from the config and does not inspect any target-specific field.

generate(config, target)[source]

Generate all files for a validated config.

Engine auto-discovers operations from the foundry.operations entry-point group, runs the hierarchical engine once over the full config tree, and hands the resulting build store to foundry’s generic assembler.

Parameters:
  • config (FoundryConfig) – Validated config model.

  • target (Target) – The selected target; its template_dir is used to build the Jinja environment passed to renderers.

Return type:

list[GeneratedFile]

Returns:

Flat list of generated files.

Raises:

GenerationError – If config semantics are invalid (e.g. a resource references an unknown operation, or an operation’s options fail introspection).

foundry.assembler

Assembler: combine fragments into output files.

Each dispatched render gets a RenderCtx with store and instance_id set to the current entry. Renderers yield a FileFragment (declaring the output file and its wrapper template) plus one or more SnippetFragment contributions into the file’s slot lists. This module folds them: files with the same path merge via |, snippets render (either from value or their template), and each file’s wrapper is rendered once with every slot’s items in order.

assemble(store, registry, ctx)[source]

Turn a build store into rendered output files.

Walks every item in the store, dispatches to the registry to collect file/snippet fragments, then renders one file per declared shell with its snippets folded in.

Parameters:
  • store (BuildStore) – The build store from the engine’s build phase.

  • registry (RenderRegistry) – Render registry with all renderers registered.

  • ctx (RenderCtx) – Render context – env, config, package prefix.

Return type:

list[GeneratedFile]

Returns:

Flat list of GeneratedFile objects ready for output.

foundry.render

Render registry for output types.

The @renders decorator registers a function that knows how to turn a build output into a Fragment – a path, import set, and shell-template spec. The engine/assembler calls renderers after the build phase and then groups fragments by output path to produce final files.

class FileFragment(path, template, context=<factory>, imports=<factory>)[source]

Declares an output file’s wrapper template and scalar context.

One FileFragment per output path describes the template the assembler wraps the file in and the non-slot context passed to it. Every SnippetFragment sharing that path contributes a slot-list item that the assembler folds into context before the wrapper is rendered.

Multiple renderers may emit a FileFragment for the same path (e.g. every route handler at the resource declares the route file) — the assembler requires them to agree on template and unifies their context dicts, raising if two disagree on a shared key.

A blank template is a convention for an empty-content file (e.g. __init__.py).

path

Output path relative to the output directory.

template

Jinja2 template name that wraps the file.

context

Non-slot template variables. Merged across all FileFragments at this path (shared keys must agree).

imports

Imports the wrapper itself needs, on top of any contributed by snippets.

Fragment = foundry.render.FileFragment | foundry.render.SnippetFragment

Union of fragment types a renderer may yield.

class RenderCtx(env, config, package_prefix='', language='', store=<factory>, instance_id='')[source]

Context passed to every renderer function.

env

Jinja2 environment for template lookups.

config

The full project config dict (or model).

package_prefix

Dotted prefix for generated imports, e.g. "_generated".

language

Target language identifier used to render import blocks (e.g. "python"). Must match a formatter declared in the foundry.import_formatters entry-point group.

store

The build store. Renderers reach ancestor scope instances through it (e.g. a handler rendered at operation scope looks up its resource via store.ancestor_of(instance_id, "resource")).

instance_id

Id of the scope instance whose output is being rendered. Paired with store for ancestor and self lookups.

class RenderRegistry(_entries=<factory>)[source]

Maps output types to renderer functions.

Example:

registry = RenderRegistry()

@registry.renders(RouteHandler)
def render_route(handler, ctx):
    return Fragment(...)
render(obj, ctx)[source]

Produce fragments for a build output.

Every registered renderer returns an iterable of fragments (typically as a generator via yield). Renderers usually yield a FileFragment declaring the output file plus one or more SnippetFragment contributions into its slots.

Parameters:
  • obj (object) – The build output to render.

  • ctx (RenderCtx) – Render context.

Return type:

list[FileFragment | SnippetFragment]

Returns:

A list of fragments. May be empty if the renderer decides not to contribute.

Raises:

LookupError – No renderer registered for the type.

renders(output_type)[source]

Register a renderer for output_type.

Parameters:

output_type (type) – The output class this renderer handles.

Return type:

Callable[[Callable[[Any, RenderCtx], Iterable[FileFragment | SnippetFragment]]], Callable[[Any, RenderCtx], Iterable[FileFragment | SnippetFragment]]]

Returns:

The original function, unmodified.

class SnippetFragment(path, slot, template=None, context=<factory>, value=None, imports=<factory>)[source]

A contribution slotted into a file’s context list.

Each snippet becomes one entry in file.context[slot] — a list the wrapper template iterates over. Snippets at the same path may target different slots.

Supply exactly one of template (rendered by the assembler into a string) or value (used as-is, may be any type — useful for dict slots the wrapper iterates over itself).

path

Output path; must match a FileFragment.

slot

Key in the file’s context this snippet appends to.

template

Jinja2 template the assembler renders against context to produce a string slot item. Mutually exclusive with value.

context

Template variables for template.

value

Raw slot item — any type, used as-is. Mutually exclusive with template.

imports

Imports this contribution needs in the output file’s import block.

render_slot_item(env)[source]

Return the slot-list item this snippet contributes.

When template is set the assembler renders it against context and strips surrounding whitespace, so the surrounding file template can join items with its own separators without fighting jinja’s trailing newline. Otherwise value is passed through unchanged.

Return type:

object

registry = RenderRegistry(_entries={<class 'kiln.operations.types.SchemaClass'>: <function _schema_fragment>, <class 'kiln.operations.types.EnumClass'>: <function _enum_fragment>, <class 'kiln.operations.types.RouteHandler'>: <function _handler_fragment>, <class 'kiln.operations.types.SerializerFn'>: <function _serializer_fragment>, <class 'kiln.operations.types.TestCase'>: <function _testcase_fragment>, <class 'foundry.outputs.StaticFile'>: <function _static_fragment>})

Process-wide render registry.

Targets’ renderer modules register into this singleton at import time. Because foundry discovers operations via the foundry.operations entry-point group and loading an operation transitively imports its renderer module, no separate renderer-discovery step is needed — by the time the pipeline’s assembler runs, every renderer is registered.

kiln.operations

FastAPI renderers for build output types.

Each renderer is an @registry.renders(SomeOutput) generator that yields fragments: a FileFragment declaring the output file’s wrapper template and scalar context, plus one or more SnippetFragment contributions into its slot lists. The assembler groups fragments by path, folds snippets into the file’s context, and renders each wrapper template once.

Per-op RouteHandler rendering is owned by each op module (e.g. kiln.operations.list). Those modules call build_handler_fragment() with their op-specific body template, context, and import tuple. This module keeps only the cross-cutting renderers (schema / enum / serializer / testcase / static) plus a generic RouteHandler fallback for hand-written handlers that aren’t one of the registered subclasses.

render_enum_class(enum)[source]

Render an Enum class definition string.

Kept as a Python string builder because repr-formatted member values aren’t something jinja filters express cleanly. Called from _enum_fragment() to pre-render the slot value.

Return type:

str

utils_imports()[source]

Return import pairs for the ingot runtime helpers.

The three CRUD ops that load-or-404 a row (get, update, delete) all need get_object_from_query_or_404 and assert_rowcount; this centralizes the pair.

Return type:

list[tuple[str, str]]

Get operation: GET /{pk} – retrieve a single resource.

class Get[source]

GET /{pk} – retrieve a single resource.

Options

alias of FieldsOptions

build(ctx, options)[source]

Produce output for GET /{pk}.

Parameters:
  • ctx (BuildContext[OperationConfig]) – Build context for the "get" operation entry.

  • options (FieldsOptions) – Parsed FieldsOptions.

Yields:

The {Model}Resource schema, its serializer, the route handler, and a test case.

Return type:

Iterable[object]

List operation: POST /search – list/filter/sort/paginate resources.

class List[source]

POST /search – list/filter/sort/paginate resources.

Always emits a single POST /search route; never a GET. When filters, ordering, or pagination is configured, a SearchRequest body carries the query, the handler calls the matching ingot helpers, and (for pagination) a Page schema wraps the response. With no extensions configured, the handler takes no body and returns list[{Model}ListItem].

class Options(**data)[source]

Options for the list operation.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

build(ctx, options)[source]

Produce output for POST /search.

Parameters:
  • ctx (BuildContext[OperationConfig]) – Build context for the "list" operation entry.

  • options (Options) – Parsed Options.

Yields:

The {Model}ListItem schema, its serializer, and the POST /search handler + test case. Additionally yields extension schemas (filter / sort / search-request / page) when the corresponding options are configured.

Return type:

Iterable[object]

Create operation: POST / – create a new resource.

class Create[source]

POST / – create a new resource.

Options

alias of FieldsOptions

build(ctx, options)[source]

Produce output for POST /.

Parameters:
  • ctx (BuildContext[OperationConfig]) – Build context for the "create" operation entry.

  • options (FieldsOptions) – Parsed FieldsOptions.

Yields:

The {Model}CreateRequest schema, the route handler, and a test case.

Return type:

Iterable[object]

Update operation: PATCH /{pk} – partially update a resource.

class Update[source]

PATCH /{pk} – partially update a resource.

Options

alias of FieldsOptions

build(ctx, options)[source]

Produce output for PATCH /{pk}.

Parameters:
  • ctx (BuildContext[OperationConfig]) – Build context for the "update" operation entry.

  • options (FieldsOptions) – Parsed FieldsOptions.

Yields:

The {Model}UpdateRequest schema (all fields optional), the route handler, and a test case.

Return type:

Iterable[object]

Delete operation: DELETE /{pk} – delete a resource.

class Delete[source]

DELETE /{pk} – delete a resource.

Options

alias of EmptyOptions

build(ctx, _options)[source]

Produce output for DELETE /{pk}.

Parameters:
Yields:

The route handler and a test case.

Return type:

Iterable[object]

Action operation: custom endpoint via function introspection.

class Action[source]

Custom action endpoint via function introspection.

Dispatches on the presence of a fn attribute rather than a literal name match: any OperationConfig whose options include fn becomes an action.

class Options(**data)[source]

Options for action operations.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

build(ctx, options)[source]

Produce output for a custom action endpoint.

Parameters:
  • ctx (BuildContext[OperationConfig]) – Build context for the action’s operation entry.

  • options (Options) – Parsed Action.Options with fn path.

Yields:

The route handler and a test case.

Return type:

Iterable[object]

when(ctx)[source]

Activate whenever the op config carries an fn field.

Return type:

bool

Auth operation: augments CRUD handlers with auth dependencies.

Auth is a resource-scoped operation that runs after the CRUD and action operations have produced their route handlers and test cases. When the project has auth configured and the current resource opts in, it:

  • Appends current_user: Annotated[dict, Depends(...)] to each RouteHandler’s extra_deps so the template renders the auth dependency.

  • Appends the get_current_user import to the handler’s extra_imports so the assembler includes it.

  • Flips TestCase.requires_auth so the generated tests expect a 401 without credentials.

This is the first example of an operation in the augment role: it produces no new outputs, only mutates earlier ones.

class Auth[source]

Augment CRUD/action handlers and tests with auth.

Runs at resource scope with after_children=True so all operation-scope ops under this resource have already produced their handlers and test cases by the time auth sweeps through.

Options

alias of EmptyOptions

build(ctx, _options)[source]

Mutate earlier handlers/tests to require auth.

Parameters:
Return type:

Iterable[object]

Returns:

Empty iterable – this operation only mutates earlier output and emits no new objects.

when(ctx)[source]

Apply only when auth is configured and the resource opts in.

Parameters:

ctx (BuildContext[ResourceConfig]) – Build context for the current resource.

Return type:

bool

Returns:

True when the project config has auth set and the resource has require_auth (default True).

Scaffold operations: db sessions and auth dependencies.

Produces StaticFile objects for infrastructure files. Split into two operations:

class AuthScaffold[source]

Generate auth/ infrastructure files.

Options

alias of EmptyOptions

build(ctx, _options)[source]

Produce static files for auth dependencies and router.

Parameters:
  • ctx (BuildContext[ProjectConfig]) – Build context with project config. when has already confirmed ctx.instance.auth is not None.

  • _options (BaseModel) – Unused (no options).

Yields:

StaticFile for the auth/ package, the dependencies module, and the JWT router (only when using the default flow).

Return type:

Iterable[StaticFile]

when(ctx)[source]

Apply only when the project config has auth set.

Parameters:

ctx (BuildContext[ProjectConfig]) – Build context with project config.

Return type:

bool

Returns:

True when ctx.instance.auth is not None.

class Scaffold[source]

Generate db/ infrastructure files.

Options

alias of EmptyOptions

build(ctx, _options)[source]

Produce static files for db sessions.

Parameters:
Yields:

StaticFile objects for the db/ package and one session module per configured database.

Return type:

Iterable[StaticFile]

Router operations: per-app router and project router.

class ProjectRouter[source]

Generate routes/__init__.py mounting all apps.

Options

alias of EmptyOptions

build(ctx, _options)[source]

Produce the project-level router file.

Only produces output for configs that have an apps list. ProjectConfig wraps a single-app shorthand into an implicit app with prefix="" at validation time, so every project config routed through generate() has at least one app and this op runs unconditionally in the normal pipeline.

Parameters:
Yields:

Single StaticFile for the project router, or nothing for configs that have no apps at all.

Return type:

Iterable[StaticFile]

class Router[source]

Generate one app’s {module}/routes/__init__.py.

Runs in the post-children phase of the app scope so the build store is fully populated with resource-scope output beneath this app. Emits one RouterMount per resource that produced at least one RouteHandler plus a single StaticFile that aggregates them into the app’s router module.

Options

alias of EmptyOptions

build(ctx, _options)[source]

Produce this app’s router-aggregation file.

Parameters:
  • ctx (BuildContext[App]) – Build context for one App; store is fully populated with resource-scope output because after_children=True.

  • _options (BaseModel) – Unused.

Yields:

A single StaticFile for the app’s routes package, carrying one routes entry per resource that produced a RouteHandler. Nothing is yielded when no resource in the app produced a handler.

Return type:

Iterable[StaticFile]

Runtime helpers for kiln-generated FastAPI projects.

These are the shared route utilities that generated code imports directly (from ingot import apply_offset_pagination, ...) rather than having its own copy emitted into the target project.

Everything here is pure Python — the kiln CLI knows to emit imports pointing at this module instead of scaffolding a utils.py into the generated app.

apply_filters(stmt, node, model)[source]

Build WHERE clauses from a typed filter expression.

Accepts a typed Pydantic filter model — either a single condition (with field, op, value) or a combiner (with and_ / or_ lists of nested conditions). Models that match none of these shapes are treated as a no-op and the statement is returned unchanged.

Parameters:
  • stmt (Select) – The SQLAlchemy SELECT statement to filter.

  • node (BaseModel) – A Pydantic model representing the filter tree.

  • model (type) – The SQLAlchemy model class providing columns.

Return type:

Select

Returns:

The statement with WHERE clauses applied.

apply_keyset_pagination(stmt, model, cursor, cursor_field, page_size, max_page_size)[source]

Apply keyset (cursor-based) pagination to a SELECT.

Adds a WHERE cursor_field > cursor clause when a cursor is provided, clamps page_size, and adds LIMIT page_size + 1 (the extra row detects whether more results exist).

Parameters:
  • stmt (Select) – The SQLAlchemy SELECT statement.

  • model (type) – The SQLAlchemy model class providing columns.

  • cursor (Any) – The cursor value (already cast to the correct type), or None.

  • cursor_field (str) – Name of the cursor column.

  • page_size (int) – Requested page size.

  • max_page_size (int) – Maximum allowed page size.

Return type:

tuple[Select, int]

Returns:

(paginated_stmt, effective_page_size) tuple. The caller is responsible for executing paginated_stmt.

apply_offset_pagination(stmt, offset, limit, max_page_size)[source]

Apply offset pagination to a SELECT.

Clamps limit to max_page_size, applies OFFSET / LIMIT, and builds a companion COUNT(*) statement so the caller can fetch the total alongside the page.

Parameters:
  • stmt (Select) – The SQLAlchemy SELECT statement (without offset/limit applied).

  • offset (int) – Number of rows to skip.

  • limit (int) – Requested page size.

  • max_page_size (int) – Hard ceiling on limit.

Return type:

tuple[Select, Select, int]

Returns:

(paginated_stmt, count_stmt, effective_limit) tuple. The caller is responsible for executing both statements.

apply_ordering(stmt, sort_clauses, model, default_field, default_dir='asc')[source]

Apply one or more sort clauses to a SELECT statement.

Each clause is a Pydantic model with field (an enum whose .value is the column name) and dir (SortDirection).

When sort_clauses is None or empty, the default field and direction are used.

Parameters:
  • stmt (Select) – The SQLAlchemy SELECT statement to sort.

  • sort_clauses (Sequence[BaseModel] | None) – List of sort clause models, or None.

  • model (type) – The SQLAlchemy model class providing columns.

  • default_field (str) – Column name to sort by when no clauses are provided.

  • default_dir (Literal['asc', 'desc']) – Direction for the default sort.

Return type:

Select

Returns:

The statement with ORDER BY applied.

assert_rowcount(result, *, expected=1, status_code=404, detail='Not found')[source]

Raise HTTPException when result did not affect expected rows.

Parameters:
  • result (Any) – SQLAlchemy CursorResult from an execute() call.

  • expected (int) – Number of rows that must have been affected.

  • status_code (int) – HTTP status code for the raised exception.

  • detail (str) – The error message for the response.

Raises:

HTTPException – When result.rowcount does not equal expected.

Return type:

None

async get_object_from_query_or_404(db, stmt, *, detail='Not found')[source]

Execute stmt and return the first row, or raise HTTP 404.

Parameters:
  • db (AsyncSession) – The async database session.

  • stmt (Any) – A SQLAlchemy selectable statement.

  • detail (str) – The error message for the 404 response.

Return type:

Any

Returns:

The first row from the result set.

Raises:

HTTPException – With status 404 when no row is found.