diff options
| author | Chris McDonough <chrism@plope.com> | 2012-01-05 05:54:54 -0500 |
|---|---|---|
| committer | Chris McDonough <chrism@plope.com> | 2012-01-05 05:54:54 -0500 |
| commit | 78535833058ba2c01f44eef311f8bacdb1ee4bc7 (patch) | |
| tree | 3546e720b25893f33881f0747e412707b003c2af | |
| parent | 5864016d6432d19fe9588f57d5b1ad95aac9c6c8 (diff) | |
| download | pyramid-78535833058ba2c01f44eef311f8bacdb1ee4bc7.tar.gz pyramid-78535833058ba2c01f44eef311f8bacdb1ee4bc7.tar.bz2 pyramid-78535833058ba2c01f44eef311f8bacdb1ee4bc7.zip | |
- String values passed to ``route_url`` or ``route_path`` that are meant to
replace "remainder" matches will now be URL-quoted except for embedded
slashes. For example::
config.add_route('remain', '/foo*remainder')
request.route_path('remain', remainder='abc / def')
# -> '/foo/abc%20/%20def'
Previously string values passed as remainder replacements were tacked on
untouched, without any URL-quoting. But this doesn't really work logically
if the value passed is Unicode (raw unicode cannot be placed in a URL or in
a path) and it is inconsistent with the rest of the URL generation
machinery if the value is a string (it won't be quoted unless by the
caller).
Some folks will have been relying on the older behavior to tack on query
string elements and anchor portions of the URL; sorry, you'll need to
change your code to use the ``_query`` and/or ``_anchor`` arguments to
``route_path`` or ``route_url`` to do this now.
- If you pass a bytestring that contains non-ASCII characters to
``add_route`` as a pattern, it will now fail at startup time. Use Unicode
instead.
| -rw-r--r-- | pyramid/tests/test_urldispatch.py | 24 | ||||
| -rw-r--r-- | pyramid/urldispatch.py | 23 |
2 files changed, 42 insertions, 5 deletions
diff --git a/pyramid/tests/test_urldispatch.py b/pyramid/tests/test_urldispatch.py index 856bdcb78..286cc1573 100644 --- a/pyramid/tests/test_urldispatch.py +++ b/pyramid/tests/test_urldispatch.py @@ -365,6 +365,30 @@ class TestCompileRoute(unittest.TestCase): # should be a native string self.assertEqual(type(result), str) + def test_highorder_pattern_utf8(self): + pattern = b'/La Pe\xc3\xb1a/{city}' + self.assertRaises(ValueError, self._callFUT, pattern) + + def test_generate_with_string_remainder_and_unicode_replacement(self): + pattern = text_(b'/abc*remainder', 'utf-8') + _, generator = self._callFUT(pattern) + result = generator( + {'remainder': text_(b'/Qu\xc3\xa9bec/La Pe\xc3\xb1a', 'utf-8')} + ) + self.assertEqual(result, '/abc/Qu%C3%A9bec/La%20Pe%C3%B1a') + # should be a native string + self.assertEqual(type(result), str) + + def test_generate_with_string_remainder_and_nonstring_replacement(self): + pattern = text_(b'/abc/*remainder', 'utf-8') + _, generator = self._callFUT(pattern) + result = generator( + {'remainder': None} + ) + self.assertEqual(result, '/abc/None') + # should be a native string + self.assertEqual(type(result), str) + class TestCompileRouteFunctional(unittest.TestCase): def matches(self, pattern, path, expected): from pyramid.urldispatch import _compile_route diff --git a/pyramid/urldispatch.py b/pyramid/urldispatch.py index cb0e57c4d..fcd6c55c9 100644 --- a/pyramid/urldispatch.py +++ b/pyramid/urldispatch.py @@ -111,7 +111,13 @@ def _compile_route(route): # want to accept bytestrings with high-order characters in them here as # we have no idea what the encoding represents. if route.__class__ is not text_type: - route = text_(route, 'ascii') + try: + route = text_(route, 'ascii') + except UnicodeDecodeError: + raise ValueError( + 'The pattern value passed to add_route must be ' + 'either a Unicode string or a plain string without ' + 'any non-ASCII characters (you provided %r).' % route) if old_route_re.search(route) and not route_re.search(route): route = old_route_re.sub(update_pattern, route) @@ -202,13 +208,20 @@ def _compile_route(route): if v.__class__ is text_type: # url_quote below needs bytes, not unicode on Py2 v = v.encode('utf-8') - if k == remainder and is_nonstr_iter(v): - v = '/'.join([quote_path_segment(x) for x in v]) # native - elif k != remainder: + + if k == remainder: + # a stararg argument + if is_nonstr_iter(v): + v = '/'.join([quote_path_segment(x) for x in v]) # native + else: + if v.__class__ not in string_types: + v = str(v) + v = quote_path_segment(v, safe='/') + else: if v.__class__ not in string_types: v = str(v) # v may be bytes (py2) or native string (py3) - v = url_quote(v, safe='') # defaults to utf8 encoding on py3 + v = quote_path_segment(v) # at this point, the value will be a native string newdict[k] = v |
