Skip to content

Commit cf185ae

Browse files
committed
Fix path_template variable parsing containing dots
see #2437
1 parent ddaa319 commit cf185ae

File tree

2 files changed

+32
-17
lines changed

2 files changed

+32
-17
lines changed

dash/_pages.py

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
from os.path import isfile, join
44
import collections
55
from urllib.parse import parse_qs
6-
from fnmatch import fnmatch
76
import re
87
import flask
98

@@ -101,23 +100,18 @@ def _parse_path_variables(pathname, path_template):
101100
returns **{"asset_id": "a100"}
102101
"""
103102

104-
# parse variable definitions e.g. <var_name> from template
105-
# and create pattern to match
106-
wildcard_pattern = re.sub("<.*?>", "*", path_template)
107-
var_pattern = re.sub("<.*?>", "(.*)", path_template)
103+
# Copy of re._special_chars_map from Python >= 3.7 for backwards compatibility with 3.6
104+
_special_chars_map = {i: '\\' + chr(i) for i in b'()[]{}?*+-|^$\\.&~# \t\n\r\v\f'}
105+
escaped_template = path_template.translate(_special_chars_map)
108106

109-
# check that static sections of the pathname match the template
110-
if not fnmatch(pathname, wildcard_pattern):
111-
return None
112-
113-
# parse variable names e.g. var_name from template
114-
var_names = re.findall("<(.*?)>", path_template)
115-
116-
# parse variables from path
117-
variables = re.findall(var_pattern, pathname)
118-
variables = variables[0] if isinstance(variables[0], tuple) else variables
119-
120-
return dict(zip(var_names, variables))
107+
pattern = re.compile(
108+
re.sub(
109+
"<(?P<var>.*?)>", r"(?P<\g<var>>.*)",
110+
escaped_template
111+
)
112+
)
113+
match = pattern.match(pathname)
114+
return match.groupdict() if match else None
121115

122116

123117
def _create_redirect_function(redirect_to):

tests/unit/test_path_template.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from dash._pages import _parse_path_variables
2+
3+
4+
def test_path_template_general():
5+
path_template = "some/<var>/static"
6+
assert _parse_path_variables("some/value/static", path_template) == {'var': 'value'}
7+
8+
9+
def test_path_template():
10+
path_template = "<foo>/<bar>"
11+
assert _parse_path_variables("one/two", path_template) == {'foo': 'one', 'bar': 'two'}
12+
13+
14+
def test_path_template_dots():
15+
path_template = "<foo>.<bar>"
16+
assert _parse_path_variables("one.two", path_template) == {'foo': 'one', 'bar': 'two'}
17+
18+
19+
def test_path_template_none():
20+
path_template = "novars"
21+
assert _parse_path_variables("one/two", path_template) is None

0 commit comments

Comments
 (0)