diff options
| author | Chris McDonough <chrism@plope.com> | 2013-08-29 23:55:36 -0400 |
|---|---|---|
| committer | Chris McDonough <chrism@plope.com> | 2013-08-29 23:55:36 -0400 |
| commit | c29603ed0d8fd0b55789eb8f975c901961864d66 (patch) | |
| tree | 7826c387f61d17513162b1e47ee1e19723a1cc6a | |
| parent | 06f57f4616758f1f05cb0402fc970089d4483f28 (diff) | |
| download | pyramid-c29603ed0d8fd0b55789eb8f975c901961864d66.tar.gz pyramid-c29603ed0d8fd0b55789eb8f975c901961864d66.tar.bz2 pyramid-c29603ed0d8fd0b55789eb8f975c901961864d66.zip | |
get rid of remainder_name on route, and just default to passing traverse; add route_remainder_name argument to resource_url
| -rw-r--r-- | CHANGES.txt | 8 | ||||
| -rw-r--r-- | docs/narr/hybrid.rst | 30 | ||||
| -rw-r--r-- | pyramid/interfaces.py | 9 | ||||
| -rw-r--r-- | pyramid/tests/test_url.py | 62 | ||||
| -rw-r--r-- | pyramid/tests/test_urldispatch.py | 11 | ||||
| -rw-r--r-- | pyramid/url.py | 55 | ||||
| -rw-r--r-- | pyramid/urldispatch.py | 7 |
7 files changed, 81 insertions, 101 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 70bc31840..d3d3f64ce 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -5,10 +5,10 @@ Features -------- - You can now generate "hybrid" urldispatch/traversal URLs more easily - by using the new ``route_name`` and ``route_kw`` arguments to - ``request.resource_url`` and ``request.resource_path``. See the new section - of the "Combining Traversal and URL Dispatch" documentation chapter entitled - "Hybrid URL Generation". + by using the new ``route_name``, ``route_kw`` and ``route_remainder_name`` + arguments to ``request.resource_url`` and ``request.resource_path``. See + the new section of the "Combining Traversal and URL Dispatch" documentation + chapter entitled "Hybrid URL Generation". - It is now possible to escape double braces in Pyramid scaffolds (unescaped, these represent replacement values). You can use ``\{\{a\}\}`` to diff --git a/docs/narr/hybrid.rst b/docs/narr/hybrid.rst index 58d89fc98..a29ccb2ac 100644 --- a/docs/narr/hybrid.rst +++ b/docs/narr/hybrid.rst @@ -563,7 +563,8 @@ Generating Hybrid URLs The :meth:`pyramid.request.Request.resource_url` method and the :meth:`pyramid.request.Request.resource_path` method both accept optional keyword arguments that make it easier to generate route-prefixed URLs that -contain paths to traversal resources:``route_name`` and ``route_kw``. +contain paths to traversal resources:``route_name``, ``route_kw``, and +``route_remainder_name``. Any route that has a pattern that contains a ``*remainder`` pattern (any stararg remainder pattern, such as ``*traverse`` or ``*subpath`` or ``*fred``) @@ -615,15 +616,36 @@ You can pass ``route_kw`` in to fill in ``{id}`` above: If you pass ``route_kw`` but do not pass ``route_name``, ``route_kw`` will be ignored. -All other values that are normally passable to ``resource_path`` and -``resource_url`` (such as ``query``, ``anchor``, ``host``, ``port``, etc) work -as you might expect in this configuration too. +By default this feature works by calling ``route_url`` under the hood, +and passing the value of the resource path to that function as ``traverse``. +If your route has a different ``*stararg`` remainder name (such as +``*subpath``), you can tell ``resource_url`` or ``resource_path`` to use that +instead of ``traverse`` by passing ``route_remainder_name``. For example, +if you have the following route: + +.. code-block:: python + + config.add_route('mysection', '/mysection*subpath') + +You can fill in the ``*subpath`` value using ``resource_url`` by doing: + +.. code-block:: python + + request.resource_path(a, route_name='mysection', + route_remainder_name='subpath') + +If you pass ``route_remainder_name`` but do not pass ``route_name``, +``route_remainder_name`` will be ignored. If you try to use ``resource_path`` or ``resource_url`` when the ``route_name`` argument points at a route that does not have a remainder stararg, an error will not be raised, but the generated URL will not contain any remainder information either. +All other values that are normally passable to ``resource_path`` and +``resource_url`` (such as ``query``, ``anchor``, ``host``, ``port``, and +positional elements) work as you might expect in this configuration. + Note that this feature is incompatible with the ``__resource_url__`` feature (see :ref:`overriding_resource_url_generation`) implemented on resource objects. Any ``__resource_url__`` supplied by your resource will be ignored diff --git a/pyramid/interfaces.py b/pyramid/interfaces.py index 0f3f9fa1e..3f43494a8 100644 --- a/pyramid/interfaces.py +++ b/pyramid/interfaces.py @@ -692,15 +692,6 @@ class IRoute(Interface): pregenerator = Attribute('This attribute should either be ``None`` or ' 'a callable object implementing the ' '``IRoutePregenerator`` interface') - remainder_name = Attribute( - 'The name of any stararg remainder that is present at the end of ' - 'the pattern. For example, if the pattern is ``/foo*bar``, the ' - '``remainder_name`` will be ``bar``; if the pattern is ` ' - '`/foo*traverse``, the ``remainder_name`` will be ``traverse``. ' - 'If the route does not have a stararg remainder name in its pattern, ' - 'the value of ``remainder_name`` will be ``None``. This attribute ' - 'is new as of Pyramid 1.5.' - ) def match(path): """ diff --git a/pyramid/tests/test_url.py b/pyramid/tests/test_url.py index 30dad86fd..f6117777f 100644 --- a/pyramid/tests/test_url.py +++ b/pyramid/tests/test_url.py @@ -269,13 +269,12 @@ class TestURLMethodsMixin(unittest.TestCase): # no virtual_path_tuple on adapter adapter.virtual_path = '/a/b/c/' route = DummyRoute('/1/2/3') - route.remainder_name = 'fred' mapper = DummyRoutesMapper(route) request.registry.registerUtility(mapper, IRoutesMapper) root = DummyContext() result = request.resource_url(root, route_name='foo') self.assertEqual(result, 'http://example.com:5432/1/2/3') - self.assertEqual(route.kw, {'fred': ('', 'a', 'b', 'c', '')}) + self.assertEqual(route.kw, {'traverse': ('', 'a', 'b', 'c', '')}) def test_resource_url_with_route_name_remainder_on_adapter(self): from pyramid.interfaces import IRoutesMapper @@ -289,13 +288,12 @@ class TestURLMethodsMixin(unittest.TestCase): # virtual_path_tuple on adapter adapter.virtual_path_tuple = ('', 'a', 'b', 'c', '') route = DummyRoute('/1/2/3') - route.remainder_name = 'fred' mapper = DummyRoutesMapper(route) request.registry.registerUtility(mapper, IRoutesMapper) root = DummyContext() result = request.resource_url(root, route_name='foo') self.assertEqual(result, 'http://example.com:5432/1/2/3') - self.assertEqual(route.kw, {'fred': ('', 'a', 'b', 'c', '')}) + self.assertEqual(route.kw, {'traverse': ('', 'a', 'b', 'c', '')}) def test_resource_url_with_route_name_and_app_url(self): from pyramid.interfaces import IRoutesMapper @@ -309,13 +307,12 @@ class TestURLMethodsMixin(unittest.TestCase): # virtual_path_tuple on adapter adapter.virtual_path_tuple = ('', 'a', 'b', 'c', '') route = DummyRoute('/1/2/3') - route.remainder_name = 'fred' mapper = DummyRoutesMapper(route) request.registry.registerUtility(mapper, IRoutesMapper) root = DummyContext() result = request.resource_url(root, route_name='foo', app_url='app_url') self.assertEqual(result, 'app_url/1/2/3') - self.assertEqual(route.kw, {'fred': ('', 'a', 'b', 'c', '')}) + self.assertEqual(route.kw, {'traverse': ('', 'a', 'b', 'c', '')}) def test_resource_url_with_route_name_and_scheme_host_port_etc(self): from pyramid.interfaces import IRoutesMapper @@ -329,7 +326,6 @@ class TestURLMethodsMixin(unittest.TestCase): # virtual_path_tuple on adapter adapter.virtual_path_tuple = ('', 'a', 'b', 'c', '') route = DummyRoute('/1/2/3') - route.remainder_name = 'fred' mapper = DummyRoutesMapper(route) request.registry.registerUtility(mapper, IRoutesMapper) root = DummyContext() @@ -337,7 +333,7 @@ class TestURLMethodsMixin(unittest.TestCase): host='host', port='port', query={'a':'1'}, anchor='anchor') self.assertEqual(result, 'scheme://host:port/1/2/3?a=1#anchor') - self.assertEqual(route.kw, {'fred': ('', 'a', 'b', 'c', '')}) + self.assertEqual(route.kw, {'traverse': ('', 'a', 'b', 'c', '')}) def test_resource_url_with_route_name_and_route_kwargs(self): from pyramid.interfaces import IRoutesMapper @@ -351,7 +347,6 @@ class TestURLMethodsMixin(unittest.TestCase): # virtual_path_tuple on adapter adapter.virtual_path_tuple = ('', 'a', 'b', 'c', '') route = DummyRoute('/1/2/3') - route.remainder_name = 'fred' mapper = DummyRoutesMapper(route) request.registry.registerUtility(mapper, IRoutesMapper) root = DummyContext() @@ -360,7 +355,7 @@ class TestURLMethodsMixin(unittest.TestCase): self.assertEqual(result, 'http://example.com:5432/1/2/3') self.assertEqual( route.kw, - {'fred': ('', 'a', 'b', 'c', ''), + {'traverse': ('', 'a', 'b', 'c', ''), 'a':'1', 'b':'2'} ) @@ -377,12 +372,31 @@ class TestURLMethodsMixin(unittest.TestCase): # virtual_path_tuple on adapter adapter.virtual_path_tuple = ('', 'a', 'b', 'c', '') route = DummyRoute('/1/2/3') - route.remainder_name = 'fred' mapper = DummyRoutesMapper(route) request.registry.registerUtility(mapper, IRoutesMapper) root = DummyContext() result = request.resource_url(root, 'e1', 'e2', route_name='foo') self.assertEqual(result, 'http://example.com:5432/1/2/3/e1/e2') + self.assertEqual(route.kw, {'traverse': ('', 'a', 'b', 'c', '')}) + + def test_resource_url_with_route_name_and_remainder_name(self): + from pyramid.interfaces import IRoutesMapper + environ = { + 'wsgi.url_scheme':'http', + 'SERVER_PORT':'8080', + 'SERVER_NAME':'example.com', + } + request = self._makeOne(environ) + adapter = self._registerResourceURL(request.registry) + # virtual_path_tuple on adapter + adapter.virtual_path_tuple = ('', 'a', 'b', 'c', '') + route = DummyRoute('/1/2/3') + mapper = DummyRoutesMapper(route) + request.registry.registerUtility(mapper, IRoutesMapper) + root = DummyContext() + result = request.resource_url(root, route_name='foo', + route_remainder_name='fred') + self.assertEqual(result, 'http://example.com:5432/1/2/3') self.assertEqual(route.kw, {'fred': ('', 'a', 'b', 'c', '')}) def test_resource_path(self): @@ -570,31 +584,6 @@ class TestURLMethodsMixin(unittest.TestCase): self.assertEqual(result, 'http://example2.com/1/2/3/element1?q=1#anchor') - def test_route_url_with_remainder(self): - from pyramid.interfaces import IRoutesMapper - request = self._makeOne() - route = DummyRoute('/1/2/3/') - route.remainder_name = 'fred' - mapper = DummyRoutesMapper(route=route) - request.registry.registerUtility(mapper, IRoutesMapper) - result = request.route_url('flub', _remainder='abc') - self.assertEqual(result, - 'http://example.com:5432/1/2/3/') - self.assertEqual(route.kw['fred'], 'abc') - self.assertFalse('_remainder' in route.kw) - - def test_route_url_with_remainder_name_already_in_kw(self): - from pyramid.interfaces import IRoutesMapper - request = self._makeOne() - route = DummyRoute('/1/2/3/') - route.remainder_name = 'fred' - mapper = DummyRoutesMapper(route=route) - request.registry.registerUtility(mapper, IRoutesMapper) - self.assertRaises( - ValueError, - request.route_url, 'flub', _remainder='abc', fred='foo' - ) - def test_route_url_integration_with_real_request(self): # to try to replicate https://github.com/Pylons/pyramid/issues/213 from pyramid.interfaces import IRoutesMapper @@ -1268,7 +1257,6 @@ class DummyRoutesMapper: class DummyRoute: pregenerator = None name = 'route' - remainder_name = None def __init__(self, result='/1/2/3'): self.result = result diff --git a/pyramid/tests/test_urldispatch.py b/pyramid/tests/test_urldispatch.py index ba5f2eeb5..1755d9f47 100644 --- a/pyramid/tests/test_urldispatch.py +++ b/pyramid/tests/test_urldispatch.py @@ -522,17 +522,6 @@ class TestCompileRouteFunctional(unittest.TestCase): self.generates('/foo/:_abc', {'_abc':'20'}, '/foo/20') self.generates('/foo/:abc_def', {'abc_def':'20'}, '/foo/20') -class Test_get_remainder_name(unittest.TestCase): - def _callFUT(self, pattern): - from pyramid.urldispatch import get_remainder_name - return get_remainder_name(pattern) - - def test_it_nostararg(self): - self.assertEqual(self._callFUT('/bob'), None) - - def test_it_withstararg(self): - self.assertEqual(self._callFUT('/bob*dean'), 'dean') - class DummyContext(object): """ """ diff --git a/pyramid/url.py b/pyramid/url.py index 90b8f667f..fda2c72c7 100644 --- a/pyramid/url.py +++ b/pyramid/url.py @@ -192,15 +192,6 @@ class URLMethodsMixin(object): are passed, ``_app_url`` takes precedence and any values passed for ``_scheme``, ``_host``, and ``_port`` will be ignored. - If a ``_remainder`` keyword argument is supplied, it will be used to - replace *any* ``*remainder`` stararg at the end of the route pattern. - For example, if the route pattern is ``/foo/*traverse``, and you pass - ``_remainder=('a', 'b', 'c')``, it is entirely equivalent to passing - ``traverse=('a', 'b', 'c')``, and in either case the generated path - will be ``/foo/a/b/c``. It is an error to pass both ``*remainder`` and - the explicit value for a remainder name; a :exc:`ValueError` will be - raised. This feature was added in Pyramid 1.5. - This function raises a :exc:`KeyError` if the URL cannot be generated due to missing replacement names. Extra replacement names are ignored. @@ -222,7 +213,6 @@ class URLMethodsMixin(object): if route.pregenerator is not None: elements, kw = route.pregenerator(self, elements, kw) - remainder_name = route.remainder_name anchor = '' qs = '' app_url = None @@ -258,16 +248,6 @@ class URLMethodsMixin(object): else: app_url = self.application_url - remainder = kw.pop('_remainder', None) - - if remainder and remainder_name: - if remainder_name in kw: - raise ValueError( - 'Cannot pass both "%s" and "_remainder", ' - 'these conflict for this route' % remainder_name - ) - kw[remainder_name] = remainder - path = route.generate(kw) # raises KeyError if generate fails if elements: @@ -426,7 +406,7 @@ class URLMethodsMixin(object): :ref:`overriding_resource_url_generation`. .. versionadded:: 1.5 - ``route_name`` and ``route_kw`` + ``route_name``, ``route_kw``, and ``route_remainder_name`` If ``route_name`` is passed, this function will delegate its URL production to the ``route_url`` function. Calling @@ -439,21 +419,34 @@ class URLMethodsMixin(object): 'element1', 'element2', _query={'a':'1'}, - _remainder=remainder_path, + traverse=traversal_path, ) It is only sensible to pass ``route_name`` if the route being named has a ``*remainder`` stararg value such as ``*traverse``. The remainder value will be ignored in the output otherwise. + By default, the resource path value will be passed as the name + ``traverse`` when ``route_url`` is called. You can influence this by + passing a different ``route_remainder_name`` value if the route has a + different ``*stararg`` value at its end. For example if the route + pattern you want to replace has a ``*subpath`` stararg ala + ``/foo*subpath``:: + + request.resource_url( + resource, + route_name='myroute', + route_remainder_name='subpath' + ) + If ``route_name`` is passed, it is also permissible to pass ``route_kw``, which will passed as additional keyword arguments to ``route_url``. Saying ``resource_url(someresource, 'element1', 'element2', route_name='blogentry', route_kw={'id':'4'}, - _query={'a':'1'})`` is equivalent to:: + _query={'a':'1'})`` is roughly equivalent to:: remainder_path = request.resource_path_tuple(someobject) - kw = {'id':'4', '_query':{'a':'1'}, '_remainder':remainder_path} + kw = {'id':'4', '_query':{'a':'1'}, 'traverse':traversal_path} url = request.route_url( 'blogentry', 'element1', @@ -461,10 +454,12 @@ class URLMethodsMixin(object): **kw, ) - If ``route_kw`` is passed, but ``route_name`` is not passed, - ``route_kw`` will be ignored. If ``route_name`` is passed, the - ``__resource_url__`` method of the resource passed is ignored - unconditionally. + If ``route_kw`` or ``route_remainder_name`` is passed, but + ``route_name`` is not passed, both ``route_kw`` and + ``route_remainder_name`` will be ignored. If ``route_name`` + is passed, the ``__resource_url__`` method of the resource passed is + ignored unconditionally. This feature is incompatible with + resources which generate their own URLs. .. note:: @@ -527,7 +522,8 @@ class URLMethodsMixin(object): # older user-supplied IResourceURL adapter without 1.5 # virtual_path_tuple remainder = tuple(url_adapter.virtual_path.split('/')) - newkw['_remainder'] = remainder + remainder_name = kw.get('route_remainder_name', 'traverse') + newkw[remainder_name] = remainder for name in ( 'app_url', 'scheme', 'host', 'port', 'query', 'anchor' @@ -540,6 +536,7 @@ class URLMethodsMixin(object): route_kw = kw.get('route_kw') if route_kw is not None: newkw.update(route_kw) + return self.route_url(route_name, *elements, **newkw) if 'app_url' in kw: diff --git a/pyramid/urldispatch.py b/pyramid/urldispatch.py index 75bff904d..fe4d433c3 100644 --- a/pyramid/urldispatch.py +++ b/pyramid/urldispatch.py @@ -33,7 +33,6 @@ class Route(object): self.pattern = pattern self.path = pattern # indefinite b/w compat, not in interface self.match, self.generate = _compile_route(pattern) - self.remainder_name = get_remainder_name(pattern) self.name = name self.factory = factory self.predicates = predicates @@ -234,9 +233,3 @@ def _compile_route(route): return result return matcher, generator - -def get_remainder_name(pattern): - match = star_at_end.search(pattern) - if match: - return match.groups()[0] - |
