From 2f5ea8449fd229b3e7c2574131eee89b4eef743c Mon Sep 17 00:00:00 2001 From: Marcelo Trylesinski Date: Tue, 14 Dec 2021 19:08:33 +0100 Subject: [PATCH] Allow 'name' in keyworded args for url_for() and url_path_for() --- starlette/applications.py | 4 ++-- starlette/requests.py | 4 ++-- starlette/routing.py | 22 +++++++++++++++------- starlette/templating.py | 4 ++-- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/starlette/applications.py b/starlette/applications.py index c95fa0a5c..fae440196 100644 --- a/starlette/applications.py +++ b/starlette/applications.py @@ -104,8 +104,8 @@ def debug(self, value: bool) -> None: self._debug = value self.middleware_stack = self.build_middleware_stack() - def url_path_for(self, name: str, **path_params: typing.Any) -> URLPath: - return self.router.url_path_for(name, **path_params) + def url_path_for(self, *args: str, **path_params: typing.Any) -> URLPath: + return self.router.url_path_for(*args, **path_params) async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: scope["app"] = self diff --git a/starlette/requests.py b/starlette/requests.py index cf129702e..73191d384 100644 --- a/starlette/requests.py +++ b/starlette/requests.py @@ -169,9 +169,9 @@ def state(self) -> State: self._state = State(self.scope["state"]) return self._state - def url_for(self, name: str, **path_params: typing.Any) -> str: + def url_for(self, *args: str, **path_params: typing.Any) -> str: router: Router = self.scope["router"] - url_path = router.url_path_for(name, **path_params) + url_path = router.url_path_for(*args, **path_params) return url_path.make_absolute_url(base_url=self.base_url) diff --git a/starlette/routing.py b/starlette/routing.py index 982980c3c..5fd2f4e17 100644 --- a/starlette/routing.py +++ b/starlette/routing.py @@ -156,7 +156,7 @@ class BaseRoute: def matches(self, scope: Scope) -> typing.Tuple[Match, Scope]: raise NotImplementedError() # pragma: no cover - def url_path_for(self, name: str, **path_params: typing.Any) -> URLPath: + def url_path_for(self, *args: str, **path_params: typing.Any) -> URLPath: raise NotImplementedError() # pragma: no cover async def handle(self, scope: Scope, receive: Receive, send: Send) -> None: @@ -235,7 +235,9 @@ def matches(self, scope: Scope) -> typing.Tuple[Match, Scope]: return Match.FULL, child_scope return Match.NONE, {} - def url_path_for(self, name: str, **path_params: typing.Any) -> URLPath: + def url_path_for(self, *args: str, **path_params: typing.Any) -> URLPath: + assert len(args) == 1, "url_path_for() takes exactly one positional argument" + name = args[0] seen_params = set(path_params.keys()) expected_params = set(self.param_convertors.keys()) @@ -301,7 +303,9 @@ def matches(self, scope: Scope) -> typing.Tuple[Match, Scope]: return Match.FULL, child_scope return Match.NONE, {} - def url_path_for(self, name: str, **path_params: typing.Any) -> URLPath: + def url_path_for(self, *args: str, **path_params: typing.Any) -> URLPath: + assert len(args) == 1, "url_path_for() takes exactly one positional argument" + name = args[0] seen_params = set(path_params.keys()) expected_params = set(self.param_convertors.keys()) @@ -374,7 +378,9 @@ def matches(self, scope: Scope) -> typing.Tuple[Match, Scope]: return Match.FULL, child_scope return Match.NONE, {} - def url_path_for(self, name: str, **path_params: typing.Any) -> URLPath: + def url_path_for(self, *args: str, **path_params: typing.Any) -> URLPath: + assert len(args) == 1, "url_path_for() takes exactly one positional argument" + name = args[0] if self.name is not None and name == self.name and "path" in path_params: # 'name' matches "". path_params["path"] = path_params["path"].lstrip("/") @@ -444,7 +450,9 @@ def matches(self, scope: Scope) -> typing.Tuple[Match, Scope]: return Match.FULL, child_scope return Match.NONE, {} - def url_path_for(self, name: str, **path_params: typing.Any) -> URLPath: + def url_path_for(self, *args: str, **path_params: typing.Any) -> URLPath: + assert len(args) == 1, "url_path_for() takes exactly one positional argument" + name = args[0] if self.name is not None and name == self.name and "path" in path_params: # 'name' matches "". path = path_params.pop("path") @@ -584,10 +592,10 @@ async def not_found(self, scope: Scope, receive: Receive, send: Send) -> None: response = PlainTextResponse("Not Found", status_code=404) await response(scope, receive, send) - def url_path_for(self, name: str, **path_params: typing.Any) -> URLPath: + def url_path_for(self, *args: str, **path_params: typing.Any) -> URLPath: for route in self.routes: try: - return route.url_path_for(name, **path_params) + return route.url_path_for(*args, **path_params) except NoMatchFound: pass raise NoMatchFound() diff --git a/starlette/templating.py b/starlette/templating.py index 18d5eb40c..b576ddc61 100644 --- a/starlette/templating.py +++ b/starlette/templating.py @@ -63,9 +63,9 @@ def _create_env( self, directory: typing.Union[str, PathLike] ) -> "jinja2.Environment": @pass_context - def url_for(context: dict, name: str, **path_params: typing.Any) -> str: + def url_for(context: dict, *args: str, **path_params: typing.Any) -> str: request = context["request"] - return request.url_for(name, **path_params) + return request.url_for(*args, **path_params) loader = jinja2.FileSystemLoader(directory) env = jinja2.Environment(loader=loader, autoescape=True)