summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Madden <jason+github@nextthought.com>2016-08-05 05:58:45 -0500
committerGitHub <noreply@github.com>2016-08-05 05:58:45 -0500
commit2ba32e65f7156b10675256e81aea837f693e515e (patch)
tree436cdab2591fe776a6844c44b49d53f6da06ff98
parent1bf6305c14f8e7d4c683b30cd8a7e72c4cf2049e (diff)
parent2c7af36ae6217a02e614d8af14ca06a20b7147b1 (diff)
downloadzope-traversing-2ba32e65f7156b10675256e81aea837f693e515e.tar.gz
Merge pull request #3 from zopefoundation/handle-unicode-gracefully
Gracefully handle UnicodeEncodeError on Python 2.
-rw-r--r--CHANGES.rst3
-rw-r--r--src/zope/traversing/adapters.py17
-rw-r--r--src/zope/traversing/tests/test_conveniencefunctions.py17
-rw-r--r--src/zope/traversing/tests/test_traverser.py4
4 files changed, 39 insertions, 2 deletions
diff --git a/CHANGES.rst b/CHANGES.rst
index 9d97773..3b81f8c 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -8,6 +8,8 @@ Changes
- Drop support for Python 2.6.
+- Gracefully handle ``UnicodeEncodeError`` that can be produced when
+ doing attribute lookup on Python 2 by instead raising a ``LocationError``.
4.0.0 (2014-03-21)
------------------
@@ -283,4 +285,3 @@ No further changes since 3.4.0a1.
Initial release as a separate project, corresponds to ``zope.traversing``
from Zope 3.4.0a1
-
diff --git a/src/zope/traversing/adapters.py b/src/zope/traversing/adapters.py
index f1aeba0..a4985a2 100644
--- a/src/zope/traversing/adapters.py
+++ b/src/zope/traversing/adapters.py
@@ -38,7 +38,14 @@ class DefaultTraversable(object):
def traverse(self, name, furtherPath):
subject = self._subject
__traceback_info__ = (subject, name, furtherPath)
- attr = getattr(subject, name, _marker)
+ try:
+ attr = getattr(subject, name, _marker)
+ except UnicodeEncodeError:
+ # If we're on Python 2, and name was a unicode string the
+ # name would have been encoded using the system encoding
+ # (usually ascii). Failure to encode means invalid
+ # attribute name.
+ attr = _marker
if attr is not _marker:
return attr
if hasattr(subject, '__getitem__'):
@@ -133,6 +140,14 @@ def traversePathElement(obj, name, further_path, default=_marker,
try:
return traversable.traverse(nm, further_path)
+ except UnicodeEncodeError:
+ # If we're on Python 2, and nm was a unicode string, and the traversable
+ # tried to do an attribute lookup, the nm would have been encoded using the
+ # system encoding (usually ascii). Failure to encode means invalid attribute
+ # name.
+ if default is not _marker:
+ return default
+ raise LocationError(obj, name)
except LocationError:
if default is not _marker:
return default
diff --git a/src/zope/traversing/tests/test_conveniencefunctions.py b/src/zope/traversing/tests/test_conveniencefunctions.py
index 72c1861..b69d165 100644
--- a/src/zope/traversing/tests/test_conveniencefunctions.py
+++ b/src/zope/traversing/tests/test_conveniencefunctions.py
@@ -118,6 +118,23 @@ class Test(PlacelessSetup, TestCase):
self.folder, './item'
)
+ def testTraverseNameUnicode(self):
+ from zope.traversing.api import traverseName
+ from zope.interface import implementer
+
+ @implementer(ITraversable)
+ class BrokenTraversable(object):
+ def traverse(self, name, furtherPath):
+ getattr(self, u'\u2019', None)
+ # The above actually works on Python 3
+ raise LocationError()
+
+ self.assertRaises(
+ LocationError,
+ traverseName,
+ BrokenTraversable(), '')
+
+
def testGetName(self):
from zope.traversing.api import getName
self.assertEqual(
diff --git a/src/zope/traversing/tests/test_traverser.py b/src/zope/traversing/tests/test_traverser.py
index a6e638d..2c81ac3 100644
--- a/src/zope/traversing/tests/test_traverser.py
+++ b/src/zope/traversing/tests/test_traverser.py
@@ -271,6 +271,10 @@ class DefaultTraversableTests(unittest.TestCase):
self.assertRaises(LocationError, df.traverse, 'bar', [])
+ def testUnicodeTraversal(self):
+ df = DefaultTraversable(object())
+ self.assertRaises(LocationError, df.traverse, u'\u2019', ())
+
def test_suite():
loader = unittest.TestLoader()
suite = loader.loadTestsFromTestCase(TraverserTests)