Skip to content

Commit 2c1d71a

Browse files
committed
Added current_connection and refactored current_injector
1 parent c2e78ad commit 2c1d71a

File tree

10 files changed

+149
-148
lines changed

10 files changed

+149
-148
lines changed

ellar/core/__init__.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,15 @@
22

33
from .conf import Config, ConfigDefaultTypesMixin
44
from .connection import HTTPConnection, Request, WebSocket
5-
from .context import ApplicationContext, config, current_injector
6-
from .execution_context import ExecutionContext, HostContext
5+
from .execution_context import (
6+
ExecutionContext,
7+
HostContext,
8+
HttpRequestConnectionContext,
9+
config,
10+
current_connection,
11+
current_injector,
12+
with_injector_context,
13+
)
714
from .guards import GuardConsumer
815
from .interceptors import EllarInterceptorConsumer
916
from .modules import (
@@ -15,6 +22,7 @@
1522
)
1623
from .services import Reflector, reflector
1724
from .shortcuts import host, mount
25+
from .templating import TemplateRenderingService
1826
from .versioning import VersioningSchemes
1927

2028
__all__ = [
@@ -38,8 +46,11 @@
3846
"host",
3947
"current_injector",
4048
"config",
41-
"ApplicationContext",
4249
"ForwardRefModule",
50+
"TemplateRenderingService",
51+
"HttpRequestConnectionContext",
52+
"current_connection",
53+
"with_injector_context",
4354
]
4455

4556

ellar/core/connection/http.py

Lines changed: 0 additions & 27 deletions
This file was deleted.

ellar/core/connection/websocket.py

Lines changed: 0 additions & 12 deletions
This file was deleted.

ellar/core/context.py

Lines changed: 0 additions & 103 deletions
This file was deleted.

ellar/core/execution_context/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,19 @@
44
from .execution import ExecutionContext
55
from .factory import ExecutionContextFactory, HostContextFactory
66
from .host import HostContext
7+
from .injector import config, current_injector, with_injector_context
8+
from .request import HttpRequestConnectionContext, current_connection
79

810
__all__ = [
911
"ExecutionContext",
1012
"HostContext",
1113
"ExecutionContextFactory",
1214
"HostContextFactory",
15+
"current_injector",
16+
"config",
17+
"HttpRequestConnectionContext",
18+
"current_connection",
19+
"with_injector_context",
1320
]
1421

1522
add_default_resolver(ExecutionContext, ExecutionContextParameter)

ellar/core/execution_context/factory.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from .websocket import WebSocketHostContext
2121

2222
if t.TYPE_CHECKING: # pragma: no cover
23-
from ellar.common.routing import RouteOperationBase
23+
from ellar.core.routing import RouteOperationBase
2424

2525

2626
@injectable()

ellar/core/execution_context/host.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from ellar.common.compatible import cached_property
1111
from ellar.common.interfaces import IHostContext
1212
from ellar.common.types import TReceive, TScope, TSend
13-
from ellar.core.context import current_injector
13+
from ellar.core.execution_context import current_injector
1414

1515
if t.TYPE_CHECKING: # pragma: no cover
1616
from ellar.app.main import App
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import os
2+
import typing as t
3+
from contextlib import asynccontextmanager
4+
from contextvars import ContextVar
5+
6+
from ellar.common.constants import ELLAR_CONFIG_MODULE
7+
from ellar.common.logging import logger
8+
from ellar.core.conf import Config
9+
from ellar.di import EllarInjector
10+
from ellar.utils.functional import SimpleLazyObject, empty
11+
12+
_injector_context_var: ContextVar[EllarInjector] = ContextVar("ellar.di.EllarInjector")
13+
_injector_context_var.set(empty)
14+
15+
16+
def _get_injector() -> EllarInjector:
17+
injector_ctx = _injector_context_var.get()
18+
if injector_ctx is empty:
19+
raise RuntimeError("ApplicationContext is not available at this scope.")
20+
return injector_ctx
21+
22+
23+
def _get_application_config() -> Config:
24+
injector_ctx = _injector_context_var.get()
25+
if injector_ctx is not empty:
26+
return t.cast(Config, injector_ctx.get(Config))
27+
28+
config_module = os.environ.get(ELLAR_CONFIG_MODULE)
29+
if not config_module:
30+
logger.warning(
31+
"You are trying to access app config outside app context "
32+
"and %s is not specified. This may cause differences in config "
33+
"values with the app" % (ELLAR_CONFIG_MODULE,)
34+
)
35+
36+
return Config(config_module=config_module)
37+
38+
39+
config: Config = t.cast(Config, SimpleLazyObject(func=_get_application_config))
40+
41+
current_injector: EllarInjector = t.cast(
42+
EllarInjector, SimpleLazyObject(func=_get_injector)
43+
)
44+
45+
46+
def _clear_lazy_objects() -> None:
47+
current_injector._wrapped = empty # type:ignore[attr-defined]
48+
config._wrapped = empty
49+
50+
51+
@asynccontextmanager
52+
async def with_injector_context(
53+
injector: EllarInjector,
54+
) -> t.AsyncGenerator[EllarInjector, t.Any]:
55+
_clear_lazy_objects()
56+
_injector_reset_token = _injector_context_var.set(injector)
57+
58+
yield injector
59+
60+
_injector_context_var.reset(_injector_reset_token)
61+
_clear_lazy_objects()
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import typing as t
2+
from types import TracebackType
3+
4+
from ellar.common import IHostContext
5+
from ellar.common.logging import logger
6+
from ellar.di import (
7+
RequestScopeContext,
8+
register_request_scope_context,
9+
request_context_var,
10+
)
11+
from ellar.events import request_started, request_teardown
12+
from ellar.utils.functional import SimpleLazyObject, empty
13+
14+
15+
def _clear_lazy_objects() -> None:
16+
current_connection._wrapped = empty # type:ignore[attr-defined]
17+
18+
19+
class HttpRequestConnectionContext(RequestScopeContext):
20+
def __init__(self, host_context: IHostContext) -> None:
21+
super().__init__()
22+
self._reset_token: t.Optional[t.Any] = None
23+
self.host_context = host_context
24+
25+
async def __aenter__(self) -> "HttpRequestConnectionContext":
26+
self._reset_token = request_context_var.set(self)
27+
28+
register_request_scope_context(IHostContext, self.host_context)
29+
30+
_clear_lazy_objects()
31+
32+
await request_started.run()
33+
return self
34+
35+
@t.no_type_check
36+
async def __aexit__(
37+
self,
38+
exc_type: t.Optional[t.Any],
39+
exc_value: t.Optional[BaseException],
40+
tb: t.Optional[TracebackType],
41+
) -> None:
42+
try:
43+
request_context_var.reset(self._reset_token)
44+
except ValueError as vex:
45+
logger.exception(vex)
46+
finally:
47+
_clear_lazy_objects()
48+
49+
await request_teardown.run()
50+
51+
52+
def _get_connection() -> IHostContext:
53+
request_context: t.Union[HttpRequestConnectionContext, t.Any] = (
54+
request_context_var.get(empty)
55+
)
56+
if request_context is empty:
57+
raise RuntimeError("HTTPHostContext is only available during request.")
58+
59+
return request_context.host_context
60+
61+
62+
current_connection: IHostContext = t.cast(
63+
IHostContext, SimpleLazyObject(func=_get_connection)
64+
)

ellar/core/interceptors/consumer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from ellar.di import injectable
88

99
if t.TYPE_CHECKING: # pragma: no cover
10-
from ellar.common.routing import RouteOperationBase
10+
from ellar.core.routing import RouteOperationBase
1111

1212

1313
@injectable

0 commit comments

Comments
 (0)