Coverage for src/lexigram/graphql/directives/registry.py: 100%
23 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-25 04:37 +0800
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-25 04:37 +0800
1"""Registry implementation for GraphQL directive handlers.
3This module was split out of the package root to keep ``__init__`` focused
4on exports. ``DirectiveRegistry`` satisfies
5:class:`~lexigram.contracts.graphql.DirectiveHandler`.
6"""
8from __future__ import annotations
10from typing import Any
13class DirectiveRegistry:
14 """Registry-based implementation of :class:`~lexigram.contracts.graphql.DirectiveHandler`.
16 Maps directive names to handler callables via :meth:`on` (decorator syntax
17 or explicit :meth:`register` call). When :meth:`apply_directive` is invoked
18 with an unknown directive name, the target is returned unchanged if no
19 default handler is registered, or the default handler is called.
21 Args:
22 default_handler: Optional fallback for unrecognised directives.
23 Receives the same ``(directive_name, args, target)`` signature.
24 When ``None`` (default), unknown directives are silently ignored.
25 """
27 def __init__(
28 self,
29 default_handler: Any | None = None,
30 ) -> None:
31 self._handlers: dict[str, Any] = {}
32 self._default = default_handler
34 def register(
35 self,
36 directive_name: str,
37 handler: Any,
38 ) -> None:
39 """Register a handler callable for a directive.
41 Args:
42 directive_name: Name of the GraphQL directive (without ``@``).
43 handler: Callable ``(directive_name, args, target) -> target``.
44 """
45 self._handlers[directive_name] = handler
47 def on(self, directive_name: str) -> Any:
48 """Decorator that registers a handler for *directive_name*.
50 Example::
52 @registry.on("auth")
53 def apply_auth(name, args, target):
54 target.__roles__ = args.get("roles", [])
55 return target
57 Args:
58 directive_name: Name of the directive.
60 Returns:
61 Decorator that registers the decorated function.
62 """
64 def decorator(func: Any) -> Any:
65 self.register(directive_name, func)
66 return func
68 return decorator
70 def apply_directive(
71 self,
72 directive_name: str,
73 args: dict[str, Any],
74 target: Any,
75 ) -> Any:
76 """Apply a registered directive handler to *target*.
78 If no handler is registered under *directive_name* the default handler
79 is called if one was supplied; otherwise *target* is returned as-is.
81 Args:
82 directive_name: Directive to apply (without ``@``).
83 args: Directive arguments from the schema.
84 target: Schema element to transform.
86 Returns:
87 The (possibly transformed) *target*.
88 """
89 handler = self._handlers.get(directive_name, self._default)
90 if handler is None:
91 return target
92 return handler(directive_name, args, target)
94 def __contains__(self, directive_name: str) -> bool:
95 """Return ``True`` if a handler is registered for *directive_name*."""
96 return directive_name in self._handlers
98 def __repr__(self) -> str:
99 names = sorted(self._handlers)
100 return f"DirectiveRegistry(directives={names!r})"