Skip to content

Renderer registry

The per-backend, per-node emit registry.

renderer_registry

Per-(backend, IR-node) emit registry.

Third-party backends and third-party IR nodes plug into the transpile pipeline through this registry without editing the in-tree renderers. Every backend registers an emit function per IRNode variant it supports; the BackendRenderer base class dispatches through the registry at render time.

Two entry points:

An IRArg subclass plugs in the same way: register the argument class's emit through emit_hook under the backend name and it becomes visible to every renderer that walks the argument tree through the registry.

The registry composes with RendererBase: backends can subclass RendererBase and hard-code their emit tables, or drive dispatch through this registry. Both paths are supported.

EmitFn module-attribute

EmitFn = Callable[..., Any]

An emit function: called with the renderer, its context, and one IR node or argument, returning the backend's fragment for it.

RendererLookupError

Bases: LookupError

No emit function is registered for the requested (backend_name, node_or_arg_type) combination.

RendererDuplicateError

Bases: ValueError

A second emit function is being registered for a combination that already has one, without replace=True. Silently shadowing a registered emit is a subtle bug source; the registry rejects the second registration unless the caller opts in.

BackendRenderer

Bases: ABC

Abstract renderer that dispatches emit through the registry.

Concrete renderers set the backend_name class attribute and call :meth:emit_node to route each IRNode through the registered emit function. Any IRArg a renderer needs to walk is dispatched through :meth:emit_arg.

Existing renderers under quivers.transpile.renderers subclass RendererBase instead and hard-code their emit tables; both paths coexist. Third- party renderers with a small backend surface pick this base for the extensibility; large in-tree backends stay on RendererBase until a migration is warranted.

emit_node

emit_node(ctx, node: IRNode)

Dispatch a top-level IR node through the registry.

PARAMETER DESCRIPTION
ctx

The renderer's emission context, passed through to the emit.

node

The IR node to emit.

TYPE: IRNode

RETURNS DESCRIPTION
object

Whatever the registered emit returns.

RAISES DESCRIPTION
RendererLookupError

If no emit is registered for the node's class.

Source code in src/quivers/transpile/renderer_registry.py
def emit_node(self, ctx, node: IRNode):
    """Dispatch a top-level IR node through the registry.

    Parameters
    ----------
    ctx
        The renderer's emission context, passed through to the emit.
    node
        The IR node to emit.

    Returns
    -------
    object
        Whatever the registered emit returns.

    Raises
    ------
    RendererLookupError
        If no emit is registered for the node's class.
    """
    emit_fn = get_emit(self.backend_name, type(node))
    return emit_fn(self, ctx, node)

emit_arg

emit_arg(ctx, arg: IRArg)

Dispatch an IR argument through the registry.

PARAMETER DESCRIPTION
ctx

The renderer's emission context, passed through to the emit.

arg

The IR argument to emit.

TYPE: IRArg

RETURNS DESCRIPTION
object

Whatever the registered emit returns.

RAISES DESCRIPTION
RendererLookupError

If no emit is registered for the argument's class.

Source code in src/quivers/transpile/renderer_registry.py
def emit_arg(self, ctx, arg: IRArg):
    """Dispatch an IR argument through the registry.

    Parameters
    ----------
    ctx
        The renderer's emission context, passed through to the emit.
    arg
        The IR argument to emit.

    Returns
    -------
    object
        Whatever the registered emit returns.

    Raises
    ------
    RendererLookupError
        If no emit is registered for the argument's class.
    """
    emit_fn = get_emit(self.backend_name, type(arg))
    return emit_fn(self, ctx, arg)

emit_hook

emit_hook(backend_name: str, node_type: type, *, replace: bool = False) -> Callable[[EmitFn], EmitFn]

Decorator that registers an emit function for a (backend_name, node_type) combination.

node_type is an IRNode or IRArg subclass. Registration happens at import time of the module that declares the emit; downstream libraries add support for a new backend or a new node type by importing their module once.

replace=True allows re-registering over an existing entry. Left unset, a second registration for the same key raises :class:RendererDuplicateError so a user does not silently shadow a built-in emit.

PARAMETER DESCRIPTION
backend_name

The backend the emit renders for.

TYPE: str

node_type

The IR node or argument class the emit handles.

TYPE: type

replace

Whether to overwrite an existing registration.

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
Callable[[EmitFn], EmitFn]

A decorator that registers its argument and returns it unchanged.

RAISES DESCRIPTION
RendererDuplicateError

From the decorator, if the combination is registered and replace is false.

Source code in src/quivers/transpile/renderer_registry.py
def emit_hook(
    backend_name: str,
    node_type: type,
    *,
    replace: bool = False,
) -> Callable[[EmitFn], EmitFn]:
    """Decorator that registers an emit function for a
    `(backend_name, node_type)` combination.

    ``node_type`` is an [`IRNode`][quivers.transpile.ir.IRNode] or
    [`IRArg`][quivers.transpile.ir.IRArg] subclass. Registration
    happens at import time of the module that declares the emit;
    downstream libraries add support for a new backend or a new
    node type by importing their module once.

    ``replace=True`` allows re-registering over an existing entry.
    Left unset, a second registration for the same key raises
    :class:`RendererDuplicateError` so a user does not silently
    shadow a built-in emit.

    Parameters
    ----------
    backend_name
        The backend the emit renders for.
    node_type
        The IR node or argument class the emit handles.
    replace
        Whether to overwrite an existing registration.

    Returns
    -------
    Callable[[EmitFn], EmitFn]
        A decorator that registers its argument and returns it unchanged.

    Raises
    ------
    RendererDuplicateError
        From the decorator, if the combination is registered and
        ``replace`` is false.
    """

    def decorator(fn: EmitFn) -> EmitFn:
        backend_table = _REGISTRY.setdefault(backend_name, {})
        if node_type in backend_table and not replace:
            raise RendererDuplicateError(
                f"emit_hook: backend {backend_name!r} already has an "
                f"emit registered for {node_type.__name__!r}; pass "
                "`replace=True` to override."
            )
        backend_table[node_type] = fn
        return fn

    return decorator

get_emit

get_emit(backend_name: str, node_type: type) -> EmitFn

Look up the registered emit for a (backend_name, node_type) combination. Raises :class:RendererLookupError with the list of registered types for the backend when no emit is present.

PARAMETER DESCRIPTION
backend_name

The backend to look in.

TYPE: str

node_type

The IR node or argument class to look up.

TYPE: type

RETURNS DESCRIPTION
EmitFn

The registered emit function.

RAISES DESCRIPTION
RendererLookupError

If the backend has no registrations, or none for node_type.

Source code in src/quivers/transpile/renderer_registry.py
def get_emit(backend_name: str, node_type: type) -> EmitFn:
    """Look up the registered emit for a `(backend_name, node_type)`
    combination. Raises :class:`RendererLookupError` with the list
    of registered types for the backend when no emit is present.

    Parameters
    ----------
    backend_name
        The backend to look in.
    node_type
        The IR node or argument class to look up.

    Returns
    -------
    EmitFn
        The registered emit function.

    Raises
    ------
    RendererLookupError
        If the backend has no registrations, or none for ``node_type``.
    """
    backend_table = _REGISTRY.get(backend_name)
    if backend_table is None:
        raise RendererLookupError(
            f"get_emit: no emits registered for backend "
            f"{backend_name!r}. Registered backends: "
            f"{sorted(_REGISTRY)}"
        )
    if node_type not in backend_table:
        registered = sorted(t.__name__ for t in backend_table)
        raise RendererLookupError(
            f"get_emit: backend {backend_name!r} has no emit for "
            f"{node_type.__name__!r}. Registered: {registered}"
        )
    return backend_table[node_type]

registered_backends

registered_backends() -> list[str]

List every backend that has at least one registered emit.

RETURNS DESCRIPTION
list[str]

The backend names, sorted.

Source code in src/quivers/transpile/renderer_registry.py
def registered_backends() -> list[str]:
    """List every backend that has at least one registered emit.

    Returns
    -------
    list[str]
        The backend names, sorted.
    """
    return sorted(_REGISTRY)

registered_node_types

registered_node_types(backend_name: str) -> list[str]

List the class names of every node type registered for backend_name.

PARAMETER DESCRIPTION
backend_name

The backend to list.

TYPE: str

RETURNS DESCRIPTION
list[str]

The registered class names, sorted; empty for an unknown backend.

Source code in src/quivers/transpile/renderer_registry.py
def registered_node_types(backend_name: str) -> list[str]:
    """List the class names of every node type registered for
    ``backend_name``.

    Parameters
    ----------
    backend_name
        The backend to list.

    Returns
    -------
    list[str]
        The registered class names, sorted; empty for an unknown backend.
    """
    table = _REGISTRY.get(backend_name, {})
    return sorted(t.__name__ for t in table)

unregister

unregister(backend_name: str, node_type: type) -> None

Remove a registration. Used by tests to reset state; not typically called in production code.

PARAMETER DESCRIPTION
backend_name

The backend the registration belongs to.

TYPE: str

node_type

The IR node or argument class to remove.

TYPE: type

Source code in src/quivers/transpile/renderer_registry.py
def unregister(backend_name: str, node_type: type) -> None:
    """Remove a registration. Used by tests to reset state; not
    typically called in production code.

    Parameters
    ----------
    backend_name
        The backend the registration belongs to.
    node_type
        The IR node or argument class to remove.
    """
    if backend_name in _REGISTRY and node_type in _REGISTRY[backend_name]:
        del _REGISTRY[backend_name][node_type]
        if not _REGISTRY[backend_name]:
            del _REGISTRY[backend_name]