Skip to content

Commit d5a8035

Browse files
committed
refactored operation creation and building
1 parent 048ab57 commit d5a8035

23 files changed

+311
-143
lines changed

ellar/common/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
ROUTE_OPENAPI_PARAMETERS = "ROUTE_OPENAPI_PARAMETERS"
5555

5656
OPERATION_ENDPOINT_KEY = "OPERATION_ENDPOINT"
57+
ROUTE_OPERATION_PARAMETERS = "__ROUTE_OPERATION_PARAMETERS__"
5758
ROUTE_INTERCEPTORS = "ROUTE_INTERCEPTORS"
5859
CONTROLLER_OPERATION_HANDLER_KEY = "CONTROLLER_OPERATION_HANDLER"
5960
CONTROLLER_CLASS_KEY = "CONTROLLER_CLASS_KEY"

ellar/common/decorators/controller.py

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import inspect
22
import typing as t
33
from abc import ABC
4-
from types import FunctionType
54

65
from ellar.common.compatible import AttributeDict
76
from ellar.common.constants import (
@@ -11,21 +10,26 @@
1110
CONTROLLER_WATERMARK,
1211
NOT_SET,
1312
OPERATION_ENDPOINT_KEY,
13+
ROUTE_OPERATION_PARAMETERS,
1414
)
1515
from ellar.common.exceptions import ImproperConfiguration
16+
from ellar.common.logger import logger
1617
from ellar.common.models import ControllerBase, ControllerType
17-
from ellar.common.routing.controller import ControllerRouteOperationBase
1818
from ellar.di import RequestScope, injectable
1919
from ellar.reflect import REFLECT_TYPE, reflect
2020

21+
from ..routing.controller import (
22+
ControllerRouteOperation,
23+
ControllerWebsocketRouteOperation,
24+
)
25+
from ..routing.schema import RouteParameters, WsRouteParameters
26+
2127

2228
def get_route_functions(
2329
cls: t.Type,
24-
) -> t.Iterable[t.Union[t.Callable, ControllerRouteOperationBase]]:
30+
) -> t.Iterable[t.Callable]:
2531
for method in cls.__dict__.values():
26-
if hasattr(method, OPERATION_ENDPOINT_KEY) or isinstance(
27-
method, ControllerRouteOperationBase
28-
):
32+
if hasattr(method, OPERATION_ENDPOINT_KEY):
2933
yield method
3034

3135

@@ -35,20 +39,30 @@ def reflect_all_controller_type_routes(cls: t.Type[ControllerBase]) -> None:
3539
for base_cls in reversed(bases):
3640
if base_cls not in [ABC, ControllerBase, object]:
3741
for item in get_route_functions(base_cls):
38-
operation = item
39-
if callable(item) and type(item) == FunctionType:
40-
operation = reflect.get_metadata( # type: ignore
41-
CONTROLLER_OPERATION_HANDLER_KEY, item
42-
)
43-
endpoint_func = operation.endpoint # type:ignore
44-
if reflect.has_metadata(CONTROLLER_CLASS_KEY, endpoint_func):
42+
if reflect.has_metadata(CONTROLLER_CLASS_KEY, item):
4543
raise Exception(
4644
f"{cls.__name__} Controller route tried to be processed more than once."
47-
f"\n-RouteFunction - {endpoint_func}."
45+
f"\n-RouteFunction - {item}."
4846
f"\n-Controller route function can not be reused once its under a `@Controller` decorator."
4947
)
48+
reflect.define_metadata(CONTROLLER_CLASS_KEY, cls, item)
49+
50+
parameter = item.__dict__[ROUTE_OPERATION_PARAMETERS]
51+
operation: t.Union[
52+
ControllerRouteOperation, ControllerWebsocketRouteOperation
53+
]
54+
if isinstance(parameter, RouteParameters):
55+
operation = ControllerRouteOperation(**parameter.dict())
56+
elif isinstance(parameter, WsRouteParameters):
57+
operation = ControllerWebsocketRouteOperation(**parameter.dict())
58+
else: # pragma: no cover
59+
logger.warning(
60+
f"Parameter type is not recognized. {type(parameter) if not isinstance(parameter, type) else parameter}"
61+
)
62+
continue
63+
64+
del item.__dict__[ROUTE_OPERATION_PARAMETERS]
5065

51-
reflect.define_metadata(CONTROLLER_CLASS_KEY, cls, endpoint_func)
5266
reflect.define_metadata(
5367
CONTROLLER_OPERATION_HANDLER_KEY,
5468
[operation],

ellar/common/routing/mount.py

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,14 @@
99
CONTROLLER_CLASS_KEY,
1010
GUARDS_KEY,
1111
NOT_SET,
12-
OPERATION_ENDPOINT_KEY,
1312
VERSIONING_KEY,
1413
)
1514
from ellar.common.helper import get_unique_control_type
1615
from ellar.common.models import GuardCanActivate
1716
from ellar.common.types import TReceive, TScope, TSend
1817
from ellar.reflect import reflect
1918

20-
from .operation_definitions import OperationDefinitions, TOperation, TWebsocketOperation
19+
from .operation_definitions import OperationDefinitions
2120
from .route import RouteOperation
2221
from .route_collections import RouteCollection
2322
from .schema import RouteParameters, WsRouteParameters
@@ -146,33 +145,32 @@ def __init__(
146145
reflect.define_metadata(
147146
VERSIONING_KEY, set(version or []), self.get_control_type()
148147
)
148+
self._pre_build_routes: t.List[t.Union[RouteParameters, WsRouteParameters]] = []
149149

150-
def _get_operation(self, route_parameter: RouteParameters) -> TOperation:
151-
_operation_class = self._get_http_operations_class(route_parameter.endpoint)
152-
_operation = _operation_class(**route_parameter.dict())
153-
setattr(route_parameter.endpoint, OPERATION_ENDPOINT_KEY, True)
154-
self._set_other_router_attributes(_operation)
155-
return _operation
156-
157-
def _get_ws_operation(
158-
self, ws_route_parameters: WsRouteParameters
159-
) -> TWebsocketOperation:
160-
_ws_operation_class = self._get_ws_operations_class(
161-
ws_route_parameters.endpoint
162-
)
163-
_operation = _ws_operation_class(**ws_route_parameters.dict())
164-
setattr(ws_route_parameters.endpoint, OPERATION_ENDPOINT_KEY, True)
165-
self._set_other_router_attributes(_operation)
166-
return _operation
167-
168-
def _set_other_router_attributes(
169-
self, operation: t.Union[TWebsocketOperation, TOperation]
170-
) -> None:
171-
if not reflect.has_metadata(CONTROLLER_CLASS_KEY, operation.endpoint):
150+
def get_pre_build_routes(
151+
self,
152+
) -> t.List[t.Union[RouteParameters, WsRouteParameters]]:
153+
return self._pre_build_routes
154+
155+
def clear_pre_build_routes(self) -> None:
156+
self._pre_build_routes.clear()
157+
158+
def _get_operation(self, route_parameter: RouteParameters) -> t.Callable:
159+
endpoint = super()._get_operation(route_parameter)
160+
self._pre_build_routes.append(route_parameter)
161+
self._set_other_router_attributes(endpoint)
162+
return endpoint
163+
164+
def _get_ws_operation(self, ws_route_parameters: WsRouteParameters) -> t.Callable:
165+
endpoint = super()._get_ws_operation(ws_route_parameters)
166+
self._pre_build_routes.append(ws_route_parameters)
167+
self._set_other_router_attributes(endpoint)
168+
return endpoint
169+
170+
def _set_other_router_attributes(self, operation_handler: t.Callable) -> None:
171+
if not reflect.has_metadata(CONTROLLER_CLASS_KEY, operation_handler):
172172
# this is need to happen before adding operation to router else we lose ability to
173173
# get extra information about operation that is set on the router.
174174
reflect.define_metadata(
175-
CONTROLLER_CLASS_KEY, self.get_control_type(), operation.endpoint
175+
CONTROLLER_CLASS_KEY, self.get_control_type(), operation_handler
176176
)
177-
178-
self.app.routes.append(operation) # type:ignore

ellar/common/routing/operation_definitions.py

Lines changed: 44 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
from types import FunctionType
55

66
from ellar.common.constants import (
7-
CONTROLLER_OPERATION_HANDLER_KEY,
87
DELETE,
98
GET,
109
HEAD,
@@ -13,22 +12,11 @@
1312
PATCH,
1413
POST,
1514
PUT,
15+
ROUTE_OPERATION_PARAMETERS,
1616
TRACE,
1717
)
18-
from ellar.common.helper import class_base_function_regex
19-
from ellar.common.types import TCallable
20-
from ellar.reflect import reflect
2118

22-
from .controller.route import ControllerRouteOperation
23-
from .controller.websocket.route import ControllerWebsocketRouteOperation
24-
from .route import RouteOperation
2519
from .schema import RouteParameters, WsRouteParameters
26-
from .websocket import WebsocketRouteOperation
27-
28-
TOperation = t.Union[RouteOperation, ControllerRouteOperation]
29-
TWebsocketOperation = t.Union[
30-
WebsocketRouteOperation, ControllerWebsocketRouteOperation
31-
]
3220

3321

3422
def _websocket_connection_attributes(func: t.Callable) -> t.Callable:
@@ -43,11 +31,11 @@ def _wrap(connect_handler: t.Callable) -> t.Callable:
4331
"Invalid type. Please make sure you passed the websocket handler."
4432
)
4533

46-
_item: t.Optional[WebsocketRouteOperation] = reflect.get_metadata(
47-
CONTROLLER_OPERATION_HANDLER_KEY, websocket_handler
34+
_item: t.Optional[WsRouteParameters] = getattr(
35+
websocket_handler, ROUTE_OPERATION_PARAMETERS, None
4836
)
4937

50-
if not _item or not isinstance(_item, WebsocketRouteOperation):
38+
if not _item or not isinstance(_item, WsRouteParameters):
5139
raise Exception(
5240
"Invalid type. Please make sure you passed the websocket handler."
5341
)
@@ -74,41 +62,43 @@ def _wrap(connect_handler: t.Callable) -> t.Callable:
7462
class OperationDefinitions:
7563
__slots__ = ()
7664

77-
def _get_http_operations_class(self, func: t.Callable) -> t.Type[TOperation]:
78-
if class_base_function_regex.match(repr(func)):
79-
return ControllerRouteOperation
80-
return RouteOperation
81-
82-
def _get_ws_operations_class(self, func: t.Callable) -> t.Type[TWebsocketOperation]:
83-
if class_base_function_regex.match(repr(func)):
84-
return ControllerWebsocketRouteOperation
85-
return WebsocketRouteOperation
86-
87-
def _get_operation(self, route_parameter: RouteParameters) -> TOperation:
88-
_operation_class = self._get_http_operations_class(route_parameter.endpoint)
89-
_operation = _operation_class(**route_parameter.dict())
65+
# def _get_http_operations_class(self, func: t.Callable) -> t.Type[TOperation]:
66+
# if class_base_function_regex.match(repr(func)):
67+
# return ControllerRouteOperation
68+
# return RouteOperation
69+
#
70+
# def _get_ws_operations_class(self, func: t.Callable) -> t.Type[TWebsocketOperation]:
71+
# if class_base_function_regex.match(repr(func)):
72+
# return ControllerWebsocketRouteOperation
73+
# return WebsocketRouteOperation
74+
75+
def _get_operation(self, route_parameter: RouteParameters) -> t.Callable:
9076
setattr(route_parameter.endpoint, OPERATION_ENDPOINT_KEY, True)
91-
return _operation
92-
93-
def _get_ws_operation(
94-
self, ws_route_parameters: WsRouteParameters
95-
) -> TWebsocketOperation:
96-
_ws_operation_class = self._get_ws_operations_class(
97-
ws_route_parameters.endpoint
98-
)
99-
_operation = _ws_operation_class(**ws_route_parameters.dict())
77+
setattr(route_parameter.endpoint, ROUTE_OPERATION_PARAMETERS, route_parameter)
78+
return route_parameter.endpoint
79+
80+
def _get_ws_operation(self, ws_route_parameters: WsRouteParameters) -> t.Callable:
81+
# _ws_operation_class = self._get_ws_operations_class(
82+
# ws_route_parameters.endpoint
83+
# )
84+
# _operation = _ws_operation_class(**ws_route_parameters.dict())
10085
setattr(ws_route_parameters.endpoint, OPERATION_ENDPOINT_KEY, True)
101-
return _operation
86+
setattr(
87+
ws_route_parameters.endpoint,
88+
ROUTE_OPERATION_PARAMETERS,
89+
ws_route_parameters,
90+
)
91+
return ws_route_parameters.endpoint
10292

10393
def _get_decorator_or_operation(
10494
self, path: t.Union[str, t.Callable], endpoint_parameter_partial: t.Callable
105-
) -> t.Callable[[TCallable], t.Union[TOperation, TCallable]]:
95+
) -> t.Callable:
10696
if callable(path):
10797
route_parameter = endpoint_parameter_partial(endpoint=path, path="/")
10898
self._get_operation(route_parameter=route_parameter)
10999
return path
110100

111-
def _decorator(endpoint_handler: TCallable) -> TCallable:
101+
def _decorator(endpoint_handler: t.Callable) -> t.Callable:
112102
_route_parameter = endpoint_parameter_partial(
113103
endpoint=endpoint_handler, path=path
114104
)
@@ -126,7 +116,7 @@ def get(
126116
response: t.Union[
127117
t.Dict[int, t.Type], t.List[t.Tuple[int, t.Type]], t.Type, t.Any
128118
] = None,
129-
) -> t.Callable[[TCallable], t.Union[TOperation, TCallable]]:
119+
) -> t.Callable:
130120
methods = [GET]
131121
endpoint_parameter_partial = partial(
132122
RouteParameters,
@@ -146,7 +136,7 @@ def post(
146136
response: t.Union[
147137
t.Dict[int, t.Type], t.List[t.Tuple[int, t.Type]], t.Type, t.Any
148138
] = None,
149-
) -> t.Callable[[TCallable], t.Union[TOperation, TCallable]]:
139+
) -> t.Callable:
150140
methods = [POST]
151141
endpoint_parameter_partial = partial(
152142
RouteParameters,
@@ -166,7 +156,7 @@ def put(
166156
response: t.Union[
167157
t.Dict[int, t.Type], t.List[t.Tuple[int, t.Type]], t.Type, t.Any
168158
] = None,
169-
) -> t.Callable[[TCallable], t.Union[TOperation, TCallable]]:
159+
) -> t.Callable:
170160
methods = [PUT]
171161
endpoint_parameter_partial = partial(
172162
RouteParameters,
@@ -186,7 +176,7 @@ def patch(
186176
response: t.Union[
187177
t.Dict[int, t.Type], t.List[t.Tuple[int, t.Type]], t.Type, t.Any
188178
] = None,
189-
) -> t.Callable[[TCallable], t.Union[TOperation, TCallable]]:
179+
) -> t.Callable:
190180
methods = [PATCH]
191181
endpoint_parameter_partial = partial(
192182
RouteParameters,
@@ -206,7 +196,7 @@ def delete(
206196
response: t.Union[
207197
t.Dict[int, t.Type], t.List[t.Tuple[int, t.Type]], t.Type, t.Any
208198
] = None,
209-
) -> t.Callable[[TCallable], t.Union[TOperation, TCallable]]:
199+
) -> t.Callable:
210200
methods = [DELETE]
211201
endpoint_parameter_partial = partial(
212202
RouteParameters,
@@ -226,7 +216,7 @@ def head(
226216
response: t.Union[
227217
t.Dict[int, t.Type], t.List[t.Tuple[int, t.Type]], t.Type, t.Any
228218
] = None,
229-
) -> t.Callable[[TCallable], t.Union[TOperation, TCallable]]:
219+
) -> t.Callable:
230220
methods = [HEAD]
231221
endpoint_parameter_partial = partial(
232222
RouteParameters,
@@ -246,7 +236,7 @@ def options(
246236
response: t.Union[
247237
t.Dict[int, t.Type], t.List[t.Tuple[int, t.Type]], t.Type, t.Any
248238
] = None,
249-
) -> t.Callable[[TCallable], t.Union[TOperation, TCallable]]:
239+
) -> t.Callable:
250240
methods = [OPTIONS]
251241
endpoint_parameter_partial = partial(
252242
RouteParameters,
@@ -266,7 +256,7 @@ def trace(
266256
response: t.Union[
267257
t.Dict[int, t.Type], t.List[t.Tuple[int, t.Type]], t.Type, t.Any
268258
] = None,
269-
) -> t.Callable[[TCallable], t.Union[TOperation, TCallable]]:
259+
) -> t.Callable:
270260
methods = [TRACE]
271261
endpoint_parameter_partial = partial(
272262
RouteParameters,
@@ -287,8 +277,8 @@ def http_route(
287277
response: t.Union[
288278
t.Dict[int, t.Type], t.List[t.Tuple[int, t.Type]], t.Type, t.Any
289279
] = None,
290-
) -> t.Callable[[TCallable], t.Union[TOperation, TCallable]]:
291-
def _decorator(endpoint_handler: TCallable) -> TCallable:
280+
) -> t.Callable:
281+
def _decorator(endpoint_handler: t.Callable) -> t.Callable:
292282
endpoint_parameter = RouteParameters(
293283
name=name,
294284
path=path,
@@ -311,10 +301,10 @@ def ws_route(
311301
encoding: t.Optional[str] = "json",
312302
use_extra_handler: bool = False,
313303
extra_handler_type: t.Optional[t.Type] = None,
314-
) -> t.Callable[[TCallable], t.Union[TCallable, TWebsocketOperation]]:
304+
) -> t.Callable:
315305
def _decorator(
316-
endpoint_handler: TCallable,
317-
) -> t.Union[TCallable, TWebsocketOperation]:
306+
endpoint_handler: t.Callable,
307+
) -> t.Callable:
318308
endpoint_parameter = WsRouteParameters(
319309
name=name,
320310
path=path,

0 commit comments

Comments
 (0)