Skip to content

Commit 02532b1

Browse files
committed
Removed Request, response, websocket, executioncontext from been inject by injector.
1 parent 70d8cbd commit 02532b1

File tree

9 files changed

+153
-143
lines changed

9 files changed

+153
-143
lines changed

ellar/core/context/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
IExecutionContextFactory,
88
IHostContext,
99
IHostContextFactory,
10+
IHTTPConnectionContextFactory,
1011
IHTTPHostContext,
12+
IWebSocketContextFactory,
1113
IWebSocketHostContext,
1214
)
1315

@@ -23,4 +25,6 @@
2325
"IHostContextFactory",
2426
"ExecutionContextFactory",
2527
"HostContextFactory",
28+
"IHTTPConnectionContextFactory",
29+
"IWebSocketContextFactory",
2630
]

ellar/core/context/factory.py

Lines changed: 17 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import typing as t
2-
from abc import ABC, abstractmethod
32

4-
from ellar.constants import ASGI_CONTEXT_VAR
5-
from ellar.di import injectable
3+
from ellar.constants import SCOPED_CONTEXT_VAR
4+
from ellar.di import injectable, request_scope
65
from ellar.di.exceptions import ServiceUnavailable
76
from ellar.services import Reflector
8-
from ellar.types import T, TReceive, TScope, TSend
7+
from ellar.types import TReceive, TScope, TSend
98

109
from .exceptions import HostContextException
1110
from .execution import ExecutionContext
@@ -16,41 +15,18 @@
1615
IExecutionContextFactory,
1716
IHostContext,
1817
IHostContextFactory,
18+
IHTTPConnectionContextFactory,
19+
IWebSocketContextFactory,
20+
empty_receive,
21+
empty_send,
1922
)
2023
from .websocket import WebSocketHostContext
2124

2225
if t.TYPE_CHECKING: # pragma: no cover
2326
from ellar.core.routing import RouteOperationBase
2427

2528

26-
class SubHostContextFactory(t.Generic[T], ABC):
27-
"""
28-
Factory for creating HostContext types and validating them.
29-
"""
30-
31-
context_type: t.Type[T]
32-
33-
__slots__ = ()
34-
35-
@injectable()
36-
def __call__(self, context: IHostContext) -> T:
37-
self.validate(context)
38-
return self.create_context_type(context)
39-
40-
@abstractmethod
41-
def validate(self, context: IHostContext) -> None:
42-
pass
43-
44-
def create_context_type(self, context: IHostContext) -> T:
45-
scope, receive, send = context.get_args()
46-
return self.context_type( # type:ignore
47-
scope=scope,
48-
receive=receive,
49-
send=send,
50-
)
51-
52-
53-
class HTTPConnectionContextFactory(SubHostContextFactory[HTTPHostContext]):
29+
class HTTPConnectionContextFactory(IHTTPConnectionContextFactory):
5430
context_type = HTTPHostContext
5531

5632
def validate(self, context: IHostContext) -> None:
@@ -62,7 +38,7 @@ def validate(self, context: IHostContext) -> None:
6238
pass
6339

6440

65-
class WebSocketContextFactory(SubHostContextFactory[WebSocketHostContext]):
41+
class WebSocketContextFactory(IWebSocketContextFactory):
6642
context_type = WebSocketHostContext
6743

6844
def validate(self, context: IHostContext) -> None:
@@ -72,25 +48,18 @@ def validate(self, context: IHostContext) -> None:
7248
)
7349

7450

75-
@injectable()
51+
@injectable(scope=request_scope)
7652
class HostContextFactory(IHostContextFactory):
7753
__slots__ = ()
7854

79-
def create_context(self) -> IHostContext:
80-
scoped_request_args = ASGI_CONTEXT_VAR.get()
81-
82-
if not scoped_request_args:
83-
raise ServiceUnavailable()
84-
85-
scope, receive, send = scoped_request_args.get_args()
55+
def create_context(
56+
self, scope: TScope, receive: TReceive = empty_receive, send: TSend = empty_send
57+
) -> IHostContext:
8658
host_context = HostContext(scope=scope, receive=receive, send=send)
87-
host_context.get_service_provider().update_scoped_context(
88-
IHostContext, host_context # type: ignore
89-
)
9059
return host_context
9160

9261

93-
@injectable()
62+
@injectable(scope=request_scope)
9463
class ExecutionContextFactory(IExecutionContextFactory):
9564
__slots__ = ("reflector",)
9665

@@ -101,10 +70,10 @@ def create_context(
10170
self,
10271
operation: "RouteOperationBase",
10372
scope: TScope,
104-
receive: TReceive,
105-
send: TSend,
73+
receive: TReceive = empty_receive,
74+
send: TSend = empty_send,
10675
) -> IExecutionContext:
107-
scoped_request_args = ASGI_CONTEXT_VAR.get()
76+
scoped_request_args = SCOPED_CONTEXT_VAR.get()
10877

10978
if not scoped_request_args:
11079
raise ServiceUnavailable()
@@ -116,8 +85,5 @@ def create_context(
11685
operation_handler=operation.endpoint,
11786
reflector=self.reflector,
11887
)
119-
i_execution_context.get_service_provider().update_scoped_context(
120-
IExecutionContext, i_execution_context # type: ignore
121-
)
12288

12389
return i_execution_context

ellar/core/context/host.py

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

7-
from .interface import IHostContext, IHTTPHostContext, IWebSocketHostContext
7+
from .interface import (
8+
IHostContext,
9+
IHTTPConnectionContextFactory,
10+
IHTTPHostContext,
11+
IWebSocketContextFactory,
12+
IWebSocketHostContext,
13+
)
814

915
if t.TYPE_CHECKING: # pragma: no cover
1016
from ellar.core.main import App
@@ -36,11 +42,25 @@ def get_service_provider(self) -> "EllarInjector":
3642
def _service_provider(self) -> "EllarInjector":
3743
return self.scope[SCOPE_SERVICE_PROVIDER] # type:ignore
3844

45+
@cached_property
46+
def _get_websocket_context(self) -> IWebSocketHostContext:
47+
ws_context_factory: IWebSocketContextFactory = self.get_service_provider().get(
48+
IWebSocketContextFactory
49+
)
50+
return ws_context_factory(self)
51+
52+
@cached_property
53+
def _get_http_context(self) -> IHTTPHostContext:
54+
http_context_factory: IHTTPConnectionContextFactory = (
55+
self.get_service_provider().get(IHTTPConnectionContextFactory)
56+
)
57+
return http_context_factory(self)
58+
3959
def switch_to_http_connection(self) -> IHTTPHostContext:
40-
return self.get_service_provider().get(IHTTPHostContext) # type: ignore
60+
return self._get_http_context # type: ignore
4161

4262
def switch_to_websocket(self) -> IWebSocketHostContext:
43-
return self.get_service_provider().get(IWebSocketHostContext) # type: ignore
63+
return self._get_websocket_context # type: ignore
4464

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

ellar/core/context/http.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from starlette.responses import Response
55

66
from ellar.compatible import cached_property
7+
from ellar.constants import SCOPED_RESPONSE
78
from ellar.core.connection import HTTPConnection, Request
89
from ellar.types import TReceive, TScope, TSend
910

@@ -43,22 +44,22 @@ def _request(self) -> Request:
4344

4445
@property
4546
def has_response(self) -> bool:
46-
return self._response is not None
47+
return SCOPED_RESPONSE in self.scope
4748

4849
def get_response(self) -> Response:
49-
if not self._response:
50+
if SCOPED_RESPONSE not in self.scope:
5051
if self.scope["type"] != "http":
5152
raise HostContextException(
5253
f"Response is not allow for connection type scope[type]={self.scope['type']}"
5354
)
5455

55-
self._response = Response(
56+
self.scope[SCOPED_RESPONSE] = Response(
5657
background=BackgroundTasks(),
5758
content=None,
5859
status_code=-100,
5960
)
6061

61-
return self._response
62+
return t.cast(Response, self.scope[SCOPED_RESPONSE])
6263

6364
def get_request(self) -> Request:
6465
return self._request # type: ignore

ellar/core/context/interface.py

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
from ellar.core.connection import HTTPConnection, Request, WebSocket
55
from ellar.core.response import Response
6-
from ellar.types import TReceive, TScope, TSend
6+
from ellar.types import T, TMessage, TReceive, TScope, TSend
77

88
if t.TYPE_CHECKING: # pragma: no cover
99
from ellar.core import ControllerBase
@@ -12,6 +12,14 @@
1212
from ellar.di.injector import EllarInjector
1313

1414

15+
async def empty_receive() -> t.NoReturn:
16+
raise RuntimeError("Receive channel has not been made available")
17+
18+
19+
async def empty_send(message: TMessage) -> t.NoReturn:
20+
raise RuntimeError("Send channel has not been made available")
21+
22+
1523
class IHTTPHostContext(ABC):
1624
@property
1725
@abstractmethod
@@ -75,7 +83,12 @@ def get_class(self) -> t.Optional[t.Type["ControllerBase"]]:
7583

7684
class IHostContextFactory(ABC):
7785
@abstractmethod
78-
def create_context(self) -> IHostContext:
86+
def create_context(
87+
self,
88+
scope: TScope,
89+
receive: TReceive = empty_receive,
90+
send: TSend = empty_send,
91+
) -> IHostContext:
7992
pass
8093

8194

@@ -85,7 +98,41 @@ def create_context(
8598
self,
8699
operation: "RouteOperationBase",
87100
scope: TScope,
88-
receive: TReceive,
89-
send: TSend,
101+
receive: TReceive = empty_receive,
102+
send: TSend = empty_send,
90103
) -> IExecutionContext:
91104
pass
105+
106+
107+
class SubHostContextFactory(t.Generic[T], ABC):
108+
"""
109+
Factory for creating HostContext types and validating them.
110+
"""
111+
112+
context_type: t.Type[T]
113+
114+
__slots__ = ()
115+
116+
def __call__(self, context: IHostContext) -> T:
117+
self.validate(context)
118+
return self.create_context_type(context)
119+
120+
@abstractmethod
121+
def validate(self, context: IHostContext) -> None:
122+
pass
123+
124+
def create_context_type(self, context: IHostContext) -> T:
125+
scope, receive, send = context.get_args()
126+
return self.context_type( # type:ignore
127+
scope=scope,
128+
receive=receive,
129+
send=send,
130+
)
131+
132+
133+
class IHTTPConnectionContextFactory(SubHostContextFactory[IHTTPHostContext], ABC):
134+
context_typ: t.Type[IHTTPHostContext]
135+
136+
137+
class IWebSocketContextFactory(SubHostContextFactory[IWebSocketHostContext], ABC):
138+
context_type: t.Type[IWebSocketHostContext]

ellar/core/middleware/exceptions.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from starlette.exceptions import HTTPException
44

55
from ellar.constants import SCOPE_SERVICE_PROVIDER
6-
from ellar.core.context import IHostContext
6+
from ellar.core.context import IHostContextFactory
77
from ellar.types import ASGIApp, TMessage, TReceive, TScope, TSend
88

99
if t.TYPE_CHECKING: # pragma: no cover
@@ -35,7 +35,6 @@ async def sender(message: TMessage) -> None:
3535
if message["type"] == "http.response.start":
3636
response_started = True
3737
await send(message)
38-
return
3938

4039
try:
4140
await self.app(scope, receive, sender)
@@ -63,7 +62,8 @@ async def sender(message: TMessage) -> None:
6362
"EllarInjector", scope[SCOPE_SERVICE_PROVIDER]
6463
)
6564

66-
context = service_provider.get(IHostContext)
65+
context_factory = service_provider.get(IHostContextFactory)
66+
context = context_factory.create_context(scope)
6767

6868
if context.get_type() == "http":
6969
response = await handler.catch(context, exc)
@@ -72,4 +72,5 @@ async def sender(message: TMessage) -> None:
7272
raise RuntimeError(msg) from exc
7373
await response(scope, receive, sender)
7474
elif context.get_type() == "websocket":
75-
await handler.catch(context, exc)
75+
ws_context = context_factory.create_context(scope, receive, send)
76+
await handler.catch(ws_context, exc)

ellar/core/middleware/function.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from starlette.responses import Response
44

55
from ellar.core.connection import HTTPConnection
6-
from ellar.core.context import IHostContext
6+
from ellar.core.context import IHostContext, IHostContextFactory
77
from ellar.types import ASGIApp, TReceive, TScope, TSend
88

99
AwaitableCallable = t.Callable[..., t.Awaitable]
@@ -56,7 +56,8 @@ async def __call__(self, scope: TScope, receive: TReceive, send: TSend) -> None:
5656
if not connection.service_provider: # pragma: no cover
5757
raise Exception("Service Provider is required")
5858

59-
context = connection.service_provider.get(IHostContext)
59+
context_factory = connection.service_provider.get(IHostContextFactory)
60+
context = context_factory.create_context(scope, receive, send)
6061

6162
async def call_next() -> None:
6263
await self.app(scope, receive, send)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
from .base import (
2+
BaseRouteParameterResolver,
3+
IRouteParameterResolver,
4+
RouteParameterModelField,
5+
)
6+
from .bulk_parameter import (
7+
BulkBodyParameterResolver,
8+
BulkFormParameterResolver,
9+
BulkParameterResolver,
10+
)
11+
from .non_parameter.base import BaseConnectionParameterResolver, NonParameterResolver
12+
from .parameter import (
13+
BodyParameterResolver,
14+
CookieParameterResolver,
15+
FileParameterResolver,
16+
FormParameterResolver,
17+
HeaderParameterResolver,
18+
PathParameterResolver,
19+
QueryParameterResolver,
20+
WsBodyParameterResolver,
21+
)
22+
23+
__all__ = [
24+
"IRouteParameterResolver",
25+
"RouteParameterModelField",
26+
"BaseRouteParameterResolver",
27+
"BodyParameterResolver",
28+
"WsBodyParameterResolver",
29+
"FormParameterResolver",
30+
"PathParameterResolver",
31+
"CookieParameterResolver",
32+
"HeaderParameterResolver",
33+
"BulkParameterResolver",
34+
"BulkBodyParameterResolver",
35+
"BulkFormParameterResolver",
36+
"QueryParameterResolver",
37+
"FileParameterResolver",
38+
"NonParameterResolver",
39+
"BaseConnectionParameterResolver",
40+
]

0 commit comments

Comments
 (0)