Coverage for src / lexigram / contracts / core / module.py: 0%
42 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-15 18:57 +0800
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-15 18:57 +0800
1"""Module protocol definitions for the Lexigram framework.
3These protocols define the structural contracts for modules at the
4contract layer. External packages (``lexigram-web``, ``lexigram-sql``,
5etc.) can type-hint against these protocols without importing
6``lexigram`` core.
8The concrete implementations (``Module``, ``DynamicModule``,
9``ModuleMetadata``, ``ModuleCompiler``) live in
10``lexigram.di.module`` within the core package.
11"""
13from __future__ import annotations
15from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
17if TYPE_CHECKING:
18 from collections.abc import Sequence
21@runtime_checkable
22class ModuleMetadataProtocol(Protocol):
23 """Structural protocol for module metadata descriptors.
25 Satisfied by :class:`lexigram.di.module.ModuleMetadata`.
26 """
28 @property
29 def name(self) -> str: ...
31 @property
32 def providers(self) -> tuple[type, ...]: ...
34 @property
35 def imports(self) -> tuple[type | Any, ...]: ...
37 @property
38 def exports(self) -> tuple[type, ...]: ...
40 @property
41 def controllers(self) -> tuple[type, ...]: ...
43 @property
44 def is_global(self) -> bool: ...
47@runtime_checkable
48class ModuleProtocol(Protocol):
49 """Structural protocol for module classes.
51 A class satisfies this protocol if it has a ``__lexigram_module__``
52 attribute containing module metadata. This is automatically
53 attached by the ``@module()`` decorator.
55 Usage in type hints::
57 def process_module(mod: ModuleProtocol) -> None:
58 meta = mod.__lexigram_module__
59 print(f"Module {meta.name} has {len(meta.providers)} providers")
60 """
62 @property
63 def __lexigram_module__(self) -> ModuleMetadataProtocol: ...
66@runtime_checkable
67class DynamicModuleProtocol(Protocol):
68 """Structural protocol for dynamic module descriptors.
70 Satisfied by :class:`lexigram.di.module.DynamicModule`.
71 Dynamic modules are returned by factory methods like
72 ``Module.configure()`` and fully replace the static ``@module()``
73 metadata for the referenced module class.
74 """
76 @property
77 def module(self) -> type:
78 """The module class this descriptor configures (identity key)."""
79 ...
81 @property
82 def providers(self) -> list[type | Any]:
83 """Provider classes or pre-constructed instances."""
84 ...
86 @property
87 def imports(self) -> list[type | Any]:
88 """Module classes or nested dynamic module descriptors."""
89 ...
91 @property
92 def exports(self) -> list[type]:
93 """Contract types visible to importing modules."""
94 ...
96 @property
97 def controllers(self) -> list[type]:
98 """Controller classes for this module."""
99 ...
101 @property
102 def is_global(self) -> bool:
103 """Whether exports are universally visible."""
104 ...
106 @property
107 def resolved_name(self) -> str:
108 """Module name (explicit or derived from module class)."""
109 ...
112@runtime_checkable
113class CompiledModuleGraphProtocol(Protocol):
114 """Structural protocol for the compiled module graph.
116 Satisfied by :class:`lexigram.di.module.CompiledModuleGraph`.
117 """
119 @property
120 def provider_order(self) -> Sequence[Any]:
121 """Providers in registration order."""
122 ...
124 @property
125 def global_exports(self) -> frozenset[type]:
126 """Union of all global module exports."""
127 ...
129 @property
130 def warnings(self) -> list[str]:
131 """Non-fatal issues detected during compilation."""
132 ...
134 def is_visible(self, from_module: type, service_type: type) -> bool:
135 """Check if a service type is visible to a module."""
136 ...
138 def get_module_names(self) -> list[str]:
139 """Return all module names in the graph."""
140 ...
143@runtime_checkable
144class ModuleCompilerProtocol(Protocol):
145 """Protocol for the module graph compiler.
147 Satisfied by :class:`lexigram.di.module.ModuleCompiler`.
148 """
150 def compile(
151 self,
152 root_modules: list[type | Any],
153 standalone_providers: list[Any] | None = None,
154 ) -> CompiledModuleGraphProtocol:
155 """Compile the module graph.
157 Args:
158 root_modules: Module classes and/or dynamic module instances.
159 standalone_providers: Provider instances not belonging to
160 any module.
162 Returns:
163 A compiled module graph ready for the orchestrator.
164 """
165 ...
168@runtime_checkable
169class ModuleRegistryProtocol(Protocol):
170 """Protocol for runtime module ownership tracking.
172 Satisfied by :class:`lexigram.di.module.ModuleRegistry`.
173 """
175 def register_ownership(
176 self,
177 service_type: type,
178 module_class: type | None,
179 provider_name: str | None = None,
180 ) -> None:
181 """Record that a service type was registered by a module."""
182 ...
184 def get_owner(self, service_type: type) -> type | None:
185 """Get the module that owns a service type."""
186 ...
188 def get_module_services(self, module_class: type) -> frozenset[type]:
189 """Get all service types registered by a module."""
190 ...
192 def validate_exports(
193 self,
194 graph: CompiledModuleGraphProtocol,
195 container: Any | None = None,
196 ) -> list[str]:
197 """Validate that declared exports were actually registered."""
198 ...
201__all__ = [
202 "CompiledModuleGraphProtocol",
203 "DynamicModuleProtocol",
204 "ModuleCompilerProtocol",
205 "ModuleMetadataProtocol",
206 "ModuleProtocol",
207 "ModuleRegistryProtocol",
208]