Theme
Version
GitHub PyPI Discord
On this page

Citry instance and config

The Citry instance that scopes components, settings, and caches, and the warning class for deprecated features.

View source

Citry class

Global instance that scopes all component state.

A Citry instance owns:

  • A private component-name registry reached through the engine's methods
  • Settings
  • Transient rendering state

All Component classes are assigned to a Citry instance at class definition time. If no instance is specified, the default instance is used.

Call initialize() after startup-time registration and before a server starts request threads. Lazy initialization remains available, but a thread that encounters lifecycle work owned by another thread receives CitryLifecycleInProgress.

View source

mode attribute

mode: Literal['production', 'development']
View source

render_template function

render_template(source: str, variables: Mapping[str, Any] | None = None, slots: Mapping[str, Any] | None = None, template_globals: Mapping[str, Any] | None = None, provides: Mapping[str, Any] | None = None, foreign_compile_contexts: Sequence[ForeignCompileContext] = (), origin: str = '<render_template>') -> CitryRender

Render trusted standalone Citry template source through the normal pipeline.

View source

engine_id attribute

engine_id: str

Return this engine's opaque process-lifetime identity token.

The value is stable for this Citry instance, including across clear(), and differs for another instance in the same process. Component-introspection consumers combine it with Component.class_id and Component.definition_id when they need to confirm that retained metadata still describes an exact live component generation.

Returns

str: A non-time-derived token intended only for same-process identity

View source

atomic_registration function

atomic_registration() -> Iterator[None]

Publish a group of component registrations together.

Component classes defined inside the block register normally and fire the normal class and registration hooks. If the block raises, Citry restores its component names, class-ID and file indexes, tag rules, and discovery state to their values at entry. Another thread receives CitryLifecycleInProgress rather than observing or changing the group before it commits.

This context manager is additive: it publishes new classes and aliases; unregister() rejects removals inside the block. Start the block outside other component lifecycle operations; nested atomic-registration blocks are rejected. Ordinary component definitions and their hooks remain reentrant inside the block.

Rollback covers the Citry registration and installation indexes listed above. Rendered-output caches, side effects that extension hooks or ordinary Python write elsewhere, and registrations made to another Citry instance are outside the transaction.

Yields

  • None - None. Component factories return their own created classes; the
  • None - context manager only owns publication and rollback.

Example

with app.atomic_registration():
    class AcmeButton(Component):
        citry = app

Raises

  • CitryLifecycleInProgress - If another thread owns component lifecycle work for this Citry instance.
  • RuntimeError - If called inside another component lifecycle operation on the same thread.
View source

register_library function

register_library(library: ComponentLibrary | ModuleType) -> LibraryInstallation

Materialize and publish an engine-neutral component library.

A library package may be passed directly when it exposes its manifest as __citry_library__. Registration creates a distinct concrete Component class for every definition and this Citry instance. The classes and immutable installation record become visible together.

Repeating the same manifest and exact definition generation returns the existing installation without rerunning component hooks. Clear the Citry instance before installing a reloaded or changed manifest with the same library name.

Example

import citry_ui
from citry import Citry

app = Citry()
installed = app.register_library(citry_ui)

Parameters

Returns

LibraryInstallation: The exact active [`LibraryInstallation`](/reference/component-libraries/#citry-libraryinstallation).

Raises

  • LibraryManifestChanged - If the library name is already associated with another manifest or definition generation.
  • LibraryInstallationStale - If internal registry mutation damaged an active installation.
  • ValueError - If a required extension is not installed or a component identity collides with existing state.
  • AlreadyRegistered - If a registry name is already occupied.
  • CitryLifecycleInProgress - If another thread owns lifecycle work.
  • RuntimeError - If registration is attempted recursively.
View source

get_library_installation function

get_library_installation(name: str) -> LibraryInstallation

Return one exact active component-library installation.

Parameters

  • name str - The manifest's case-sensitive library name.

Returns

LibraryInstallation: The current immutable installation handle.

Raises

  • LibraryNotInstalled - If no library with name is active.
  • LibraryInstallationStale - If private registry mutation damaged the installation record.
  • CitryLifecycleInProgress - If another thread owns lifecycle work.
View source

register function

register(comp_cls: type[Component], name: str | None = None) -> None

Register an additional name for a component owned by this instance.

Component classes register automatically when they are defined. This method supports same-engine aliases and re-registering a class after it was removed. A class owned by another Citry instance is rejected.

Fires on_component_registered once per call, after the registry accepts the class.

Raises

  • AlreadyRegistered - If the requested name or class ID belongs to a different component, or the name is reserved.
  • ValueError - If the component belongs to another Citry instance, is a retired built-in generation, or the requested name is invalid.
  • CitryLifecycleInProgress - If another thread is changing component lifecycle state.
View source

unregister function

unregister(comp_cls_or_name: type[Component] | str) -> None

Unregister one name or all names for a component owned by this instance.

Fires on_component_unregistered once per call, after the registry removes the class.

Raises

  • NotRegistered - If the requested class or name is not registered.
  • ValueError - If asked to remove a built-in's canonical name or unregister the built-in class.
  • CitryLifecycleInProgress - If another thread is changing component lifecycle state.
  • RuntimeError - If called inside atomic_registration() on this thread; atomic registration is additive.
View source

get function

get(name: str) -> type[Component]

Look up a component by name.

Parameters

  • name str - Registered component name, matched case-insensitively.

Returns

type[Component]: The registered component class.

Raises

  • NotRegistered - If no component has this name after discovery.
  • CitryLifecycleInProgress - If another thread is changing component lifecycle state.
View source

has function

has(name: str) -> bool

Check whether a component name is registered.

Parameters

  • name str - Registered component name, matched case-insensitively.

Returns

bool: Whether the name is registered after discovery.

Raises

  • CitryLifecycleInProgress - If another thread is changing component lifecycle state.
View source

components attribute

components: dict[str, type[Component]]

All registered components as a name-to-class mapping.

Raises

  • CitryLifecycleInProgress - If another thread is changing component lifecycle state.
View source

inspect_components function

inspect_components(include_builtins: bool = False, resolve_assets: bool = False, include_default_values: bool = False, include_extensions: Iterable[str] = ()) -> ComponentCatalog

Return an immutable catalog of the currently registered components.

The method completes normal lazy discovery and built-in creation, then copies the registry once. Schema and asset metadata are built from that copy after lifecycle coordination is released. Asset inspection never reads source content or changes render and hot-reload caches.

Parameters

  • include_builtins bool - Include Citry's framework component classes.
  • resolve_assets bool - Check declared asset paths on the filesystem and report resolved, missing, and searched paths.
  • include_default_values bool - Copy portable literal schema defaults into field metadata. Default factories are never called.
  • include_extensions Iterable[str] - Installed extensions whose versioned component metadata inspectors should run. Names are deduplicated and sorted; no inspector runs unless explicitly requested.

Returns

ComponentCatalog: A canonically ordered [`ComponentCatalog`](/reference/component-introspection/#citry-componentcatalog)

Raises

  • TypeError - If a boolean option is not a bool or include_extensions is a string or non-iterable.
  • ComponentIntrospectionError - If a requested extension is missing, unsupported, fails, or publishes invalid metadata.
  • CitryLifecycleInProgress - If another thread is changing component lifecycle state.
View source

inspect_component function

inspect_component(component: str | type[Component], resolve_assets: bool = False, include_default_values: bool = False, include_extensions: Iterable[str] = ()) -> ComponentInfo

Inspect one component selected from a copied registry snapshot.

A string is matched case-insensitively as a registered component name. A class must be owned by this engine and present under at least one name. Looking up an alias does not change the record's deterministic primary name.

Parameters

  • component str | type[Component] - A registered name or exact registered component class.
  • resolve_assets bool - Check declared asset paths on the filesystem.
  • include_default_values bool - Copy portable literal schema defaults.
  • include_extensions Iterable[str] - Installed extensions whose versioned metadata inspectors should run. Names are deduplicated and sorted.

Returns

ComponentInfo: A value-only [`ComponentInfo`](/reference/component-introspection/#citry-componentinfo) record.

Raises

  • TypeError - If component is neither a string nor a class, a boolean option is not a bool, or include_extensions is a string or non-iterable.
  • NotRegistered - If the name or exact class is absent from the copied registry snapshot.
  • ComponentIntrospectionError - If a requested extension is missing, unsupported, fails, or publishes invalid metadata.
  • CitryLifecycleInProgress - If another thread is changing component lifecycle state.
View source

inspect_component_graph function

inspect_component_graph(include_builtins: bool = False) -> ComponentGraph

Return authored component dependencies from one registry snapshot.

Citry copies the complete name-to-class registry once, then reads each selected component's effective primary template without rendering or running template-load transforms. The graph includes direct dependency and reverse-reference queries plus explicit unresolved targets and source problems.

Experimental

The component graph is experimental. Its methods, attributes, and JSON output may change in a minor release without a deprecation period.

Example

graph = app.inspect_component_graph()

for dependency in graph.dependencies("account-page"):
    print(dependency.name)

for dependent in graph.dependents("avatar"):
    print(dependent.name)

Note

The graph covers component tags in authored primary templates. It does not claim to find components composed from Python, inserted by a transform, or chosen by a dynamic selector.

Parameters

  • include_builtins bool - Include Citry's framework component definitions and references to them.

Returns

ComponentGraph: A versioned [`ComponentGraph`](/reference/component-graph/#citry-componentgraph) containing

Raises

  • TypeError - If include_builtins is not a bool.
  • CitryLifecycleInProgress - If another thread is changing component lifecycle state.
View source

initialize function

initialize() -> None

Prepare component registration state before starting worker threads.

This imports configured component modules when autodiscovery is enabled, creates Citry's built-in components, and builds the current parse-time tag rules. Component template, JavaScript, and CSS asset files remain lazy and are loaded when rendering needs them.

Call this after startup-time component registration and before a server begins handling requests concurrently. Repeated successful calls are safe; a later registration invalidates tag rules, and another call rebuilds them.

Initialization is retryable rather than globally transactional. If a module import fails, components from earlier successful module imports may remain registered, while the incomplete work is retried later.

Returns

None: None.

Raises

  • CitryLifecycleInProgress - If another thread is changing component lifecycle state.
  • RuntimeError - If called recursively from an active lifecycle hook or initialization on this thread.
View source

template_analysis function

template_analysis() -> TemplateAnalysis

Capture the complete component contracts used to analyze templates.

The method completes normal component discovery and built-in registration under the engine's lifecycle guard. The returned snapshot keeps normalized registered names and parse-time input and slot rules together, so tooling cannot observe a partial registry. Call it again after component registration or reload to capture the new state.

Experimental

Part of the experimental template analysis API that citry check, citry format and citry-lsp use. It may change in a minor release without a deprecation period.

Returns

TemplateAnalysis: An immutable [`TemplateAnalysis`](/reference/template-analysis/#citry-templateanalysis) snapshot.

Raises

  • CitryLifecycleInProgress - If another thread is changing component lifecycle state.
  • RuntimeError - If discovery or component initialization fails.
View source

autodiscover function

autodiscover(dirs: Sequence[str | Path] | None = None) -> list[str]

Import this instance's component modules so their classes register.

With no argument, imports every component module under the instance's dirs - the same scan the autodiscover setting performs automatically on first use - and marks that automatic scan done, so it will not run again. Pass dirs to import an extra set of directories on demand without affecting the automatic scan.

The directories must be importable: each one (or a parent of it) is on sys.path/PYTHONPATH, which is how a component file is mapped to the import name Python uses for it. A directory that holds component modules but is not importable raises ValueError.

Returns the dotted import paths of the modules that were imported. Safe to call more than once: an already-imported module has its components re-registered directly, so a call after clear() rebuilds the registry and a call that changes nothing is a no-op.

A scan is marked complete only after every module imports successfully. If one module raises, registrations it made to this Citry instance during that import are rolled back, and a later call can retry it. Earlier modules and dependency modules that imported successfully remain registered. Python side effects and registrations made to another Citry instance are outside this rollback. Calling autodiscover() recursively from a component module raises RuntimeError.

Raises

  • CitryLifecycleInProgress - If another thread is changing component lifecycle state.
  • RuntimeError - If a component module starts another autodiscovery scan on the same instance.
View source

urls attribute

urls: tuple[URLRoute, ...]

This instance's HTTP route table (framework-neutral URLRoutes).

The web-integration adapters (citry.contrib.asgi and friends) mount these into the host application; the routes serve cached component JS/CSS, the client runtime, and extension endpoints.

View source

commands attribute

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

This instance's CLI commands, keyed by extension name.

Each registered extension contributes the commands it declares in Extension.commands; the citry command-line tool reaches one as citry ext run <extension name> <command name>. See ExtensionManager.commands for ordering and the uniqueness guarantee.

View source

mounted_prefix attribute

mounted_prefix: str | None

Where this instance's routes are mounted (e.g. "/citry"), or None when nothing is mounted.

View source

set_mounted_prefix function

set_mounted_prefix(prefix: str) -> None

Record where this instance's routes are mounted in the host app.

The adapters' mount() call this; call it directly only in a process that builds URLs without mounting the routes itself (for example a worker that renders fragments served by another process). prefix must start with /; a trailing / is dropped.

Raises

  • ValueError - If prefix does not start with /.
View source

build_url function

build_url(path: str) -> str

An absolute URL path for one of this instance's routes.

path is the route's full path (no leading slash), e.g. "cache/Table_a1b2c3.js". When the host app is served below a sub-path, such as /shop, the URL starts with that sub-path. The integration that mount() set up reads it from the current request. Raises RuntimeError when no web integration is mounted, since the URL would point nowhere.

View source

runtime_url function

runtime_url() -> str

Return the URL of Citry's browser runtime for a page you write by hand.

Pages that Citry renders already link the runtime. Call this when your own HTML loads it, for example a page that only receives HTML fragments. The URL holds a hash of the runtime file, so the browser keeps the file between page loads. The hash, and so the URL, changes when an upgrade changes the file. The URL includes the mount prefix and any sub-path the app is served below, such as /shop. When the app sets cache, this call also stores the runtime there, so another worker can serve it.

A static HTML file that you write once keeps the old hash after an upgrade. Link the stable /citry/citry.js from that file instead; the browser then downloads it on every page load.

Example

from citry import citry

src = citry.runtime_url()
html = f'<script src="{src}"></script>'

Returns

str: The runtime's URL path, such as ``/citry/citry.<hash>.js``.

Raises

  • RuntimeError - If no web integration is mounted, because the URL would point nowhere.
  • Exception - Any error the cache backend raises when it cannot store the runtime.
View source

get_component_by_class_id function

get_component_by_class_id(class_id: str) -> type[Component]

Look up a registered component class by its class_id.

class_id is the stable identifier (MyComp.class_id) used in cache keys and script URLs. Raises KeyError when no registered class has that id.

Raises

  • KeyError - If no registered component has this class ID.
  • CitryLifecycleInProgress - If another thread is changing component lifecycle state.
View source

get_components_for_file function

get_components_for_file(path: str | Path) -> list[type[Component]]

The component classes whose assets resolved to path.

Most callers want invalidate_file(), which both finds these classes and resets them. This lower-level lookup is for a caller that wants the classes without resetting (a custom hot-reload handler, a test). Dead weakrefs are pruned on read.

View source

invalidate_file function

invalidate_file(path: str | Path) -> list[type[Component]]

Drop cached template/JS/CSS for every component that loaded an asset from path, so the next render re-reads it from disk.

Returns the component classes it reset. An empty list means the file backs no loaded component, which a hot-reload handler can read as "not mine" and, if it wants, fall through to a full restart. This is the call a file watcher makes, whatever web framework hosts the app; see Hot reload during development for the built-in watchers.

View source

invalidate_all function

invalidate_all() -> list[type[Component]]

Reset cached template/JS/CSS for every component that has loaded a file, so the next render re-reads them all from disk. Returns the reset classes (in first-seen order).

For when a change cannot be mapped to a single path: a bulk edit, a branch switch, or a custom watcher reporting an event it cannot resolve to one file. Unlike clear(), this leaves the registry and autodiscovery untouched.

View source

clear function

clear() -> None

Clear registrations and caches, and re-arm autodiscovery.

Raises

  • CitryLifecycleInProgress - If another thread is changing component lifecycle state.
  • RuntimeError - If called from another lifecycle operation on this thread.
View source

citry module

The Citry global instance - scopes all component state.

A Citry instance owns a component registry, settings, and transient rendering state. Every Component subclass is assigned to a Citry instance, either by declaring citry = my_citry in its class body or by using the default instance.

Example

Using the default instance (most common)::

from citry import Component

class MyTable(Component):
    template = "<table>...</table>"

Using a custom instance::

from citry import Citry, Component

my_citry = Citry()

class MyTable(Component):
    citry = my_citry
    template = "<table>...</table>"

Isolated instances for testing::

def test_my_component():
    test_citry = Citry()
    # Components registered here don't leak to other tests
    class MyTable(Component):
        citry = test_citry
        template = "..."
View source

AlreadyRegistered class

Bases: Exception

Raised when registering a component under a name that is already taken.

View source

NotRegistered class

Bases: Exception

Raised when looking up a component name that is not registered.

View source

CitryLifecycleInProgress class

Bases: RuntimeError

Raised when another thread is changing Citry's component lifecycle state.

Component discovery, registration, built-in creation, and clearing publish related state together. A competing thread receives this error rather than observing an incomplete registry or waiting in a way that can deadlock with Python's module-import locks.

One case waits instead: when a render or component lookup needs the Citry instance's one-time setup (discovery and built-in creation) while another thread runs it, it waits for that setup to finish. A thread that is importing a module, or running a reset, still receives this error, because the setup may be waiting for that import or for the locks the reset holds; so does a wait that lasts over a minute. Finish Citry.initialize() before starting request threads to avoid both this error and that wait during normal server operation. An operation that encounters it may also be retried after the active lifecycle operation finishes.

View source

CitrySettings class

Immutable settings for a Citry instance.

Every security mode is enforced during serialization. The defaults preserve established output; restrictive JavaScript modes inventory, omit, or reject client behavior without changing render-cache data.

Experimental

The event_result_resolvers and event_payload_codecs settings are experimental. What a resolver or a codec receives and returns may change in a minor release without a deprecation period.

Attributes

  • extensions Sequence[type[Extension] | Extension | str] - The extensions to install on the instance. Each entry is an Extension subclass, a ready-made instance, or an import string like "myapp.extensions.MyExtension". The set is fixed once the instance is constructed.
  • extensions_defaults Mapping[str, Mapping[str, Any]] - Default config values for extensions, keyed by extension name, e.g. {"events": {"_csrf": True}}. When an extension reads a config field for a component, the component's own nested config class wins, a value given here fills in next, and the extension's built-in default comes last.
  • dirs tuple[Path, ...] - Directories searched when resolving a component's asset files (template_file, js_file, css_file, and Dependencies entries), after the directory of the component's own .py file. Entries are converted to Path and must be absolute; this is validated at construction (a relative entry raises ValueError).
  • cache CitryCache | str | None - Where citry stores what it caches: a CitryCache object or an import string like "myapp.caching.MyCache". None gives the instance its own in-memory cache. The live backend built from this setting is Citry.cache.
  • sandbox_expressions bool - Whether template expressions ({{ ... }} and dynamic c-* attributes) are evaluated in the security sandbox. On by default. Turning it off evaluates expressions as plain Python, which is faster but removes security guardrails. Only do so when every template comes from a trusted source.
  • autodiscover bool - Whether to import the component modules under dirs the first time a component is looked up, so their classes register themselves without being imported by hand. On by default; when no dirs are set there is nothing to scan, so the default instance does nothing. The directories must be importable (on sys.path/PYTHONPATH). See Citry.autodiscover and citry.autodiscovery.
  • mode Mode - The build environment, "production" (the default) or "development". In "production", each configured i18n catalog package must ship a valid compiled manifest, written by citry ext run i18n compile; creating the Citry instance raises ValueError when it is missing or does not match. "development" turns on three things. Citry reads the Fluent sources directly; if the package also contains a compiled manifest, it must still match the sources, so delete or recompile it after editing translations. Turning a page into HTML issues a RuntimeWarning when two elements on it share an id, naming the components that wrote them. And Citry logs a browser console warning when Vue's first render of a part Vue builds in the browser differs from the HTML the server sent. A production page does not load this browser check. An unrecognized value raises ValueError at construction. See Development mode.
  • template_globals Mapping[str, Any] - Variables exposed to every component's template without being returned from each template_data(). They are merged into every component's template variables on render, so a template can reference one directly ({{ site_name }}). A component's own template_data wins when it returns a key of the same name, so globals act as defaults. The value given here is the starting set; the live, editable copy is Citry.template_globals, which is how you add or change a global after the instance exists (including the default instance, created at import before your code runs).
  • lint LintSettings - Template lint severities, analysis-only variables, and the render-time input type check. Runtime globals are discovered from Citry.template_globals and do not need to be repeated here.
  • security_csp SecurityCspMode - Content Security Policy check for rendered output. Citry scans the final HTML for raw <script> and <style> elements, inline on* attributes, and javascript: URLs. "off" skips the check, "warn" keeps the output and emits one RuntimeWarning listing what it found, and "strict" raises instead of returning that output. citry check and the editor also report these problems where a template writes them directly, as a warning with "warn" and an error with "strict" (citry.template.csp-incompatible).
  • security_javascript SecurityJavascriptMode - JavaScript delivery policy. "allow" keeps current behavior, "warn" inventories client requirements, "omit" leaves Citry-managed JavaScript out, and "forbid" rejects rendered subtrees that require executable client behavior.
  • security_script_integrity SecurityScriptIntegrityMode - Whether Citry reports script hashes. Under both values, the tags Citry writes for files it serves carry integrity, so the browser refuses a file with different content. "off" reports no hashes. "citry" also lists SHA-384 hashes of Script objects, inline ones included, in SerializedSecurity when Citry knows their exact content, and raises TypeError for a ready-made tag, whose content it cannot read.
  • ssr bool - Send each interactive Vue document's content in its HTML. On by default: the server writes the page for Vue to adopt ("hydrate"), and a page it cannot write that way carries Citry's ordinary server HTML, which Vue replaces when it mounts. False sends an empty mount element instead, and Vue builds the page in the browser. Noninteractive output is unchanged.
  • ssr_element_threshold int - Send small pages without their content. A page whose server-written HTML holds this many elements or fewer is sent with an empty mount element and built in the browser, which can be slightly faster for a small page because the browser does not parse the HTML and then hydrate it. The default, 0, keeps the content of every page that contains an element, which search engines and readers without JavaScript need. It applies only to pages Vue can adopt; a page Vue replaces always carries its server HTML. Must be a non-negative int: another type raises TypeError and a negative value raises ValueError when the settings are created.
  • vue_asset_max_bytes int | None - The most bytes each Citry instance keeps in memory, when cache is not set, for the files that pages fetch by URL: the compiled component code and stylesheets of interactive pages, and the stylesheets that css_data() produces on any page. The default is 64 MiB, and None removes the limit. Pages load these files possibly long after they were rendered. When the total grows past this limit, the files used longest ago are dropped, and a page that is still open gets a 404 if it later asks for one of them. The files of a page being written out are never dropped, so the total can exceed the limit by that page's files. When cache is set, the files are stored there, this setting has no effect, and the backend's own size limit and rules for dropping entries apply. Must be a positive int or None: another type raises TypeError and zero or a negative value raises ValueError when the settings are created.
  • max_component_depth int - How many components deep a page may nest before rendering stops with RecursionError. The default, 2000, is far deeper than real pages nest, so it only stops a component that keeps rendering itself, such as a tree node whose data lists the node among its own children. Without the limit that render would run until the process runs out of memory. The error names the chain of components, and <c-error-fallback> cannot catch it. Must be a positive int: another type raises TypeError and zero or less raises ValueError when the settings are created. A component rendered from a {{ ... }} expression or through <c-component> starts a new count. Python's own recursion limit stops that kind of recursion much earlier, with an ordinary RecursionError that <c-error-fallback> can catch, so avoid wrapping such a recursion in one.
  • model_base object -

    The kind of class Citry generates from every plain schema declaration (class Kwargs: with annotated fields, and likewise TemplateData, JsData, CssData, State, and Events data classes), and a base those classes share. None (the default) generates dataclasses. A Pydantic v2 or v1 model class generates models that subclass it, so its model_config and validators apply, and Pydantic, not Citry, checks the values. A dataclass generates dataclasses that subclass it and copy its options. typing.NamedTuple generates named tuples. State cannot be a named tuple, a frozen dataclass, or a frozen model, because handlers change it. Classes decorated with @dataclass, hand-written models, Slots, Citry's own components, and components from an installed library are kept as written. An import string such as "pydantic.BaseModel" is imported when the Citry instance is built.

    Building the settings raises TypeError for a value of another kind, a named tuple class with fields, a dataclass with its own __init__, a Pydantic dataclass, or a root model. An import string is checked only when Citry is built: ImportError if it cannot be imported, TypeError if it names a class of another kind. A component class fails when it is defined if a schema cannot take the kind; see "Share one schema base" in the inputs and validation guide.

  • id_generator Callable[[], str] | str | None - A function returning the per-render id stamped on each component instance (component.id; static output also writes it into each component root's data-cid-<id> attribute). Given as a callable or a "path.to.func" import string; passing a class also works: it is called once, and the resulting object is used as the generator (handy when the generator keeps state, like a counter). None uses the built-in generator. Override it for stable ids in snapshot tests. The generator must return ids that are unique among the components on one page and contain only lowercase ASCII letters, digits, hyphens, and underscores. The lowercase rule is required because the id is embedded in an HTML attribute name. This does not touch class_id, which stays a stable hash of the component's import path.
  • secret str | list[str] | None - The signing secret for values citry hands to the browser and must recognize when they come back, such as the state the Events extension round-trips on each event call. A single string is the common form. A list means key rotation: the first entry signs new values, and a value signed by any entry still verifies, so already-issued values stay valid while a new key rolls out. A list or a tuple is accepted. Every entry must be a string with at least one character that is not whitespace; Citry(...) raises ValueError otherwise, and TypeError for an entry that is not a plain str. A bare string is stored as a one-element list. None (the default) means no secret is set. Django projects can reuse SECRET_KEY and any SECRET_KEY_FALLBACKS by passing citry.contrib.django.secret().
  • event_result_resolvers Sequence[Any] - Experimental. Result resolvers for the Events extension. When an event handler returns a value, citry converts it into the actions sent back to the browser (the instructions the client runtime applies: re-render this component, redirect, and so on). A resolver adds support for your own return types: it is given the handler's return value and either converts it into those actions or declines, letting the next resolver try. Resolvers run in order, after the built-in conversions; the first one to convert the value wins.
  • event_payload_codecs Sequence[Any] - Experimental. Payload codecs for the Events extension's HTTP endpoints. A codec reads one request format (identified by its content type) into the event call the extension expects, so clients are not limited to the built-in JSON, form, and query formats. Codecs given here are tried before the built-in ones, in order.
View source

LintSettings class

Configure Citry's template and browser lint rules, analysis-only variables, and the render-time input type check.

Raises

  • TypeError - If a variable or global collection is not a mapping, or check_input_types or include_libraries is not a bool.
  • ValueError - If a severity or variable name is invalid.

Attributes

  • rule_unknown_template_variable LintSeverity - Severity for a free template root that is absent from the proven component namespace. The default is "error". A schema that explicitly allows extra fields caps this rule at "warning".
  • rule_i18n_missing_param_type LintSeverity - Severity for a translation message variable that has no @param comment giving its type, when the variable is used only as plain text on the server. Variables used in a selector, a formatting function, a browser call, or as a Slot always need a type, whatever this setting says. The default is "warning".
  • template_variables Mapping[str, object] - Extra variables known to template analysis but not injected at runtime. Values are annotations. Use Annotated[T, "description"] to attach concise documentation.
  • rule_unknown_vue_variable LintSeverity - Severity for a free Vue-expression root absent from the component's proven browser scope. The default is "error".
  • vue_variables Mapping[str, object] - Extra variables or custom Vue magics known only to browser analysis. Values use the same annotation convention as template_variables.
  • rule_unknown_component_js_variable LintSeverity - Severity for a free variable used inside a $component initializer. The default is "error".
  • component_js_globals Mapping[str, object] - Extra globals available to component JavaScript analysis. Values use the same annotation convention as template_variables.
  • rule_unknown_component_js_member LintSeverity - Severity for a this.<name> or component.<name> read in component JavaScript that the component instance does not have. The default is "error".
  • rule_vue_python_variable LintSeverity - Severity for a Vue expression that reads a name bound in Python by an enclosing c-for loop or c-fill binding. The browser looks that name up in Vue state, not in the Python loop. The default is "warning".
  • rule_alpine_attribute LintSeverity - Severity for an Alpine x-* attribute, such as x-data or x-on:click, on an HTML element. Citry uses Vue, so nothing reads the attribute. Set "ignore" when another library on the page reads x-* attributes. The default is "warning".
  • rule_alpine_cloak LintSeverity - Severity for x-cloak on an HTML element. Nothing in Citry removes the attribute, so a [x-cloak] CSS rule keeps the element hidden. The default is "error".
  • rule_invalid_attribute_value LintSeverity - Severity for a static HTML attribute value outside the keywords that attribute accepts, such as draggable="treu" or <input type="datetime">, or for a boolean attribute with a value other than its own name, such as disabled="false", which still disables the element. For an enumerated value, the browser ignores it or falls back to a default. The default is "error". citry check --static and an editor that has not loaded the application report it as a warning, because they cannot read your settings.
  • rule_i18n_cross_language_fallback LintSeverity - Severity for a tr() call, a self.i18n.tr() call, or a message in I18n.client_messages that has no translation in some locale, so that locale shows text from a fallback language. The page still renders, but the text cannot mark its own language. Set "error" to require complete translations. A <c-trans> message is always an error, because rendering it in another language fails. The default is "warning".
  • rule_misspelled_directive LintSeverity - Severity for a c-* attribute on an HTML element or custom element that looks like a misspelled Citry directive, such as c-iff for c-if, or c-show, which Citry writes as c-if. Citry renders it as an ordinary attribute, so the directive never runs; with c-iff the element always shows up. An attribute of the element, such as c-id, is not reported. It also covers a v-* name that looks like a misspelled Vue directive, such as v-iff, which Vue looks up as a custom directive, so the page fails in the browser. The default is "error".
  • rule_misspelled_attribute LintSeverity - Severity for an attribute on an HTML element that is one typing slip from an attribute the element accepts, such as titel for title. The browser ignores it. The default is "error".
  • rule_misspelled_element LintSeverity - Severity for a lowercase tag that is one typing slip from an HTML element, such as <dvi>. The default is "error".
  • rule_unknown_aria_attribute LintSeverity - Severity for an aria-* attribute that WAI-ARIA does not define, such as aria-expandd. Browsers and screen readers ignore it. The default is "error".
  • rule_invalid_nesting LintSeverity - Severity for an element that the browser moves out of an open <p>, such as <div> inside <p>. The default is "error".
  • rule_duplicate_id LintSeverity - Severity for a literal id that two elements of one template always render. The default is "error".
  • rule_missing_image_alt LintSeverity - Severity for an <img> with no alt text. The default is "warning".
  • rule_invalid_model_target LintSeverity - Severity for v-model on an element that holds no value, such as <form>, which fails when Vue compiles the page. The default is "error".
  • rule_misspelled_event LintSeverity - Severity for a listener on an HTML element whose event name is one typing slip from a DOM event, such as @clik. The listener never runs. The default is "warning".
  • rule_invalid_vue_expression LintSeverity - Severity for a Vue expression, such as the value of v-if or @click, that is not valid JavaScript, which fails when Vue compiles the page. The default is "error".
  • rule_vue_compile_error LintSeverity - Severity for anything else Vue's compiler rejects in a template, such as a <div> directly inside a <table>, a <button> inside a <button>, or a v-else with no v-if before it. A component that uses Vue fails to render. The default is "error".
  • rule_undeclared_component_event LintSeverity - Severity for a listener on a component tag for an event the child component does not declare in emits, such as @hdie for hide. The listener usually never runs. citry check reports it only with your application loaded. The default is "warning".
  • rule_missing_component_key LintSeverity - Severity for a component that <c-for> repeats without a #c-key on a page that uses Vue, which fails to render once the loop has two items. citry check reports it only with your application loaded. The default is "error".
  • rule_prop_assignment LintSeverity - Severity for a component that assigns to one of its own Vue props, such as @click="open = false" or this.open = false in its JavaScript. Props are read-only, so the value does not change. citry check reports it only with your application loaded. The default is "error".
  • rule_unknown_server_event_field LintSeverity - Severity for an @c-* binding that sends a key the handler's data class does not declare, such as save({amont: 1}). The server rejects the call. citry check and the editor report it only with your application loaded. The default is "error".
  • rule_missing_server_event_field LintSeverity - Severity for an @c-* binding that leaves out a field the handler's data class requires. The server rejects the call. citry check and the editor report it only with your application loaded. The default is "error".
  • rule_incompatible_server_event_field LintSeverity - Severity for an @c-* binding that sends a literal value the handler's data class does not accept, such as save({amount: '1'}) for an int field. The server rejects the call. citry check and the editor report it only with your application loaded. The default is "error".
  • rule_unknown_form_field LintSeverity - Severity for a control in a form whose @c-submit sends the form's fields to a server event, when the handler's data class does not declare the control's name, such as <input name="qeury"> for a field q. The server rejects each submit that sends the control. citry check and the editor report it only with your application loaded. The default is "error".
  • rule_missing_form_field LintSeverity - Severity for a form whose @c-submit sends its fields to a server event, when no control in it is named after a field the handler's data class requires. It is reported only when the template shows every control the form can send. citry check and the editor report it only with your application loaded. The default is "error".
  • rule_misspelled_css_variable LintSeverity - Severity for a var(--name) in a component's CSS that is one typing slip from a key its css_data() returns, such as var(--acent) for accent. Nothing sets the property. citry check reports it only with your application loaded. The default is "warning".
  • rule_misspelled_css_class LintSeverity - Severity for a class in a template's static class attribute that is one typing slip (two neighboring letters swapped, one doubled, or one left out) from a class the component's own css or css_file styles, such as bord for .board. It is reported only when no CSS Citry reads declares the class as written: no registered component's CSS, Dependencies stylesheet, or template <style> element. Slips that usually name another class of the same family are not reported: a moved, doubled, or dropped digit (col-1 for .col-12); a missing final s after a letter other than s (tab for .tabs); one of these short words, as the whole class or as its last word after a hyphen or underscore, next to its longer form: close (closed), collapse (collapsed), disable (disabled), enable (enabled), fade (faded), mute (muted), show (shown), slide (slider), and toggle (toggled or toggler), as in navbar-toggle for .navbar-toggler; and one letter typed twice or left out in a first hyphen-separated part of one or two characters (p-4 for .px-4). citry check reports it only with your application loaded. The default is "warning".
  • rule_undeclared_css_class LintSeverity - Severity for every class in a template's static class attribute that no CSS Citry reads declares: no registered component's css, css_file, inline or local-file Dependencies stylesheet, or template <style> element. Classes from a site stylesheet or a CSS framework such as Tailwind, and classes used only by JavaScript or tests, are reported too, so the default is "ignore", which turns the rule off. citry check reports it only with your application loaded.
  • rule_undeclared_css_variable LintSeverity - Severity for every var(--name) without a fallback in a component's css or css_file when nothing Citry reads sets --name: no registered component's CSS, css_data() key, template style attribute or binding, JavaScript, or Python module. Properties that a site stylesheet, a design system, or a script outside Citry sets are reported too, so the default is "ignore", which turns the rule off. citry check reports it only with your application loaded.
  • rule_unknown_url LintSeverity - Severity for a literal href, src, or action path, such as /abuot, that none of your app's routes or static files serves. citry check and the editor report it only with your application loaded, and only when a web framework integration (citry.contrib.flask.mount(), citry.contrib.fastapi.mount(), or citry.contrib.django.urlpatterns()) ran while the app loaded. A path another server answers, such as a reverse proxy, is reported too. The default is "warning".
  • rule_unknown_inject_key LintSeverity - Severity for a self.inject("key") call with a literal key and no default when no provider above the component gives that key. Citry looks for a <c-provide> around the component's tag in the templates that use it, a provide() call in the components above it or in the component whose <c-slot> shows it, and a literal key in a render(provides={...}) call in your project. When no template uses the component, Citry compares the key with every key the app provides. A key passed only in a provides dictionary built elsewhere, or provided by a helper function, is reported too, so the default is "warning". citry check and the editor report it only with your application loaded.
  • check_input_types bool - Check that each value a component receives has the type its annotation names. On by default, in development and in production. Unlike the rule_* settings, this check runs when a component renders, not in citry check or the editor. Citry compares each field of the component's Kwargs, TemplateData, JsData, CssData, and State with the field's annotation and raises TypeError for a value of the wrong type, so Card(title=123) against title: str fails with a message that names the component, the field, the expected type, and the value. Only the outer type is checked: list[str] accepts any list. A field's own default always passes. Annotations Citry cannot test while the page runs, such as Any, a type variable, or a name imported only for type checkers, are not checked, and Pydantic models and dataclasses that write their own __init__ keep their own rules. Inputs and validation lists what each annotation accepts. A component overrides this for itself with check_input_types in its nested Lint class.
  • include_libraries bool - Whether citry check also reports findings in the components of a library you registered with register_library(), such as Citry UI. Off by default: you cannot change a library's code, so citry check leaves those findings out of the report and the exit status, and prints how many it left out. Set it to True, or pass citry check --include-libraries, to report them, for example in the library's own tests. A finding is left out only when every component it belongs to comes from a library, so a finding in your subclass of a library component, including one in the code it inherits, is reported. The editor ignores this setting and checks every file you open.
View source

CitryCache class

Bases: Protocol

The cache backend interface.

Implement these four methods to plug in any store (Redis, diskcache, Django's cache framework, ...). Keys and values are strings.

View source

get function

get(key: str) -> str | None

Return the value for key, or None when absent or expired.

View source

set function

set(key: str, value: str, ttl: float | None = None) -> None

Store value under key. ttl is seconds until expiry; None means keep forever.

View source

delete function

delete(key: str) -> None

Remove key if present (no error when absent).

View source

has function

has(key: str) -> bool

Whether key is present (and not expired).

View source

InMemoryCache class

The default cache backend: a thread-safe store inside the current process.

Unbounded by default for ordinary entries. Pass max_entries to cap them; when full, the entry that was read or written longest ago is dropped to make room. An entry written with a ttl is removed once it expires, at the next write, even when nothing reads its key again.

Server-stored Events State (keys that start with citry:state:) is kept apart, under its own budget of state_max_bytes (64 MiB by default), and does not count toward max_entries. When the budget is full, the State entry read or written longest ago is dropped, so many State writes cannot push out page assets. A dropped entry makes that visitor's next call answer stale_state, and the page offers a reload. An entry larger than the whole budget is not stored, and Citry logs a warning. state_max_bytes=None removes the budget.

Single-process only: each instance is its own store. With several worker processes, a page rendered by one worker can link a generated Vue bundle or css_data() stylesheet that another worker then answers with a 404, so use a shared backend such as RedisCache or DjangoCache instead.

Raises

  • ValueError - When max_entries or state_max_bytes is not a positive int or None.
View source

set function

set(key: str, value: str, ttl: float | None = None) -> None
View source

clear function

clear() -> None

Drop all entries. Called by Citry.clear().

View source

CitryDeprecationWarning class

Bases: DeprecationWarning

Warn that your code uses a Citry feature that will be removed.

Citry uses this class for every deprecation warning: when you call a deprecated function, pass a deprecated argument, or set a deprecated setting. The warning names the line in your code that used the feature and says what to use instead. From 1.0.0 on, a deprecated feature keeps working, and keeps warning, for at least two minor releases and at least three months, and is removed only in a major release.

It is a subclass of DeprecationWarning, so Python shows it by default only in code run as __main__ and under test runners such as pytest. A web server started some other way does not show it. Turn it into an error in your tests to find every use before you upgrade.

Example

# pyproject.toml
[tool.pytest.ini_options]
filterwarnings = [
    "error::citry.CitryDeprecationWarning",
]
Citry version: 0.9.0