Repository navigation
Conversation
dcc.Location reports window.location.pathname, which the browser
percent-encodes. The pages router matched that raw value, so path
variables reached layout() and title() still encoded ("hello%20world")
and pages whose path has spaces or non-ASCII characters fell through to
the 404 page. The server-side meta tags already use the decoded
request.path, so the two disagreed.
Decode the pathname before stripping the prefix and matching it. Also
let a path variable hold a decoded newline: fnmatch already accepts one,
but the regex that extracts the variables did not.
Fixes plotly#2360
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…oding # Conflicts: # dash/_pages.py
T4rk1n
left a comment
There was a problem hiding this comment.
Decoding in the router puts the callback in line with the server side, which already matches against the decoded request.path (Flask, Quart and the ASGI scope["path"] all decode). The new test fails on dev and passes here. Nothing blocking.
One correction to the description: the re.DOTALL paragraph is out of date. _path_template_regex already has re.DOTALL on dev, and this diff doesn't touch _pages.py. Can you drop that paragraph so it doesn't confuse whoever reads the history later?
| page, path_variables = _path_to_page( | ||
| self.strip_relative_path(pathname_) | ||
| self.strip_relative_path( | ||
| unquote(pathname_) if pathname_ else pathname_ |
There was a problem hiding this comment.
Decoding the whole path before matching means an encoded / inside a variable now counts as a real separator. With path_template="/files/<a>/<b>", the URL /files/x/y%2Fz used to give a="x", b="y%2Fz". Now it decodes to /files/x/y/z and gives a="x/y", b="z". The meta tags already behave this way, so I'm fine with it as a follow-up and not a blocker. Can you add this case to the "Behaviour change" note so it's written down?
| - [#4023](https://github.com/plotly/dash/pull/4023) Fix extra callback registrations when an app runs from the parent directory: `python app/app.py`, where the script shares its folder's name, registered its callbacks twice, and `python app/main.py` imported a sibling `app/app.py` as the parent package and registered its callbacks too (regression introduced in 4.4.1 by [#3883](https://github.com/plotly/dash/pull/3883)). Fixes [#4011](https://github.com/plotly/dash/issues/4011). | ||
| - [#4057](https://github.com/plotly/dash/pull/4057) Fixed background callbacks to update `set_props` instead of replacing. Fixes [#3104](https://github.com/plotly/dash/issues/3104). | ||
| - [#3923](https://github.com/plotly/dash/pull/3923) Fix a blank page and `SyntaxError` in the browser when a dict component ID has a dot in one of its values. Fixes [#3480](https://github.com/plotly/dash/issues/3480). | ||
| - [#2360](https://github.com/plotly/dash/issues/2360) Fix Pages passing percent-encoded path variables to `layout` and `title` (`hello%20world` instead of `hello world`), and showing the 404 page for pages whose `path` has spaces or non-ASCII characters. The pages router now decodes the `pathname` it gets from the browser before matching it, like the server already does for the meta tags. |
There was a problem hiding this comment.
Can you link the PR instead of the issue, and put Fixes [#2360](...) at the end like the other entries? Please also cut it to two lines max. Something like: [#4074](...) Fix Pages passing percent-encoded path variables to layout and title, and the 404 page for paths with spaces or non-ASCII characters. Fixes [#2360](...).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks for the review! Dropped the outdated |
|



Fixes #2360
dcc.Location gives the pages router
window.location.pathname, which the browser percent-encodes. The router matched that raw value, so:layout()andtitle()still encoded:/report/hello worldgavename="hello%20world"and the tab title "Report hello%20world"paththat has spaces or non-ASCII characters showed the 404 pageThe server-side meta tags already use the decoded
request.path, so on first load og:title said "Report hello world" and then the router changed the tab title to "Report hello%20world".This runs
urllib.parse.unquoteon the pathname in both router callbacks (sync and async) before the prefix is stripped and the page is matched. Query parameters were already decoded byparse_qs, so path variables now behave the same way.Behaviour change: apps that already call
unquote()on path variables now decode twice, which only matters for values containing a literal%XX. Apathregistered in encoded form (for example/my%20page) now has to be written decoded. An encoded/inside a path variable now counts as a separator: withpath_template="/files/<a>/<b>",/files/x/y%2Fzgivesa="x/y", b="z"instead ofa="x", b="y%2Fz", which is what the meta tags already do.Tested on Windows: the new unit test fails before the change and passes after.
pytest tests/unithas the same 7 Windows-only failures (path separators in the pages tests, andtest_process_server_smoke) with and without the change. I also checked it by hand in Chrome against a local app. I did not run the Selenium integration tests.I used Claude Code to write this change.
Contributor Checklist
optionals
CHANGELOG.md