Skip to content

Commit

Permalink
use append_slash=False on add_route to specify no trailing slash
Browse files Browse the repository at this point in the history
  • Loading branch information
mmerickel committed Nov 12, 2018
1 parent ebb56eb commit 22ae59a
Show file tree
Hide file tree
Showing 6 changed files with 40 additions and 26 deletions.
16 changes: 4 additions & 12 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ Features

- 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
``with config.route_prefix_context(...)`` by specifying
``append_slash=False`` when adding a route with an empty pattern. For
example, ``config.add_route(name, '/', append_slash=False, ...)``.
See https://github.com/Pylons/pyramid/pull/3420

Bug Fixes
---------
Expand Down Expand Up @@ -74,15 +75,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
---------------------

4 changes: 4 additions & 0 deletions docs/glossary.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
10 changes: 4 additions & 6 deletions docs/narr/urldispatch.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -998,23 +998,21 @@ 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 ``append_slash=False`` to the call to ``add_route``.

.. code-block:: python
:linenos:
from pyramid.config import Configurator
def users_include(config):
config.add_route('show_users', '')
config.add_route('show_users', '', append_slash=False)
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
Expand Down
4 changes: 3 additions & 1 deletion src/pyramid/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -602,7 +602,9 @@ def main(global_config, **settings):
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
Expand Down
28 changes: 23 additions & 5 deletions src/pyramid/config/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def add_route(
path=None,
pregenerator=None,
static=False,
append_slash=None,
**predicates
):
""" Add a :term:`route configuration` to the current
Expand Down Expand Up @@ -139,6 +140,20 @@ def add_route(
.. versionadded:: 1.1
append_slash
This argument is normally ``None`` and only has an effect when
adding the route with an active :term:`route prefix` and the pattern
is ``''`` or ``'/'``. By default, a slash (``/``) is always appended
to the route such that if the ``route_prefix='/users'``, the
resulting route pattern would be ``/users/``. Sometimes this behavior
is not necessary, and a pattern without a trailing slash is
preferred. In these cases, ``append_slash=False`` may be supplied
such that when a route prefix is active, the resulting route pattern
would be ``/users``..
.. versionadded:: 2.0
Predicate Arguments
pattern
Expand Down Expand Up @@ -364,10 +379,14 @@ def external_url_pregenerator(request, elements, kw):
static = True

elif self.route_prefix:
if pattern == '':
if (pattern == '' or pattern == '/') and (
append_slash is not None and not append_slash
):
pattern = self.route_prefix.rstrip('/')
else:
pattern = self.route_prefix.rstrip('/') + '/' + pattern.lstrip('/')
pattern = (
self.route_prefix.rstrip('/') + '/' + pattern.lstrip('/')
)

mapper = self.get_routes_mapper()

Expand Down Expand Up @@ -517,9 +536,8 @@ def get_routes_mapper(self):

@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.
Expand Down
4 changes: 2 additions & 2 deletions tests/test_config/test_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ def test_add_route_with_route_prefix(self):
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_route_prefix_with_append_slash_False(self):
config = self._makeOne(autocommit=True)
config.route_prefix = 'root'
config.add_route('name', '')
config.add_route('name', '', append_slash=False)
self._assertRoute(config, 'name', 'root')

def test_add_route_with_root_slash_with_route_prefix(self):
Expand Down

0 comments on commit 22ae59a

Please sign in to comment.