diff --git a/src/werkzeug/routing/exceptions.py b/src/werkzeug/routing/exceptions.py index eeabd4ed1..1cf24ceb7 100644 --- a/src/werkzeug/routing/exceptions.py +++ b/src/werkzeug/routing/exceptions.py @@ -34,9 +34,10 @@ class RequestRedirect(HTTPException, RoutingException): code = 308 - def __init__(self, new_url: str) -> None: + def __init__(self, new_url: str, new_path: t.Optional[str] = None) -> None: super().__init__(new_url) self.new_url = new_url + self.new_path = new_path def get_response( self, @@ -98,7 +99,8 @@ def _score_rule(rule: Rule) -> float: str(rule.endpoint), str(self.endpoint), ).ratio(), - 0.01 * bool(set(self.values or ()).issubset(rule.arguments)), + 0.01 * bool(set(self.values or () + ).issubset(rule.arguments)), 0.01 * bool(rule.methods and self.method in rule.methods), ] ) @@ -134,7 +136,8 @@ def __str__(self) -> str: f" Did you forget to specify values {sorted(missing_values)!r}?" ) else: - message.append(f" Did you mean {self.suggested.endpoint!r} instead?") + message.append( + f" Did you mean {self.suggested.endpoint!r} instead?") return "".join(message) diff --git a/src/werkzeug/routing/map.py b/src/werkzeug/routing/map.py index 4d15e8824..134b2da34 100644 --- a/src/werkzeug/routing/map.py +++ b/src/werkzeug/routing/map.py @@ -222,7 +222,8 @@ def bind( server_name = server_name.lower() if self.host_matching: if subdomain is not None: - raise RuntimeError("host matching enabled and a subdomain was provided") + raise RuntimeError( + "host matching enabled and a subdomain was provided") elif subdomain is None: subdomain = self.default_subdomain if script_name is None: @@ -602,12 +603,14 @@ def match( path_part = f"/{path_info.lstrip('/')}" if path_info else "" try: - result = self.map._matcher.match(domain_part, path_part, method, websocket) + result = self.map._matcher.match( + domain_part, path_part, method, websocket) except RequestPath as e: # safe = https://url.spec.whatwg.org/#url-path-segment-string new_path = quote(e.path_info, safe="!$&'()*+,/:;=@") raise RequestRedirect( - self.make_redirect_url(new_path, query_args) + self.make_redirect_url(new_path, query_args), + new_path, ) from None except RequestAliasRedirect as e: raise RequestRedirect( @@ -617,11 +620,13 @@ def match( e.matched_values, method, query_args, - ) + ), + path_part, ) from None except NoMatch as e: if e.have_match_for: - raise MethodNotAllowed(valid_methods=list(e.have_match_for)) from None + raise MethodNotAllowed( + valid_methods=list(e.have_match_for)) from None if e.websocket_mismatch: raise WebsocketMismatch() from None @@ -631,9 +636,10 @@ def match( rule, rv = result if self.map.redirect_defaults: - redirect_url = self.get_default_redirect(rule, method, rv, query_args) + (redirect_url, redirect_path) = self.get_default_redirect( + rule, method, rv, query_args) if redirect_url is not None: - raise RequestRedirect(redirect_url) + raise RequestRedirect(redirect_url, redirect_path) if rule.redirect_to is not None: if isinstance(rule.redirect_to, str): @@ -642,7 +648,8 @@ def _handle_match(match: t.Match[str]) -> str: value = rv[match.group(1)] return rule._converters[match.group(1)].to_url(value) - redirect_url = _simple_rule_re.sub(_handle_match, rule.redirect_to) + redirect_url = _simple_rule_re.sub( + _handle_match, rule.redirect_to) else: redirect_url = rule.redirect_to(self, **rv) @@ -655,7 +662,8 @@ def _handle_match(match: t.Match[str]) -> str: urljoin( f"{self.url_scheme or 'http'}://{netloc}{self.script_name}", redirect_url, - ) + ), + redirect_url, ) if return_rule: @@ -736,8 +744,9 @@ def get_default_redirect( if r.provides_defaults_for(rule) and r.suitable_for(values, method): values.update(r.defaults) # type: ignore domain_part, path = r.build(values) # type: ignore - return self.make_redirect_url(path, query_args, domain_part=domain_part) - return None + return self.make_redirect_url(path, query_args, + domain_part=domain_part), path + return None, None def encode_query_args(self, query_args: t.Mapping[str, t.Any] | str) -> str: if not isinstance(query_args, str):