Skip to content

Commit 29cab0c

Browse files
committed
Removed IHostContext and IExecutionContext concrete type registration
1 parent e3d224d commit 29cab0c

File tree

25 files changed

+143
-125
lines changed

25 files changed

+143
-125
lines changed

ellar/core/connection/http.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class HTTPConnection(StarletteHTTPConnection):
1616
def service_provider(self) -> "RequestServiceProvider":
1717
assert (
1818
SCOPE_SERVICE_PROVIDER in self.scope
19-
), "DIRequestServiceProviderMiddleware must be installed to access request.service_provider"
19+
), "RequestServiceProviderMiddleware must be installed to access request.service_provider"
2020
return t.cast("RequestServiceProvider", self.scope[SCOPE_SERVICE_PROVIDER])
2121

2222

ellar/core/context/__init__.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,16 @@
44
from .interface import (
55
IExecutionContext,
66
IHostContext,
7-
IHTTPConnectionHost,
8-
IWebSocketConnectionHost,
7+
IHTTPHostContext,
8+
IWebSocketHostContext,
99
)
1010

1111
__all__ = [
1212
"IExecutionContext",
1313
"ExecutionContext",
1414
"IHostContext",
15-
"IHTTPConnectionHost",
16-
"IWebSocketConnectionHost",
15+
"IHTTPHostContext",
16+
"IWebSocketHostContext",
1717
"HostContext",
1818
"HostContextException",
1919
]

ellar/core/context/execution.py

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import typing as t
22

3+
from ellar.compatible import cached_property
34
from ellar.constants import CONTROLLER_CLASS_KEY, SCOPE_SERVICE_PROVIDER
5+
from ellar.di import injectable
6+
from ellar.di.providers import ModuleProvider
47
from ellar.services.reflector import Reflector
58
from ellar.types import TReceive, TScope, TSend
69

@@ -14,7 +17,12 @@
1417
from ellar.di.injector import RequestServiceProvider
1518

1619

20+
@injectable()
1721
class ExecutionContext(HostContext, IExecutionContext):
22+
"""
23+
Context for route functions and controllers
24+
"""
25+
1826
__slots__ = ("_operation_handler",)
1927

2028
def __init__(
@@ -23,25 +31,24 @@ def __init__(
2331
scope: TScope,
2432
receive: TReceive,
2533
send: TSend,
26-
operation_handler: t.Callable = None,
34+
operation_handler: t.Callable,
35+
reflector: Reflector,
2736
) -> None:
2837
super(ExecutionContext, self).__init__(scope=scope, receive=receive, send=send)
2938
self._operation_handler = operation_handler
30-
31-
def set_operation(self, operation: t.Optional["RouteOperationBase"] = None) -> None:
32-
if operation:
33-
self._operation_handler = operation.endpoint
39+
self.reflector = reflector
3440

3541
def get_handler(self) -> t.Callable:
3642
assert self._operation_handler, "Operation is not available yet."
3743
return self._operation_handler
3844

45+
@cached_property
46+
def _get_class(self) -> t.Optional[t.Type["ControllerBase"]]:
47+
result = self.reflector.get(CONTROLLER_CLASS_KEY, self.get_handler())
48+
return t.cast(t.Optional[t.Type["ControllerBase"]], result)
49+
3950
def get_class(self) -> t.Optional[t.Type["ControllerBase"]]:
40-
reflector = self.get_service_provider().get(Reflector)
41-
result: t.Optional[t.Type["ControllerBase"]] = reflector.get(
42-
CONTROLLER_CLASS_KEY, self.get_handler()
43-
)
44-
return result
51+
return self._get_class # type: ignore
4552

4653
@classmethod
4754
def create_context(
@@ -50,13 +57,14 @@ def create_context(
5057
scope: TScope,
5158
receive: TReceive,
5259
send: TSend,
53-
operation: t.Optional["RouteOperationBase"] = None,
54-
) -> "ExecutionContext":
55-
execution_context = cls(
60+
operation: "RouteOperationBase",
61+
) -> IExecutionContext:
62+
provider = ModuleProvider(
63+
cls,
5664
scope=scope,
5765
receive=receive,
5866
send=send,
59-
operation_handler=operation.endpoint if operation else None,
67+
operation_handler=operation.endpoint,
6068
)
6169
request_provider: "RequestServiceProvider" = t.cast(
6270
"RequestServiceProvider", scope.get(SCOPE_SERVICE_PROVIDER)
@@ -67,8 +75,7 @@ def create_context(
6775
"Please ensure `RequestServiceProviderMiddleware` is registered."
6876
)
6977

70-
request_provider.update_context(
71-
t.cast(t.Type, IExecutionContext), execution_context
72-
)
73-
request_provider.update_context(cls, execution_context)
78+
request_provider.update_context(IExecutionContext, provider)
79+
execution_context = request_provider.get(IExecutionContext) # type: ignore
80+
7481
return execution_context

ellar/core/context/factory.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,16 @@
44
from ellar.types import T
55

66
from .exceptions import HostContextException
7-
from .http import HTTPConnectionHost
7+
from .http import HTTPHostContext
88
from .interface import IHostContext
9-
from .websocket import WebSocketConnectionHost
9+
from .websocket import WebSocketHostContext
1010

1111

1212
class HostContextFactory(t.Generic[T], ABC):
13+
"""
14+
Factory for creating HostContext types and validating them.
15+
"""
16+
1317
context_type: t.Type[T]
1418

1519
__slots__ = ("context", "_context_type", "validate_func")
@@ -37,15 +41,20 @@ def create_context_type(self) -> T:
3741
)
3842

3943

40-
class HTTPConnectionContextFactory(HostContextFactory[HTTPConnectionHost]):
41-
context_type = HTTPConnectionHost
44+
class HTTPConnectionContextFactory(HostContextFactory[HTTPHostContext]):
45+
context_type = HTTPHostContext
4246

4347
def validate(self) -> None:
48+
"""
49+
Validation is skipped here because HTTPConnection is compatible with websocket and http
50+
During websocket connection, we can still get HTTPConnection available,
51+
but we can get request instance or response.
52+
"""
4453
pass
4554

4655

47-
class WebSocketContextFactory(HostContextFactory[WebSocketConnectionHost]):
48-
context_type = WebSocketConnectionHost
56+
class WebSocketContextFactory(HostContextFactory[WebSocketHostContext]):
57+
context_type = WebSocketHostContext
4958

5059
def validate(self) -> None:
5160
if self.context.get_type() != "websocket":

ellar/core/context/host.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from ellar.constants import SCOPE_SERVICE_PROVIDER
55
from ellar.types import TReceive, TScope, TSend
66

7-
from .interface import IHostContext, IHTTPConnectionHost, IWebSocketConnectionHost
7+
from .interface import IHostContext, IHTTPHostContext, IWebSocketHostContext
88

99
if t.TYPE_CHECKING: # pragma: no cover
1010
from ellar.core.main import App
@@ -36,11 +36,11 @@ def get_service_provider(self) -> "RequestServiceProvider":
3636
def _service_provider(self) -> "RequestServiceProvider":
3737
return self.scope[SCOPE_SERVICE_PROVIDER] # type:ignore
3838

39-
def switch_to_http_connection(self) -> IHTTPConnectionHost:
40-
return self.get_service_provider().get(IHTTPConnectionHost) # type: ignore
39+
def switch_to_http_connection(self) -> IHTTPHostContext:
40+
return self.get_service_provider().get(IHTTPHostContext) # type: ignore
4141

42-
def switch_to_websocket(self) -> IWebSocketConnectionHost:
43-
return self.get_service_provider().get(IWebSocketConnectionHost) # type: ignore
42+
def switch_to_websocket(self) -> IWebSocketHostContext:
43+
return self.get_service_provider().get(IWebSocketHostContext) # type: ignore
4444

4545
def get_type(self) -> str:
4646
return str(self.scope["type"])

ellar/core/context/http.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,14 @@
88
from ellar.types import TReceive, TScope, TSend
99

1010
from .exceptions import HostContextException
11-
from .interface import IHTTPConnectionHost
11+
from .interface import IHTTPHostContext
1212

1313

14-
class HTTPConnectionHost(IHTTPConnectionHost):
14+
class HTTPHostContext(IHTTPHostContext):
15+
"""
16+
Provides a context around HTTP Connection
17+
"""
18+
1519
__slots__ = (
1620
"scope",
1721
"receive",

ellar/core/context/interface.py

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,10 @@
88
if t.TYPE_CHECKING: # pragma: no cover
99
from ellar.core import ControllerBase
1010
from ellar.core.main import App
11-
from ellar.core.routing import RouteOperationBase
1211
from ellar.di.injector import RequestServiceProvider
1312

1413

15-
class IHTTPConnectionHost(ABC):
14+
class IHTTPHostContext(ABC):
1615
@property
1716
@abstractmethod
1817
def has_response(self) -> bool:
@@ -31,7 +30,7 @@ def get_client(self) -> HTTPConnection:
3130
"""Returns HTTPConnection instance"""
3231

3332

34-
class IWebSocketConnectionHost(ABC):
33+
class IWebSocketHostContext(ABC):
3534
@abstractmethod
3635
def get_client(self) -> WebSocket:
3736
"""Returns WebSocket instance"""
@@ -43,11 +42,11 @@ def get_service_provider(self) -> "RequestServiceProvider":
4342
"""Gets RequestServiceProvider instance"""
4443

4544
@abstractmethod
46-
def switch_to_http_connection(self) -> IHTTPConnectionHost:
45+
def switch_to_http_connection(self) -> IHTTPHostContext:
4746
"""Returns HTTPConnection instance"""
4847

4948
@abstractmethod
50-
def switch_to_websocket(self) -> IWebSocketConnectionHost:
49+
def switch_to_websocket(self) -> IWebSocketHostContext:
5150
"""Returns WebSocket instance"""
5251

5352
@abstractmethod
@@ -64,10 +63,6 @@ def get_args(self) -> t.Tuple[TScope, TReceive, TSend]:
6463

6564

6665
class IExecutionContext(IHostContext, ABC):
67-
@abstractmethod
68-
def set_operation(self, operation: t.Optional["RouteOperationBase"] = None) -> None:
69-
"""re-inits operation handler"""
70-
7166
@abstractmethod
7267
def get_handler(self) -> t.Callable:
7368
"""Gets operation handler"""

ellar/core/context/websocket.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22
from ellar.core.connection import WebSocket
33
from ellar.types import TReceive, TScope, TSend
44

5-
from .interface import IWebSocketConnectionHost
5+
from .interface import IWebSocketHostContext
66

77

8-
class WebSocketConnectionHost(IWebSocketConnectionHost):
8+
class WebSocketHostContext(IWebSocketHostContext):
9+
"""
10+
Provides a context around websocket session
11+
"""
12+
913
__slots__ = (
1014
"scope",
1115
"receive",

ellar/core/guard/base.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from starlette.status import HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN
77

88
from ellar.core.connection import HTTPConnection
9-
from ellar.core.context import ExecutionContext
9+
from ellar.core.context import IExecutionContext
1010
from ellar.core.exceptions import APIException
1111

1212

@@ -18,7 +18,7 @@ class GuardCanActivate(ABC, metaclass=ABCMeta):
1818
detail: str = "Not authenticated"
1919

2020
@abstractmethod
21-
async def can_activate(self, context: ExecutionContext) -> bool:
21+
async def can_activate(self, context: IExecutionContext) -> bool:
2222
pass
2323

2424
def raise_exception(self) -> None:
@@ -41,7 +41,7 @@ async def handle_request(self, *, connection: HTTPConnection) -> t.Optional[t.An
4141
def get_guard_scheme(cls) -> t.Dict:
4242
pass
4343

44-
async def can_activate(self, context: ExecutionContext) -> bool:
44+
async def can_activate(self, context: IExecutionContext) -> bool:
4545
connection = context.switch_to_http_connection().get_client()
4646
result = await self.handle_request(connection=connection)
4747
if result:

ellar/core/main.py

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,7 @@
55

66
from ellar.constants import LOG_LEVELS
77
from ellar.core.conf import Config
8-
from ellar.core.context import (
9-
ExecutionContext,
10-
HostContext,
11-
IExecutionContext,
12-
IHostContext,
13-
)
8+
from ellar.core.context import IExecutionContext, IHostContext
149
from ellar.core.datastructures import State, URLPath
1510
from ellar.core.events import EventHandler, RouterEventManager
1611
from ellar.core.exceptions.interfaces import (
@@ -32,7 +27,7 @@
3227
from ellar.core.templating import AppTemplating, Environment
3328
from ellar.core.versioning import VERSIONING, BaseAPIVersioning
3429
from ellar.di.injector import EllarInjector
35-
from ellar.di.providers import ModuleProvider
30+
from ellar.di.providers import ServiceUnavailableProvider
3631
from ellar.logger import logger
3732
from ellar.services.reflector import Reflector
3833
from ellar.types import ASGIApp, T, TReceive, TScope, TSend
@@ -269,21 +264,18 @@ def _run_module_application_ready(
269264
module_ref.run_application_ready(self)
270265

271266
def _finalize_app_initialization(self) -> None:
267+
272268
self.injector.container.register_instance(self)
273269
self.injector.container.register_instance(Reflector())
274270
self.injector.container.register_instance(self.config, Config)
275271
self.injector.container.register_instance(self.jinja_environment, Environment)
276272
self.injector.container.register_scoped(
277273
IHostContext,
278-
ModuleProvider(
279-
HostContext, scope={"type": "invalid"}, receive=None, send=None
280-
),
274+
ServiceUnavailableProvider("Service Unavailable at the current context."),
281275
)
282276
self.injector.container.register_scoped(
283277
IExecutionContext,
284-
ModuleProvider(
285-
ExecutionContext, scope={"type": "invalid"}, receive=None, send=None
286-
),
278+
ServiceUnavailableProvider("Service Unavailable at the current context."),
287279
)
288280
self._run_module_application_ready()
289281

0 commit comments

Comments
 (0)