Reference¶
Config schema¶
Each target has its own pydantic schema that its config files parse into:
be–ProjectConfig(covered in detail below).be_root–RootConfig(be_root config schema).fe–ProjectConfig(fe config schema).fe_root–RootConfig(fe_root config schema).
The classes below correspond directly to the fields you write in
the matching .jsonnet / .json file for that target.
be config schema¶
Field types¶
The type field on FieldSpec accepts:
Type |
Python annotation |
Used in |
Notes |
|---|---|---|---|
|
|
request/response schemas, pk |
Default for primary keys. |
|
|
schemas, action params |
|
|
|
schemas |
Added |
|
|
schemas, pk |
|
|
|
schemas |
|
|
|
schemas |
|
|
|
schemas |
|
|
|
schemas |
|
|
|
schemas |
|
|
generated sub-schema class |
read-op schemas ( |
Dumps a related model inline. Requires |
be_root config schema¶
fe config schema¶
fe_root config schema¶
Built-in operations¶
Every built-in operation is registered under its target’s own
entry-point group in pyproject.toml: be.operations for the
be target, be_root.operations for be_root, and so on.
See Usage for what each one generates and Extending kiln
for the operation protocol.
The table below covers be’s built-in ops; be_root, fe,
and fe_root each ship a single project-scope RootScaffold /
OpenApiTsConfig op (see be_root config schema, fe config schema,
fe_root config schema for the configs that drive them).
Name |
Module |
Scope |
Description |
|---|---|---|---|
|
|
project |
Two project-scope ops live here. |
|
|
resource |
The five CRUD endpoints. Each op lives in its own module alongside the FastAPI renderer for its output. |
|
|
resource |
Custom action endpoints: |
|
|
resource |
Cross-cutting augmenter. Appends |
|
|
app |
Emits |
|
|
project |
Multi-app projects only. Emits the top-level
|
Generated file layout¶
The table below summarises every file be can produce. Paths are
relative to the --out directory (or to the config’s
package_prefix when --out is omitted). {module} is the
app’s module config field. {name} is the lowercase,
snake-cased model name.
Path |
Produced by |
Overwrite |
|---|---|---|
|
|
Yes |
|
|
Yes |
|
|
Yes |
|
|
Yes |
|
|
Yes |
|
|
Yes |
|
|
Yes |
|
CRUD + |
Yes |
|
|
Yes |
|
CRUD + |
Yes |
|
|
Yes |
Every file is overwritten on every generation run.
codegen API¶
Targets¶
- class Target(name, language, schema, template_dir, operations_entry_point, jsonnet_stdlib_dir=None)[source]¶
A concrete code-generation target.
- name¶
Short identifier, used for
--targetdispatch when multiple targets are installed and as the jsonnet stdlib import prefix.
- language¶
Language-identifier the target generates for (e.g.
"python"). Passed tocodegen.imports.format_imports()so the assembler renders import blocks in the right syntax. Targets declare their formatter under thecodegen.import_formattersentry-point group.
- schema¶
CodegenConfigsubclass the target’s config files validate against. Codegen’s loader instantiates this.
- template_dir¶
Directory of Jinja templates the target’s renderers reference. Codegen builds the Jinja environment rooted here.
- operations_entry_point¶
Entry-point group name where the target’s
@operation-decorated classes are registered, e.g."be.operations"or"be_root.operations". At build time the pipeline callsload_registry()against this group to assemble a fresh, target-private registry; targets installed side-by-side never see each other’s ops.
- jsonnet_stdlib_dir¶
Optional directory of jsonnet
.libsonnetfiles exposed to configs as<name>/...imports.Nonewhen the target ships no stdlib.
- exception CLIError[source]¶
Base class for errors the CLI should render cleanly.
Subclasses set
prefixto control how the error is labelled when rendered at the CLI boundary.
Engine¶
- class Engine(registry=<factory>, package_prefix='')[source]¶
Orchestrates the build phase of code generation.
- registry¶
OperationRegistryholding the ops to run. Defaults to an empty registry – production callers always pass one populated bycodegen.operation.load_registry(); tests pass their own isolated registry.
- package_prefix¶
Dotted prefix for generated imports, forwarded to every
BuildContext.
- build(config)[source]¶
Run the build phase over all scopes and operations.
Walks the scope tree depth-first. At each scope instance, pre-phase operations (
after_children=False) run before descending into children; post-phase operations (after_children=True) run after every child scope instance completes, so they can aggregate earlier output from the store.- Parameters:
config (
BaseModel) – The project config model instance.- Return type:
- Returns:
An
BuildStorecontaining all objects produced by operations.
- class BuildContext(config, scope, instance, instance_id, store, package_prefix='')[source]¶
Context passed to every operation’s
buildmethod.Parameterized on two types:
InstanceT– the scope’s per-instance config (e.g.ResourceConfigfor resource-scope ops).ConfigT– the project root config. The engine itself usesBuildContext[Any, BaseModel]since it’s target-agnostic; target-specific ops (e.g. all of be) annotateBuildContext[X, ProjectConfig]and get typed access toctx.config.*without casting.
- config¶
The full project config (top-level model).
- scope¶
The scope this operation is running in.
- instance¶
The config object for the current scope instance (e.g. one resource’s config dict).
- instance_id¶
Human-readable identifier for the instance within its scope.
- store¶
The build store for querying earlier operations’ output.
- package_prefix¶
Dotted prefix for generated imports (e.g.
"_generated"). Extensions use this to resolve their own import paths.
Operations¶
- operation(name, *, scope, requires=None, after_children=False, dispatch_on=None, registry=None)[source]¶
Decorate a class as a be operation.
The decorated class must define:
Options: apydantic.BaseModelsubclass (defaults toEmptyOptionsif absent).build(self, ctx, options) -> list: produces output objects for the engine to collect.
Optionally it may define:
when(self, ctx) -> bool: when present and returningFalse, the engine skips this operation for the current build context. Use this for conditional operations (e.g. auth, which only runs when the project has auth configured).
Operations can also modify earlier operations’ outputs by inspecting
storeand mutating the objects returned byoutputs_under()in place. Combined withrequiresfor ordering andwhenfor activation, a single operation mechanism covers both “produce” and “augment” roles.The decorator stashes the captured
OperationMetaon the class under_operation_meta;load_registry()reads it back at entry-point load time. When registry is supplied (the test path) the decorator also pushes the entry into that registry directly, so unit tests can keep ops out of the entry-point flow entirely.- Parameters:
name (
str) – Unique operation name.scope (
str) – Scope name (e.g."resource","app","project").requires (
list[str] |None) – Operation names that must run first.after_children (
bool) – WhenTrue(project scope only), defer this operation until every child scope has executed sobuildcan walk child output in the store. The engine rejects this flag at any other scope.dispatch_on (
str|None) – Attribute name on the scope instance to compare against name. When set, the engine skips the op unlessgetattr(ctx.instance, dispatch_on) == name. Designed for scopes whose instance is a discriminated-union config (e.g.OperationConfigentries under a resource), where multiple ops share one scope and each matches a single entry.registry (
OperationRegistry|None) – Optional registry to push into directly.None(the production path) means the decorator only stashes meta on the class; the pipeline picks it up later viaload_registry(). Tests pass an isolated registry to keep their ops out of the entry-point flow.
- Return type:
- Returns:
Class decorator.
Example:
@operation("get", scope="resource") class Get: class Options(BaseModel): fields: list[FieldSpec] | None = None def build(self, ctx, options): return [RouteHandler(...)]
- load_registry(entry_point_group)[source]¶
Build a fresh
OperationRegistryfrom an entry-point group.Walks
entry_point_group, loads each declared class, and registers it via theOperationMetastashed on the class by theoperation()decorator. Each call returns a brand-new registry, so two targets sharing a process never see each other’s ops.- Parameters:
entry_point_group (
str) – Dotted entry-point group name, e.g."be.operations"or"be_root.operations".- Return type:
- Returns:
A populated
OperationRegistry.- Raises:
TypeError – If a discovered class is missing the
_operation_metaattribute – typically because the class wasn’t decorated withoperation().
- class OperationMeta(name, scope, requires=(), after_children=False, dispatch_on=None)[source]¶
Metadata attached to a decorated operation class.
- name¶
Unique operation name (e.g.
"get").
- scope¶
Scope name this operation runs in (e.g.
"resource").
- requires¶
Names of operations that must run before this one within the same scope.
- after_children¶
When
True, this project-scope operation runs after all child scopes have executed, so itsbuildmethod can inspect objects produced at the resource/app scopes via the build store. Ignored outside the project scope (the engine raises if set).
- dispatch_on¶
Attribute name on
ctx.instancewhose value must equalnamefor this op to run. Engine skips the op silently when the attribute is absent or mismatched. Use it at scopes where the instance is a discriminated union — every registered op at the scope shares the scope walk and each dispatches to its own entry by name.
- class EmptyOptions(**data)[source]¶
Default options model for operations with no config.
- model_config: ClassVar[ConfigDict] = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class OperationRegistry(entries=<factory>)[source]¶
Collection of
(meta, cls)entries with query helpers.Populated by the
operation()decorator at decoration time. The engine reads entries from the registry to walk scopes, group by scope, and topo-sort within a scope — it never needs to look up metadata on individual classes.- entries¶
(meta, cls)pairs, in registration order.
Scopes¶
- class Scope(name, config_key, parent=None, resolve_path=())[source]¶
A named level in the config tree.
- name¶
Human-readable scope name, e.g.
"resource".
- config_key¶
The config field name that produced this scope, e.g.
"resources". Empty string for the root (project) scope.
- parent¶
The parent scope, or
Nonefor the root.
- resolve_path¶
Dotted attribute path from
parent’s scope instance to this scope’s list of items. Empty for the root scope. Defaults to(config_key,)for a direct child.
- class ScopeTree(iterable=(), /)[source]¶
Flat collection of scopes with convenience lookups.
Subclassing
tuplegives callers the usual iteration/index/len ergonomics for free, while the two methods below handle the recurring “children of X” and “scope for id Y” patterns so the engine, store, and assembler don’t each reinvent the search.Construct from
discover_scopes()output:tree = ScopeTree(discover_scopes(MyConfig))
- scope_for(instance_id)[source]¶
Return the
Scopeaninstance_idbelongs to.Instance ids produced by the engine are dot-joined paths of the form
"project.<config_key>.<index>...". Eachconfig_keymaps to exactly one child scope at the current level, so the scope is recovered by walking the tree fromPROJECTusing segment pairs.- Parameters:
instance_id (
str) – A dot-path instance id (e.g."project.apps.0.resources.2").- Return type:
- Returns:
The
Scopethe id terminates at.- Raises:
ValueError – If the id doesn’t start with
"project"or references aconfig_keynot present in this tree.
- class Scoped(name)[source]¶
Annotatedmarker tagging a list field as a codegen scope.Attach to a
list[SomeBaseModel]field on aCodegenConfigsubclass to declare that the field defines a scope level. Only marked fields produce scopes; unmarked lists are plain data.- name¶
The scope’s name, e.g.
"app"or"resource". Required — field names on configs are conventionally plural (apps,resources), but a scope refers to one instance, so the name is spelled out explicitly rather than derived by a heuristic.
- discover_scopes(config_cls)[source]¶
Derive scopes from a Pydantic model’s
Scopedmarkers.The top-level config is always the
"project"scope. Each field declaredAnnotated[list[T], Scoped()]becomes a child scope of the current level; the item typeTis then itself descended into to discover grandchild scopes.Non-list
BaseModelfields (e.g.App.config) are traversed transparently: their nestedScopedfields become scopes rooted at the enclosing level, withresolve_pathreflecting the full attribute walk.Each scoped item type is descended into at most once, so discovery always terminates. If the same type appears in multiple scoped lists only the first occurrence is descended — subsequent ones still produce their own scope but no grandchildren.
- codegen.scope.PROJECT¶
The root scope – always present in every generation run.
Typed outputs¶
Every operation’s build method returns instances of the types
below. Framework-agnostic types live in codegen.outputs;
FastAPI-specific output dataclasses live in
be.operations.types.
- class StaticFile(path, template, context=<factory>, if_exists='overwrite', executable=False, banner=True)[source]¶
A file rendered directly from a template.
Used for scaffold files (auth, db sessions), utils, and other files that don’t need the assembler’s multi-contributor merging.
if_existsdefaults to"overwrite"(be’s regenerated scaffold behaviour);"skip"makescodegen.output.write_files()leave existing files alone – right for one-shot bootstraps like be_root, where--force/--force-pathsis the explicit opt-in to clobber.executablesets the user/group/other+xbits on the emitted file after write – use for shell scripts the surroundingjustfile/package.jsoninvokes via a bare./scripts/foo.shrather thanbash ./scripts/foo.sh.bannerdefaults toTrue; setFalseto suppress the engine-emitted autogenerated header for files that must not carry one.
Render registry¶
- 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 aFileFragmentdeclaring the output file plus one or moreSnippetFragmentcontributions into its slots.- Parameters:
- Return type:
- 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 RenderCtx(env, config, package_prefix='', language='', target_name='', 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 thecodegen.import_formattersentry-point group.
- target_name¶
The target’s short name (e.g.
"be"), used by the engine-emitted file banner to name thecodegen generate --target <name>command that produced the file.
- 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")).
- class BuildStore(scope_tree=<factory>, _items=<factory>, _instances=<factory>, _children=<factory>, _parent_of=<factory>)[source]¶
Accumulator for objects produced during the build phase.
Outputs are keyed by
(instance_id, op_name). Ancestry between instances is tracked separately so tree walks don’t have to parse dot-path ids.Query methods at a glance¶
Scope-instance lookup (return config objects, not outputs):
ancestor_of()— walk up to find an enclosing scope’s config instance.ancestor_id_of()— same walk, but return the id. Useful when you need the id to pass into the output-query methods below.children()— direct children of an instance, optionally filtered by child scope.scope_of()— resolve an id’sScope.
Output lookup (return objects emitted by ops):
outputs_under()— every output of a type at or below a given instance id. Good for aggregate passes (Auth at resource scope sweeping handlers).outputs_under_ancestor()— walk up to a named scope first, then collect. Good for ops that need to reach sideways via a shared ancestor.output_under_ancestor()— singular form; raises if nothing matches. For ops that expect exactly one target (e.g. a modifier op finding its parent op’sListResult).entries()— raw(instance_id, op_name, items)tuples. The assembler uses this; ops rarely need to.
Mutation:
add()— engine calls this with an op’s yielded outputs. Ops normally don’t call it directly.register_instance()— engine calls this before invokingbuild()at a scope instance. Ops never call it.
Typical extension recipes¶
Read an ancestor’s config (e.g. resource model from operation scope):
resource = ctx.store.ancestor_of(ctx.instance_id, "resource")
Augment every handler in your subtree (e.g. Auth):
for handler in ctx.store.outputs_under( ctx.instance_id, RouteHandler ): handler.extra_deps.append(...)
Reach a specific output your parent scope produced (e.g. a modifier finding its parent op’s bundle):
bundle = ctx.store.output_under_ancestor( ctx.instance_id, "operation", ListResult ) bundle.search_request.body_context["has_filter"] = True
- scope_tree¶
ScopeTreefor the build’s config. Required for thescope_of()derivation (and therefore forchild_scope=filtering onchildren()). Defaults to empty so ad-hoc store-level tests can skip it when they don’t care.
- _items¶
Internal storage mapping
(instance_id, op_name)keys to object lists.
- _instances¶
Map from
instance_idto the scope-instance config object.
- _children¶
Map from a parent instance id to its registered child instance ids, in insertion order.
- _parent_of¶
Map from an instance id to its parent id; drives
ancestor_of()/ancestor_id_of().
- ancestor_id_of(instance_id, scope_name)[source]¶
Return the enclosing instance id at scope_name, if any.
Mirrors
ancestor_of()but returns the ancestor’s id instead of its instance. Ops that need to scan outputs under a higher scope use this to get the idoutputs_under()wants.
- ancestor_of(instance_id, scope_name)[source]¶
Return the enclosing instance at scope_name, if any.
Walks
_parent_ofedges from instance_id toward the root and returns the first instance whose scope name matches. Used by descendant ops that need data from a higher scope (e.g. an operation-scope op reading its enclosing resource’smodel).
- children(parent_id, *, child_scope=None)[source]¶
Return child instances of parent_id.
Children come back in registration (config) order. When child_scope is given, only children in that scope are returned (requires
scope_treeto be populated).
- entries()[source]¶
Iterate stored entries as
(instance_id, op_name, items).Used by the assembler to walk the store and dispatch each item to the correct renderer.
- instance_at(instance_id)[source]¶
Return the instance registered at instance_id, if any.
Renderers occasionally need the registered instance for the current dispatch entry (e.g. an op that yields outputs at its own scope rather than at a child scope).
ancestor_of()walks parents only, so a renderer dispatched at the same scope as the wanted instance cannot recover it that way – this accessor closes the gap.
- output_under_ancestor(instance_id, scope_name, output_type)[source]¶
Return the sole output_type output under the named ancestor.
Singular form of
outputs_under_ancestor()— raisesLookupErrorwhen no ancestor is registered at scope_name or when the ancestor produced no output of output_type. Returns the first match when more than one exists; callers that care about multiplicity should use the plural form.- Return type:
TypeVar(T)
- outputs_under(ancestor_id, output_type)[source]¶
Return every output_type output at or below ancestor_id.
Walks the store by path prefix, so output produced at any depth under ancestor_id surfaces — useful for ops that aggregate or mutate outputs from deeper scopes (e.g. auth adding dependencies to every handler under a resource).
- outputs_under_ancestor(instance_id, scope_name, output_type)[source]¶
Return outputs under the ancestor of instance_id at scope_name.
Convenience for the common “walk up to a named scope, then look for outputs there” pattern — used by ops that need to reach outputs an ancestor (or sibling via a shared ancestor) produced. Returns
[]when no ancestor at that scope is registered.
- register_instance(instance_id, instance, *, parent=None)[source]¶
Remember the scope-instance object for instance_id.
Called by the engine before operations run at each scope instance. Renderers access these via
ancestor_of()when they need a higher scope’s config.
- class FileFragment(path, template, context=<factory>, imports=<factory>, if_exists='overwrite', executable=False, banner=True)[source]¶
Declares an output file’s wrapper template and scalar context.
One
FileFragmentper output path describes the template the assembler wraps the file in and the non-slot context passed to it. EverySnippetFragmentsharing that path contributes a slot-list item that the assembler folds intocontextbefore the wrapper is rendered.Multiple renderers may emit a
FileFragmentfor the same path (e.g. every route handler at the resource declares the route file) — the assembler requires them to agree ontemplateand unifies theircontextdicts, raising if two disagree on a shared key.A blank
templateis 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.
- if_exists¶
Write policy for the assembled output. Set to
"skip"to makecodegen.output.write_files()leave existing files alone (be_root’s re-bootstrap-safe path); defaults to"overwrite". When two fragments at the same path disagree, the assembler picks the stricter"overwrite"– a single contributor that wants the file regenerated wins over any contributor that would have been happy to skip.
- executable¶
When
True,codegen.output.write_files()chmods the emitted file+x(user/group/other). Use for shell scripts that the renderedjustfile/package.jsoninvokes via a bare./scripts/foo.sh. Merge:Truewins – any contributor that wants the bit set wins over a peer that doesn’t care.
- banner¶
When
True(default), the engine prepends the autogenerated header banner (seecodegen.banner) to the rendered content. SetFalseto suppress it for files that must not carry one. Merge:Falsewins – any contributor that opts out suppresses it.
- 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) orvalue(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
contextto produce a string slot item. Mutually exclusive withvalue.
- 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
templateis set the assembler renders it againstcontextand strips surrounding whitespace, so the surrounding file template can join items with its own separators without fighting jinja’s trailing newline. Otherwisevalueis passed through unchanged.- Return type:
- Fragment¶
Union of fragment types a renderer may yield.
- codegen.render.registry¶
Process-wide
RenderRegistrypopulated at import time.
Output¶
- class GeneratedFile(path, content, if_exists='overwrite', executable=False)[source]¶
Immutable final output – a path and its content.
- path¶
Output path relative to the output directory.
- content¶
File contents as a string.
- if_exists¶
Per-file write policy honored by
codegen.output.write_files()."overwrite"(the default) always replaces the target on disk – the historical behaviour every be scaffold output relied on."skip"writes the file only if it does not yet exist; right for one-shot scaffolding (e.g. be_root’s bootstrap) where users edit the file post-generation and a re-run should be non-destructive.--force/--force-pathson the CLI override"skip"back to"overwrite"for the affected files.
- executable¶
When
True,codegen.output.write_files()chmods the emitted file+x(user/group/other) so it can be invoked as./path/to/filerather thanbash path/to/file. Pairs with shebang-bearing shell scripts emitted alongside a justfile or package.json.
- write_files(files, out_dir, *, force=False, force_paths=None)[source]¶
Write generated files to disk, honoring per-file write policy.
Each file’s
pathis joined with out_dir to determine the target path. Parent directories are created as needed.The
if_existspolicy decides what happens when the target already exists:"overwrite"(default for be output) – replace unconditionally."skip"(be_root’s bootstrap files) – leave the existing file untouched.
force and force_paths let the caller override
"skip"back to"overwrite"from the CLI without changing the file declarations themselves:force=Trueclobbers every"skip"file.force_paths={"main.py", "pyproject.toml"}clobbers only those paths – handy for resetting a single bootstrapped file without touching the rest. Each entry is a glob (fnmatch), soforce_paths={"*.just"}resets every justfile module andforce_paths={"config/*"}the config tree; a plain filename with no wildcard is an exact match.
- Parameters:
files (
Sequence[GeneratedFile]) – Sequence ofGeneratedFileobjects.out_dir (
Path) – Root directory for output paths.force (
bool) – WhenTrue, treat every file as"overwrite"regardless of its declared policy.force_paths (
Iterable[str] |None) – Optional collection of glob patterns (matched againstpath, which is relative to out_dir) whose"skip"declaration should be overridden to"overwrite". Ignored when force isTrue.
- Return type:
- Returns:
Number of files written (skipped files do not count).
Naming and imports¶
- class Name(raw)[source]¶
Derives conventional identifiers from a base string.
Accepts either a
PascalCaseclass name (e.g."Article") or asnake_caseidentifier (e.g."publish_article") and exposes the common derived forms used by code generators.Examples:
model = Name("Article") model.pascal # "Article" model.lower # "article" model.suffixed("Resource") # "ArticleResource" action = Name("publish_article") action.pascal # "PublishArticle" action.slug # "publish-article" action.suffixed("Request") # "PublishArticleRequest"
- classmethod from_dotted(dotted_path)[source]¶
Create a
Namefrom a dotted import path.- Parameters:
dotted_path (
str) – A fully-qualified class path such as"myapp.models.Article".- Return type:
- Returns:
A
(module, Name)tuple, e.g.("myapp.models", Name("Article")). Callers that need the raw class-name string can read it fromName.raw.- Raises:
ValueError – If dotted_path contains fewer than two parts.
- property lower: str¶
Fully lowercased form, no separator inserted.
"Article"→"article","publish_article"→"publish_article".Use
snakeinstead when the result will become a file/module/function name and the input is PascalCase –Name("NotificationPreference").lowercollapses to"notificationpreference"whereassnakereturns"notification_preference".
- static parent_path(dotted, *, levels=1)[source]¶
Return dotted’s ancestor module path.
Drops the last levels dot-separated segments. When the path runs out of segments before levels are stripped, it stops and returns whatever remains rather than producing an empty string — so callers can chain the op without guarding for short paths:
>>> Name.parent_path("blog.models.Article") 'blog.models' >>> Name.parent_path("blog.models.Article", levels=2) 'blog' >>> Name.parent_path("single", levels=2) 'single'
levels=2is the common idiom for “app module from model dotted path” –"blog.models.Article" -> "blog".- Return type:
- property pascal: str¶
PascalCase form of the name.
If the raw string contains no underscores and already starts with an uppercase letter it is returned as-is (assumed to already be PascalCase, e.g.
"StockMovement"from a dotted import path).
- property slug: str¶
Hyphenated slug form (for URL segments).
"publish_article"→"publish-article","NotificationPreference"→"notification-preference"(PascalCase boundaries are split first viasnake).
- property snake: str¶
snake_case form (for file/module/function names).
"Article"→"article","NotificationPreference"→"notification_preference","XMLParser"→"xml_parser","publish_article"→"publish_article".PascalCase / camelCase boundaries become underscores so multi-word model classes produce readable identifiers. Strings already containing underscores pass through unchanged (only lowercased) on the assumption that the caller chose their own boundaries.
- prefix_import(prefix, *parts)[source]¶
Build a Python import path under prefix (which may be empty).
- class ImportCollector(*others)[source]¶
Accumulates imports as
(module, name)pairs.A bare import is
(module, None)(e.g. Pythonimport uuid). A from-import is(module, name)(e.g. Pythonfrom datetime import datetime). Multiple calls for the same module are merged; duplicates are deduplicated.Examples:
collector = ImportCollector() collector.add("uuid") collector.add_from("datetime", "datetime", "date") collector.format("python") # "import uuid\nfrom datetime import date, datetime\n"
Rendering is delegated to a language formatter looked up via the
codegen.import_formattersentry-point group.- add_from(module, *names)[source]¶
Register an import of names from module.
Multiple calls with the same module are merged.
- Return type:
- property from_imports: dict[str, list[str]]¶
{module: [name, ...]}of from-imports, preserving order.
Jinja environment¶
- create_jinja_env(*template_dirs)[source]¶
Create a Jinja2 environment for code generation.
The returned environment has
trim_blocksandlstrip_blocksenabled so that block tags ({% if %},{% for %}, etc.) do not add extra blank lines to the rendered output.- Parameters:
*template_dirs (
Path) – One or more directories to search for templates. Earlier directories take priority.- Return type:
- Returns:
A configured
jinja2.Environment.
- render_template(env, template_name, **context)[source]¶
Render template_name against context and return the raw result.
Every jinja call in codegen/be flows through this helper so whitespace policy lives at the call site, not hidden inside a render wrapper. Callers handle trimming themselves:
Inline code snippets typically want
.strip().Whole-file output wants
.rstrip() + "\n".Slot contributions that the outer template controls typically want the raw output, unmodified.
- Parameters:
env (
Environment) – The Jinja2 environment to use.template_name (
str) – Template path relative to the environment’s template directories.**context (
object) – Template context variables.
- Return type:
- Returns:
The rendered template, exactly as jinja produced it.
Stdlib reference¶
The following .libsonnet files ship inside the be package and
are importable from any config file using the be/ prefix.
be/auth/jwt.libsonnet¶
Configures JWT authentication.
local auth = import 'be/auth/jwt.libsonnet';
auth.jwt({
secret_env: "JWT_SECRET",
algorithm: "HS256",
token_url: "/auth/token",
exclude_paths: ["/docs", "/openapi.json", "/health"],
verify_credentials_fn: "myapp.auth.verify_credentials",
})
To supply a custom get_current_user dependency instead of the
generated JWT flow, set get_current_user_fn to a dotted import
path. In that case verify_credentials_fn is not required.
be/db/databases.libsonnet¶
Configures async PostgreSQL connections.
local db = import 'be/db/databases.libsonnet';
db.postgres("primary", {
url_env: "DATABASE_URL",
default: true,
echo: false,
pool_size: 5,
max_overflow: 10,
pool_timeout: 30,
pool_recycle: -1,
pool_pre_ping: true,
})
Resources that omit db_key use the database with default: true.
pgqueuer integration¶
be does not scaffold pgqueuer wiring. See Background tasks (pgqueuer) for
the full guide — the two helpers in ingot.queue
(ingot.queue.get_queue() for transactional-outbox enqueue,
ingot.queue.open_worker_driver() for the SQLAlchemy→asyncpg
DSN bridge), the worker-factory pattern, and how to run the
worker with pgqueuer’s own CLI.