- URL Dispatch now allows for replacement markers to be located anywhere
in the pattern, instead of immediately following a ``/``.
- Added ``marker_pattern`` option to ``add_route`` to supply a dict of
regular expressions to be used for markers in the pattern instead of the
default regular expression that matched everything except a ``/``.
| | |
| | | Features |
| | | -------- |
| | | |
| | | - URL Dispatch now allows for replacement markers to be located anywhere |
| | | in the pattern, instead of immediately following a ``/``. |
| | | - Added ``marker_pattern`` option to ``add_route`` to supply a dict of |
| | | regular expressions to be used for markers in the pattern instead of the |
| | | default regular expression that matched everything except a ``/``. |
| | | - Add a ``pyramid.url.route_path`` API, allowing folks to generate relative |
| | | URLs. Calling ``route_path`` is the same as calling |
| | | ``pyramid.url.route_url`` with the argument ``_app_url`` equal to the empty |
| | |
| | | |
| | | /:foo/bar/baz |
| | | |
| | | A patttern segment (an individual item between ``/`` characters in the |
| | | pattern) may either be a literal string (e.g. ``foo``) *or* it may be |
| | | a segment replacement marker (e.g. ``:foo``) or a certain combination |
| | | of both. |
| | | A pattern segment (an individual item between ``/`` characters in the pattern) |
| | | may either be a literal string (e.g. ``foo``) *or* it may be a replacement |
| | | marker (e.g. ``:foo``) or a certain combination of both. A replacement marker |
| | | does not need to be preceded by a ``/`` character. |
| | | |
| | | A segment replacement marker is in the format ``:name``, where this |
| | | means "accept any characters up to the next nonalphaunumeric character |
| | | A replacement marker is in the format ``:name``, where this |
| | | means "accept any characters up to the next non-alphanumeric character |
| | | and use this as the ``name`` matchdict value." For example, the |
| | | following pattern defines one literal segment ("foo") and two dynamic |
| | | segments ("baz", and "bar"): |
| | | replacement markers ("baz", and "bar"): |
| | | |
| | | .. code-block:: text |
| | | |
| | |
| | | a literal ``.html`` at the end of the segment represented by |
| | | ``:name.html`` (it only contains ``biz``, not ``biz.html``). |
| | | |
| | | This does not mean, however, that you can use two segment replacement |
| | | markers in the same segment. For instance, ``/:foo:bar`` is a |
| | | nonsensical route pattern. It will never match anything. |
| | | To capture both segments, two replacement markers can be used: |
| | | |
| | | .. code-block:: text |
| | | |
| | | foo/:name.:ext |
| | | |
| | | The literal path ``/foo/biz.html`` will match the above route pattern, and the |
| | | match result will be ``{'name': 'biz', 'ext': 'html'}``. This occurs because |
| | | the replacement marker ``:name`` has a literal part of ``.`` between the other |
| | | replacement marker ``:ext``. |
| | | |
| | | It is possible to use two replacement markers without any literal characters |
| | | between them, for instance ``/:foo:bar``. This would be a nonsensical pattern |
| | | without specifying any ``pattern_regexes`` to restrict valid values of each |
| | | replacement marker. |
| | | |
| | | Segments must contain at least one character in order to match a |
| | | segment replacement marker. For example, for the URL ``/abc/``: |
| | |
| | | as ``path``. ``path`` continues to work as an alias for |
| | | ``pattern``. |
| | | |
| | | ``marker_pattern`` |
| | | A dict of regular expression replacements for replacement markers in the |
| | | pattern to use when generating the complete regular expression used to |
| | | match the route. By default, every replacement marker in the pattern is |
| | | replaced with the regular expression ``[^/]+``. Values in this dict will |
| | | be used instead if present. |
| | | |
| | | ``xhr`` |
| | | This value should be either ``True`` or ``False``. If this value is |
| | | specified and is ``True``, the :term:`request` must possess an |
| | |
| | | def add_route(self, |
| | | name, |
| | | pattern=None, |
| | | marker_pattern=None, |
| | | view=None, |
| | | view_for=None, |
| | | permission=None, |
| | |
| | | to this function will be used to represent the pattern |
| | | value if the ``pattern`` argument is ``None``. If both |
| | | ``path`` and ``pattern`` are passed, ``pattern`` wins. |
| | | |
| | | |
| | | marker_pattern |
| | | |
| | | A dict of regular expression's that will be used in the place |
| | | of the default ``[^/]+`` regular expression for all replacement |
| | | markers in the route pattern. |
| | | |
| | | xhr |
| | | |
| | | This value should be either ``True`` or ``False``. If this |
| | |
| | | raise ConfigurationError('"pattern" argument may not be None') |
| | | |
| | | return mapper.connect(name, pattern, factory, predicates=predicates, |
| | | pregenerator=pregenerator) |
| | | pregenerator=pregenerator, |
| | | marker_pattern=marker_pattern) |
| | | |
| | | def get_routes_mapper(self): |
| | | """ Return the :term:`routes mapper` object associated with |
| | |
| | | self.assertEqual(mapper.generate('abc', {}), 123) |
| | | |
| | | class TestCompileRoute(unittest.TestCase): |
| | | def _callFUT(self, pattern): |
| | | def _callFUT(self, pattern, marker_pattern=None): |
| | | from pyramid.urldispatch import _compile_route |
| | | return _compile_route(pattern) |
| | | return _compile_route(pattern, marker_pattern) |
| | | |
| | | def test_no_star(self): |
| | | matcher, generator = self._callFUT('/foo/:baz/biz/:buz/bar') |
| | |
| | | from pyramid.exceptions import URLDecodeError |
| | | matcher, generator = self._callFUT('/:foo') |
| | | self.assertRaises(URLDecodeError, matcher, '/%FF%FE%8B%00') |
| | | |
| | | def test_custom_regex(self): |
| | | matcher, generator = self._callFUT('foo/:baz/biz/:buz.:bar', |
| | | {'buz': '[^/\.]+'}) |
| | | self.assertEqual(matcher('/foo/baz/biz/buz.bar'), |
| | | {'baz':'baz', 'buz':'buz', 'bar':'bar'}) |
| | | self.assertEqual(matcher('foo/baz/biz/buz/bar'), None) |
| | | self.assertEqual(generator({'baz':1, 'buz':2, 'bar': 'html'}), '/foo/1/biz/2.html') |
| | | |
| | | class TestCompileRouteMatchFunctional(unittest.TestCase): |
| | | def matches(self, pattern, path, expected): |
| | |
| | | self.matches('/:x', '', None) |
| | | self.matches('/:x', '/', None) |
| | | self.matches('/abc/:def', '/abc/', None) |
| | | self.matches('/abc/:def:baz', '/abc/bleep', None) # bad pattern |
| | | self.matches('', '/', {}) |
| | | self.matches('/', '/', {}) |
| | | self.matches('/:x', '/a', {'x':'a'}) |
| | |
| | | class Route(object): |
| | | implements(IRoute) |
| | | def __init__(self, name, pattern, factory=None, predicates=(), |
| | | pregenerator=None): |
| | | pregenerator=None, marker_pattern=None): |
| | | self.pattern = pattern |
| | | self.path = pattern # indefinite b/w compat, not in interface |
| | | self.match, self.generate = _compile_route(pattern) |
| | | self.match, self.generate = _compile_route(pattern, marker_pattern) |
| | | self.name = name |
| | | self.factory = factory |
| | | self.predicates = predicates |
| | |
| | | return self.routes.get(name) |
| | | |
| | | def connect(self, name, pattern, factory=None, predicates=(), |
| | | pregenerator=None): |
| | | pregenerator=None, marker_pattern=None): |
| | | if name in self.routes: |
| | | oldroute = self.routes[name] |
| | | self.routelist.remove(oldroute) |
| | | route = Route(name, pattern, factory, predicates, pregenerator) |
| | | route = Route(name, pattern, factory, predicates, pregenerator, |
| | | marker_pattern) |
| | | self.routelist.append(route) |
| | | self.routes[name] = route |
| | | return route |
| | |
| | | return {'route':None, 'match':None} |
| | | |
| | | # stolen from bobo and modified |
| | | route_re = re.compile(r'(/:[a-zA-Z]\w*)') |
| | | def _compile_route(route): |
| | | route_re = re.compile(r'(:[a-zA-Z]\w*)') |
| | | def _compile_route(route, marker_pattern=None): |
| | | marker_pattern = marker_pattern or {} |
| | | if not route.startswith('/'): |
| | | route = '/' + route |
| | | star = None |
| | |
| | | gen.append(prefix) |
| | | while pat: |
| | | name = pat.pop() |
| | | name = name[2:] |
| | | gen.append('/%%(%s)s' % name) |
| | | name = '/(?P<%s>[^/]+)' % name |
| | | name = name[1:] |
| | | gen.append('%%(%s)s' % name) |
| | | name = '(?P<%s>%s)' % (name, marker_pattern.get(name, '[^/]+')) |
| | | rpat.append(name) |
| | | s = pat.pop() |
| | | if s: |