From 4811dd2df18c41e7913744a4e51ebef578e662e0 Mon Sep 17 00:00:00 2001 From: Jonas Wloka Date: Thu, 2 Mar 2023 15:42:52 +0100 Subject: [PATCH] Fix path_template variable parsing containing dots see #2437 --- dash/_pages.py | 28 +++++++++++----------------- tests/unit/test_path_template.py | 21 +++++++++++++++++++++ 2 files changed, 32 insertions(+), 17 deletions(-) create mode 100644 tests/unit/test_path_template.py diff --git a/dash/_pages.py b/dash/_pages.py index 26083a2ef0..3491729380 100644 --- a/dash/_pages.py +++ b/dash/_pages.py @@ -3,7 +3,6 @@ from os.path import isfile, join import collections from urllib.parse import parse_qs -from fnmatch import fnmatch import re import flask @@ -101,23 +100,18 @@ def _parse_path_variables(pathname, path_template): returns **{"asset_id": "a100"} """ - # parse variable definitions e.g. from template - # and create pattern to match - wildcard_pattern = re.sub("<.*?>", "*", path_template) - var_pattern = re.sub("<.*?>", "(.*)", path_template) + # Copy of re._special_chars_map from Python >= 3.7 for backwards compatibility with 3.6 + _special_chars_map = {i: '\\' + chr(i) for i in b'()[]{}?*+-|^$\\.&~# \t\n\r\v\f'} + escaped_template = path_template.translate(_special_chars_map) - # check that static sections of the pathname match the template - if not fnmatch(pathname, wildcard_pattern): - return None - - # parse variable names e.g. var_name from template - var_names = re.findall("<(.*?)>", path_template) - - # parse variables from path - variables = re.findall(var_pattern, pathname) - variables = variables[0] if isinstance(variables[0], tuple) else variables - - return dict(zip(var_names, variables)) + pattern = re.compile( + re.sub( + "<(?P.*?)>", r"(?P<\g>.*)", + escaped_template + ) + ) + match = pattern.match(pathname) + return match.groupdict() if match else None def _create_redirect_function(redirect_to): diff --git a/tests/unit/test_path_template.py b/tests/unit/test_path_template.py new file mode 100644 index 0000000000..8247288cca --- /dev/null +++ b/tests/unit/test_path_template.py @@ -0,0 +1,21 @@ +from dash._pages import _parse_path_variables + + +def test_path_template_general(): + path_template = "some//static" + assert _parse_path_variables("some/value/static", path_template) == {'var': 'value'} + + +def test_path_template(): + path_template = "/" + assert _parse_path_variables("one/two", path_template) == {'foo': 'one', 'bar': 'two'} + + +def test_path_template_dots(): + path_template = "." + assert _parse_path_variables("one.two", path_template) == {'foo': 'one', 'bar': 'two'} + + +def test_path_template_none(): + path_template = "novars" + assert _parse_path_variables("one/two", path_template) is None