Enhancement: Route-Prefixing re Issue #406 #414

Closed
wants to merge 10 commits into
from
@@ -71,6 +71,7 @@
from pyramid.config.util import (
action_method,
ActionInfo,
+ route_pattern,
)
from pyramid.config.views import ViewsConfiguratorMixin
from pyramid.config.zca import ZCAConfiguratorMixin
@@ -105,7 +106,8 @@ class Configurator(
``authorization_policy``, ``renderers``, ``debug_logger``,
``locale_negotiator``, ``request_factory``, ``renderer_globals_factory``,
``default_permission``, ``session_factory``, ``default_view_mapper``,
- ``autocommit``, ``exceptionresponse_view`` and ``route_prefix``.
+ ``autocommit``, ``exceptionresponse_view``, ``route_prefix``, and
+ ``route_suffix``.
If the ``registry`` argument is passed as a non-``None`` value, it must
be an instance of the :class:`pyramid.registry.Registry` class
@@ -239,6 +241,11 @@ class Configurator(
:meth:`pyramid.config.Configurator.add_route` will have the specified path
prepended to their pattern. This parameter is new in Pyramid 1.2.
+ If ``route_suffix`` is passed, all routes added with
+ :meth:`pyramid.config.Configurator.add_route` will have the specified path
+ appended to their pattern. The ``route_suffix`` parameter is new in
+ Pyramid X.X.
+
If ``introspector`` is passed, it must be an instance implementing the
attributes and methods of :class:`pyramid.interfaces.IIntrospector`. If
``introspector`` is not passed (or is passed as ``None``), the default
@@ -272,6 +279,7 @@ def __init__(self,
autocommit=False,
exceptionresponse_view=default_exceptionresponse_view,
route_prefix=None,
+ route_suffix=None,
introspector=None,
):
if package is None:
@@ -283,6 +291,7 @@ def __init__(self,
self.registry = registry
self.autocommit = autocommit
self.route_prefix = route_prefix
+ self.route_suffix = route_suffix
if registry is None:
registry = Registry(self.package_name)
self.registry = registry
@@ -589,7 +598,7 @@ def commit(self):
self.action_state.execute_actions(introspector=self.introspector)
self.action_state = ActionState() # old actions have been processed
- def include(self, callable, route_prefix=None):
+ def include(self, callable, route_prefix=None, route_suffix=None):
"""Include a configuration callables, to support imperative
application extensibility.
@@ -681,25 +690,39 @@ def main(global_config, **settings):
pattern.
The ``route_prefix`` parameter is new as of Pyramid 1.2.
+
+ When the ``route_prefix`` parameter is provided to the outer-most
+ ``include`` it has a special configurative property. If ``route_prefix``
+ ends with a ``/`` then the route will end with a ``/``. If the
+ ``route_prefix`` does not end with a ``/`` then the route created will
+ also not end with a ``/``. In this way implementers may mount existing
+ callables or third-party modules with a slash-appended-style that
+ matches the rest of their application.
+
+ The above configurative behaviour of ``route_prefix`` is new as of
+ Pyramid 1.X.
+
+ The ``route_suffix`` parameter complements ``route_prefix``, allowing a
+ suffix to be appended to included routes. However ``route_suffix`` does
+ not have the same configurative property as ``route_prefix``, since
+ ``route_prefix`` ultimately decides whether the mounted routes
+ (including the suffix) will be slash-appended or not.
+
+ The ``route_suffix`` parameter is new as of Pyramid 1.X.
+
"""
# """ <-- emacs
action_state = self.action_state
- if route_prefix is None:
- route_prefix = ''
-
- old_route_prefix = self.route_prefix
- if old_route_prefix is None:
- old_route_prefix = ''
-
- route_prefix = '%s/%s' % (
- old_route_prefix.rstrip('/'),
- route_prefix.lstrip('/')
- )
- route_prefix = route_prefix.strip('/')
- if not route_prefix:
- route_prefix = None
+ if not route_prefix is None:
+ route_prefix = route_pattern(
+ (self.route_prefix or []) + [route_prefix]
+ )
+ if not route_suffix is None:
+ route_suffix = route_pattern(
+ [route_suffix] + (self.route_suffix or [])
+ )
c = self.maybe_dotted(callable)
module = inspect.getmodule(c)
@@ -720,6 +743,7 @@ def main(global_config, **settings):
package=package_of(module),
autocommit=self.autocommit,
route_prefix=route_prefix,
+ route_suffix=route_suffix,
)
configurator.basepath = os.path.dirname(sourcefile)
configurator.includepath = self.includepath + (spec,)
@@ -780,7 +804,8 @@ def with_context(cls, context):
registry=context.registry,
package=context.package,
autocommit=context.autocommit,
- route_prefix=context.route_prefix
+ route_prefix=context.route_prefix,
+ route_suffix=context.route_suffix,
)
configurator.basepath = context.basepath
configurator.includepath = context.includepath
@@ -798,6 +823,7 @@ def with_package(self, package):
package=package,
autocommit=self.autocommit,
route_prefix=self.route_prefix,
+ route_suffix=self.route_suffix,
)
configurator.basepath = self.basepath
configurator.includepath = self.includepath
@@ -15,6 +15,7 @@
action_method,
make_predicates,
as_sorted_tuple,
+ route_pattern,
)
class RoutesConfiguratorMixin(object):
@@ -368,8 +369,13 @@ def add_route(self,
if pattern is None:
raise ConfigurationError('"pattern" argument may not be None')
+ pattern = route_pattern(
+ (self.route_prefix or []) + [pattern] + (self.route_suffix or [])
+ )
if self.route_prefix:
- pattern = self.route_prefix.rstrip('/') + '/' + pattern.lstrip('/')
+ pattern.match_slash_style = True
+
+ pattern = str(pattern)
mapper = self.get_routes_mapper()
@@ -291,3 +291,29 @@ def as_sorted_tuple(val):
val = tuple(sorted(val))
return val
+class route_pattern(list):
+ """ Utility: centralise route-pattern operations.
+
+ If ``match_slash_style`` is ``True`` then the slash-style of the first-item
+ determines the slash-style of the route-pattern; if the first-item is
+ slash-appended then the route-pattern will be slash-appended, otherwise the
+ route-pattern will be non-slash-appended. """
+
+ def __init__(self, *args):
+ super(route_pattern, self).__init__(*args)
+ self.match_slash_style = False
+
+ def __str__(self):
+ l = None
+ for r in self:
+ if l is not None and self.match_slash_style:
+ r = r.rstrip('/')
+ if l.endswith('/'):
+ r = '%s/' % r
+ if l is None:
+ l = ''
+ if l and r:
+ l = '%s/%s' % (l.rstrip('/'), r.lstrip('/'))
+ else:
+ l = l + r
+ return l