diff options
| -rw-r--r-- | CHANGES.txt | 7 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_traversal.py | 49 | ||||
| -rw-r--r-- | repoze/bfg/traversal.py | 34 |
3 files changed, 54 insertions, 36 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 8fcd79818..5c6a4efc2 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -18,6 +18,13 @@ Backwards Incompatibilities Implementation Changes ---------------------- +- ``repoze.bfg.traversal.split_path`` now also handles decoding + path segments to unicode (for speed, because its results are + LRU cached). + +- ``repoze.bfg.traversal.step`` was made a method of the + ModelGraphTraverser. + - Use "precooked" Request subclasses (e.g. ``repoze.bfg.request.GETRequest``) that correspond to HTTP request methods within ``router.py`` when constructing a request diff --git a/repoze/bfg/tests/test_traversal.py b/repoze/bfg/tests/test_traversal.py index b1a94617e..e7b8158c4 100644 --- a/repoze/bfg/tests/test_traversal.py +++ b/repoze/bfg/tests/test_traversal.py @@ -7,21 +7,41 @@ class SplitPathTests(unittest.TestCase): from repoze.bfg.traversal import split_path return split_path(path) - def test_cleanPath_path_startswith_endswith(self): - self.assertEqual(self._callFUT('/foo/'), ['foo']) + def test_path_startswith_endswith(self): + self.assertEqual(self._callFUT('/foo/'), [u'foo']) - def test_cleanPath_empty_elements(self): - self.assertEqual(self._callFUT('foo///'), ['foo']) + def test_empty_elements(self): + self.assertEqual(self._callFUT('foo///'), [u'foo']) - def test_cleanPath_onedot(self): - self.assertEqual(self._callFUT('foo/./bar'), ['foo', 'bar']) + def test_onedot(self): + self.assertEqual(self._callFUT('foo/./bar'), [u'foo', u'bar']) - def test_cleanPath_twodots(self): - self.assertEqual(self._callFUT('foo/../bar'), ['bar']) + def test_twodots(self): + self.assertEqual(self._callFUT('foo/../bar'), [u'bar']) - def test_cleanPath_element_urllquoted(self): + def test_element_urllquoted(self): self.assertEqual(self._callFUT('/foo/space%20thing/bar'), - ['foo', 'space thing', 'bar']) + [u'foo', u'space thing', u'bar']) + + def test_segments_are_unicode(self): + result = self._callFUT('/foo/bar') + self.assertEqual(type(result[0]), unicode) + self.assertEqual(type(result[1]), unicode) + + def test_utf8(self): + import urllib + la = 'La Pe\xc3\xb1a' + encoded = urllib.quote(la) + decoded = unicode(la, 'utf-8') + path = '/'.join([encoded, encoded]) + self.assertEqual(self._callFUT(path), [decoded, decoded]) + + def test_utf16(self): + import urllib + la = unicode('La Pe\xc3\xb1a', 'utf-8').encode('utf-16') + encoded = urllib.quote(la) + path = '/'.join([encoded, encoded]) + self.assertRaises(TypeError, self._callFUT, path) class ModelGraphTraverserTests(unittest.TestCase): def setUp(self): @@ -171,12 +191,6 @@ class ModelGraphTraverserTests(unittest.TestCase): self.assertRaises(TypeError, policy, environ) def test_non_utf8_path_segment_settings_unicode_path_segments_fails(self): - defaultkw = {'unicode_path_segments':True} - settings = DummySettings(**defaultkw) - from repoze.bfg.interfaces import ISettings - import zope.component - gsm = zope.component.getGlobalSiteManager() - gsm.registerUtility(settings, ISettings) foo = DummyContext() root = DummyContext(foo) policy = self._makeOne(root) @@ -413,6 +427,3 @@ class DummyContext(object): class DummyRequest: application_url = 'http://example.com:5432' # app_url never ends with slash -class DummySettings: - def __init__(self, **kw): - self.__dict__.update(kw) diff --git a/repoze/bfg/traversal.py b/repoze/bfg/traversal.py index c86e5b457..4c1cb0eb3 100644 --- a/repoze/bfg/traversal.py +++ b/repoze/bfg/traversal.py @@ -114,24 +114,14 @@ def split_path(path): elif segment == '..': del clean[-1] else: + try: + segment = segment.decode('utf-8') + except UnicodeDecodeError: + raise TypeError('Could not decode path segment %r using the ' + 'UTF-8 decoding scheme' % segment) clean.append(segment) return clean -def step(ob, name, default): - try: - name = name.decode('utf-8') - except UnicodeDecodeError: - raise TypeError('Could not decode path segment "%s" using the ' - 'UTF-8 decoding scheme' % name) - if name.startswith('@@'): - return name[2:], default - if not hasattr(ob, '__getitem__'): - return name, default - try: - return name, ob[name] - except KeyError: - return name, default - _marker = object() class ModelGraphTraverser(object): @@ -145,14 +135,14 @@ class ModelGraphTraverser(object): path = environ.get('PATH_INFO', '/') path = list(split_path(path)) locatable = self.locatable - _step = step + step = self._step ob = self.root name = '' while path: segment = path.pop(0) - segment, next = _step(ob, segment, _marker) + segment, next = step(ob, segment, _marker) if next is _marker: name = segment break @@ -162,6 +152,16 @@ class ModelGraphTraverser(object): return ob, name, path + def _step(self, ob, name, default): + if name.startswith('@@'): + return name[2:], default + if not hasattr(ob, '__getitem__'): + return name, default + try: + return name, ob[name] + except KeyError: + return name, default + class RoutesModelTraverser(object): classProvides(ITraverserFactory) implements(ITraverser) |
