Skip to content

Commit f23e417

Browse files
committed
Expose redirect path in RequestRedirect
When we raise the RequestRedirect exception we have already computed the new url that should be redirected to in the exception (new_url) but we don't pass the the new path that we used to compute the url. This causes another layer of redirection in depending code that needs to urlparse() the url to get the path we already have the data for. This adds new_path to the RequestRedirect exception and populates it with the path used when computing new_url. Fixes: pallets#3000
1 parent 9a69323 commit f23e417

File tree

2 files changed

+26
-14
lines changed

2 files changed

+26
-14
lines changed

src/werkzeug/routing/exceptions.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,10 @@ class RequestRedirect(HTTPException, RoutingException):
3434

3535
code = 308
3636

37-
def __init__(self, new_url: str) -> None:
37+
def __init__(self, new_url: str, new_path: t.Optional[str] = None) -> None:
3838
super().__init__(new_url)
3939
self.new_url = new_url
40+
self.new_path = new_path
4041

4142
def get_response(
4243
self,
@@ -98,7 +99,8 @@ def _score_rule(rule: Rule) -> float:
9899
str(rule.endpoint),
99100
str(self.endpoint),
100101
).ratio(),
101-
0.01 * bool(set(self.values or ()).issubset(rule.arguments)),
102+
0.01 * bool(set(self.values or ()
103+
).issubset(rule.arguments)),
102104
0.01 * bool(rule.methods and self.method in rule.methods),
103105
]
104106
)
@@ -134,7 +136,8 @@ def __str__(self) -> str:
134136
f" Did you forget to specify values {sorted(missing_values)!r}?"
135137
)
136138
else:
137-
message.append(f" Did you mean {self.suggested.endpoint!r} instead?")
139+
message.append(
140+
f" Did you mean {self.suggested.endpoint!r} instead?")
138141
return "".join(message)
139142

140143

src/werkzeug/routing/map.py

+20-11
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,8 @@ def bind(
222222
server_name = server_name.lower()
223223
if self.host_matching:
224224
if subdomain is not None:
225-
raise RuntimeError("host matching enabled and a subdomain was provided")
225+
raise RuntimeError(
226+
"host matching enabled and a subdomain was provided")
226227
elif subdomain is None:
227228
subdomain = self.default_subdomain
228229
if script_name is None:
@@ -602,12 +603,14 @@ def match(
602603
path_part = f"/{path_info.lstrip('/')}" if path_info else ""
603604

604605
try:
605-
result = self.map._matcher.match(domain_part, path_part, method, websocket)
606+
result = self.map._matcher.match(
607+
domain_part, path_part, method, websocket)
606608
except RequestPath as e:
607609
# safe = https://url.spec.whatwg.org/#url-path-segment-string
608610
new_path = quote(e.path_info, safe="!$&'()*+,/:;=@")
609611
raise RequestRedirect(
610-
self.make_redirect_url(new_path, query_args)
612+
self.make_redirect_url(new_path, query_args),
613+
new_path,
611614
) from None
612615
except RequestAliasRedirect as e:
613616
raise RequestRedirect(
@@ -617,11 +620,13 @@ def match(
617620
e.matched_values,
618621
method,
619622
query_args,
620-
)
623+
),
624+
path_part,
621625
) from None
622626
except NoMatch as e:
623627
if e.have_match_for:
624-
raise MethodNotAllowed(valid_methods=list(e.have_match_for)) from None
628+
raise MethodNotAllowed(
629+
valid_methods=list(e.have_match_for)) from None
625630

626631
if e.websocket_mismatch:
627632
raise WebsocketMismatch() from None
@@ -631,9 +636,10 @@ def match(
631636
rule, rv = result
632637

633638
if self.map.redirect_defaults:
634-
redirect_url = self.get_default_redirect(rule, method, rv, query_args)
639+
(redirect_url, redirect_path) = self.get_default_redirect(
640+
rule, method, rv, query_args)
635641
if redirect_url is not None:
636-
raise RequestRedirect(redirect_url)
642+
raise RequestRedirect(redirect_url, redirect_path)
637643

638644
if rule.redirect_to is not None:
639645
if isinstance(rule.redirect_to, str):
@@ -642,7 +648,8 @@ def _handle_match(match: t.Match[str]) -> str:
642648
value = rv[match.group(1)]
643649
return rule._converters[match.group(1)].to_url(value)
644650

645-
redirect_url = _simple_rule_re.sub(_handle_match, rule.redirect_to)
651+
redirect_url = _simple_rule_re.sub(
652+
_handle_match, rule.redirect_to)
646653
else:
647654
redirect_url = rule.redirect_to(self, **rv)
648655

@@ -655,7 +662,8 @@ def _handle_match(match: t.Match[str]) -> str:
655662
urljoin(
656663
f"{self.url_scheme or 'http'}://{netloc}{self.script_name}",
657664
redirect_url,
658-
)
665+
),
666+
redirect_url,
659667
)
660668

661669
if return_rule:
@@ -736,8 +744,9 @@ def get_default_redirect(
736744
if r.provides_defaults_for(rule) and r.suitable_for(values, method):
737745
values.update(r.defaults) # type: ignore
738746
domain_part, path = r.build(values) # type: ignore
739-
return self.make_redirect_url(path, query_args, domain_part=domain_part)
740-
return None
747+
return self.make_redirect_url(path, query_args,
748+
domain_part=domain_part), path
749+
return None, None
741750

742751
def encode_query_args(self, query_args: t.Mapping[str, t.Any] | str) -> str:
743752
if not isinstance(query_args, str):

0 commit comments

Comments
 (0)