summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.rst30
-rw-r--r--docs/glossary.rst4
-rw-r--r--docs/narr/urldispatch.rst10
-rw-r--r--src/pyramid/config/__init__.py4
-rw-r--r--src/pyramid/config/routes.py39
-rw-r--r--tests/test_config/test_routes.py16
6 files changed, 74 insertions, 29 deletions
diff --git a/CHANGES.rst b/CHANGES.rst
index 53bf0ab60..1790bb69f 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -12,11 +12,22 @@ Features
documentation for more information about why this change was made.
See https://github.com/Pylons/pyramid/pull/3413
-- It is now possible to declare a route to not contain a trailing slash
- when it is mounted via ``config.include(..., route_prefix=...)`` or via
- ``with config.route_prefix_context(...)`` by specifying a ``pattern`` of
- ``''``. This change is backward incompatible, see the notes below.
- See https://github.com/Pylons/pyramid/pull/3414
+- It is now possible to control whether a route pattern contains a trailing
+ slash when it is composed with a route prefix using
+ ``config.include(..., route_prefix=...)`` or
+ ``with config.route_prefix_context(...)``. This can be done by specifying
+ an empty pattern and setting the new argument
+ ``inherit_slash=True``. For example::
+
+ with config.route_prefix_context('/users'):
+ config.add_route('users', '', inherit_slash=True)
+
+ In the example, the resulting pattern will be ``/users``. Similarly, if the
+ route prefix were ``/users/`` then the final pattern would be ``/users/``.
+ If the ``pattern`` was ``'/'``, then the final pattern would always be
+ ``/users/``. This new setting is only available if the pattern supplied
+ to ``add_route`` is the empty string (``''``).
+ See https://github.com/Pylons/pyramid/pull/3420
Bug Fixes
---------
@@ -74,15 +85,6 @@ Backward Incompatibilities
documentation for more information about why this change was made.
See https://github.com/Pylons/pyramid/pull/3413
-- Changed the URL generation and matching for a route with a ``pattern`` of
- ``''`` when the route is mounted with a ``route_prefix`` via either
- ``config.include(..., route_prefix=...)`` or
- ``with config.route_prefix_context(...)``. This route will now match a bare
- URL without a trailing slash. To preserve the old behavior, set the
- ``pattern`` to ``'/'`` instead of the empty string and the URL will contain
- a trailing slash. This only affects mounted routes using ``route_prefix``.
- See https://github.com/Pylons/pyramid/pull/3414
-
Documentation Changes
---------------------
diff --git a/docs/glossary.rst b/docs/glossary.rst
index 4668efe6d..f42b298df 100644
--- a/docs/glossary.rst
+++ b/docs/glossary.rst
@@ -1203,3 +1203,7 @@ Glossary
A media type is a nested structure containing a top-level type and a subtype.
Optionally, a media type can also contain parameters specific to the type.
See :rfc:`6838` for more information about media types.
+
+ route prefix
+ A route prefix is a path prefix that is prepended to any routes that are configured while it is active.
+ A route prefix can be set via :meth:`pyramid.config.Configurator.include` or :meth:`pyramid.config.Configurator.route_prefix_context`.
diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst
index 701326fab..3b737b46d 100644
--- a/docs/narr/urldispatch.rst
+++ b/docs/narr/urldispatch.rst
@@ -971,7 +971,7 @@ application from small and potentially reusable components.
The :meth:`pyramid.config.Configurator.include` method accepts an argument
named ``route_prefix`` which can be useful to authors of URL-dispatch-based
applications. If ``route_prefix`` is supplied to the include method, it must
-be a string. This string represents a route prefix that will be prepended to
+be a string. This string represents a :term:`route prefix` that will be prepended to
all route patterns added by the *included* configuration. Any calls to
:meth:`pyramid.config.Configurator.add_route` within the included callable will
have their pattern prefixed with the value of ``route_prefix``. This can be
@@ -998,8 +998,7 @@ then only match if the URL path is ``/users/show``, and when the
:meth:`pyramid.request.Request.route_url` function is called with the route
name ``show_users``, it will generate a URL with that same path.
-To create a prefixed route that matches requests to the ``route_prefix``
-without a trailing slash set the route ``pattern`` to an empty string.
+To create a route that matches requests to the ``route_prefix`` without a trailing slash, pass ``inherit_slash=True`` to the call to ``add_route``.
.. code-block:: python
:linenos:
@@ -1007,14 +1006,13 @@ without a trailing slash set the route ``pattern`` to an empty string.
from pyramid.config import Configurator
def users_include(config):
- config.add_route('show_users', '')
+ config.add_route('show_users', '', inherit_slash=True)
def main(global_config, **settings):
config = Configurator()
config.include(users_include, route_prefix='/users')
-The above configuration will match ``/users`` instead of ``/users/`` if a slash
-had been supplied to the :meth:`pyramid.config.Configurator.add_route` function.
+The above configuration will match ``/users`` instead of ``/users/``.
Route prefixes are recursive, so if a callable executed via an include itself
turns around and includes another callable, the second-level route prefix will
diff --git a/src/pyramid/config/__init__.py b/src/pyramid/config/__init__.py
index 00c3e6a02..475f0d9a2 100644
--- a/src/pyramid/config/__init__.py
+++ b/src/pyramid/config/__init__.py
@@ -602,7 +602,9 @@ class Configurator(
configuration conflict by registering something with the same
configuration parameters.
- If the ``route_prefix`` is supplied, it must be a string. Any calls
+ If the ``route_prefix`` is supplied, it must be a string and will
+ have a similar effect to using
+ :meth:`pyramid.config.Configurator.route_prefix_context`. Any calls
to :meth:`pyramid.config.Configurator.add_route` within the included
callable will have their pattern prefixed with the value of
``route_prefix``. This can be used to help mount a set of routes at a
diff --git a/src/pyramid/config/routes.py b/src/pyramid/config/routes.py
index 7bddf4e67..e7e981df0 100644
--- a/src/pyramid/config/routes.py
+++ b/src/pyramid/config/routes.py
@@ -40,6 +40,7 @@ class RoutesConfiguratorMixin(object):
path=None,
pregenerator=None,
static=False,
+ inherit_slash=None,
**predicates
):
""" Add a :term:`route configuration` to the current
@@ -139,6 +140,26 @@ class RoutesConfiguratorMixin(object):
.. versionadded:: 1.1
+ inherit_slash
+
+ This argument can only be used when the ``pattern`` is an empty
+ string (``''``). By default, the composed route pattern will always
+ includes a trailing slash, but this argument provides a way to
+ opt-out if both, you and the integrator (the developer setting the
+ :term:`route prefix`), agree that the pattern should not contain
+ a trailing slash. For example:
+
+ .. code-block:: python
+
+ with config.route_prefix_context('/users'):
+ config.add_route('users', '', inherit_slash=True)
+
+ In this example, the resulting route pattern will be ``/users``.
+ Alternatively, if the route prefix were ``/users/`` then the
+ resulting route pattern would be ``/users/``.
+
+ .. versionadded:: 2.0
+
Predicate Arguments
pattern
@@ -329,6 +350,11 @@ class RoutesConfiguratorMixin(object):
if pattern is None:
raise ConfigurationError('"pattern" argument may not be None')
+ if inherit_slash and pattern != '':
+ raise ConfigurationError(
+ '"inherit_slash" may only be used with an empty pattern'
+ )
+
# check for an external route; an external route is one which is
# is a full url (e.g. 'http://example.com/{id}')
parsed = urlparse.urlparse(pattern)
@@ -364,10 +390,12 @@ class RoutesConfiguratorMixin(object):
static = True
elif self.route_prefix:
- if pattern == '':
- pattern = self.route_prefix.rstrip('/')
+ if pattern == '' and inherit_slash:
+ pattern = self.route_prefix
else:
- pattern = self.route_prefix.rstrip('/') + '/' + pattern.lstrip('/')
+ pattern = (
+ self.route_prefix.rstrip('/') + '/' + pattern.lstrip('/')
+ )
mapper = self.get_routes_mapper()
@@ -517,9 +545,8 @@ class RoutesConfiguratorMixin(object):
@contextlib.contextmanager
def route_prefix_context(self, route_prefix):
- """ Return this configurator with the
- :attr:`pyramid.config.Configurator.route_prefix` attribute mutated to
- include the new ``route_prefix``.
+ """
+ Return this configurator with a :term:`route prefix` temporarily set.
When the context exits, the ``route_prefix`` is reset to the original.
diff --git a/tests/test_config/test_routes.py b/tests/test_config/test_routes.py
index 1c1ed6700..e6540c343 100644
--- a/tests/test_config/test_routes.py
+++ b/tests/test_config/test_routes.py
@@ -54,10 +54,22 @@ class RoutesConfiguratorMixinTests(unittest.TestCase):
config.add_route('name', 'path')
self._assertRoute(config, 'name', 'root/path')
- def test_add_route_with_empty_string_with_route_prefix(self):
+ def test_add_route_with_inherit_errors(self):
+ from pyramid.exceptions import ConfigurationError
+
+ config = self._makeOne(autocommit=True)
+ self.assertRaises(
+ ConfigurationError,
+ config.add_route,
+ 'name',
+ '/',
+ inherit_slash=True,
+ )
+
+ def test_add_route_with_route_prefix_with_inherit_slash(self):
config = self._makeOne(autocommit=True)
config.route_prefix = 'root'
- config.add_route('name', '')
+ config.add_route('name', '', inherit_slash=True)
self._assertRoute(config, 'name', 'root')
def test_add_route_with_root_slash_with_route_prefix(self):