Skip to content

Commit 5985248

Browse files
authored
Merge pull request #18 from KiraPC/no-multiple-router
No multiple router
2 parents 77096fb + 348df35 commit 5985248

File tree

5 files changed

+57
-26
lines changed

5 files changed

+57
-26
lines changed
Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
"""FastAPI Router Contoller, FastAPI utility to allow Controller Class usage"""
22

3-
__version__ = "0.1.0"
4-
53
from fastapi_router_controller.lib.controller import Controller as Controller
64
from fastapi_router_controller.lib.controller import OPEN_API_TAGS as ControllersTags
75
from fastapi_router_controller.lib.controller_loader import ControllerLoader as ControllerLoader

fastapi_router_controller/lib/controller.py

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import inspect
2-
import copy
2+
from copy import deepcopy
33
from fastapi import APIRouter, Depends
4+
from fastapi_router_controller.lib.exceptions import MultipleResourceException, MultipleRouterException
45

56
OPEN_API_TAGS = []
67
__app_controllers__ = []
@@ -35,22 +36,29 @@ class Controller:
3536
3637
It expose some utilities and decorator functions to define a router controller class
3738
"""
38-
39-
RC_KEY = "__router__"
40-
SIGNATURE_KEY = "__signature__"
39+
RC_KEY = '__router__'
40+
SIGNATURE_KEY = '__signature__'
41+
HAS_CONTROLLER_KEY = '__has_controller__'
42+
RESOURCE_CLASS_KEY = '__resource_cls__'
4143

4244
def __init__(self, router: APIRouter, openapi_tag: dict = None) -> None:
4345
"""
4446
:param router: The FastApi router to link to the Class
4547
:param openapi_tag: An openapi object that will describe your routes in the openapi tamplate
4648
"""
47-
self.router = copy.deepcopy(router)
49+
# Each Controller must be linked to one fastapi router
50+
if hasattr(router, Controller.HAS_CONTROLLER_KEY):
51+
raise MultipleRouterException()
52+
53+
self.router = deepcopy(router)
4854
self.openapi_tag = openapi_tag
4955
self.cls = None
5056

5157
if openapi_tag:
5258
OPEN_API_TAGS.append(openapi_tag)
5359

60+
setattr(router, Controller.HAS_CONTROLLER_KEY, True)
61+
5462
def __get_parent_routes(self, router: APIRouter):
5563
"""
5664
Private utility to get routes from an extended class
@@ -65,14 +73,21 @@ def __get_parent_routes(self, router: APIRouter):
6573
self.router.add_api_route(route.path, route.endpoint, **options)
6674

6775
def add_resource(self, cls):
68-
if self.cls and cls != self.cls:
69-
raise Exception("Every controller needs its own router!")
70-
self.cls = cls
71-
# check if cls was extended from another Controller
76+
'''
77+
Mark a class as Controller Resource
78+
'''
79+
# check if the same controller was already used for another cls (Resource)
80+
if hasattr(self, Controller.RESOURCE_CLASS_KEY) and getattr(self, Controller.RESOURCE_CLASS_KEY) != cls:
81+
raise MultipleResourceException()
82+
83+
# check if cls (Resource) was exteded from another
7284
if hasattr(cls, Controller.RC_KEY):
7385
self.__get_parent_routes(cls.__router__)
74-
cls.__router__ = self.router
86+
87+
setattr(cls, Controller.RC_KEY, self.router)
88+
setattr(self, Controller.RESOURCE_CLASS_KEY, cls)
7589
cls.router = lambda: Controller.__parse_controller_router(cls)
90+
7691
return cls
7792

7893
def resource(self):
@@ -101,28 +116,31 @@ def __parse_controller_router(cls):
101116

102117
dependencies = None
103118
if hasattr(cls, "dependencies"):
104-
dependencies = copy.deepcopy(cls.dependencies)
119+
dependencies = deepcopy(cls.dependencies)
105120
delattr(cls, "dependencies")
106121

107122
for route in router.routes:
108-
# get the signature of the endpoint function
109-
signature = inspect.signature(route.endpoint)
110-
# get the parameters of the endpoint function
111-
signature_parameters = list(signature.parameters.values())
112123
# add class dependencies
113124
if dependencies:
114125
for depends in dependencies[::-1]:
115126
route.dependencies.insert(0, depends)
127+
128+
# get the signature of the endpoint function
129+
signature = inspect.signature(route.endpoint)
130+
# get the parameters of the endpoint function
131+
signature_parameters = list(signature.parameters.values())
116132

117133
# replace the class instance with the itself FastApi Dependecy
118134
signature_parameters[0] = signature_parameters[0].replace(
119135
default=Depends(cls)
120136
)
137+
121138
# set self and after it the keyword args
122139
new_parameters = [signature_parameters[0]] + [
123140
parameter.replace(kind=inspect.Parameter.KEYWORD_ONLY)
124141
for parameter in signature_parameters[1:]
125142
]
143+
126144
new_signature = signature.replace(parameters=new_parameters)
127145
setattr(route.endpoint, Controller.SIGNATURE_KEY, new_signature)
128146

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
class MultipleRouterException(Exception):
2+
def __init__(self):
3+
super().__init__('Router already used by another Controller')
4+
5+
class MultipleResourceException(Exception):
6+
def __init__(self):
7+
super().__init__('Controller already used by another Resource')

tests/test_controller.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -87,22 +87,19 @@ def root(
8787
id += self.x.create()
8888
return SampleObject(id=id)
8989

90-
def hello(self, f: Filter, y=Depends(get_y)):
90+
def hello(self,
91+
f: Filter, y=Depends(get_y)
92+
):
9193
_id = f.foo
9294
_id += y
9395
_id += self.x.create()
9496
return SampleObject(id=_id)
9597

96-
app = FastAPI(
97-
title="A sample application using fastapi_router_controller",
98-
version="0.1.0",
99-
openapi_tags=ControllersTags,
100-
)
101-
10298
router = APIRouter()
10399
controller = Controller(router, openapi_tag={"name": "sample_controller"})
104100

105101
controller.add_resource(SampleController)
102+
106103
controller.route.add_api_route(
107104
"/",
108105
SampleController.root,
@@ -111,9 +108,20 @@ def hello(self, f: Filter, y=Depends(get_y)):
111108
response_model=SampleObject,
112109
methods=["GET"],
113110
)
111+
114112
controller.route.add_api_route(
115-
"/hello", SampleController.hello, response_model=SampleObject, methods=["POST"]
113+
"/hello",
114+
SampleController.hello,
115+
response_model=SampleObject,
116+
methods=["POST"]
117+
)
118+
119+
app = FastAPI(
120+
title="A sample application using fastapi_router_controller",
121+
version="0.1.0",
122+
openapi_tags=ControllersTags,
116123
)
124+
117125
app.include_router(SampleController.router())
118126
return app
119127

tests/test_inherit.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,4 @@ def test_invalid(self):
9292
class Controller2(Base):
9393
...
9494

95-
self.assertEqual(str(ex.exception), "Every controller needs its own router!")
95+
self.assertEqual(str(ex.exception), "Controller already used by another Resource")

0 commit comments

Comments
 (0)