Skip to content

Commit

Permalink
Gracefully handle UnicodeEncodeError on python 2.
Browse files Browse the repository at this point in the history
This can be produced when doing an attribute lookup. Turn it into a
LocationError (or the default value).

zope.container.traversal.ContainerTraversable was doing this already,
see https://github.com/zopefoundation/zope.container/blob/master/src/zope/container/traversal.py#L105
  • Loading branch information
jamadden committed Aug 4, 2016
1 parent 1bf6305 commit 2c7af36
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 2 deletions.
3 changes: 2 additions & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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)
------------------
Expand Down Expand Up @@ -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

17 changes: 16 additions & 1 deletion src/zope/traversing/adapters.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,14 @@ def __init__(self, subject):
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__'):
Expand Down Expand Up @@ -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
Expand Down
17 changes: 17 additions & 0 deletions src/zope/traversing/tests/test_conveniencefunctions.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,23 @@ def testTraverseNameBadValue(self):
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(
Expand Down
4 changes: 4 additions & 0 deletions src/zope/traversing/tests/test_traverser.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,10 @@ def testNotFound(self):

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)
Expand Down

0 comments on commit 2c7af36

Please sign in to comment.