Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

get rid of remainder_name on route, and just default to passing trave…

…rse; add route_remainder_name argument to resource_url
  • Loading branch information...
commit c29603ed0d8fd0b55789eb8f975c901961864d66 1 parent 06f57f4
@mcdonc mcdonc authored
View
8 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
View
30 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
View
9 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):
"""
View
62 pyramid/tests/test_url.py
@@ -269,13 +269,12 @@ def test_resource_url_with_route_name_no_remainder_on_adapter(self):
# 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 @@ def test_resource_url_with_route_name_remainder_on_adapter(self):
# 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 @@ def test_resource_url_with_route_name_and_app_url(self):
# 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 @@ def test_resource_url_with_route_name_and_scheme_host_port_etc(self):
# 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 @@ def test_resource_url_with_route_name_and_scheme_host_port_etc(self):
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 @@ def test_resource_url_with_route_name_and_route_kwargs(self):
# 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 @@ def test_resource_url_with_route_name_and_route_kwargs(self):
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 @@ def test_resource_url_with_route_name_and_elements(self):
# 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 @@ def test_route_url_with_anchor_app_url_elements_and_query(self):
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 @@ def get_route(self, route_name):
class DummyRoute:
pregenerator = None
name = 'route'
- remainder_name = None
def __init__(self, result='/1/2/3'):
self.result = result
View
11 pyramid/tests/test_urldispatch.py
@@ -522,17 +522,6 @@ def test_generator_functional_oldstyle(self):
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):
""" """
View
55 pyramid/url.py
@@ -192,15 +192,6 @@ def route_url(self, route_name, *elements, **kw):
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 @@ def route_url(self, route_name, *elements, **kw):
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 @@ def route_url(self, route_name, *elements, **kw):
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 @@ def resource_url(self, resource, *elements, **kw):
: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 @@ def resource_url(self, resource, *elements, **kw):
'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 @@ def resource_url(self, resource, *elements, **kw):
**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 @@ def resource_url(self, resource, *elements, **kw):
# 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 @@ def resource_url(self, resource, *elements, **kw):
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:
View
7 pyramid/urldispatch.py
@@ -33,7 +33,6 @@ def __init__(self, name, pattern, factory=None, predicates=(),
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 generator(dict):
return result
return matcher, generator
-
-def get_remainder_name(pattern):
- match = star_at_end.search(pattern)
- if match:
- return match.groups()[0]
-
Please sign in to comment.
Something went wrong with that request. Please try again.