Skip to content

Commit

Permalink
Refactor Resource.resolve to avoid linear search of methods (#9899)
Browse files Browse the repository at this point in the history
(cherry picked from commit 2249f2d)
  • Loading branch information
bdraco committed Nov 15, 2024
1 parent 85f3531 commit f4aa8a0
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 25 deletions.
1 change: 1 addition & 0 deletions CHANGES/9899.misc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Improved performance of resolving resources when multiple methods are registered for the same route -- by :user:`bdraco`.
45 changes: 20 additions & 25 deletions aiohttp/web_urldispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,9 @@ async def _default_expect_handler(request: Request) -> None:
class Resource(AbstractResource):
def __init__(self, *, name: Optional[str] = None) -> None:
super().__init__(name=name)
self._routes: List[ResourceRoute] = []
self._routes: Dict[str, ResourceRoute] = {}
self._any_route: Optional[ResourceRoute] = None
self._allowed_methods: Set[str] = set()

def add_route(
self,
Expand All @@ -359,14 +361,12 @@ def add_route(
*,
expect_handler: Optional[_ExpectHandler] = None,
) -> "ResourceRoute":

for route_obj in self._routes:
if route_obj.method == method or route_obj.method == hdrs.METH_ANY:
raise RuntimeError(
"Added route will never be executed, "
"method {route.method} is already "
"registered".format(route=route_obj)
)
if route := self._routes.get(method, self._any_route):
raise RuntimeError(
"Added route will never be executed, "
f"method {route.method} is already "
"registered"
)

route_obj = ResourceRoute(method, handler, self, expect_handler=expect_handler)
self.register_route(route_obj)
Expand All @@ -376,23 +376,18 @@ def register_route(self, route: "ResourceRoute") -> None:
assert isinstance(
route, ResourceRoute
), f"Instance of Route class is required, got {route!r}"
self._routes.append(route)
if route.method == hdrs.METH_ANY:
self._any_route = route
else:
self._allowed_methods.add(route.method)
self._routes[route.method] = route

async def resolve(self, request: Request) -> _Resolve:
allowed_methods: Set[str] = set()

match_dict = self._match(request.rel_url.path_safe)
if match_dict is None:
return None, allowed_methods

for route_obj in self._routes:
route_method = route_obj.method
allowed_methods.add(route_method)

if route_method == request.method or route_method == hdrs.METH_ANY:
return (UrlMappingMatchInfo(match_dict, route_obj), allowed_methods)
else:
return None, allowed_methods
if (match_dict := self._match(request.rel_url.path_safe)) is None:
return None, set()
if route := self._routes.get(request.method, self._any_route):
return UrlMappingMatchInfo(match_dict, route), self._allowed_methods
return None, self._allowed_methods

@abc.abstractmethod
def _match(self, path: str) -> Optional[Dict[str, str]]:
Expand All @@ -402,7 +397,7 @@ def __len__(self) -> int:
return len(self._routes)

def __iter__(self) -> Iterator["ResourceRoute"]:
return iter(self._routes)
return iter(self._routes.values())

# TODO: implement all abstract methods

Expand Down

0 comments on commit f4aa8a0

Please sign in to comment.