Skip to content

Commit

Permalink
Bugfix the calculation of weighting based on static parts
Browse files Browse the repository at this point in the history
Only static parts that are more than a `/` should count towards the
weighting, as they influence the part for matching. This prevents
`/<path:p>/<a>/<b>` matching before `/<path:p>/b` as only the latter
rule should have a static weighting as it is the only rule that has a
static part.
  • Loading branch information
pgjones committed Jul 27, 2022
1 parent 5c85706 commit 56a24f7
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 2 deletions.
2 changes: 2 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Unreleased
slashes mode is disabled for the rule. :issue:`2467`
- Fix router so that partial part matches are not allowed
i.e. ``/2df`` does not match ``/<int>``. :pr:`2470`
- Fix router static part weighting, so that simpler routes are matched
before more complex ones. :issue:`2471`
- Restore ``ValidationError`` to be importable from
``werkzeug.routing``. :issue:`2465`

Expand Down
8 changes: 6 additions & 2 deletions src/werkzeug/routing/rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -592,8 +592,12 @@ def _parse_rule(self, rule: str) -> t.Iterable[RulePart]:

data = match.groupdict()
if data["static"]:
static_weights.append((static_parts, -len(data["static"])))
static_parts += 1
# Only static parts beyond the `/` count towards the
# weighting (more static parts mean the rule should be
# weighted first).
if data["static"] != "/":
static_weights.append((static_parts, -len(data["static"])))
static_parts += 1
self._trace.append((False, data["static"]))

if data["variable"] is not None:
Expand Down
12 changes: 12 additions & 0 deletions tests/test_routing.py
Original file line number Diff line number Diff line change
Expand Up @@ -1394,6 +1394,18 @@ def test_rule_websocket_methods():
r.Rule("/ws", endpoint="ws", websocket=True, methods=["get", "head", "options"])


def test_path_weighting():
m = r.Map(
[
r.Rule("/<path:path>/c", endpoint="simple"),
r.Rule("/<path:path>/<a>/<b>", endpoint="complex"),
]
)
a = m.bind("localhost", path_info="/a/b/c")

assert a.match() == ("simple", {"path": "a/b"})


def test_newline_match():
m = r.Map([r.Rule("/hello", endpoint="hello")])
a = m.bind("localhost")
Expand Down

0 comments on commit 56a24f7

Please sign in to comment.