Skip to content

Commit 4798e84

Browse files
committed
removed route function direct access on controllers for easy controller testing.
1 parent 98bccd1 commit 4798e84

File tree

5 files changed

+63
-37
lines changed

5 files changed

+63
-37
lines changed

ninja_extra/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@
88
TRACE = "TRACE"
99
ROUTE_METHODS = [POST, PUT, PATCH, DELETE, GET, HEAD, OPTIONS, TRACE]
1010
THROTTLED_FUNCTION = "__throttled_endpoint__"
11+
ROUTE_FUNCTION = "__route_function__"

ninja_extra/controllers/base.py

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
from ninja.signature import is_async
3030
from ninja.utils import normalize_path
3131

32-
from ninja_extra.constants import THROTTLED_FUNCTION
32+
from ninja_extra.constants import ROUTE_FUNCTION, THROTTLED_FUNCTION
3333
from ninja_extra.exceptions import APIException, NotFound, PermissionDenied, bad_request
3434
from ninja_extra.helper import get_function_name
3535
from ninja_extra.operation import ControllerPathView, Operation
@@ -59,13 +59,13 @@ class MissingAPIControllerDecoratorException(Exception):
5959

6060
def get_route_functions(cls: Type) -> Iterable[RouteFunction]:
6161
for method in cls.__dict__.values():
62-
if isinstance(method, RouteFunction):
63-
yield method
62+
if hasattr(method, ROUTE_FUNCTION):
63+
yield getattr(method, ROUTE_FUNCTION)
6464

6565

6666
def get_all_controller_route_function(
6767
controller: Union[Type["ControllerBase"], Type]
68-
) -> List[RouteFunction]:
68+
) -> List[RouteFunction]: # pragma: no cover
6969
route_functions: List[RouteFunction] = []
7070
for item in dir(controller):
7171
attr = getattr(controller, item)
@@ -317,7 +317,7 @@ def tags(self, value: Union[str, List[str], None]) -> None:
317317
tag = [value]
318318
self._tags = tag
319319

320-
def __call__(self, cls: Type) -> Type["ControllerBase"]:
320+
def __call__(self, cls: Type) -> Union[Type, Type["ControllerBase"]]:
321321
from ninja_extra.throttling import throttle
322322

323323
self.auto_import = getattr(cls, "auto_import", self.auto_import)
@@ -468,7 +468,9 @@ def add_api_operation(
468468

469469

470470
@overload
471-
def api_controller(prefix_or_class: Type) -> Type[ControllerBase]: # pragma: no cover
471+
def api_controller(
472+
prefix_or_class: Type,
473+
) -> Union[Type[ControllerBase], Callable[..., Any], Any]: # pragma: no cover
472474
...
473475

474476

@@ -479,7 +481,7 @@ def api_controller(
479481
tags: Union[Optional[List[str]], str] = None,
480482
permissions: Optional["PermissionType"] = None,
481483
auto_import: bool = True,
482-
) -> APIController: # pragma: no cover
484+
) -> Union[Type[ControllerBase], Callable[..., Any], Any]: # pragma: no cover
483485
...
484486

485487

@@ -489,21 +491,23 @@ def api_controller(
489491
tags: Union[Optional[List[str]], str] = None,
490492
permissions: Optional["PermissionType"] = None,
491493
auto_import: bool = True,
492-
) -> Union[Type[ControllerBase], APIController]:
494+
) -> Union[Type[ControllerBase], Callable[..., Any], Any]:
493495
if isinstance(prefix_or_class, type):
494-
_api_controller = APIController(
496+
return APIController(
495497
prefix="",
496498
auth=auth,
497499
tags=tags,
498500
permissions=permissions,
499501
auto_import=auto_import,
500-
)
501-
return _api_controller(prefix_or_class)
502-
503-
return APIController(
504-
prefix=prefix_or_class,
505-
auth=auth,
506-
tags=tags,
507-
permissions=permissions,
508-
auto_import=auto_import,
509-
)
502+
)(prefix_or_class)
503+
504+
def _decorator(cls: Type) -> Union[Type[ControllerBase], Any]:
505+
return APIController(
506+
prefix=str(prefix_or_class),
507+
auth=auth,
508+
tags=tags,
509+
permissions=permissions,
510+
auto_import=auto_import,
511+
)(cls)
512+
513+
return _decorator

ninja_extra/controllers/route/__init__.py

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,15 @@
1515
from ninja.signature import is_async
1616
from ninja.types import TCallable
1717

18-
from ninja_extra.constants import DELETE, GET, PATCH, POST, PUT, ROUTE_METHODS
18+
from ninja_extra.constants import (
19+
DELETE,
20+
GET,
21+
PATCH,
22+
POST,
23+
PUT,
24+
ROUTE_FUNCTION,
25+
ROUTE_METHODS,
26+
)
1927
from ninja_extra.controllers.response import ControllerResponse, ControllerResponseMeta
2028
from ninja_extra.permissions import BasePermission
2129
from ninja_extra.schemas import RouteParameter
@@ -141,7 +149,7 @@ def _create_route_function(
141149
List[Union[Type[BasePermission], BasePermission, Any]]
142150
] = None,
143151
openapi_extra: Optional[Dict[str, Any]] = None,
144-
) -> RouteFunction:
152+
) -> TCallable:
145153
if response is NOT_SET:
146154
response = get_type_hints(view_func).get("return") or NOT_SET
147155
route_obj = cls(
@@ -168,7 +176,8 @@ def _create_route_function(
168176
if route_obj.is_async:
169177
route_function_class = AsyncRouteFunction
170178

171-
return route_function_class(route=route_obj)
179+
setattr(view_func, ROUTE_FUNCTION, route_function_class(route=route_obj))
180+
return view_func
172181

173182
@classmethod
174183
def get(
@@ -192,7 +201,7 @@ def get(
192201
List[Union[Type[BasePermission], BasePermission, Any]]
193202
] = None,
194203
openapi_extra: Optional[Dict[str, Any]] = None,
195-
) -> Callable[[TCallable], RouteFunction]:
204+
) -> Callable[[TCallable], TCallable]:
196205
"""
197206
A GET Operation method decorator
198207
eg.
@@ -220,7 +229,7 @@ def get_operation(self):
220229
:return: Route[GET]
221230
"""
222231

223-
def decorator(view_func: TCallable) -> RouteFunction:
232+
def decorator(view_func: TCallable) -> TCallable:
224233
return cls._create_route_function(
225234
view_func,
226235
path=path,
@@ -266,7 +275,7 @@ def post(
266275
List[Union[Type[BasePermission], BasePermission, Any]]
267276
] = None,
268277
openapi_extra: Optional[Dict[str, Any]] = None,
269-
) -> Callable[[TCallable], RouteFunction]:
278+
) -> Callable[[TCallable], TCallable]:
270279
"""
271280
A POST Operation method decorator
272281
eg.
@@ -294,7 +303,7 @@ def post_operation(self, create_schema: Schema):
294303
:return: Route[POST]
295304
"""
296305

297-
def decorator(view_func: TCallable) -> RouteFunction:
306+
def decorator(view_func: TCallable) -> TCallable:
298307
return cls._create_route_function(
299308
view_func,
300309
path=path,
@@ -340,7 +349,7 @@ def delete(
340349
List[Union[Type[BasePermission], BasePermission, Any]]
341350
] = None,
342351
openapi_extra: Optional[Dict[str, Any]] = None,
343-
) -> Callable[[TCallable], RouteFunction]:
352+
) -> Callable[[TCallable], TCallable]:
344353
"""
345354
A DELETE Operation method decorator
346355
eg.
@@ -368,7 +377,7 @@ def delete_operation(self, some_id: int):
368377
:return: Route[DELETE]
369378
"""
370379

371-
def decorator(view_func: TCallable) -> RouteFunction:
380+
def decorator(view_func: TCallable) -> TCallable:
372381
return cls._create_route_function(
373382
view_func,
374383
path=path,
@@ -414,7 +423,7 @@ def patch(
414423
List[Union[Type[BasePermission], BasePermission, Any]]
415424
] = None,
416425
openapi_extra: Optional[Dict[str, Any]] = None,
417-
) -> Callable[[TCallable], RouteFunction]:
426+
) -> Callable[[TCallable], TCallable]:
418427
"""
419428
A PATCH Operation method decorator
420429
eg.
@@ -443,7 +452,7 @@ def patch_operation(self, some_id: int):
443452
:return: Route[PATCH]
444453
"""
445454

446-
def decorator(view_func: TCallable) -> RouteFunction:
455+
def decorator(view_func: TCallable) -> TCallable:
447456
return cls._create_route_function(
448457
view_func,
449458
path=path,
@@ -489,7 +498,7 @@ def put(
489498
List[Union[Type[BasePermission], BasePermission, Any]]
490499
] = None,
491500
openapi_extra: Optional[Dict[str, Any]] = None,
492-
) -> Callable[[TCallable], RouteFunction]:
501+
) -> Callable[[TCallable], TCallable]:
493502
"""
494503
A PUT Operation method decorator
495504
eg.
@@ -518,7 +527,7 @@ def put_operation(self, some_id: int):
518527
:return: Route[PUT]
519528
"""
520529

521-
def decorator(view_func: TCallable) -> RouteFunction:
530+
def decorator(view_func: TCallable) -> TCallable:
522531
return cls._create_route_function(
523532
view_func,
524533
path=path,
@@ -565,7 +574,7 @@ def generic(
565574
List[Union[Type[BasePermission], BasePermission, Any]]
566575
] = None,
567576
openapi_extra: Optional[Dict[str, Any]] = None,
568-
) -> Callable[[TCallable], RouteFunction]:
577+
) -> Callable[[TCallable], TCallable]:
569578
"""
570579
A Custom Operation method decorator, for creating route with more than one operation
571580
eg.
@@ -595,7 +604,7 @@ def list_create(self, some_schema: Optional[Schema] = None):
595604
:return: Route[PATCH]
596605
"""
597606

598-
def decorator(view_func: TCallable) -> RouteFunction:
607+
def decorator(view_func: TCallable) -> TCallable:
599608
return cls._create_route_function(
600609
view_func,
601610
path=path,

ninja_extra/controllers/route/route_functions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from ...dependency_resolver import get_injector, service_resolver
1212
from .context import RouteContext, get_route_execution_context
1313

14-
if TYPE_CHECKING:
14+
if TYPE_CHECKING: # pragma: no cover
1515
from ninja_extra.operation import ControllerOperation
1616

1717
from ...controllers.base import APIController, ControllerBase

ninja_extra/helper.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,20 @@
11
import inspect
2-
from typing import Any
2+
import typing as t
33

4+
from ninja_extra.constants import ROUTE_FUNCTION
45

5-
def get_function_name(func_class: Any) -> str:
6+
if t.TYPE_CHECKING: # pragma: no cover
7+
from ninja_extra.controllers import RouteFunction
8+
9+
10+
def get_function_name(func_class: t.Any) -> str:
611
if inspect.isfunction(func_class) or inspect.isclass(func_class):
712
return str(func_class.__name__)
813
return str(func_class.__class__.__name__)
14+
15+
16+
@t.no_type_check
17+
def get_route_function(func: t.Callable) -> t.Optional["RouteFunction"]:
18+
if hasattr(func, ROUTE_FUNCTION):
19+
return func.__dict__[ROUTE_FUNCTION]
20+
return None # pragma: no cover

0 commit comments

Comments
 (0)