Theme
Version
GitHub PyPI Discord
On this page

Extensions

The plugin system: the extension base, its commands, and the hook context objects. Some hooks and types on this page are experimental and may change in a minor release; each entry says so. The experimental parts are the API for embedding Citry tags in another template engine, the "payload" render cache mode, browser_plugin(), and prepare_browser_render().

View source

Extension class

Base class for all extensions.

Subclass this, set name (a lowercase Python identifier), and implement the on_* hooks you care about. Every hook has an empty default, so an extension only overrides what it needs (the manager calls only the hooks an extension actually overrides). The on_* methods below are the full hook catalog.

View source

name attribute

name: str

Name of the extension. Lowercase, a valid Python identifier. Determines the attribute the per-component config is reachable under (component.<name>) and, through class_name, the nested class name.

View source

class_name attribute

class_name: str

PascalCase name of the per-component nested config class, derived from name at subclass creation (my_extension -> MyExtension).

View source

Config attribute

Base class the per-component nested config inherits from.

View source

commands attribute

CLI commands this extension provides (see ExtensionCommand).

View source

introspection_version attribute

introspection_version: int | None

Positive schema version when this extension publishes component metadata.

View source

render_cache_mode attribute

render_cache_mode: Literal['deny', 'stateless', 'payload']

Whether settled render state from this extension can be replayed.

Experimental

The "payload" mode, where an extension saves its own data with a cached render and restores it on a cache hit, is experimental. It may change in a minor release without a deprecation period. "deny" and "stateless" are stable.

View source

render_cache_version attribute

render_cache_version: int | None

Positive compatibility version for stateless or payload replay.

View source

citry attribute

citry: Citry

The Citry instance this extension instance belongs to. Set by the manager when the extension is attached (extensions are per-instance, so the back-reference is unambiguous).

View source

urls attribute

urls: list[URLRoute]

HTTP routes this extension provides, as URLRoute values.

Mounted by the web-integration adapters as part of Citry.urls: a user extension's routes live under ext/<extension name>/; built-in extensions own their paths directly. Override as an attribute or property; handlers can reach engine state through self.citry.

View source

validate_config_fields function

validate_config_fields(fields: Mapping[str, Any], component: type[Component] | None = None) -> None

Check the config fields declared for this extension, at declaration time.

Users configure an extension in two places: a component's nested config class (class View: for an extension named "view"), and the engine-wide extensions_defaults setting. Citry calls this method once per declaration:

  • At engine construction, with the extension's entry in the extensions_defaults setting (component is None).
  • At component class definition, with the fields declared on the component's nested config class, including fields from its user-written base classes (component is the component class).

The base implementation accepts everything: by default an extension's config may hold any fields, even methods. Override it to reject bad fields early, so a typo in a field name fails at startup or at class definition instead of surfacing later as a confusing downstream error. Because both declaration sites are checked, the fields are known-valid by the time the config class is instantiated.

Example

An extension whose config accepts exactly one field:

from citry import Extension

class TimeoutExtension(Extension):
    name = "timeout"

    def validate_config_fields(
        self, fields, *, component=None
    ):
        for name in fields:
            if name != "seconds":
                msg = (
                    f"unknown field {name!r}; "
                    "the only field is 'seconds'"
                )
                raise ValueError(msg)

Parameters

  • fields Mapping[str, Any] - The declared fields, mapping field name to declared value. For a nested config class, dunder names, the Config base's members, and citry's own bookkeeping attributes are already filtered out.
  • component type[Component] | None - The component class the fields were declared on, or None when the fields come from the extensions_defaults setting.

Raises

  • ValueError - When a field is not valid for this extension. Raise it with a message naming the offending field and what is valid; citry prefixes the declaration site (the component and its nested class, or the setting).
View source

inspect_component function

inspect_component(ctx: ComponentIntrospectionContext) -> dict[str, object] | None

Publish this extension's allowlisted metadata for one component.

Citry calls this direct query method only when a caller explicitly requests the extension by name. Override it together with a positive introspection_version. Return an exact built-in dict made only from strict JSON values, or None when this component has no entry. The method must be observational, deterministic, reentrant, and thread-safe; it must not render, load assets, mutate registration, or depend on request state.

Parameters

  • ctx ComponentIntrospectionContext - The owning engine, temporary live component class, and its already-built core metadata record. ctx.info.extensions is always empty.

Returns

dict[str, object] | None: An extension-owned JSON object, or ``None``.

View source

inspect_template_namespace function

inspect_template_namespace(ctx: TemplateNamespaceContext) -> TemplateNamespaceContribution | None

Describe template variables supplied by this extension.

Citry calls this observational hook while it captures tooling analysis. Return detached annotations only. Do not render, mutate the component, or depend on request state. The contribution can add known names or report that the extension preserves unenumerated extras, but it cannot change lint severity.

Parameters

Returns

TemplateNamespaceContribution | None: Portable namespace metadata, or ``None`` for no contribution.

View source

on_extension_created function

on_extension_created(ctx: OnExtensionCreatedContext) -> None

Called once when this extension instance is created.

View source

on_component_class_created function

on_component_class_created(ctx: OnComponentClassCreatedContext) -> None

Called after a Component class is defined, before it is registered.

View source

on_component_registered function

on_component_registered(ctx: OnComponentRegisteredContext) -> None

Called after a Component class is registered.

View source

on_component_unregistered function

on_component_unregistered(ctx: OnComponentUnregisteredContext) -> None

Called after a Component class is unregistered.

View source

on_citry_cleared function

on_citry_cleared(ctx: OnCitryClearedContext) -> None

Called during Citry.clear() after its registry is empty.

Citry calls this hook in the middle of Citry.clear(). Rendering from it raises RuntimeError. Do not make it wait for another thread that renders: that thread can wait for the clear to finish, so both can hang.

View source

on_component_input function

on_component_input(ctx: OnComponentInputContext) -> None

Called when a component starts rendering, before template_data.

Inspect or mutate ctx.kwargs / ctx.slots in place. These are the authoritative raw mappings. Citry normalizes Slots and constructs the component's final typed kwargs and slots once all input hooks finish.

View source

on_component_data function

on_component_data(ctx: OnComponentDataContext) -> None

Called after template_data; mutate ctx.template_data to add or change template variables.

View source

on_component_rendered function

on_component_rendered(ctx: OnComponentRenderedContext) -> CitryRender | str | None

Called after a component (and its children) rendered. Return new content to replace the output, raise to replace the error, or return None to keep the original.

The content follows the rule of a {{ ... }} value, on static and interactive pages alike: a CitryRender replaces the output, a plain str is shown as text (Citry escapes it), and Markup is inserted as HTML. Never pass user input to the Markup() constructor.

HTML you serialize inside the hook, such as str(ctx.render), is a plain str, so wrap it in Markup before you return it. It does not carry the component's own data-cid-* attribute; Citry adds it to the HTML you return.

Raises

  • TypeError - Citry raises it when the hook returns a value of any other type. It is raised as soon as the hook returns, before any later extension runs, and the message names the extension and the component.
View source

on_slot_rendered function

on_slot_rendered(ctx: OnSlotRenderedContext) -> RenderPart | None

Called after a <c-slot> site rendered (a fill, or the fallback).

Return new content to replace the output, or None to keep the original. Raising propagates. As with on_component_rendered, a CitryRender replaces the output, a plain str is shown as text (Citry escapes it), and Markup is inserted as HTML, on static and interactive pages alike.

Raises

  • TypeError - Citry raises it when the hook returns a value of any other type, such as a bare Placeholder or component. It is raised before any later extension runs, and the message names the extension and the slot. Return None or the unchanged ctx.result to keep the output.
View source

on_attrs_resolved function

on_attrs_resolved(ctx: OnAttrsResolvedContext) -> dict[str, Any] | None

Called after an HTML element's dynamic attributes resolved to their final dict, before it is formatted into the output. Return a new dict to replace the attributes, or None to keep them.

Fires per element per render, only for elements with at least one dynamic attribute (a c-* value or a c-bind spread).

Raises

  • TypeError - Citry raises it when the hook returns a value that is not a mapping, such as a dict, or None, before any later extension runs. The message names the extension.
  • VueCodeError - Citry raises VueCodeError when the hook adds an attribute name that Vue would read as code (one starting with v-, :, ., ^, @, or #), or changes the value of such an attribute written in the template. Removing one, or keeping it unchanged, is allowed.
View source

on_render_context_merge function

on_render_context_merge(ctx: OnRenderContextMergeContext) -> None

Called when a nested render's output is consumed by an enclosing render (a child component settling into its parent, or an already-rendered value embedded via an expression or slot).

Merge your extension's slice of ctx.child_context.extra into ctx.parent_context.extra, with your own policy (the dependencies extension, for example, appends records preserving order). The core does not merge anything itself.

View source

export_render_cache function

export_render_cache(ctx: OnRenderCacheExportContext) -> dict[str, object]

Export this payload extension's selected strict-JSON contribution.

Experimental

Part of the experimental "payload" render cache mode, where an extension saves its own data with a cached render and restores it on a cache hit. It may change in a minor release without a deprecation period.

View source

stage_render_cache function

stage_render_cache(ctx: OnRenderCacheStageContext) -> StagedRenderCacheContribution

Validate a detached payload and return a mutation-free replay contribution.

Experimental

Part of the experimental "payload" render cache mode, where an extension saves its own data with a cached render and restores it on a cache hit. It may change in a minor release without a deprecation period.

View source

render_cache_bypass_reason function

render_cache_bypass_reason() -> str | None

Return a stable reason when this extension requires live rendering.

View source

on_serialize function

on_serialize(ctx: OnSerializeContext) -> str | None

Called at the end of CitryRender.serialize() with the joined HTML.

Return a new string to replace the output (threaded across extensions), or None to keep it. This is where serialize-time work that needs the whole page happens; the dependencies extension places the collected JS/CSS here, using ctx.placeholders for the <c-js>/<c-css> positions.

Raises

  • TypeError - Citry raises it when the hook returns a value that is not a str or None, before any later extension runs. The message names the extension.
View source

browser_plugin function

browser_plugin() -> BrowserPluginDescriptor | None

Describe the fixed Vue plugin installed before an interactive app mounts.

Experimental

This method and the types it takes and returns are experimental. They may change in a minor release without a deprecation period.

View source

prepare_browser_render function

prepare_browser_render(ctx: OnBrowserRenderPrepareContext) -> BrowserRenderContribution | None

Project this extension's selected render records into prepared browser data.

Experimental

This method and the types it takes and returns are experimental. They may change in a minor release without a deprecation period.

View source

on_template_loaded function

on_template_loaded(ctx: OnTemplateLoadedContext) -> str | None

Called with a class's template string before it is parsed. Return a new string to modify it.

Citry usually calls this hook once per class, the first time a render needs the template. It can call it again for the same class: once per thread when several threads load the class for the first time at once, and up to three times on each thread when a component on the same Citry instance is reset (by reset_template(), reset_files(), or a hot reload) while the hook runs. Every render gets the same result, so return the same text for the same input, and make any side effect replace what an earlier call did instead of adding to it. The hook runs with no Citry lock held, so it may render other components.

Raises

  • TypeError - Citry raises it when the hook returns a value that is not a str or None, before any later extension runs. The message names the extension and the component.
View source

on_template_foreign_spans function

on_template_foreign_spans(ctx: OnTemplateForeignSpansContext) -> ForeignSpanSet | None

Declare UTF-8 source ranges owned by this extension's host engine.

Citry runs this hook with no Citry lock held. When several threads render a template for the first time at once, each may call it, and every render gets the same result, so return the same ranges for the same source.

Experimental

Part of the experimental API for embedding Citry tags in another template engine. It may change in a minor release without a deprecation period.

View source

on_messages_loaded function

on_messages_loaded(ctx: OnMessagesLoadedContext) -> str | None

Called with a source declaration's source-locale Fluent text before compilation. A parent and children that inherit the same messages share its result.

Return a new string to replace the source. User extensions run in installation order. The built-in i18n extension always runs after those transformations, so it compiles the same final string the asset loader caches.

Citry usually calls this hook once per declaration, the first time a render needs the messages. It can call it again for the same declaration: once per thread when several threads load the messages for the first time at once, and up to three times on each thread when a component on the same Citry instance is reset (by reset_template(), reset_files(), or a hot reload) while the hook runs. Every render gets the same result, so return the same text for the same input, and make any side effect replace what an earlier call did instead of adding to it. The hook runs with no Citry lock held, so it may render other components.

Raises

  • TypeError - Citry raises it when the hook returns a value that is not a str or None, before any later extension runs. The message names the extension and the component.
View source

on_template_compiled function

on_template_compiled(ctx: OnTemplateCompiledContext) -> list[BodyItem] | None

Called with the node list Citry builds from a component's template, before Citry keeps it for later renders. Citry builds one per template and set of Const values. Mutate the list in place or return a new list.

Citry usually calls this hook once per list. It can call it again for the same list: once per thread when several threads render the template for the first time at once, and up to three times on each thread when a component on the same Citry instance is reset or unregistered, or the instance is cleared, while Citry builds the list. Every render gets the same result, so return the same nodes for the same input, and make any side effect replace what an earlier call did instead of adding to it. The hook runs with no Citry lock held, so it may render other components.

Raises

  • TypeError - Citry raises it when the hook returns a value that is not a sequence, such as a list, or None, before any later extension runs. A str is not accepted. The message names the extension and the component.
View source

on_template_foreign_compiled function

on_template_foreign_compiled(ctx: OnTemplateForeignCompiledContext) -> list[BodyItem] | None

Replace this provider's claims in one independent body list.

The provider must call ctx.mark_resolved exactly once for every claim it handles. Core verifies the ledger before general compiled hooks run.

Like on_template_compiled, this hook runs with no Citry lock held and can run more than once for the same node list; every render gets the same result.

Experimental

Part of the experimental API for embedding Citry tags in another template engine. It may change in a minor release without a deprecation period.

Raises

  • TypeError - Citry raises it when the hook returns a value that is not a sequence, such as a list, or None, before any later extension runs. A str is not accepted. The message names the extension and the component.
View source

on_template_reset function

on_template_reset(ctx: OnTemplateResetContext) -> None

Called after a component class's loaded template is reset.

Citry calls this hook in the middle of the reset. The hook may render components on the same thread. Do not make it wait for another thread that renders, such as by joining a thread or a future: that thread waits for the reset to finish, so both can hang.

View source

on_js_loaded function

on_js_loaded(ctx: OnJsLoadedContext) -> str | None

Called with the component's primary JS content (inline or read from js_file). Return a new string to modify it.

Citry usually calls this hook once per class, the first time a render needs the JavaScript. It can call it again for the same class: once per thread when several threads load the class for the first time at once, and up to three times on each thread when a component on the same Citry instance is reset (by reset_template(), reset_files(), or a hot reload) while the hook runs. Every render gets the same result, so return the same text for the same input, and make any side effect replace what an earlier call did instead of adding to it. The hook runs with no Citry lock held, so it may render other components.

Raises

  • TypeError - Citry raises it when the hook returns a value that is not a str or None, before any later extension runs. The message names the extension and the component.
View source

on_css_loaded function

on_css_loaded(ctx: OnCssLoadedContext) -> str | None

Called with the component's primary CSS content (inline or read from css_file). Return a new string to modify it.

Citry usually calls this hook once per class, the first time a render needs the CSS. It can call it again for the same class: once per thread when several threads load the class for the first time at once, and up to three times on each thread when a component on the same Citry instance is reset (by reset_template(), reset_files(), or a hot reload) while the hook runs. Every render gets the same result, so return the same text for the same input, and make any side effect replace what an earlier call did instead of adding to it. The hook runs with no Citry lock held, so it may render other components.

Raises

  • TypeError - Citry raises it when the hook returns a value that is not a str or None, before any later extension runs. The message names the extension and the component.
View source

ExtensionConfig class

Base for the per-component nested config class (reached as Extension.Config).

An extension named "view" (class_name == "View") lets a user define a nested class View: on a component. The manager rebuilds that nested class as a subclass of this base (binding component_class), then instantiates it per render and attaches it as component.view.

The config holds only a weak reference to its component, and the component may be None when an extension creates the config outside a component render.

View source

component_class attribute

component_class: type[Component]

The Component class this config is defined on (bound by the manager).

View source

component attribute

component: Component

The owning Component instance.

Raises

  • RuntimeError - If this config was created without a component, or the component has been garbage-collected.
View source

ExtensionManager class

Fans each lifecycle hook out across a Citry instance's extensions.

Each Citry instance builds one manager when it is created. A component class is bound to its Citry instance (and so to these extensions) when the class is defined, so the extensions are always present when a hook fires.

For each hook name, the manager calls only the extensions that override that hook, so an extension that does not implement a hook costs nothing. emit() uses the same lookup by name, which lets extensions fire their own custom hooks (e.g. on_dependencies).

View source

get_extension_command function

get_extension_command(name: str, command_name: str) -> type[ExtensionCommand]
View source

commands attribute

commands: dict[str, tuple[type[ExtensionCommand], ...]]

Every extension's CLI commands, keyed by extension name (read as Citry.commands).

Built-in extensions come first (they are prepended at construction), then the user's extensions in spec order; only extensions that declare commands appear. Extension names are unique (enforced at construction), so the keys never collide. The CLI reaches a command as citry ext run <extension name> <command name>.

View source

urls attribute

urls: tuple[URLRoute, ...]

The combined route table of every extension (read as Citry.urls).

Built-in extensions own their paths directly (e.g. the dependencies extension's cache/... and citry.js); a user extension's routes are namespaced under ext/<extension name>/ so they cannot collide with citry's own or each other's.

View source

emit function

emit(name: str, ctx: Any, result: _Result = 'none', field: str | None = None) -> Any

Dispatch hook name to the extensions that define it, combining the hooks' returned values per result:

  • "none": call every extension, ignore returns; return None.
  • "first": return the first non-None return (short-circuit).
  • "map": thread ctx.<field> - each non-None return replaces it (via dataclasses.replace) and is passed to the next extension; the final field value is returned.

An extension takes part by defining a method called name. name need not be a hook declared on Extension, so an extension can fire its own custom hook for others to implement.

Example

on_render_context_merge notifies every extension that defines it ("none")::

manager.emit("on_render_context_merge", ctx)

A custom hook can thread ctx.value through the extensions ("map") and return the final value. emit does not check the type of each return, so the code that reads the result should check it::

manager.emit(
    "on_my_value",
    ctx,
    result="map",
    field="value",
)

A custom hook can let an extension short-circuit ("first" returns the first non-None value)::

manager.emit("on_my_event", ctx, result="first")
View source

on_extension_created function

on_extension_created() -> None

Run the on_extension_created hook on every extension that implements it.

Internal: Citry calls this method itself. It is not covered by Citry's API stability promise and may change in any release. To react to this hook, implement Extension.on_extension_created() on your extension instead.

View source

on_component_class_created function

on_component_class_created(component_class: type[Component]) -> None

Run the on_component_class_created hook on every extension that implements it.

Internal: Citry calls this method itself. It is not covered by Citry's API stability promise and may change in any release. To react to this hook, implement Extension.on_component_class_created() on your extension instead.

View source

on_component_registered function

on_component_registered(name: str, component_class: type[Component]) -> None

Run the on_component_registered hook on every extension that implements it.

Internal: Citry calls this method itself. It is not covered by Citry's API stability promise and may change in any release. To react to this hook, implement Extension.on_component_registered() on your extension instead.

View source

on_component_unregistered function

on_component_unregistered(name: str, component_class: type[Component]) -> None

Run the on_component_unregistered hook on every extension that implements it.

Internal: Citry calls this method itself. It is not covered by Citry's API stability promise and may change in any release. To react to this hook, implement Extension.on_component_unregistered() on your extension instead.

View source

on_citry_cleared function

on_citry_cleared() -> None

Run the on_citry_cleared hook on every extension that implements it.

Internal: Citry calls this method itself. It is not covered by Citry's API stability promise and may change in any release. To react to this hook, implement Extension.on_citry_cleared() on your extension instead.

View source

on_component_input function

on_component_input(component: Component) -> None

Run the on_component_input hook on every extension that implements it.

Internal: Citry calls this method itself. It is not covered by Citry's API stability promise and may change in any release. To react to this hook, implement Extension.on_component_input() on your extension instead.

View source

on_component_data function

on_component_data(component: Component, context: CitryContext, template_data: dict[str, Any], js_data: dict[str, Any], css_data: dict[str, Any]) -> None

Run the on_component_data hook on every extension that implements it.

Internal: Citry calls this method itself. It is not covered by Citry's API stability promise and may change in any release. To react to this hook, implement Extension.on_component_data() on your extension instead.

View source

on_render_context_merge function

on_render_context_merge(parent_context: CitryContext, child_context: CitryContext) -> None

Run the on_render_context_merge hook on every extension that implements it.

Internal: Citry calls this method itself. It is not covered by Citry's API stability promise and may change in any release. To react to this hook, implement Extension.on_render_context_merge() on your extension instead.

View source

on_serialize function

on_serialize(context: CitryContext, selected_render: CitryRender, html: str, placeholders: dict[str, str], deps_strategy: str, deps_position: str, _script_security: _ScriptSecurityMaterializer | None = None, _security_csp: SecurityCspMode = 'off', _javascript_policy: _JavascriptPolicy | None = None, _security_javascript: SecurityJavascriptMode = 'allow') -> str

Run the on_serialize hook on every extension that implements it.

Internal: Citry calls this method itself. It is not covered by Citry's API stability promise and may change in any release. To react to this hook, implement Extension.on_serialize() on your extension instead.

View source

on_component_rendered function

on_component_rendered(component: Component, render: CitryRender | str | None, error: Exception | None, context: CitryContext | None = None) -> tuple[CitryRender | str | None, Exception | None, bool]

Thread the rendered output through the extensions; a return replaces the render, a raise replaces the error.

With context (the component's render context), a returned str or Markup becomes a render in that context before the next extension sees it, so a plain str stays text along the chain.

Internal: Citry calls this method itself. It is not covered by Citry's API stability promise and may change in any release. To react to this hook, implement Extension.on_component_rendered() on your extension instead.

View source

on_slot_rendered function

on_slot_rendered(component: Component, slot: Slot, slot_name: str, slot_node: SlotNode, slot_is_required: bool, result: RenderPart, context: CitryContext | None = None) -> RenderPart

Thread a slot's rendered output through the extensions; a return replaces the result, a raise propagates.

With context (the context of the <c-slot> site), a returned str or Markup becomes a render in that context before the next extension sees it, so a plain str stays text along the chain.

Internal: Citry calls this method itself. It is not covered by Citry's API stability promise and may change in any release. To react to this hook, implement Extension.on_slot_rendered() on your extension instead.

View source

has_hook function

has_hook(name: str) -> bool

Whether any installed extension implements the hook name.

View source

has_attrs_resolved_hook function

has_attrs_resolved_hook(runtime_candidate: bool = True) -> bool

Whether this element has an applicable resolved-attributes hook.

Internal: Citry calls this method itself. It is not covered by Citry's API stability promise and may change in any release.

View source

on_attrs_resolved function

on_attrs_resolved(component: Component, tag_name: str, attrs: dict[str, Any], runtime_candidate: bool = True) -> dict[str, Any]

Thread an element's resolved attribute dict through the extensions; a return replaces the dict, a raise propagates.

This sits on a per-element per-render hot path, so when no extension implements the hook the dict is returned without building a context.

Internal: Citry calls this method itself. It is not covered by Citry's API stability promise and may change in any release. To react to this hook, implement Extension.on_attrs_resolved() on your extension instead.

View source

on_template_loaded function

on_template_loaded(component_class: type[Component], content: str, template_id: str = '', origin: str = '', template_kind: Literal['primary', 'standalone', 'nested'] = 'primary') -> str

Run the on_template_loaded hook on every extension that implements it.

Internal: Citry calls this method itself. It is not covered by Citry's API stability promise and may change in any release. To react to this hook, implement Extension.on_template_loaded() on your extension instead.

View source

on_template_foreign_spans function

on_template_foreign_spans(component_class: type[Component], content: str, template_id: str, origin: str, template_kind: Literal['primary', 'standalone', 'nested'], foreign_compile_contexts: Mapping[str, object] | None = None) -> tuple[tuple[object, ...], dict[str, object | None]]

Collect provider spans against one final post-load source string.

Internal: Citry calls this method itself. It is not covered by Citry's API stability promise and may change in any release. To react to this hook, implement Extension.on_template_foreign_spans() on your extension instead.

View source

on_template_foreign_compiled function

on_template_foreign_compiled(component_class: type[Component], nodes: list[BodyItem], provider_metadata: Mapping[str, object | None], template_id: str, origin: str, template_kind: Literal['primary', 'standalone', 'nested']) -> list[BodyItem]

Resolve every provider claim, one independently compiled body at a time.

Internal: Citry calls this method itself. It is not covered by Citry's API stability promise and may change in any release. To react to this hook, implement Extension.on_template_foreign_compiled() on your extension instead.

View source

on_messages_loaded function

on_messages_loaded(component_class: type[Component], declaration_owner: type, content: str, origin: str) -> str

Run the on_messages_loaded hook on every extension that implements it.

Internal: Citry calls this method itself. It is not covered by Citry's API stability promise and may change in any release. To react to this hook, implement Extension.on_messages_loaded() on your extension instead.

View source

on_template_compiled function

on_template_compiled(component_class: type[Component], nodes: list[BodyItem], template_id: str = '', origin: str = '', template_kind: Literal['primary', 'standalone', 'nested'] = 'primary') -> list[BodyItem]

Run the on_template_compiled hook on every extension that implements it.

Internal: Citry calls this method itself. It is not covered by Citry's API stability promise and may change in any release. To react to this hook, implement Extension.on_template_compiled() on your extension instead.

View source

on_template_reset function

on_template_reset(component_class: type[Component]) -> None

Notify extensions after a component class's template is reset.

Internal: Citry calls this method itself. It is not covered by Citry's API stability promise and may change in any release. To react to this hook, implement Extension.on_template_reset() on your extension instead.

View source

on_js_loaded function

on_js_loaded(component_class: type[Component], content: str) -> str

Run the on_js_loaded hook on every extension that implements it.

Internal: Citry calls this method itself. It is not covered by Citry's API stability promise and may change in any release. To react to this hook, implement Extension.on_js_loaded() on your extension instead.

View source

on_css_loaded function

on_css_loaded(component_class: type[Component], content: str) -> str

Run the on_css_loaded hook on every extension that implements it.

Internal: Citry calls this method itself. It is not covered by Citry's API stability promise and may change in any release. To react to this hook, implement Extension.on_css_loaded() on your extension instead.

View source

on_files_reset function

on_files_reset(component_class: type[Component]) -> None

Notify extensions that a component class's loaded asset files were reset, so each drops its own per-class state (the dependencies built-in drops its merged result here).

This hook is not declared on the Extension base. An extension receives it by defining a method named on_files_reset. Citry calls it in the middle of the reset. The method may render components on the same thread. Do not make it wait for another thread that renders: that thread waits for the reset to finish, so both can hang.

Internal: Citry calls this method itself. It is not covered by Citry's API stability promise and may change in any release. The on_files_reset method an extension defines is public.

View source

ExtensionCommand class

Base class for an extension's CLI command.

Subclass this, set name (and usually help), declare any arguments, and define handle to do the work. A command that only groups subcommands leaves handle unset, and the runner prints its help instead of running anything. Citry turns the declarations into an argparse parser and runs the matching command; an extension lists its command classes in Extension.commands and a user reaches one as citry ext run <extension> <command>. An extension declares HTTP routes separately, in Extension.urls.

View source

name attribute

name: str

The command name (citry ext run <extension> <name>).

View source

help attribute

help: str

One-line description of the command, shown in --help output.

View source

arguments attribute

Positional arguments and options, declared with CommandArg.

View source

subcommands attribute

Nested commands. A command with subcommands usually has no handle of its own.

View source

subparser_input attribute

subparser_input: CommandSubcommand | None

Optional customization of how this command appears when nested under a parent.

View source

handle attribute

handle: CommandHandler | None

Runs the command, called with the parsed options as keyword arguments. None (the default) marks a command that only groups subcommands; a real command overrides this with def handle(self, **kwargs).

View source

citry attribute

citry: Citry | None

The engine the command runs against, bound by the runner before handle is called (like Extension.citry). A command's handle reads it to reach the component registry and the installed extensions.

View source

Debug class

Bases: Extension

Draw development-only boundaries around component and slot output.

Install this extension explicitly with Citry(extensions=[Debug]). Its per-component config has two exact boolean fields, highlight_components and highlight_slots. Set them globally in extensions_defaults["debug"] or override them in a component's nested class Debug.

The visual boundaries are real div elements. They are useful for inspecting ordinary page structure, but can affect layout, direct-child selectors, and restricted table or select content models. Do not enable them in production or use them for layout-sensitive verification.

Example

Enable both boundary types for one engine:

from citry import Citry
from citry.ext.debug import Debug

app = Citry(
    extensions=[Debug],
    extensions_defaults={
        "debug": {
            "highlight_components": True,
            "highlight_slots": True,
        },
    },
)
View source

CommandArg class

One positional argument or option, mirroring ArgumentParser.add_argument.

Every field maps to the add_argument keyword of the same name, and Citry passes the fields to argparse unchanged. A field left as None is not passed, so argparse applies its own default.

View source

name_or_flags attribute

name_or_flags: str | Sequence[str]

A positional name ("path") or a list of option flags (["--shout", "-s"]).

View source

action attribute

action: CommandAction | Action | None
View source

nargs attribute

nargs: int | Literal['*', '+', '?'] | None
View source

to_add_argument_kwargs function

to_add_argument_kwargs() -> dict[str, Any]

The add_argument keywords for this argument, minus name_or_flags.

Citry passes name_or_flags to add_argument positionally, so it is not included here; unset (None) fields are dropped.

View source

CommandArgGroup class

A titled group of arguments, mirroring ArgumentParser.add_argument_group.

Place one in a command's arguments list to group related options together in the --help output.

View source

CommandSubcommand class

How a command appears when nested under a parent, mirroring add_subparsers().add_parser.

A command sets this as its subparser_input to customize its entry in the parent's subcommand list (for example a different help line or program name).

View source

to_add_parser_kwargs function

to_add_parser_kwargs() -> dict[str, Any]

The add_parser keywords for this subcommand entry (unset fields dropped).

View source

OnAttrsResolvedContext class

View source

citry attribute

citry: Citry

The Citry instance the component belongs to.

View source

component attribute

component: Component

The component whose template holds the element. For <c-element>, this is the lexical owner, not the transparent built-in renderer.

View source

tag_name attribute

tag_name: str

The HTML tag the attributes belong to (e.g. "div").

View source

attrs attribute

attrs: dict[str, Any]

The resolved attribute dict: class/style already normalized to strings, booleans still True, omitted attributes already absent.

View source

OnCitryClearedContext class

View source

citry attribute

citry: Citry

The Citry instance whose registry and engine caches were cleared.

View source

OnComponentClassCreatedContext class

View source

citry attribute

citry: Citry

The Citry instance the component class belongs to.

View source

component_class attribute

component_class: type[Component]

The created Component class.

View source

nested_declarations function

nested_declarations(name: str) -> tuple[NestedClassDeclaration, ...]

Return the exact authored bindings for name in component C3 order.

A record whose value is None is an explicit reset, distinct from the name being absent. The classes are the original source objects, even after Citry replaces the component attribute with an effective runtime config class.

Parameters

  • name str - The nested declaration name, usually the extension's class_name.

Returns

tuple[NestedClassDeclaration, ...]: The declarations from the component through its bases in C3 order.

View source

OnComponentDataContext class

View source

citry attribute

citry: Citry

The Citry instance the component belongs to.

View source

component attribute

component: Component

The Component instance being rendered.

View source

context attribute

context: CitryContext

The render-scoped CitryContext for this component's render. Extensions stash tree-wide state in context.extra (for example the dependencies extension's render records); it bubbles up through on_render_context_merge as nested renders are consumed. context.provides is not yet populated when this hook fires.

View source

template_data attribute

template_data: dict[str, Any]

The template variables from Component.template_data() (mutable).

View source

js_data attribute

js_data: dict[str, Any]

The JS variables from Component.js_data() (mutable). Citry sends them to the browser as fields on the component's Vue instance, and checks the keys after this hook runs.

View source

css_data attribute

css_data: dict[str, Any]

The CSS variables from Component.css_data() (mutable). Consumed by the built-in dependencies extension.

View source

OnComponentInputContext class

View source

citry attribute

citry: Citry

The Citry instance the component belongs to.

View source

component attribute

component: Component

The Component instance being rendered.

View source

kwargs attribute

kwargs: dict[str, Any]

The keyword arguments passed to the component (mutable plain dict).

View source

slots attribute

slots: dict[str, Any]

The slot fills passed to the component (mutable plain dict).

View source

OnComponentRegisteredContext class

View source

citry attribute

citry: Citry

The Citry instance the component was registered with.

View source

name attribute

name: str

The name the component was registered under.

View source

component_class attribute

component_class: type[Component]

The registered Component class.

View source

OnComponentRenderedContext class

View source

citry attribute

citry: Citry

The Citry instance the component belongs to.

View source

component attribute

component: Component

The Component instance that was rendered.

View source

render attribute

render: CitryRender | str | None

The rendered output, or None if rendering failed.

View source

error attribute

error: Exception | None

The error raised during rendering, or None if it succeeded.

View source

OnComponentUnregisteredContext class

View source

citry attribute

citry: Citry

The Citry instance the component was unregistered from.

View source

name attribute

name: str

The name the component was registered under.

View source

component_class attribute

component_class: type[Component]

The unregistered Component class.

View source

OnCssLoadedContext class

View source

citry attribute

citry: Citry

The Citry instance the component class belongs to.

View source

component_class attribute

component_class: type[Component]

The Component class whose CSS was loaded.

View source

content attribute

content: str

The CSS content (inline or read from css_file).

View source

OnExtensionCreatedContext class

View source

citry attribute

citry: Citry

The Citry instance the extension belongs to.

View source

extension attribute

extension: Extension

The created extension instance.

View source

OnFilesResetContext class

View source

citry attribute

citry: Citry

The Citry instance the component class belongs to.

View source

component_class attribute

component_class: type[Component]

The Component class whose loaded asset files were reset.

View source

OnJsLoadedContext class

View source

citry attribute

citry: Citry

The Citry instance the component class belongs to.

View source

component_class attribute

component_class: type[Component]

The Component class whose JS was loaded.

View source

content attribute

content: str

The JS content (inline or read from js_file).

View source

OnMessagesLoadedContext class

View source

citry attribute

citry: Citry

The Citry instance the component class belongs to.

View source

component_class attribute

component_class: type[Component]

The Component class whose source messages were loaded.

View source

declaration_owner attribute

declaration_owner: type

The class that authored the inherited messages/messages_file pair.

View source

content attribute

content: str

The source-locale Fluent text before compilation.

View source

origin attribute

origin: str

A file path or inline module::Class.messages label for diagnostics.

View source

OnRenderContextMergeContext class

View source

citry attribute

citry: Citry

The Citry instance the render belongs to.

View source

parent_context attribute

parent_context: CitryContext

The context of the render that consumed the nested one.

View source

child_context attribute

child_context: CitryContext

The context of the consumed nested render.

View source

OnSerializeContext class

View source

citry attribute

citry: Citry

The Citry instance the render belongs to.

View source

context attribute

context: CitryContext

The root render's CitryContext. Its extra holds everything that bubbled up during the render. One render can be serialized several times, and every serialize() call sees the same extra: a value your on_serialize hook stores there is still there the next time the hook runs, so set or delete it on every run rather than adding it only when it is missing.

View source

selected_render attribute

selected_render: CitryRender

The final hook-selected render tree being serialized.

View source

html attribute

html: str

The joined HTML (threaded: return a new string to replace it).

View source

placeholders attribute

placeholders: dict[str, str]

The placeholder parts found during serialization: unique placeholder id (the Placeholder.key plus a counter and a private serialization identity) to the exact text standing in for it in html. Match the key prefix rather than relying on the private suffix.

View source

deps_strategy attribute

deps_strategy: str

The serialize(deps_strategy=...) argument.

View source

deps_position attribute

deps_position: str

The serialize(deps_position=...) argument.

View source

OnSlotRenderedContext class

View source

citry attribute

citry: Citry

The Citry instance the component belongs to.

View source

component attribute

component: Component

The component whose template holds the <c-slot> that was rendered.

View source

slot attribute

slot: Slot

The Slot that was rendered: the fill, or the fallback when no fill was given.

View source

slot_name attribute

slot_name: str

The resolved slot name ("default" for an unnamed slot).

View source

slot_node attribute

slot_node: SlotNode

The runtime SlotNode at whose site the slot rendered.

View source

slot_is_required attribute

slot_is_required: bool

Whether the slot resolved as required.

View source

result attribute

result: RenderPart

The rendered output (a str or a CitryRender).

View source

ForeignSpan class

One half-open UTF-8 byte range declared by a host provider.

Experimental

Part of the experimental API for embedding Citry tags in another template engine. It may change in a minor release without a deprecation period.

View source

ForeignSpanSet class

The spans and private compile metadata returned by one provider.

Experimental

Part of the experimental API for embedding Citry tags in another template engine. It may change in a minor release without a deprecation period.

View source

ForeignCompileContext class

Bases: Protocol

Typed adapter state participating in standalone-template cache identity.

Experimental

Part of the experimental API for embedding Citry tags in another template engine. It may change in a minor release without a deprecation period.

View source

provider attribute

provider: str

Name of the extension allowed to receive this context.

View source

cache_fingerprint attribute

cache_fingerprint: str | bytes

Deterministic identity for compile-time host state.

View source

OnTemplateForeignSpansContext class

Hold the template source that a foreign-spans hook receives.

Extension.on_template_foreign_spans() gets this object. The extension that implements the hook reads content and marks the parts that another template engine owns.

Experimental

Part of the experimental API for embedding Citry tags in another template engine. It may change in a minor release without a deprecation period.

View source

OnTemplateCompiledContext class

View source

citry attribute

citry: Citry

The Citry instance the component class belongs to.

View source

component_class attribute

component_class: type[Component]

The Component class whose template was compiled.

View source

nodes attribute

nodes: list[BodyItem]

The generated body node list.

View source

template_id attribute

template_id: str

Immutable identity of the exact template record being compiled.

View source

origin attribute

origin: str

Authored source origin used for diagnostics.

View source

template_kind attribute

template_kind: Literal['primary', 'standalone', 'nested']
View source

ForeignClaim class

One provider-owned runtime claim in an independently compiled body.

Experimental

Part of the experimental API for embedding Citry tags in another template engine. It may change in a minor release without a deprecation period.

View source

OnTemplateForeignCompiledContext class

Owner-dispatched compilation context for one independent body list.

Experimental

Part of the experimental API for embedding Citry tags in another template engine. It may change in a minor release without a deprecation period.

View source

template_kind attribute

template_kind: Literal['primary', 'standalone', 'nested']
View source

mark_resolved function

mark_resolved(*claims: ForeignClaim = ()) -> None

Record explicit outcomes for claims replaced by the provider.

View source

compiled_body function

compiled_body(nodes: list[BodyItem]) -> CompiledBody

Compile and protect one Citry run for later host-selected rendering.

View source

OnTemplateLoadedContext class

View source

citry attribute

citry: Citry

The Citry instance the component class belongs to.

View source

component_class attribute

component_class: type[Component]

The Component class whose template was loaded.

View source

content attribute

content: str

The template string (before parsing).

View source

template_id attribute

template_id: str

Immutable identity of this loaded template record.

View source

origin attribute

origin: str

Source origin used for diagnostics.

View source

template_kind attribute

template_kind: Literal['primary', 'standalone', 'nested']
View source

OnTemplateResetContext class

View source

citry attribute

citry: Citry

The Citry instance the component class belongs to.

View source

component_class attribute

component_class: type[Component]

The Component class whose loaded template was reset.

View source

TemplateNamespaceContext class

Give an extension one component whose template namespace it can describe.

View source

citry attribute

citry: Citry

The owning Citry instance.

View source

component_class attribute

component_class: type[Component]

The temporary live component class from the registry snapshot.

View source

TemplateNamespaceContribution class

Publish analysis-only variables added by one installed extension.

Attributes

  • template_variables Mapping[str, object] - Variable names mapped to annotations, using the same format as LintSettings.template_variables.
  • allows_extra_variables bool - Whether this extension intentionally preserves additional names that it cannot enumerate. Such unknown names are linted as warnings, never silently accepted.
Citry version: 0.9.0