Coverage for src/lexigram/web/__init__.py: 89%
18 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"""Lexigram Web - Progressive Web Framework for Lexigram"""
3from __future__ import annotations
5import importlib.metadata
6from typing import TYPE_CHECKING, Any
8__path__ = __import__("pkgutil").extend_path(__path__, __name__)
10from lexigram.web.constants import __version__ as __version__
12if TYPE_CHECKING:
13 from lexigram.web.config import WebConfig
14 from lexigram.web.di.provider import WebProvider
15 from lexigram.web.di.rate_limit import RateLimitProvider
18# =============================================================================
19# Lazy Loading to Avoid Circular Imports
20# =============================================================================
22_LAZY_IMPORTS: dict[str, tuple[str, str]] = {
23 # Discovery
24 "discover_controllers": ("lexigram.web.routing.discovery", "discover_controllers"),
25 # Config & Provider
26 "WebConfig": ("lexigram.web.config", "WebConfig"),
27 "WebProvider": ("lexigram.web.di.provider", "WebProvider"),
28 "RateLimitProvider": ("lexigram.web.di.rate_limit", "RateLimitProvider"),
29 # Modules
30 "WebModule": ("lexigram.web.module", "WebModule"),
31 # Routing
32 "Controller": ("lexigram.web.routing.controllers", "Controller"),
33 "CQRSController": ("lexigram.web.routing.cqrs", "CQRSController"),
34 "ControllerRegistry": (
35 "lexigram.web.routing.controller_registry",
36 "ControllerRegistry",
37 ),
38 "GenericController": ("lexigram.web.routing.controller", "GenericController"),
39 "Router": ("lexigram.web.routing.router", "Router"),
40 "RouteRegistry": ("lexigram.web.routing.registry", "RouteRegistry"),
41 "VersionExtractor": ("lexigram.web.routing.versioning", "VersionExtractor"),
42 "ApiVersionMetadata": ("lexigram.web.routing.versioning", "ApiVersionMetadata"),
43 "api_version": ("lexigram.web.routing.versioning", "api_version"),
44 # Parameters
45 "body": ("lexigram.web.routing.parameters", "body"),
46 "controller": ("lexigram.web.routing.controller_registry", "controller"),
47 "controller_registry": (
48 "lexigram.web.routing.controller_registry",
49 "controller_registry",
50 ),
51 "cookie": ("lexigram.web.routing.parameters", "cookie"),
52 "file": ("lexigram.web.routing.parameters", "file"),
53 "form": ("lexigram.web.routing.parameters", "form"),
54 "get_version": ("lexigram.web.routing.versioning", "get_version"),
55 "header": ("lexigram.web.routing.parameters", "header"),
56 "path": ("lexigram.web.routing.parameters", "path"),
57 "query": ("lexigram.web.routing.parameters", "query"),
58 "register_controller": ("lexigram.web.routing.registry", "register_controller"),
59 "route_registry": ("lexigram.web.routing.registry", "route_registry"),
60 "version": ("lexigram.web.routing.versioning", "version"),
61 # HTTP Methods (from decorators)
62 "delete": ("lexigram.web.routing.decorators", "delete"),
63 "get": ("lexigram.web.routing.decorators", "get"),
64 "head": ("lexigram.web.routing.decorators", "head"),
65 "options": ("lexigram.web.routing.decorators", "options"),
66 "patch": ("lexigram.web.routing.decorators", "patch"),
67 "post": ("lexigram.web.routing.decorators", "post"),
68 "put": ("lexigram.web.routing.decorators", "put"),
69 "trace": ("lexigram.web.routing.decorators", "trace"),
70 "websocket": ("lexigram.web.routing.decorators", "websocket"),
71 "websocket_handler": ("lexigram.web.websocket.decorators", "websocket_handler"),
72 # Transport
73 "Response": ("lexigram.web.transport.responses", "Response"),
74 "FastJSONResponse": ("lexigram.web.transport.responses", "FastJSONResponse"),
75 "HTMXResponse": ("lexigram.web.transport.responses", "HTMXResponse"),
76 "HTMLContent": ("lexigram.web.transport.responses", "HTMLContent"),
77 "JSONResponse": ("lexigram.web.transport.responses", "JSONResponse"),
78 "HTMLResponse": ("lexigram.web.transport.responses", "HTMLResponse"),
79 "RedirectResponse": ("lexigram.web.transport.responses", "RedirectResponse"),
80 "StreamingResponse": ("lexigram.web.transport.responses", "StreamingResponse"),
81 "FileResponse": ("lexigram.web.transport.responses", "FileResponse"),
82 "WebSocket": ("lexigram.web.transport.websockets", "WebSocket"),
83 # Template rendering
84 "render_template": ("lexigram.web.templates.core", "render_template"),
85 # Response helpers
86 "json_response": ("lexigram.web.transport.responses", "json_response"),
87 "html_response": ("lexigram.web.transport.responses", "html_response"),
88 "redirect_response": ("lexigram.web.transport.responses", "redirect_response"),
89 "streaming_response": ("lexigram.web.transport.responses", "streaming_response"),
90 "file_response": ("lexigram.web.transport.responses", "file_response"),
91 # Middleware
92 "DefaultMiddlewareStack": (
93 "lexigram.web.middleware.stack",
94 "DefaultMiddlewareStack",
95 ),
96 "InputSanitizationMiddleware": (
97 "lexigram.web.middleware.sanitization",
98 "InputSanitizationMiddleware",
99 ),
100 # Security
101 "RoleGuard": ("lexigram.web.security.guards", "RoleGuard"),
102 "guard": ("lexigram.web.security.shortcuts", "guard"),
103 "roles": ("lexigram.web.security.shortcuts", "roles"),
104 # Exceptions
105 "HTTPError": ("lexigram.web.exceptions", "HTTPError"),
106 "NotFoundError": ("lexigram.web.exceptions", "NotFoundError"),
107 "ConflictError": ("lexigram.web.exceptions", "ConflictError"),
108 "RateLimitError": ("lexigram.web.exceptions", "RateLimitError"),
109 "TooManyConnectionsError": ("lexigram.web.exceptions", "TooManyConnectionsError"),
110 "InternalServerError": ("lexigram.web.exceptions", "InternalServerError"),
111 # Background tasks
112 "background": ("lexigram.web.background.decorator", "background"),
113 "BackgroundTasks": ("lexigram.web.background.tasks", "BackgroundTasks"),
114 # Errors
115 "ProblemDetail": ("lexigram.web.errors.problem_detail", "ProblemDetail"),
116 # Request state helpers
117 "get_request_id": ("lexigram.web.dependencies.functions", "get_request_id"),
118 "get_current_user_optional": (
119 "lexigram.web.dependencies.functions",
120 "get_current_user_optional",
121 ),
122 "get_current_user_required": (
123 "lexigram.web.dependencies.functions",
124 "get_current_user_required",
125 ),
126 # Rate limiting sugar
127 "throttle": ("lexigram.web.integrations.throttle", "throttle"),
128 "RateLimitModule": ("lexigram.web.integrations.throttle", "RateLimitModule"),
129 # Quickstart
130 "app": ("lexigram.web.quickstart", "app"),
131 # DI decorators — quickstart-aware versions that also register in the
132 # quickstart service registry so classes defined in any scope are discovered.
133 "singleton": ("lexigram.web.quickstart", "singleton"),
134 "injectable": ("lexigram.web.quickstart", "injectable"),
135 "transient": ("lexigram.di", "transient"),
136 # Result bridge
137 "ResultResponseMapper": (
138 "lexigram.web.routing.result_bridge",
139 "ResultResponseMapper",
140 ),
141 "error_status": ("lexigram.web.routing.result_bridge", "error_status"),
142 # Protocols (web-layer pipe + interceptor)
143 "ParamMetadata": ("lexigram.web.protocols", "ParamMetadata"),
144 "PipeProtocol": ("lexigram.web.protocols", "PipeProtocol"),
145 "ExecutionContextProtocol": ("lexigram.web.protocols", "ExecutionContextProtocol"),
146 "CallHandlerProtocol": ("lexigram.web.protocols", "CallHandlerProtocol"),
147 "WebInterceptorProtocol": ("lexigram.web.protocols", "WebInterceptorProtocol"),
148 "WebInterceptorBase": ("lexigram.web.protocols", "WebInterceptorBase"),
149 # Provider protocols (decoupling)
150 "WebAppAccessorProtocol": ("lexigram.web.protocols", "WebAppAccessorProtocol"),
151 "ControllerSourceProtocol": ("lexigram.web.protocols", "ControllerSourceProtocol"),
152 "ConfigAccessorProtocol": ("lexigram.web.protocols", "ConfigAccessorProtocol"),
153 "ProviderResourcesProtocol": (
154 "lexigram.web.protocols",
155 "ProviderResourcesProtocol",
156 ),
157 "WebProviderProtocol": ("lexigram.web.protocols", "WebProviderProtocol"),
158 # Hooks
159 "WebRequestReceivedHook": ("lexigram.web.hooks", "WebRequestReceivedHook"),
160 "WebResponsePreparedHook": ("lexigram.web.hooks", "WebResponsePreparedHook"),
161 "WebServerStartedHook": ("lexigram.web.hooks", "WebServerStartedHook"),
162 "WebServerStoppedHook": ("lexigram.web.hooks", "WebServerStoppedHook"),
163 # Core re-exports
164 "Result": ("lexigram.result", "Result"),
165 "Ok": ("lexigram.result", "Ok"),
166 "Err": ("lexigram.result", "Err"),
167}
170def __getattr__(name: str) -> Any:
171 """Lazy load attributes to avoid circular imports."""
172 if name in _LAZY_IMPORTS:
173 import importlib
175 module_path, attr_name = _LAZY_IMPORTS[name]
176 module = importlib.import_module(module_path)
177 value = getattr(module, attr_name)
178 # Cache in module globals so __getattr__ isn't called again for this name
179 globals()[name] = value
180 return value
181 raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
184def __dir__() -> list[str]:
185 """List available attributes for IDE support."""
186 return list(__all__) + list(_LAZY_IMPORTS.keys())
189__all__ = list(_LAZY_IMPORTS)