"""
"""
# flake8: noqa: W605
import re
from wheezy.routing.utils import outer_split
[docs]def try_build_regex_route(pattern, finishing=True, kwargs=None, name=None):
"""There is no special tests to match regex selection
strategy.
"""
if isinstance(pattern, RegexRoute):
return pattern
return RegexRoute(pattern, finishing, kwargs, name)
[docs]class RegexRoute(object):
"""Route based on regular expression matching."""
__slots__ = (
"match",
"path",
"path_value",
"name",
"path_format",
"kwargs",
"regex",
)
exact_matches = None
def __init__(self, pattern, finishing=True, kwargs=None, name=None):
pattern = pattern.lstrip("^").rstrip("$")
# Choose match strategy
self.path_format, names = parse_pattern(pattern)
if kwargs:
self.kwargs = dict.fromkeys(names, "")
self.kwargs.update(kwargs)
if finishing:
self.kwargs["route_name"] = name
self.match = self.match_with_kwargs
self.path = self.path_with_kwargs
self.path_value = self.path_format % self.kwargs
else:
if finishing:
self.name = name
self.match = self.match_no_kwargs_finishing
else:
self.match = self.match_no_kwargs
self.path = self.path_no_kwargs
pattern = "^" + pattern
if finishing:
pattern = pattern + "$"
self.regex = re.compile(pattern)
[docs] def match_no_kwargs(self, path):
"""If the ``path`` match the regex pattern."""
m = self.regex.match(path)
if m:
return m.end(), m.groupdict()
return -1, None
[docs] def match_no_kwargs_finishing(self, path):
"""If the ``path`` match the regex pattern."""
m = self.regex.match(path)
if m:
kwargs = m.groupdict()
kwargs["route_name"] = self.name
return m.end(), kwargs
return -1, None
[docs] def match_with_kwargs(self, path):
"""If the ``path`` match the regex pattern."""
m = self.regex.match(path)
if m:
kwargs = m.groupdict()
return (m.end(), dict(self.kwargs, **kwargs))
return -1, None
[docs] def path_with_kwargs(self, values=None):
"""Build the path for the given route by substituting
the named places of the regual expression.
Specialization case: route was initialized with
default kwargs.
"""
if values:
return self.path_format % dict(self.kwargs, **values)
else:
return self.path_value
[docs] def path_no_kwargs(self, values):
"""Build the path for the given route by substituting
the named places of the regual expression.
Specialization case: route was initialized with
no default kwargs.
"""
return self.path_format % values
RE_SPLIT = re.compile(r"\<(\w+)\>")
[docs]def parse_pattern(pattern):
"""Returns path_format and names.
>>> parse_pattern(r'abc/(?P<id>[^/]+)')
('abc/%(id)s', ['id'])
>>> parse_pattern(r'abc/(?P<n>[^/]+)/(?P<x>\\\w+)')
('abc/%(n)s/%(x)s', ['n', 'x'])
>>> parse_pattern(r'(?P<locale>(en|ru))/home')
('%(locale)s/home', ['locale'])
>>> from wheezy.routing.curly import convert
>>> parse_pattern(convert(r'[{locale:(en|ru)}/]home'))
('%(locale)s/home', ['locale'])
>>> parse_pattern(convert(r'item[/{id:i}]'))
('item/%(id)s', ['id'])
>>> p = convert('{controller:w}[/{action:w}[/{id:i}]]')
>>> parse_pattern(p)
('%(controller)s/%(action)s/%(id)s', ['controller', 'action', 'id'])
"""
pattern = strip_optional(pattern)
parts = outer_split(pattern, sep="()")
if len(parts) % 2 == 1 and not parts[-1]:
parts = parts[:-1]
names = [RE_SPLIT.split(p)[1] for p in parts[1::2]]
parts[1::2] = ["%%(%s)s" % p for p in names]
return "".join(parts), names
[docs]def strip_optional(pattern):
"""Strip optional regex group flag.
at the beginning
>>> strip_optional('((?P<locale>(en|ru))/)?home')
'(?P<locale>(en|ru))/home'
at the end
>>> strip_optional('item(/(?P<id>\\\d+))?')
'item/(?P<id>\\\d+)'
nested:
>>> p = '(?P<controller>\\\w+)(/(?P<action>\\\w+)(/(?P<id>\\\d+))?)?'
>>> strip_optional(p)
'(?P<controller>\\\w+)/(?P<action>\\\w+)/(?P<id>\\\d+)'
"""
if ")?" not in pattern:
return pattern
parts = outer_split(pattern, sep="()")
for i in range(2, len(parts), 2):
part = parts[i]
if part.startswith("?"):
parts[i] = part[1:]
parts[i - 1] = strip_optional(parts[i - 1])
else:
parts[i - 1] = "(%s)" % parts[i - 1]
return "".join(parts)