summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Merickel <michael@merickel.org>2013-08-19 21:53:10 -0500
committerMichael Merickel <michael@merickel.org>2013-08-19 21:53:10 -0500
commitc14c8d7c86bd5cad207fab9acc053dc723dd2acf (patch)
tree35dc7e54a4a739a0f5abf37f4e80c7f332c0b40c
parent4aec433291dc7b0c08d27fe6352ecf7585052e73 (diff)
parenta6aa55360986b008b086551ea7414d3bf6e054e9 (diff)
downloadpyramid-c14c8d7c86bd5cad207fab9acc053dc723dd2acf.tar.gz
pyramid-c14c8d7c86bd5cad207fab9acc053dc723dd2acf.tar.bz2
pyramid-c14c8d7c86bd5cad207fab9acc053dc723dd2acf.zip
Merge branch 'feature.issue611-external-static-routes' of tomster/pyramid into feature.external-routes
-rw-r--r--CHANGES.txt4
-rw-r--r--docs/narr/urldispatch.rst39
-rw-r--r--pyramid/config/routes.py20
-rw-r--r--pyramid/tests/test_url.py51
4 files changed, 114 insertions, 0 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index b805a12a0..b4fe60085 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -142,6 +142,10 @@ Features
- The ``alchemy`` scaffold tests now provide better coverage. See
https://github.com/Pylons/pyramid/pull/1029
+- The ``pyramid.config.Configurator.add_route`` method now supports being called
+ with an external URL as pattern. See https://github.com/Pylons/pyramid/issues/611
+ for more information.
+
Bug Fixes
---------
diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst
index 310c160c0..8f03b1080 100644
--- a/docs/narr/urldispatch.rst
+++ b/docs/narr/urldispatch.rst
@@ -105,6 +105,7 @@ to using the previous combination of ``add_route`` and ``add_view``.
.. _route_pattern_syntax:
+
Route Pattern Syntax
~~~~~~~~~~~~~~~~~~~~
@@ -127,6 +128,10 @@ and:
/{foo}/bar/baz
+If a pattern is a valid URL it won't be ever matched against an incoming
+request. Instead it can be useful for generating external URLs. See
+:ref:`External routes <external_route_narr>` for details.
+
A pattern segment (an individual item between ``/`` characters in the
pattern) may either be a literal string (e.g. ``foo``) *or* it may be a
replacement marker (e.g. ``{foo}``) or a certain combination of both. A
@@ -754,9 +759,43 @@ other non-``name`` and non-``pattern`` arguments to
exception to this rule is use of the ``pregenerator`` argument, which is not
ignored when ``static`` is ``True``.
+:ref:`External routes <external_route_narr>` are implicitly static.
+
.. versionadded:: 1.1
the ``static`` argument to :meth:`~pyramid.config.Configurator.add_route`
+.. _external_route_narr:
+
+
+External Routes
+---------------
+
+.. versionadded:: 1.5
+
+Route patterns that are valid URLs, are treated as external routes. Like
+:ref:`static routes <static_route_narr>` they are useful for URL generation
+purposes only and are never considered for matching at request time.
+
+.. code-block:: python
+ :linenos:
+
+ >>> config = Configurator()
+ >>> config.add_route('youtube', 'https://youtube.com/watch/{video_id}')
+ ...
+ >>> request.route_url('youtube', video_id='oHg5SJYRHA0')
+ >>> "https://youtube.com/watch/oHg5SJYRHA0"
+
+All pattern replacements and calls to
+:meth:`pyramid.request.Request.route_url` will work as expected. Note that
+:meth:`pyramid.request.Request.route_path` will also just return the external
+URLs path part.
+
+.. note::
+
+ The external URL feature is implemented with a :term:`pregenerator` so you
+ cannot use both with the same route.
+
+
.. index::
single: redirecting to slash-appended routes
diff --git a/pyramid/config/routes.py b/pyramid/config/routes.py
index c86e4a2dd..0e11428db 100644
--- a/pyramid/config/routes.py
+++ b/pyramid/config/routes.py
@@ -1,4 +1,5 @@
import warnings
+from urlparse import urlparse
from pyramid.interfaces import (
IRequest,
@@ -387,6 +388,25 @@ class RoutesConfiguratorMixin(object):
if self.route_prefix:
pattern = self.route_prefix.rstrip('/') + '/' + pattern.lstrip('/')
+ if pregenerator is None:
+ parsed = urlparse(pattern)
+ if parsed.hostname:
+ pattern = parsed.path
+
+ def external_url_pregenerator(request, elements, kw):
+ if not '_app_url' in kw:
+ if '_scheme' in kw and parsed.scheme != kw['_scheme']:
+ scheme = kw['_scheme']
+ elif parsed.scheme:
+ scheme = parsed.scheme
+ else:
+ scheme = request.scheme
+ kw['_app_url'] = '{0}://{1}'.format(
+ scheme, parsed.netloc)
+ return elements, kw
+
+ pregenerator = external_url_pregenerator
+
mapper = self.get_routes_mapper()
introspectables = []
diff --git a/pyramid/tests/test_url.py b/pyramid/tests/test_url.py
index bf1c514c5..0e006f447 100644
--- a/pyramid/tests/test_url.py
+++ b/pyramid/tests/test_url.py
@@ -1027,6 +1027,57 @@ class Test_current_route_path(unittest.TestCase):
self.assertEqual(request.elements, ('abc',))
self.assertEqual(request.kw, {'_anchor':'abc'})
+class Test_external_static_url_integration(unittest.TestCase):
+
+ def setUp(self):
+ self.config = testing.setUp()
+
+ def tearDown(self):
+ testing.tearDown()
+
+ def _makeRequest(self):
+ from pyramid.request import Request
+ return Request.blank('/')
+
+ def test_generate_external_url(self):
+ self.config.add_route('acme', 'https://acme.org/path/{foo}')
+ request = self._makeRequest()
+ request.registry = self.config.registry
+ self.assertEqual(request.route_url('acme', foo='bar'),
+ 'https://acme.org/path/bar')
+
+ def test_generate_external_url_without_scheme(self):
+ self.config.add_route('acme', '//acme.org/path/{foo}')
+ request = self._makeRequest()
+ request.registry = self.config.registry
+ self.assertEqual(request.route_url('acme', foo='bar'),
+ 'http://acme.org/path/bar')
+
+
+ def test_generate_external_url_with_explicit_scheme(self):
+ self.config.add_route('acme', '//acme.org/path/{foo}')
+ request = self._makeRequest()
+ request.registry = self.config.registry
+ self.assertEqual(request.route_url('acme', foo='bar', _scheme='https'),
+ 'https://acme.org/path/bar')
+
+
+ def test_generate_external_url_with_explicit_app_url(self):
+ self.config.add_route('acme', 'http://acme.org/path/{foo}')
+ request = self._makeRequest()
+ request.registry = self.config.registry
+ self.assertEqual(request.route_url('acme', foo='bar', _app_url='http://fakeme.com'),
+ 'http://fakeme.com/path/bar')
+
+
+ def test_generate_external_url_route_path(self):
+ self.config.add_route('acme', 'https://acme.org/path/{foo}')
+ request = self._makeRequest()
+ request.registry = self.config.registry
+ self.assertEqual(request.route_path('acme', foo='bar'),
+ '/path/bar')
+
+
class DummyContext(object):
def __init__(self, next=None):
self.next = next