Coverage for src/lexigram/web/routing/controller_registry.py: 43%
37 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"""Controller registration utilities for Lexigram Web Framework.
3This module provides the ControllerRegistry.
5Note: ControllerRegistry is the canonical source for controller classes.
6RouteRegistry (in registry.py) extends this concept to track route metadata.
7Both registries work together: ControllerRegistry stores the controller classes
8while RouteRegistry stores the resolved route paths and methods.
9"""
11from __future__ import annotations
13from typing import Any
15from lexigram.logging import get_logger
16from lexigram.primitives.registry import Registry
18logger = get_logger(__name__)
21class ControllerRegistry(Registry[str, type]):
22 """Registry for managing controller registrations.
24 Canonical source for controllers: ControllerRegistry.
25 Use ControllerRegistry for controller-level operations (class registration).
26 Use RouteRegistry for route-level operations (path, method, handler).
27 """
29 def __init__(self) -> None:
30 super().__init__(name="controllers")
32 def register(
33 self,
34 key: str | type,
35 value: type | None = None,
36 *,
37 allow_overwrite: bool | None = None,
38 ) -> type | Any:
39 """Register a controller class.
41 Args:
42 key: Component key or class
43 value: The controller class to register
44 allow_overwrite: Whether to allow overwriting an existing registration
46 Returns:
47 The registered controller class
48 """
49 controller_class: type | None
50 if isinstance(key, type):
51 controller_class = key
52 name = getattr(controller_class, "__name__", str(controller_class))
53 else:
54 name = key
55 controller_class = value
57 if controller_class is None:
58 # Decorator usage: @registry.register("name")
59 def decorator(cls: type) -> type:
60 super(ControllerRegistry, self).register(name, cls)
61 return cls
63 return decorator
65 super().register(name, controller_class)
66 return controller_class
68 def get(self, name: str) -> type | None: # type: ignore[override]
69 """Get a controller class by name."""
70 return super().get(name)
72 def list_controllers(self) -> list[str]:
73 """List all registered controller names."""
74 return list(super().keys())
76 def get_all_controllers(self) -> list[type]:
77 """Get all registered controller classes."""
78 return list(super().values())
80 def clear(self) -> None:
81 """Clear all registrations.
83 Use in tests only to prevent test pollution between test cases.
84 """
85 super().clear()
88def controller(name: str | None = None) -> Any:
89 """Decorator to automatically register a controller class.
91 Args:
92 name: Optional controller name, defaults to class name
94 Example:
95 @controller()
96 class UserController(Controller):
97 @get("/users")
98 async def get_users(self):
99 return {"users": []}
100 """
102 def decorator(cls: type) -> type:
103 # Register with controller registry
104 controller_name = name or cls.__name__
105 controller_registry.register(controller_name, cls)
107 logger.debug(
108 "Auto-registered controller %s from %s.%s",
109 controller_name,
110 cls.__module__,
111 cls.__name__,
112 )
113 return cls
115 return decorator
118# Global controller registry instance
119controller_registry = ControllerRegistry()