Merge branch 'master' of github.com:Pylons/pyramid
| | |
| | | - 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 |
| | | --------- |
| | | |
| | |
| | | - Tom Lazar, 2013/08/15 |
| | | |
| | | - Andreas Zeidler, 2013/08/15 |
| | | |
| | | - Matthew Wilkes, 2013/08/23 |
| | |
| | | def pluralize(singular, plural, n, domain=None, mapping=None): |
| | | ... |
| | | |
| | | The ``singular`` and ``plural`` arguments should each be a Unicode |
| | | value representing a :term:`message identifier`. ``n`` should be an |
| | | integer. ``domain`` should be a :term:`translation domain`, and |
| | | ``mapping`` should be a dictionary that is used for *replacement |
| | | value* interpolation of the translated string. If ``n`` is plural |
| | | for the current locale, ``pluralize`` will return a Unicode |
| | | translation for the message id ``plural``, otherwise it will return a |
| | | Unicode translation for the message id ``singular``. |
| | | |
| | | The arguments provided as ``singular`` and/or ``plural`` may also be |
| | | :term:`translation string` objects, but the domain and mapping |
| | | information attached to those objects is ignored. |
| | | The simplest case is the ``singular`` and ``plural`` arguments being passed as |
| | | unicode literals. This returns the appropriate literal according to the locale |
| | | pluralization rules for the number ``n``, and interpolates ``mapping``. |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | translated = localizer.pluralize('Item', 'Items', 1, 'mydomain') |
| | | # ... use translated ... |
| | | |
| | | However, for support of other languages, the ``singular`` argument should |
| | | be a Unicode value representing a :term:`message identifier`. In this |
| | | case the ``plural`` value is ignored. |
| | | ``domain`` should be a :term:`translation domain`, and |
| | | ``mapping`` should be a dictionary that is used for *replacement |
| | | value* interpolation of the translated string. |
| | | |
| | | The value of ``n`` will be used to find the appropriate plural form for the |
| | | current language and ``pluralize`` will return a Unicode translation for the |
| | | message id ``singular``. The message file must have defined ``singular`` as a |
| | | translation with plural forms. |
| | | |
| | | The argument provided as ``singular`` may be a :term:`translation string` |
| | | object, but the domain and mapping information attached is ignored. |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | from pyramid.i18n import get_localizer |
| | | |
| | | def aview(request): |
| | | localizer = get_localizer(request) |
| | | num = 1 |
| | | translated = localizer.pluralize(_('item_plural', default="${number} items"), |
| | | None, num, 'mydomain', mapping={'number':num}) |
| | | |
| | | The corresponding message catalog must have language plural definitions and |
| | | plural alternatives set. |
| | | |
| | | .. code-block:: text |
| | | :linenos: |
| | | |
| | | "Plural-Forms: nplurals=3; plural=n==0 ? 0 : n==1 ? 1 : 2;" |
| | | |
| | | msgid "item_plural" |
| | | msgid_plural "" |
| | | msgstr[0] "No items" |
| | | msgstr[1] "${number} item" |
| | | msgstr[2] "${number} items" |
| | | |
| | | More information on complex plurals can be found in the `gettext documentation |
| | | <https://www.gnu.org/savannah-checkouts/gnu/gettext/manual/html_node/Plural-forms.html>`_. |
| | | |
| | | .. index:: |
| | | single: locale name |
| | | single: get_locale_name |
| | |
| | | |
| | | .. _route_pattern_syntax: |
| | | |
| | | |
| | | Route Pattern Syntax |
| | | ~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | |
| | | .. code-block:: text |
| | | |
| | | /{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 |
| | |
| | | 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" |
| | | |
| | | Most pattern replacements and calls to |
| | | :meth:`pyramid.request.Request.route_url` will work as expected. However, calls |
| | | to :meth:`pyramid.request.Request.route_path` against external patterns will |
| | | raise an exception, and passing ``_app_url`` to |
| | | :meth:`~pyramid.request.Request.route_url` to generate a URL against a route |
| | | that has an external pattern will also raise an exception. |
| | | |
| | | .. index:: |
| | | single: redirecting to slash-appended routes |
| | | |
| | |
| | | import warnings |
| | | |
| | | from pyramid.compat import urlparse |
| | | from pyramid.interfaces import ( |
| | | IRequest, |
| | | IRouteRequest, |
| | |
| | | if pattern is None: |
| | | raise ConfigurationError('"pattern" argument may not be None') |
| | | |
| | | if self.route_prefix: |
| | | # 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) |
| | | if parsed.hostname: |
| | | pattern = parsed.path |
| | | |
| | | original_pregenerator = pregenerator |
| | | def external_url_pregenerator(request, elements, kw): |
| | | if '_app_url' in kw: |
| | | raise ValueError( |
| | | 'You cannot generate a path to an external route ' |
| | | 'pattern via request.route_path nor pass an _app_url ' |
| | | 'to request.route_url when generating a URL for an ' |
| | | 'external route pattern (pattern was "%s") ' % |
| | | (pattern,) |
| | | ) |
| | | if '_scheme' in kw: |
| | | scheme = kw['_scheme'] |
| | | elif parsed.scheme: |
| | | scheme = parsed.scheme |
| | | else: |
| | | scheme = request.scheme |
| | | kw['_app_url'] = '{0}://{1}'.format(scheme, parsed.netloc) |
| | | |
| | | if original_pregenerator: |
| | | elements, kw = original_pregenerator( |
| | | request, elements, kw) |
| | | return elements, kw |
| | | |
| | | pregenerator = external_url_pregenerator |
| | | static = True |
| | | |
| | | elif self.route_prefix: |
| | | pattern = self.route_prefix.rstrip('/') + '/' + pattern.lstrip('/') |
| | | |
| | | mapper = self.get_routes_mapper() |
| | |
| | | * 303 - HTTPSeeOther |
| | | * 304 - HTTPNotModified |
| | | * 305 - HTTPUseProxy |
| | | * 306 - Unused (not implemented, obviously) |
| | | * 307 - HTTPTemporaryRedirect |
| | | HTTPError |
| | | HTTPClientError |
| | |
| | | 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.assertRaises(ValueError, |
| | | request.route_url, 'acme', foo='bar', _app_url='http://fakeme.com') |
| | | |
| | | 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.assertRaises(ValueError, request.route_path, 'acme', foo='bar') |
| | | |
| | | def test_generate_external_url_with_pregenerator(self): |
| | | def pregenerator(request, elements, kw): |
| | | kw['_query'] = {'q': 'foo'} |
| | | return elements, kw |
| | | self.config.add_route('acme', 'https://acme.org/path/{foo}', |
| | | pregenerator=pregenerator) |
| | | request = self._makeRequest() |
| | | request.registry = self.config.registry |
| | | self.assertEqual( |
| | | request.route_url('acme', foo='bar'), |
| | | 'https://acme.org/path/bar?q=foo') |
| | | |
| | | def test_external_url_with_route_prefix(self): |
| | | def includeme(config): |
| | | config.add_route('acme', '//acme.org/{foo}') |
| | | self.config.include(includeme, route_prefix='some_prefix') |
| | | request = self._makeRequest() |
| | | request.registry = self.config.registry |
| | | self.assertEqual( |
| | | request.route_url('acme', foo='bar'), |
| | | 'http://acme.org/bar') |
| | | |
| | | class DummyContext(object): |
| | | def __init__(self, next=None): |
| | | self.next = next |