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().
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.
name attribute
name: strName 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.
class_name attribute
class_name: strPascalCase name of the per-component nested config class, derived from name at subclass creation (my_extension -> MyExtension).
Config attribute
Config: type[ExtensionConfig]Base class the per-component nested config inherits from.
commands attribute
commands: list[type[ExtensionCommand]]CLI commands this extension provides (see ExtensionCommand).
introspection_version attribute
introspection_version: int | NonePositive schema version when this extension publishes component metadata.
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.
render_cache_version attribute
render_cache_version: int | NonePositive compatibility version for stateless or payload replay.
citry attribute
citry: CitryThe 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).
urls attribute
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.
validate_config_fields function
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_defaultssetting (componentisNone). - At component class definition, with the fields declared on the component's nested config class, including fields from its user-written base classes (
componentis 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
fieldsMapping[str, Any]- The declared fields, mapping field name to declared value. For a nested config class, dunder names, theConfigbase's members, and citry's own bookkeeping attributes are already filtered out.componenttype[Component] | None- The component class the fields were declared on, orNonewhen the fields come from theextensions_defaultssetting.
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).
inspect_component function
inspect_component(ctx: ComponentIntrospectionContext) -> dict[str, object] | NonePublish 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
ctxComponentIntrospectionContext- The owning engine, temporary live component class, and its already-built core metadata record.ctx.info.extensionsis always empty.
Returns
dict[str, object] | None: An extension-owned JSON object, or ``None``.
inspect_template_namespace function
inspect_template_namespace(ctx: TemplateNamespaceContext) -> TemplateNamespaceContribution | NoneDescribe 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
ctxTemplateNamespaceContext- The owning engine and temporary component class.
Returns
TemplateNamespaceContribution | None: Portable namespace metadata, or ``None`` for no contribution.
on_extension_created function
on_extension_created(ctx: OnExtensionCreatedContext) -> NoneCalled once when this extension instance is created.
on_component_class_created function
on_component_class_created(ctx: OnComponentClassCreatedContext) -> NoneCalled after a Component class is defined, before it is registered.
on_component_registered function
on_component_registered(ctx: OnComponentRegisteredContext) -> NoneCalled after a Component class is registered.
on_component_unregistered function
on_component_unregistered(ctx: OnComponentUnregisteredContext) -> NoneCalled after a Component class is unregistered.
on_citry_cleared function
on_citry_cleared(ctx: OnCitryClearedContext) -> NoneCalled 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.
on_component_input function
on_component_input(ctx: OnComponentInputContext) -> NoneCalled 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.
on_component_data function
on_component_data(ctx: OnComponentDataContext) -> NoneCalled after template_data; mutate ctx.template_data to add or change template variables.
on_component_rendered function
on_component_rendered(ctx: OnComponentRenderedContext) -> CitryRender | str | NoneCalled 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.
on_slot_rendered function
on_slot_rendered(ctx: OnSlotRenderedContext) -> RenderPart | NoneCalled 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 barePlaceholderor component. It is raised before any later extension runs, and the message names the extension and the slot. ReturnNoneor the unchangedctx.resultto keep the output.
on_attrs_resolved function
on_attrs_resolved(ctx: OnAttrsResolvedContext) -> dict[str, Any] | NoneCalled 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 adict, orNone, before any later extension runs. The message names the extension.VueCodeError- Citry raisesVueCodeErrorwhen the hook adds an attribute name that Vue would read as code (one starting withv-,:,.,^,@, or#), or changes the value of such an attribute written in the template. Removing one, or keeping it unchanged, is allowed.
on_render_context_merge function
on_render_context_merge(ctx: OnRenderContextMergeContext) -> NoneCalled 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.
export_render_cache function
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.
stage_render_cache function
stage_render_cache(ctx: OnRenderCacheStageContext) -> StagedRenderCacheContributionValidate 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.
render_cache_bypass_reason function
render_cache_bypass_reason() -> str | NoneReturn a stable reason when this extension requires live rendering.
on_serialize function
on_serialize(ctx: OnSerializeContext) -> str | NoneCalled 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 astrorNone, before any later extension runs. The message names the extension.
browser_plugin function
browser_plugin() -> BrowserPluginDescriptor | NoneDescribe 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.
prepare_browser_render function
prepare_browser_render(ctx: OnBrowserRenderPrepareContext) -> BrowserRenderContribution | NoneProject 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.
on_template_loaded function
on_template_loaded(ctx: OnTemplateLoadedContext) -> str | NoneCalled 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 astrorNone, before any later extension runs. The message names the extension and the component.
on_template_foreign_spans function
on_template_foreign_spans(ctx: OnTemplateForeignSpansContext) -> ForeignSpanSet | NoneDeclare 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.
on_messages_loaded function
on_messages_loaded(ctx: OnMessagesLoadedContext) -> str | NoneCalled 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 astrorNone, before any later extension runs. The message names the extension and the component.
on_template_compiled function
on_template_compiled(ctx: OnTemplateCompiledContext) -> list[BodyItem] | NoneCalled 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 alist, orNone, before any later extension runs. Astris not accepted. The message names the extension and the component.
on_template_foreign_compiled function
on_template_foreign_compiled(ctx: OnTemplateForeignCompiledContext) -> list[BodyItem] | NoneReplace 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 alist, orNone, before any later extension runs. Astris not accepted. The message names the extension and the component.
on_template_reset function
on_template_reset(ctx: OnTemplateResetContext) -> NoneCalled 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.
on_js_loaded function
on_js_loaded(ctx: OnJsLoadedContext) -> str | NoneCalled 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 astrorNone, before any later extension runs. The message names the extension and the component.
on_css_loaded function
on_css_loaded(ctx: OnCssLoadedContext) -> str | NoneCalled 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 astrorNone, before any later extension runs. The message names the extension and the component.
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.
component_class attribute
The Component class this config is defined on (bound by the manager).
component attribute
component: ComponentThe owning Component instance.
Raises
RuntimeError- If this config was created without a component, or the component has been garbage-collected.
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).
get_extension_command function
get_extension_command(name: str, command_name: str) -> type[ExtensionCommand] 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>.
urls attribute
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.
emit function
Dispatch hook name to the extensions that define it, combining the hooks' returned values per result:
"none": call every extension, ignore returns; returnNone."first": return the first non-Nonereturn (short-circuit)."map": threadctx.<field>- each non-Nonereturn replaces it (viadataclasses.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")
on_extension_created function
on_extension_created() -> NoneRun 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.
on_component_class_created function
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.
on_component_registered function
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.
on_component_unregistered function
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.
on_citry_cleared function
on_citry_cleared() -> NoneRun 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.
on_component_input function
on_component_input(component: Component) -> NoneRun 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.
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]) -> NoneRun 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.
on_render_context_merge function
on_render_context_merge(parent_context: CitryContext, child_context: CitryContext) -> NoneRun 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.
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') -> strRun 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.
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.
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) -> RenderPartThread 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.
has_attrs_resolved_hook function
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.
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.
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') -> strRun 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.
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.
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.
on_messages_loaded function
on_messages_loaded(component_class: type[Component], declaration_owner: type, content: str, origin: str) -> strRun 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.
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.
on_template_reset function
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.
on_js_loaded function
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.
on_css_loaded function
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.
on_files_reset function
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.
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.
arguments attribute
arguments: Sequence[CommandArg | CommandArgGroup]Positional arguments and options, declared with CommandArg.
subcommands attribute
subcommands: Sequence[type[ExtensionCommand]]Nested commands. A command with subcommands usually has no handle of its own.
subparser_input attribute
subparser_input: CommandSubcommand | NoneOptional customization of how this command appears when nested under a parent.
handle attribute
handle: CommandHandler | NoneRuns 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).
citry attribute
citry: Citry | NoneThe 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.
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, }, }, )
on_component_registered function
on_component_registered(ctx: OnComponentRegisteredContext) -> None on_component_unregistered function
on_component_unregistered(ctx: OnComponentUnregisteredContext) -> None render_cache_bypass_reason function
render_cache_bypass_reason() -> str | NoneRequire live rendering while debug highlighting is enabled.
on_component_rendered function
on_component_rendered(ctx: OnComponentRenderedContext) -> CitryRender | None on_slot_rendered function
on_slot_rendered(ctx: OnSlotRenderedContext) -> CitryRender | None on_render_context_merge function
on_render_context_merge(ctx: OnRenderContextMergeContext) -> None 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.
name_or_flags attribute
A positional name ("path") or a list of option flags (["--shout", "-s"]).
to_add_argument_kwargs function
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.
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.
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).
to_add_parser_kwargs function
The add_parser keywords for this subcommand entry (unset fields dropped).
OnAttrsResolvedContext class
component attribute
component: ComponentThe component whose template holds the element. For <c-element>, this is the lexical owner, not the transparent built-in renderer.
attrs attribute
The resolved attribute dict: class/style already normalized to strings, booleans still True, omitted attributes already absent.
OnCitryClearedContext class
citry attribute
citry: CitryThe Citry instance whose registry and engine caches were cleared.
OnComponentClassCreatedContext class
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
namestr- The nested declaration name, usually the extension'sclass_name.
Returns
tuple[NestedClassDeclaration, ...]: The declarations from the component through its bases in C3 order.
OnComponentDataContext class
context attribute
context: CitryContextThe 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.
template_data attribute
The template variables from Component.template_data() (mutable).
js_data attribute
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.
css_data attribute
The CSS variables from Component.css_data() (mutable). Consumed by the built-in dependencies extension.
OnComponentInputContext class
OnComponentRegisteredContext class
OnComponentRenderedContext class
render attribute
render: CitryRender | str | NoneThe rendered output, or None if rendering failed.
error attribute
error: Exception | NoneThe error raised during rendering, or None if it succeeded.
OnComponentUnregisteredContext class
OnCssLoadedContext class
OnFilesResetContext class
OnJsLoadedContext class
OnMessagesLoadedContext class
declaration_owner attribute
declaration_owner: typeThe class that authored the inherited messages/messages_file pair.
origin attribute
origin: strA file path or inline module::Class.messages label for diagnostics.
OnRenderContextMergeContext class
parent_context attribute
parent_context: CitryContextThe context of the render that consumed the nested one.
child_context attribute
child_context: CitryContextThe context of the consumed nested render.
OnSerializeContext class
context attribute
context: CitryContextThe 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.
selected_render attribute
selected_render: CitryRenderThe final hook-selected render tree being serialized.
placeholders attribute
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.
OnSlotRenderedContext class
component attribute
component: ComponentThe component whose template holds the <c-slot> that was rendered.
slot attribute
slot: SlotThe Slot that was rendered: the fill, or the fallback when no fill was given.
slot_name attribute
slot_name: strThe resolved slot name ("default" for an unnamed slot).
slot_node attribute
slot_node: SlotNodeThe runtime SlotNode at whose site the slot rendered.
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.
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.
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.
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.
OnTemplateCompiledContext class
template_id attribute
template_id: strImmutable identity of the exact template record being compiled.
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.
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.
mark_resolved function
mark_resolved(*claims: ForeignClaim = ()) -> NoneRecord explicit outcomes for claims replaced by the provider.
compiled_body function
compiled_body(nodes: list[BodyItem]) -> CompiledBodyCompile and protect one Citry run for later host-selected rendering.
OnTemplateLoadedContext class
template_id attribute
template_id: strImmutable identity of this loaded template record.
OnTemplateResetContext class
TemplateNamespaceContext class
Give an extension one component whose template namespace it can describe.
component_class attribute
The temporary live component class from the registry snapshot.
TemplateNamespaceContribution class
Publish analysis-only variables added by one installed extension.
Attributes
template_variablesMapping[str, object]- Variable names mapped to annotations, using the same format asLintSettings.template_variables.allows_extra_variablesbool- Whether this extension intentionally preserves additional names that it cannot enumerate. Such unknown names are linted as warnings, never silently accepted.