"""
"""
import re
from wheezy.routing.utils import outer_split
def try_build_regex_route(pattern, finishing=True, kwargs=None, name=None):
[docs] """ There is no special tests to match regex selection
strategy.
"""
if isinstance(pattern, RegexRoute):
return pattern
return RegexRoute(pattern, finishing, kwargs, name)
class RegexRoute(object):
[docs] """ 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)
def match_no_kwargs(self, path):
[docs] """ If the ``path`` match the regex pattern.
"""
m = self.regex.match(path)
if m:
return m.end(), m.groupdict()
return -1, None
def match_no_kwargs_finishing(self, path):
[docs] """ 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
def match_with_kwargs(self, path):
[docs] """ 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
def path_with_kwargs(self, values=None):
[docs] """ 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
def path_no_kwargs(self, values):
[docs] """ 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+)\>')
def parse_pattern(pattern):
[docs] """ Returns path_format and names.
>>> parse_pattern('abc/(?P<id>[^/]+)')
('abc/%(id)s', ['id'])
>>> parse_pattern('abc/(?P<n>[^/]+)/(?P<x>\\\w+)')
('abc/%(n)s/%(x)s', ['n', 'x'])
>>> parse_pattern('(?P<locale>(en|ru))/home')
('%(locale)s/home', ['locale'])
>>> from wheezy.routing.curly import convert
>>> parse_pattern(convert('[{locale:(en|ru)}/]home'))
('%(locale)s/home', ['locale'])
>>> parse_pattern(convert('item[/{id:i}]'))
('item/%(id)s', ['id'])
>>> p = convert(r'{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
def strip_optional(pattern):
[docs] """ 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)