summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.txt7
-rw-r--r--repoze/bfg/tests/test_traversal.py49
-rw-r--r--repoze/bfg/traversal.py34
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)