diff options
author | Jason Madden <jason+github@nextthought.com> | 2016-08-05 05:58:45 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-08-05 05:58:45 -0500 |
commit | 2ba32e65f7156b10675256e81aea837f693e515e (patch) | |
tree | 436cdab2591fe776a6844c44b49d53f6da06ff98 | |
parent | 1bf6305c14f8e7d4c683b30cd8a7e72c4cf2049e (diff) | |
parent | 2c7af36ae6217a02e614d8af14ca06a20b7147b1 (diff) | |
download | zope-traversing-2ba32e65f7156b10675256e81aea837f693e515e.tar.gz |
Merge pull request #3 from zopefoundation/handle-unicode-gracefully
Gracefully handle UnicodeEncodeError on Python 2.
-rw-r--r-- | CHANGES.rst | 3 | ||||
-rw-r--r-- | src/zope/traversing/adapters.py | 17 | ||||
-rw-r--r-- | src/zope/traversing/tests/test_conveniencefunctions.py | 17 | ||||
-rw-r--r-- | src/zope/traversing/tests/test_traverser.py | 4 |
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) |