summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Madden <jamadden@gmail.com>2017-08-25 10:01:03 -0500
committerJason Madden <jamadden@gmail.com>2017-08-25 11:44:15 -0500
commitc5b04ca74abb72b3399730e670e7442f21283d3a (patch)
tree6c3c534907cce0e55d173040c25cf8f388ebcf3c
parent64af3f227d61aac8471bc3e837886b96c647262c (diff)
downloadzope-traversing-c5b04ca74abb72b3399730e670e7442f21283d3a.tar.gz
100% coverage. Minor whitespace cleanups.py36
-rw-r--r--.coveragerc1
-rw-r--r--src/zope/traversing/adapters.py5
-rw-r--r--src/zope/traversing/api.py7
-rw-r--r--src/zope/traversing/browser/absoluteurl.py52
-rw-r--r--src/zope/traversing/browser/tests.py96
-rw-r--r--src/zope/traversing/interfaces.py7
-rw-r--r--src/zope/traversing/namespace.py54
-rw-r--r--src/zope/traversing/publicationtraverse.py4
-rw-r--r--src/zope/traversing/testing.py4
-rw-r--r--src/zope/traversing/tests/test_conveniencefunctions.py143
-rw-r--r--src/zope/traversing/tests/test_lang.py7
-rw-r--r--src/zope/traversing/tests/test_namespacetrversal.py112
-rw-r--r--src/zope/traversing/tests/test_presentation.py11
-rw-r--r--src/zope/traversing/tests/test_publicationtraverse.py106
-rw-r--r--src/zope/traversing/tests/test_skin.py10
-rw-r--r--src/zope/traversing/tests/test_traverser.py75
-rw-r--r--src/zope/traversing/tests/test_vhosting.py65
-rw-r--r--tox.ini2
18 files changed, 505 insertions, 256 deletions
diff --git a/.coveragerc b/.coveragerc
index e5477a0..d2957f4 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -9,3 +9,4 @@ exclude_lines =
raise NotImplementedError
self.fail
raise AssertionError
+ raise unittest.Skip
diff --git a/src/zope/traversing/adapters.py b/src/zope/traversing/adapters.py
index a4985a2..45d6e92 100644
--- a/src/zope/traversing/adapters.py
+++ b/src/zope/traversing/adapters.py
@@ -151,7 +151,4 @@ def traversePathElement(obj, name, further_path, default=_marker,
except LocationError:
if default is not _marker:
return default
- else:
- raise
-
- return obj
+ raise
diff --git a/src/zope/traversing/api.py b/src/zope/traversing/api.py
index 63b65d7..7a938b2 100644
--- a/src/zope/traversing/api.py
+++ b/src/zope/traversing/api.py
@@ -88,8 +88,7 @@ def traverse(object, path, default=_marker, request=None):
traverser = ITraverser(object)
if default is _marker:
return traverser.traverse(path, request=request)
- else:
- return traverser.traverse(path, default=default, request=request)
+ return traverser.traverse(path, default=default, request=request)
def traverseName(obj, name, default=_marker, traversable=None, request=None):
@@ -118,8 +117,8 @@ def traverseName(obj, name, default=_marker, traversable=None, request=None):
traversable=traversable, request=request)
if further_path:
raise NotImplementedError('further_path returned from traverse')
- else:
- return obj
+
+ return obj
def getName(obj):
diff --git a/src/zope/traversing/browser/absoluteurl.py b/src/zope/traversing/browser/absoluteurl.py
index a07f389..9b89d46 100644
--- a/src/zope/traversing/browser/absoluteurl.py
+++ b/src/zope/traversing/browser/absoluteurl.py
@@ -14,10 +14,15 @@
"""Absolute URL View components
"""
try:
- from urllib import quote as quote, unquote
-except ImportError:
from urllib.parse import quote_from_bytes as quote
+except ImportError:
+ from urllib import quote
+
+try:
from urllib.parse import unquote_to_bytes as unquote
+except ImportError:
+ from urllib import unquote
+
import zope.component
from zope.interface import implementer
@@ -29,8 +34,8 @@ from zope.i18nmessageid import MessageFactory
_ = MessageFactory('zope')
_insufficientContext = _("There isn't enough context to get URL information. "
- "This is probably due to a bug in setting up location "
- "information.")
+ "This is probably due to a bug in setting up location "
+ "information.")
_safe = '@+' # Characters that we don't want to have quoted
@@ -38,13 +43,16 @@ _safe = '@+' # Characters that we don't want to have quoted
def absoluteURL(ob, request):
return zope.component.getMultiAdapter((ob, request), IAbsoluteURL)()
-
-@implementer(IAbsoluteURL)
-class AbsoluteURL(BrowserView):
+class _EncodedUnicode(object):
def __unicode__(self):
return unquote(self.__str__()).decode('utf-8')
+
+@implementer(IAbsoluteURL)
+class AbsoluteURL(_EncodedUnicode,
+ BrowserView):
+
def __str__(self):
context = self.context
request = self.request
@@ -102,26 +110,27 @@ class AbsoluteURL(BrowserView):
return ({'name': '', 'url': self.request.getApplicationURL()}, )
base = tuple(zope.component.getMultiAdapter(
- (container, request), IAbsoluteURL).breadcrumbs())
+ (container, request), IAbsoluteURL).breadcrumbs())
name = getattr(context, '__name__', None)
if name is None:
raise TypeError(_insufficientContext)
if name:
- base += ({'name': name,
- 'url': ("%s/%s" % (base[-1]['url'],
- quote(name.encode('utf-8'), _safe)))
- }, )
+ base += (
+ {
+ 'name': name,
+ 'url': ("%s/%s" % (base[-1]['url'],
+ quote(name.encode('utf-8'), _safe)))
+ },
+ )
return base
@implementer(IAbsoluteURL)
-class SiteAbsoluteURL(BrowserView):
-
- def __unicode__(self):
- return unquote(self.__str__()).decode('utf-8')
+class SiteAbsoluteURL(_EncodedUnicode,
+ BrowserView):
def __str__(self):
context = self.context
@@ -151,9 +160,12 @@ class SiteAbsoluteURL(BrowserView):
name = getattr(context, '__name__', None)
if name:
- base += ({'name': name,
- 'url': ("%s/%s" % (base[-1]['url'],
- quote(name.encode('utf-8'), _safe)))
- }, )
+ base += (
+ {
+ 'name': name,
+ 'url': ("%s/%s" % (base[-1]['url'],
+ quote(name.encode('utf-8'), _safe)))
+ },
+ )
return base
diff --git a/src/zope/traversing/browser/tests.py b/src/zope/traversing/browser/tests.py
index 6eab7b1..861cb9c 100644
--- a/src/zope/traversing/browser/tests.py
+++ b/src/zope/traversing/browser/tests.py
@@ -13,12 +13,13 @@
##############################################################################
"""Test the AbsoluteURL view
"""
-from unittest import TestCase, main, makeSuite
+import unittest
import zope.component
from zope.component import getMultiAdapter, adapter
from zope.component.testing import PlacelessSetup
from zope.traversing.browser.absoluteurl import absoluteURL
+from zope.traversing.browser.absoluteurl import AbsoluteURL
from zope.traversing.browser.interfaces import IAbsoluteURL
from zope.traversing.testing import browserView
from zope.i18n.interfaces import IUserPreferredCharsets
@@ -70,7 +71,7 @@ class FooLocation(object):
return contained(TrivialContent(), Root(), name='bar')
-class TestAbsoluteURL(PlacelessSetup, TestCase):
+class TestAbsoluteURL(PlacelessSetup, unittest.TestCase):
def setUp(self):
PlacelessSetup.setUp(self)
@@ -127,7 +128,7 @@ class TestAbsoluteURL(PlacelessSetup, TestCase):
{'name': 'a', 'url': 'http://127.0.0.1/a'},
{'name': 'b', 'url': 'http://127.0.0.1/a/b'},
{'name': 'c', 'url': 'http://127.0.0.1/a/b/c'},
- ))
+ ))
def testParentButNoLocation(self):
request = TestRequest()
@@ -163,7 +164,7 @@ class TestAbsoluteURL(PlacelessSetup, TestCase):
({'name': '', 'url': 'http://127.0.0.1'},
{'name': 'bar', 'url': 'http://127.0.0.1/bar'},
{'name': 'foo', 'url': 'http://127.0.0.1/bar/foo'},
- ))
+ ))
def testParentTrumpsAdapter(self):
# if we have a location adapter for a content object but
@@ -210,7 +211,7 @@ class TestAbsoluteURL(PlacelessSetup, TestCase):
{'name': u'\u0441',
'url':
'http://127.0.0.1/%D0%B9/%D1%82/%D0%B5/%D1%81'},
- ))
+ ))
def testRetainSkin(self):
request = TestRequest()
@@ -230,7 +231,7 @@ class TestAbsoluteURL(PlacelessSetup, TestCase):
{'name': 'a', 'url': base + '/a'},
{'name': 'b', 'url': base + '/a/b'},
{'name': 'c', 'url': base + '/a/b/c'},
- ))
+ ))
def testVirtualHosting(self):
request = TestRequest()
@@ -245,10 +246,10 @@ class TestAbsoluteURL(PlacelessSetup, TestCase):
breadcrumbs = view.breadcrumbs()
self.assertEqual(breadcrumbs,
- ({'name': '', 'url': 'http://127.0.0.1'},
- {'name': 'b', 'url': 'http://127.0.0.1/b'},
- {'name': 'c', 'url': 'http://127.0.0.1/b/c'},
- ))
+ ({'name': '', 'url': 'http://127.0.0.1'},
+ {'name': 'b', 'url': 'http://127.0.0.1/b'},
+ {'name': 'c', 'url': 'http://127.0.0.1/b/c'},
+ ))
def testVirtualHostingWithVHElements(self):
request = TestRequest()
@@ -263,10 +264,10 @@ class TestAbsoluteURL(PlacelessSetup, TestCase):
breadcrumbs = view.breadcrumbs()
self.assertEqual(breadcrumbs,
- ({'name': '', 'url': 'http://127.0.0.1'},
- {'name': 'b', 'url': 'http://127.0.0.1/b'},
- {'name': 'c', 'url': 'http://127.0.0.1/b/c'},
- ))
+ ({'name': '', 'url': 'http://127.0.0.1'},
+ {'name': 'b', 'url': 'http://127.0.0.1/b'},
+ {'name': 'c', 'url': 'http://127.0.0.1/b/c'},
+ ))
def testVirtualHostingInFront(self):
request = TestRequest()
@@ -282,11 +283,11 @@ class TestAbsoluteURL(PlacelessSetup, TestCase):
breadcrumbs = view.breadcrumbs()
self.assertEqual(breadcrumbs,
- ({'name': '', 'url': 'http://127.0.0.1'},
- {'name': 'a', 'url': 'http://127.0.0.1/a'},
- {'name': 'b', 'url': 'http://127.0.0.1/a/b'},
- {'name': 'c', 'url': 'http://127.0.0.1/a/b/c'},
- ))
+ ({'name': '', 'url': 'http://127.0.0.1'},
+ {'name': 'a', 'url': 'http://127.0.0.1/a'},
+ {'name': 'b', 'url': 'http://127.0.0.1/a/b'},
+ {'name': 'c', 'url': 'http://127.0.0.1/a/b/c'},
+ ))
def testNoContextInformation(self):
request = TestRequest()
@@ -302,8 +303,57 @@ class TestAbsoluteURL(PlacelessSetup, TestCase):
self.assertEqual(absoluteURL(None, request), 'http://127.0.0.1')
-def test_suite():
- return makeSuite(TestAbsoluteURL)
+ def test_breadcrumbs_no_parent(self):
+
+ view = AbsoluteURL(self, None)
+ with self.assertRaisesRegexp(TypeError,
+ "There isn't enough context"):
+ view.breadcrumbs()
+
+ def test_nameless_context(self):
-if __name__ == '__main__':
- main(defaultTest='test_suite')
+ @implementer(ILocation)
+ class Context(object):
+ __parent__ = self
+ __name__ = None
+
+ class DummyAbsoluteURL(object):
+ # Our implementation of IAbsoluteURL
+ # for our parent
+
+ def __init__(self, *args):
+ pass
+
+ called = False
+
+ def __str__(self):
+ DummyAbsoluteURL.called = True
+ return ''
+
+ def breadcrumbs(self):
+ DummyAbsoluteURL.called = True
+ return ()
+
+ browserView(type(self), '', DummyAbsoluteURL, IAbsoluteURL)
+
+ request = TestRequest()
+ self.assertIsInstance(zope.component.getMultiAdapter((self, request), IAbsoluteURL),
+ DummyAbsoluteURL)
+ context = Context()
+
+ # First the view
+ view = AbsoluteURL(context, request)
+ with self.assertRaisesRegexp(TypeError,
+ "There isn't enough context"):
+ str(view)
+
+ self.assertTrue(DummyAbsoluteURL.called)
+ DummyAbsoluteURL.called = False
+
+ # Now the breadcrumbs
+ view = AbsoluteURL(context, request)
+ with self.assertRaisesRegexp(TypeError,
+ "There isn't enough context"):
+ view.breadcrumbs()
+
+ self.assertTrue(DummyAbsoluteURL.called)
diff --git a/src/zope/traversing/interfaces.py b/src/zope/traversing/interfaces.py
index 151a4ce..cd515ed 100644
--- a/src/zope/traversing/interfaces.py
+++ b/src/zope/traversing/interfaces.py
@@ -17,7 +17,8 @@
from zope.interface import Attribute
from zope.interface import Interface
from zope.interface import implementer
-from zope.component.interfaces import IObjectEvent
+from zope.interface.interfaces import IObjectEvent
+from zope.interface.interfaces import ObjectEvent
# BBB: Re-import symbols to their old location.
from zope.location.interfaces import LocationError as TraversalError
@@ -180,10 +181,10 @@ class IBeforeTraverseEvent(IObjectEvent):
@implementer(IBeforeTraverseEvent)
-class BeforeTraverseEvent(object):
+class BeforeTraverseEvent(ObjectEvent):
"""An event which gets sent on publication traverse"""
def __init__(self, ob, request):
- self.object = ob
+ ObjectEvent.__init__(self, ob)
self.request = request
diff --git a/src/zope/traversing/namespace.py b/src/zope/traversing/namespace.py
index 68b17cf..7ef07a4 100644
--- a/src/zope/traversing/namespace.py
+++ b/src/zope/traversing/namespace.py
@@ -376,9 +376,6 @@ class vh(view):
request = self.request
- traversal_stack = request.getTraversalStack()
- app_names = []
-
if not six.PY3:
# `name` comes in as unicode, we need to make it a string
# so absolute URLs don't all become unicode.
@@ -393,6 +390,9 @@ class vh(view):
request.setApplicationServer(host, proto, port)
+ traversal_stack = request.getTraversalStack()
+ app_names = []
+
if '++' in traversal_stack:
segment = traversal_stack.pop()
while segment != '++':
@@ -455,6 +455,8 @@ class adapter(SimpleHandler):
class debug(view):
+ enable_debug = __debug__
+
def traverse(self, name, ignored):
"""Debug traversal adapter
@@ -516,27 +518,37 @@ class debug(view):
... print('unknown debugging flag')
unknown debugging flag
+ Of course, if Python was started with the ``-O`` flag to
+ disable debugging, none of this is allowed (we simulate this
+ with a private setting on the instance):
+
+ >>> adapter.enable_debug = False
+ >>> adapter.traverse('source', ())
+ Traceback (most recent call last):
+ ...
+ ValueError: Debug flags only allowed in debug mode
+
"""
- if __debug__:
- request = self.request
- for flag in name.split(','):
- if flag == 'source':
- request.debug.sourceAnnotations = True
- elif flag == 'tal':
- request.debug.showTAL = True
- elif flag == 'errors':
- # TODO: I am not sure this is the best solution. What
- # if we want to enable tracebacks when also trying to
- # debug a different skin?
- skin = zope.component.getUtility(IBrowserSkinType, 'Debug')
- directlyProvides(request, providedBy(request) + skin)
- else:
- raise ValueError("Unknown debug flag: %s" % flag)
- return self.context
- else:
+ if not self.enable_debug or not __debug__:
raise ValueError("Debug flags only allowed in debug mode")
- if not __debug__:
+ request = self.request
+ for flag in name.split(','):
+ if flag == 'source':
+ request.debug.sourceAnnotations = True
+ elif flag == 'tal':
+ request.debug.showTAL = True
+ elif flag == 'errors':
+ # TODO: I am not sure this is the best solution. What
+ # if we want to enable tracebacks when also trying to
+ # debug a different skin?
+ skin = zope.component.getUtility(IBrowserSkinType, 'Debug')
+ directlyProvides(request, providedBy(request) + skin)
+ else:
+ raise ValueError("Unknown debug flag: %s" % flag)
+ return self.context
+
+ if not __debug__: # pragma: no cover
# If not in debug mode, we should get an error:
traverse.__doc__ = """Disabled debug traversal adapter
diff --git a/src/zope/traversing/publicationtraverse.py b/src/zope/traversing/publicationtraverse.py
index a030776..6a16401 100644
--- a/src/zope/traversing/publicationtraverse.py
+++ b/src/zope/traversing/publicationtraverse.py
@@ -90,10 +90,10 @@ class PublicationTraverser(object):
path.reverse()
- # Remove double dots
+ # Remove double dots if possible
while '..' in path:
l = path.index('..')
- if l < 0 or l + 2 > len(path):
+ if l + 2 > len(path):
break
del path[l:l + 2]
diff --git a/src/zope/traversing/testing.py b/src/zope/traversing/testing.py
index e10ea6b..177b76d 100644
--- a/src/zope/traversing/testing.py
+++ b/src/zope/traversing/testing.py
@@ -49,7 +49,7 @@ class ContainedProxy(object):
if name in ['__parent__', '__name__', '__obj__']:
self.__dict__[name] = value
return
- setattr(self.__obj__, name, value)
+ setattr(self.__obj__, name, value) # pragma: no cover
def __eq__(self, value):
return self.__obj__ == value
@@ -63,7 +63,7 @@ def contained(obj, root, name=None):
return obj
# BBB: Kept for backward-compatibility, in case some package depends on it.
-def setUp(): #pragma: nocover
+def setUp(): # pragma: no cover
zope.component.provideAdapter(Traverser, (None,), ITraverser)
zope.component.provideAdapter(DefaultTraversable, (None,), ITraversable)
zope.component.provideAdapter(LocationPhysicallyLocatable,
diff --git a/src/zope/traversing/tests/test_conveniencefunctions.py b/src/zope/traversing/tests/test_conveniencefunctions.py
index b69d165..be50c6a 100644
--- a/src/zope/traversing/tests/test_conveniencefunctions.py
+++ b/src/zope/traversing/tests/test_conveniencefunctions.py
@@ -13,20 +13,18 @@
##############################################################################
"""Test traversal convenience functions.
"""
-from unittest import TestCase, main, makeSuite
+import unittest
+from zope import interface
import zope.component
from zope.component.testing import PlacelessSetup
-from zope.interface import directlyProvides
from zope.location.traversing \
import LocationPhysicallyLocatable, RootPhysicallyLocatable
from zope.location.interfaces import ILocationInfo, IRoot, LocationError
-from zope.security.proxy import Proxy
-from zope.security.checker import selectChecker
from zope.traversing.adapters import Traverser, DefaultTraversable
from zope.traversing.interfaces import ITraversable, ITraverser
-from zope.traversing.testing import contained, Contained
+from zope.traversing.testing import contained
class C(object):
__parent__ = None
@@ -34,29 +32,28 @@ class C(object):
def __init__(self, name):
self.name = name
-def _proxied(*args):
- return Proxy(args, selectChecker(args))
-class Test(PlacelessSetup, TestCase):
+class TestFunctional(PlacelessSetup, unittest.TestCase):
def setUp(self):
PlacelessSetup.setUp(self)
# Build up a wrapper chain
root = C('root')
- directlyProvides(root, IRoot)
+ interface.directlyProvides(root, IRoot)
folder = C('folder')
item = C('item')
self.root = root # root is not usually wrapped
- self.folder = contained(folder, self.root, name='folder')
- self.item = contained(item, self.folder, name='item')
+ self.folder = contained(folder, self.root, name='folder')
+ self.item = contained(item, self.folder, name='item')
self.unwrapped_item = item
self.broken_chain_folder = contained(folder, None)
- self.broken_chain_item = contained(item,
- self.broken_chain_folder,
- name='item'
- )
+ self.broken_chain_item = contained(
+ item,
+ self.broken_chain_folder,
+ name='item'
+ )
root.folder = folder
folder.item = item
@@ -75,6 +72,13 @@ class Test(PlacelessSetup, TestCase):
self.tr.traverse('/folder/item')
)
+ def test_traverse_with_default(self):
+ from zope.traversing.api import traverse
+ self.assertIs(
+ traverse(self.item, '/no/path', self),
+ self
+ )
+
def testTraverseFromUnwrapped(self):
from zope.traversing.api import traverse
self.assertRaises(
@@ -88,15 +92,23 @@ class Test(PlacelessSetup, TestCase):
self.assertEqual(
traverseName(self.folder, 'item'),
self.tr.traverse('/folder/item')
- )
+ )
self.assertEqual(
traverseName(self.item, '.'),
self.tr.traverse('/folder/item')
- )
+ )
self.assertEqual(
traverseName(self.item, '..'),
self.tr.traverse('/folder')
- )
+ )
+ self.assertEqual(
+ traverseName(self.folder, 'item', default=self),
+ self.tr.traverse('/folder/item')
+ )
+ self.assertIs(
+ traverseName(self.folder, 'nothing', default=self),
+ self,
+ )
# TODO test that ++names++ and @@names work too
@@ -127,7 +139,7 @@ class Test(PlacelessSetup, TestCase):
def traverse(self, name, furtherPath):
getattr(self, u'\u2019', None)
# The above actually works on Python 3
- raise LocationError()
+ raise unittest.SkipTest("Unicode attrs legal on Py3")
self.assertRaises(
LocationError,
@@ -241,19 +253,20 @@ class Test(PlacelessSetup, TestCase):
_good_locations = (
# location returned as string
- ( u'/xx/yy/zz',
- # arguments to try in addition to the above
- '/xx/yy/zz',
- '/xx/./yy/ww/../zz',
+ (u'/xx/yy/zz',
+ # arguments to try in addition to the above
+ '/xx/yy/zz',
+ '/xx/./yy/ww/../zz',
),
- ( u'/xx/yy/zz',
- '/xx/yy/zz',
+ (u'/xx/yy/zz',
+ '/xx/yy/zz',
),
- ( u'/xx',
- '/xx',
+ (u'/xx',
+ '/xx',
),
- ( u'/',
- '/',
+ (u'/',
+ '/',
+ self.root,
),
)
@@ -285,26 +298,26 @@ class Test(PlacelessSetup, TestCase):
_good_locations = (
# location returned as string
- ( '/xx/yy/zz',
- # arguments to try in addition to the above
- '/xx/yy/zz',
- '/xx/./yy/ww/../zz',
- '/xx/./yy/ww/./../zz',
+ ('/xx/yy/zz',
+ # arguments to try in addition to the above
+ '/xx/yy/zz',
+ '/xx/./yy/ww/../zz',
+ '/xx/./yy/ww/./../zz',
),
- ( 'xx/yy/zz',
- # arguments to try in addition to the above
- 'xx/yy/zz',
- 'xx/./yy/ww/../zz',
- 'xx/./yy/ww/./../zz',
+ ('xx/yy/zz',
+ # arguments to try in addition to the above
+ 'xx/yy/zz',
+ 'xx/./yy/ww/../zz',
+ 'xx/./yy/ww/./../zz',
),
- ( '/xx/yy/zz',
- '/xx/yy/zz',
+ ('/xx/yy/zz',
+ '/xx/yy/zz',
),
- ( '/xx',
- '/xx',
+ ('/xx',
+ '/xx',
),
- ( '/',
- '/',
+ ('/',
+ '/',
),
)
@@ -361,9 +374,41 @@ class Test(PlacelessSetup, TestCase):
args = ('foo', 'bar', '.', 'baz', 'bone')
self.assertEqual(joinPath(path, *args), u'/foo/bar/baz/bone')
+ def test_joinPath_empty_args(self):
+ from zope.traversing.api import joinPath
+ path = 'abc'
+ self.assertEqual(joinPath(path), u'abc')
+
-def test_suite():
- return makeSuite(Test)
+class TestStandalone(unittest.TestCase):
+ # Unlike TestFunctional, we don't register gobs of
+ # adapters, making these tests more self-contained
-if __name__=='__main__':
- main(defaultTest='test_suite')
+ def test_getParent_no_location_info(self):
+ from zope.traversing.api import getParent
+ test = self
+ class Context(object):
+ called = False
+ def __conform__(self, iface):
+ self.called = True
+ test.assertEqual(iface, ILocationInfo)
+ raise TypeError()
+
+ context = Context()
+ with self.assertRaisesRegexp(TypeError,
+ "Not enough context"):
+ getParent(context)
+
+ self.assertTrue(context.called)
+ context.called = False
+
+ # Now give it a parent
+ context.__parent__ = self
+ self.assertIs(self, getParent(context))
+
+ self.assertTrue(context.called)
+ context.called = False
+
+ # Now if it's a root, it has no parent
+ interface.alsoProvides(context, IRoot)
+ self.assertIsNone(getParent(context))
diff --git a/src/zope/traversing/tests/test_lang.py b/src/zope/traversing/tests/test_lang.py
index 888091b..d6f9ff8 100644
--- a/src/zope/traversing/tests/test_lang.py
+++ b/src/zope/traversing/tests/test_lang.py
@@ -55,10 +55,3 @@ class Test(CleanUp, unittest.TestCase):
self.assertTrue(ob is ob2)
self.assertTrue(request.shifted)
self.assertEqual(["ru"], browser_languages.getPreferredLanguages())
-
-
-def test_suite():
- return unittest.makeSuite(Test)
-
-if __name__=='__main__':
- unittest.main(defaultTest='test_suite')
diff --git a/src/zope/traversing/tests/test_namespacetrversal.py b/src/zope/traversing/tests/test_namespacetrversal.py
index bde36c3..0fdd4db 100644
--- a/src/zope/traversing/tests/test_namespacetrversal.py
+++ b/src/zope/traversing/tests/test_namespacetrversal.py
@@ -14,12 +14,110 @@
"""Traversal Namespace Tests
"""
import re
-from unittest import main
+import unittest
from doctest import DocTestSuite
+
+from zope import interface
+from zope import component
+from zope.location.interfaces import LocationError
+from zope.traversing import namespace
+
+from zope.component.testing import PlacelessSetup
from zope.component.testing import setUp, tearDown
from zope.testing.renormalizing import RENormalizing
+class TestFunctions(unittest.TestCase):
+
+ def test_getResource_not_found(self):
+ with self.assertRaises(LocationError):
+ namespace.getResource(None, '', None)
+
+
+class TestAcquire(unittest.TestCase):
+
+ def test_excessive_depth_on_path_extension(self):
+ from zope.traversing.interfaces import ITraversable
+ from zope.traversing.namespace import ExcessiveDepth
+
+ @interface.implementer(ITraversable)
+ class Context(object):
+
+ max_call_count = 200
+
+ def traverse(self, name, path):
+ if self.max_call_count:
+ path.append("I added something")
+ self.max_call_count -= 1
+
+ acq = namespace.acquire(Context(), None)
+ with self.assertRaises(ExcessiveDepth):
+ acq.traverse(None, None)
+
+
+class TestEtc(PlacelessSetup, unittest.TestCase):
+
+ def test_traverse_non_site_name(self):
+ with self.assertRaises(LocationError) as ctx:
+ namespace.etc(None, None).traverse('foobar', ())
+
+ ex = ctx.exception
+ self.assertEqual((None, 'foobar'), ex.args)
+
+ def test_traverse_site_no_manager(self):
+ test = self
+ class Context(object):
+ def __getattribute__(self, name):
+ test.assertEqual(name, 'getSiteManager')
+ return None
+
+ context = Context()
+ with self.assertRaises(LocationError) as ctx:
+ namespace.etc(context, None).traverse('site', ())
+
+ ex = ctx.exception
+ self.assertEqual((context, 'site'), ex.args)
+
+ def test_traverse_site_lookup_error(self):
+ class Context(object):
+ called = False
+ def getSiteManager(self):
+ self.called = True
+ from zope.component import ComponentLookupError
+ raise ComponentLookupError()
+
+ context = Context()
+ with self.assertRaises(LocationError) as ctx:
+ namespace.etc(context, None).traverse('site', ())
+
+ ex = ctx.exception
+ self.assertEqual((context, 'site'), ex.args)
+ self.assertTrue(context.called)
+
+ def test_traverse_utility(self):
+ from zope.traversing.interfaces import IEtcNamespace
+ component.provideUtility(self, provides=IEtcNamespace, name='my etc name')
+
+ result = namespace.etc(None, None).traverse('my etc name', ())
+ self.assertIs(result, self)
+
+
+class TestView(unittest.TestCase):
+
+ def test_not_found(self):
+ with self.assertRaises(LocationError) as ctx:
+ namespace.view(None, None).traverse('name', ())
+
+ ex = ctx.exception
+ self.assertEqual((None, 'name'), ex.args)
+
+
+class TestVh(unittest.TestCase):
+
+ def test_invalid_vh(self):
+ with self.assertRaisesRegexp(ValueError,
+ 'Vhost directive should have the form'):
+ namespace.vh(None, None).traverse(u'invalid name', ())
def test_suite():
checker = RENormalizing([
@@ -28,9 +126,9 @@ def test_suite():
"LocationError"),
])
- return DocTestSuite('zope.traversing.namespace',
- setUp=setUp, tearDown=tearDown,
- checker=checker)
-
-if __name__ == '__main__':
- main(defaultTest='test_suite')
+ suite = unittest.defaultTestLoader.loadTestsFromName(__name__)
+ suite.addTest(DocTestSuite(
+ 'zope.traversing.namespace',
+ setUp=setUp, tearDown=tearDown,
+ checker=checker))
+ return suite
diff --git a/src/zope/traversing/tests/test_presentation.py b/src/zope/traversing/tests/test_presentation.py
index c658ca2..6299ddc 100644
--- a/src/zope/traversing/tests/test_presentation.py
+++ b/src/zope/traversing/tests/test_presentation.py
@@ -13,7 +13,7 @@
##############################################################################
"""Presentation Traverser Tests
"""
-from unittest import TestCase, main, makeSuite
+import unittest
from zope.testing.cleanup import CleanUp
from zope.interface import Interface, implementer
from zope.publisher.browser import TestRequest
@@ -38,7 +38,7 @@ class View(object):
self.content = content
-class Test(CleanUp, TestCase):
+class Test(CleanUp, unittest.TestCase):
def testView(self):
browserView(IContent, 'foo', View)
@@ -53,10 +53,3 @@ class Test(CleanUp, TestCase):
ob = Content()
r = resource(ob, TestRequest()).traverse('foo', ())
self.assertEqual(r.__class__, Resource)
-
-
-def test_suite():
- return makeSuite(Test)
-
-if __name__=='__main__':
- main(defaultTest='test_suite')
diff --git a/src/zope/traversing/tests/test_publicationtraverse.py b/src/zope/traversing/tests/test_publicationtraverse.py
index da0adf6..7f9e156 100644
--- a/src/zope/traversing/tests/test_publicationtraverse.py
+++ b/src/zope/traversing/tests/test_publicationtraverse.py
@@ -13,7 +13,8 @@
##############################################################################
"""Tests of PublicationTraverser
"""
-from unittest import TestCase, main, makeSuite
+import unittest
+
from zope.testing.cleanup import CleanUp
from zope.component import provideAdapter
from zope.interface import Interface, implementer
@@ -23,21 +24,20 @@ from zope.publisher.interfaces import NotFound
from zope.publisher.interfaces.browser import IBrowserPublisher
from zope.security.proxy import removeSecurityProxy
from zope.traversing.interfaces import ITraversable
+from zope.traversing.publicationtraverse import PublicationTraverser
-class TestPublicationTraverser(CleanUp, TestCase):
+class TestPublicationTraverser(CleanUp, unittest.TestCase):
def testViewNotFound(self):
ob = Content()
- from zope.traversing.publicationtraverse import PublicationTraverser
t = PublicationTraverser()
request = TestRequest()
self.assertRaises(NotFound, t.traverseName, request, ob, '@@foo')
def testViewFound(self):
provideAdapter(DummyViewTraverser, (Interface, Interface),
- ITraversable, name='view')
+ ITraversable, name='view')
ob = Content()
- from zope.traversing.publicationtraverse import PublicationTraverser
t = PublicationTraverser()
request = TestRequest()
proxy = t.traverseName(request, ob, '@@foo')
@@ -48,23 +48,20 @@ class TestPublicationTraverser(CleanUp, TestCase):
def testDot(self):
ob = Content()
- from zope.traversing.publicationtraverse import PublicationTraverser
t = PublicationTraverser()
request = TestRequest()
self.assertEqual(ob, t.traverseName(request, ob, '.'))
def testNameNotFound(self):
ob = Content()
- from zope.traversing.publicationtraverse import PublicationTraverser
t = PublicationTraverser()
request = TestRequest()
self.assertRaises(NotFound, t.traverseName, request, ob, 'foo')
def testNameFound(self):
provideAdapter(DummyPublishTraverse, (Interface, Interface),
- IPublishTraverse)
+ IPublishTraverse)
ob = Content()
- from zope.traversing.publicationtraverse import PublicationTraverser
t = PublicationTraverser()
request = TestRequest()
proxy = t.traverseName(request, ob, 'foo')
@@ -76,7 +73,6 @@ class TestPublicationTraverser(CleanUp, TestCase):
def testDirectTraversal(self):
request = TestRequest()
ob = DummyPublishTraverse(Content(), request)
- from zope.traversing.publicationtraverse import PublicationTraverser
t = PublicationTraverser()
proxy = t.traverseName(request, ob, 'foo')
view = removeSecurityProxy(proxy)
@@ -86,16 +82,14 @@ class TestPublicationTraverser(CleanUp, TestCase):
def testPathNotFound(self):
ob = Content()
- from zope.traversing.publicationtraverse import PublicationTraverser
t = PublicationTraverser()
request = TestRequest()
self.assertRaises(NotFound, t.traversePath, request, ob, 'foo/bar')
def testPathFound(self):
provideAdapter(DummyPublishTraverse, (Interface, Interface),
- IPublishTraverse)
+ IPublishTraverse)
ob = Content()
- from zope.traversing.publicationtraverse import PublicationTraverser
t = PublicationTraverser()
request = TestRequest()
proxy = t.traversePath(request, ob, 'foo/bar')
@@ -106,9 +100,8 @@ class TestPublicationTraverser(CleanUp, TestCase):
def testComplexPath(self):
provideAdapter(DummyPublishTraverse, (Interface, Interface),
- IPublishTraverse)
+ IPublishTraverse)
ob = Content()
- from zope.traversing.publicationtraverse import PublicationTraverser
t = PublicationTraverser()
request = TestRequest()
proxy = t.traversePath(request, ob, 'foo/../alpha//beta/./bar')
@@ -119,11 +112,11 @@ class TestPublicationTraverser(CleanUp, TestCase):
def testTraverseRelativeURL(self):
provideAdapter(DummyPublishTraverse, (Interface, Interface),
- IPublishTraverse)
+ IPublishTraverse)
provideAdapter(DummyBrowserPublisher, (Interface,),
- IBrowserPublisher)
+ IBrowserPublisher)
ob = Content()
- from zope.traversing.publicationtraverse import PublicationTraverser
+
t = PublicationTraverser()
request = TestRequest()
proxy = t.traverseRelativeURL(request, ob, 'foo/bar')
@@ -134,13 +127,78 @@ class TestPublicationTraverser(CleanUp, TestCase):
def testMissingSkin(self):
ob = Content()
- from zope.traversing.publicationtraverse import PublicationTraverser
t = PublicationTraverser()
request = TestRequest()
self.assertRaises(
NotFound, t.traversePath, request, ob, '/++skin++missingskin')
+ def test_traversePath_trailing_slash(self):
+ class Traverser(PublicationTraverser):
+ def __init__(self):
+ self.names = []
+
+ def traverseName(self, request, ob, name):
+ self.names.append(name)
+
+
+ t = Traverser()
+ t.traversePath(None, None, 'abc/def/')
+ self.assertEqual(t.names, ['abc', 'def'])
+
+ t = Traverser()
+ t.traversePath(None, None, 'abc/def///')
+
+ # Note that only *one* trailing slash is removed
+ self.assertEqual(t.names, ['abc', 'def', '', ''])
+
+
+ def test_traversePath_double_dots_cannot_remove(self):
+ class Traverser(PublicationTraverser):
+ def __init__(self):
+ self.names = []
+
+ def traverseName(self, request, ob, name):
+ self.names.append(name)
+
+
+ t = Traverser()
+ t.traversePath(None, None, '..')
+ self.assertEqual(t.names, ['..'])
+
+ def test_traverseRelativeURL_to_no_browser_publisher(self):
+ test = self
+ class Traverser(PublicationTraverser):
+ def traversePath(self, request, ob, path):
+ return ob
+
+
+ class Context(object):
+ called = False
+ def __conform__(self, iface):
+ self.called = True
+ test.assertEqual(iface, IBrowserPublisher)
+ return None
+
+ t = Traverser()
+ context = Context()
+ ob = t.traverseRelativeURL(None, context, None)
+ self.assertIs(ob, context)
+
+ self.assertTrue(context.called)
+
+class TestBeforeTraverseEvent(unittest.TestCase):
+
+ def test_interfaces(self):
+ from zope.traversing.interfaces import IBeforeTraverseEvent
+ from zope.traversing.interfaces import BeforeTraverseEvent
+ from zope.interface.verify import verifyObject
+
+ ob = BeforeTraverseEvent(self, self)
+ self.assertIs(self, ob.request)
+ self.assertIs(self, ob.object)
+ verifyObject(IBeforeTraverseEvent, ob)
+
class IContent(Interface):
pass
@@ -179,12 +237,4 @@ class DummyBrowserPublisher(object):
def browserDefault(self, request):
if self.context.name != 'more':
return self.context, ['more']
- else:
- return self.context, ()
-
-
-def test_suite():
- return makeSuite(TestPublicationTraverser)
-
-if __name__ == '__main__':
- main()
+ return self.context, ()
diff --git a/src/zope/traversing/tests/test_skin.py b/src/zope/traversing/tests/test_skin.py
index 405bb23..0a12825 100644
--- a/src/zope/traversing/tests/test_skin.py
+++ b/src/zope/traversing/tests/test_skin.py
@@ -13,7 +13,7 @@
##############################################################################
"""Test skin traversal.
"""
-from unittest import TestCase, main, makeSuite
+import unittest
import zope.component
from zope.testing.cleanup import CleanUp
@@ -29,7 +29,7 @@ class IFoo(Interface):
directlyProvides(IFoo, IBrowserSkinType)
-class Test(CleanUp, TestCase):
+class Test(CleanUp, unittest.TestCase):
def setUp(self):
super(Test, self).setUp()
@@ -52,9 +52,3 @@ class Test(CleanUp, TestCase):
ob = object()
traverser = skin(ob, request)
self.assertRaises(LocationError, traverser.traverse, 'bar', ())
-
-def test_suite():
- return makeSuite(Test)
-
-if __name__=='__main__':
- main(defaultTest='test_suite')
diff --git a/src/zope/traversing/tests/test_traverser.py b/src/zope/traversing/tests/test_traverser.py
index 2c81ac3..9eeb211 100644
--- a/src/zope/traversing/tests/test_traverser.py
+++ b/src/zope/traversing/tests/test_traverser.py
@@ -11,7 +11,7 @@
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
-"""Traverser Adapter tests.
+"""Traverser Adapter tests (adapter.py)
"""
import unittest
@@ -29,6 +29,7 @@ from zope.security.checker \
from zope.security.management import newInteraction, endInteraction
from zope.traversing.adapters import Traverser, DefaultTraversable
+from zope.traversing import adapters
from zope.traversing.interfaces import ITraversable, ITraverser
from zope.traversing.testing import contained, Contained
@@ -47,9 +48,9 @@ class TraverserTests(PlacelessSetup, unittest.TestCase):
def setUp(self):
PlacelessSetup.setUp(self)
# Build up a wrapper chain
- self.root = C('root')
- self.folder = contained(C('folder'), self.root, name='folder')
- self.item = contained(C('item'), self.folder, name='item')
+ self.root = C('root')
+ self.folder = contained(C('folder'), self.root, name='folder')
+ self.item = contained(C('item'), self.folder, name='item')
self.tr = Traverser(self.item)
def testImplementsITraverser(self):
@@ -59,6 +60,9 @@ class TraverserTests(PlacelessSetup, unittest.TestCase):
for interface in implementedBy(Traverser):
verifyClass(interface, Traverser)
+ def test_traverse_empty_path_is_context(self):
+ self.assertIs(self.item, self.tr.traverse(''))
+
class UnrestrictedNoTraverseTests(unittest.TestCase):
def setUp(self):
self.root = root = C('root')
@@ -193,9 +197,11 @@ class RestrictedTraverseTests(PlacelessSetup, unittest.TestCase):
self.tr = Traverser(ProxyFactory(root))
def testAllAllowed(self):
- defineChecker(C, Checker({'folder': CheckerPublic,
- 'item': CheckerPublic,
- }))
+ defineChecker(C,
+ Checker({
+ 'folder': CheckerPublic,
+ 'item': CheckerPublic,
+ }))
tr = Traverser(ProxyFactory(self.root))
item = self.item
@@ -210,12 +216,12 @@ class RestrictedTraverseTests(PlacelessSetup, unittest.TestCase):
folder = self.folder
self.assertRaises(Unauthorized, tr.traverse,
- ('', 'folder', 'item'))
+ ('', 'folder', 'item'))
self.assertRaises(Unauthorized, tr.traverse,
- ('folder', 'item'))
+ ('folder', 'item'))
self.assertEqual(tr.traverse(('', 'folder')), folder)
self.assertEqual(tr.traverse(('folder', '..', 'folder')),
- folder)
+ folder)
self.assertEqual(tr.traverse(('folder',)), folder)
def testException(self):
@@ -230,10 +236,10 @@ class RestrictedTraverseTests(PlacelessSetup, unittest.TestCase):
# AttributeError becomes LocationError if there's no __getitem__
# on the object
self.assertRaises(LocationError, tr.traverse,
- ('foobar', 'attributeerror'))
+ ('foobar', 'attributeerror'))
# Other exceptions raised as usual
self.assertRaises(ValueError, tr.traverse,
- ('foobar', 'valueerror'))
+ ('foobar', 'valueerror'))
class DefaultTraversableTests(unittest.TestCase):
@@ -275,14 +281,37 @@ class DefaultTraversableTests(unittest.TestCase):
df = DefaultTraversable(object())
self.assertRaises(LocationError, df.traverse, u'\u2019', ())
-def test_suite():
- loader = unittest.TestLoader()
- suite = loader.loadTestsFromTestCase(TraverserTests)
- suite.addTest(loader.loadTestsFromTestCase(DefaultTraversableTests))
- suite.addTest(loader.loadTestsFromTestCase(UnrestrictedNoTraverseTests))
- suite.addTest(loader.loadTestsFromTestCase(UnrestrictedTraverseTests))
- suite.addTest(loader.loadTestsFromTestCase(RestrictedTraverseTests))
- return suite
-
-if __name__=='__main__':
- unittest.TextTestRunner().run(test_suite())
+
+class TestFunctions(unittest.TestCase):
+
+ def test_traversePathElement_UnicodeEncodeError_with_default(self):
+ test = self
+ class Traversable(object):
+ called = False
+ fail = test.fail
+ def traverse(self, nm, further_path):
+ self.called = True
+ u'\xff'.encode("ascii")
+ self.fail("Should not be reached")
+
+ t = Traversable()
+ self.assertIs(self,
+ adapters.traversePathElement(None, None, (),
+ default=self,
+ traversable=t))
+ self.assertTrue(t.called)
+
+
+ def test_traversePathElement_LocationError_with_default(self):
+ class Traversable(object):
+ called = False
+ def traverse(self, nm, further_path):
+ self.called = True
+ raise LocationError()
+
+ t = Traversable()
+ self.assertIs(self,
+ adapters.traversePathElement(None, None, (),
+ default=self,
+ traversable=t))
+ self.assertTrue(t.called)
diff --git a/src/zope/traversing/tests/test_vhosting.py b/src/zope/traversing/tests/test_vhosting.py
index 38864e2..ed393e6 100644
--- a/src/zope/traversing/tests/test_vhosting.py
+++ b/src/zope/traversing/tests/test_vhosting.py
@@ -14,17 +14,10 @@
"""Functional tests for virtual hosting.
"""
import os
-import transaction
import unittest
-try:
- from StringIO import StringIO
-except ImportError:
- from io import StringIO
+from io import StringIO
-try:
- from UserDict import UserDict
-except ImportError:
- from collections import UserDict
+import transaction
import zope.component
import zope.interface
@@ -44,7 +37,7 @@ from zope.testing.cleanup import cleanUp
from zope.traversing.adapters import traversePathElement
from zope.traversing.api import traverse
-from zope.traversing.testing import browserResource, Contained, contained
+from zope.traversing.testing import browserResource, Contained
class MyObj(Contained):
@@ -55,19 +48,19 @@ class IFolder(zope.interface.Interface):
pass
@zope.interface.implementer(IFolder, IBrowserPublisher)
-class Folder(Contained, UserDict):
+class Folder(Contained, dict):
def __init__(self):
- UserDict.__init__(self, {})
+ dict.__init__(self, {})
def __setitem__(self, name, value):
value.__parent__ = self
value.__name__ = name
- UserDict.__setitem__(self, name, value)
+ dict.__setitem__(self, name, value)
def publishTraverse(self, request, name):
subob = self.get(name, None)
if subob is None:
- raise NotFound(self.context, name, request)
+ raise NotFound(self.context, name, request) # pragma: no cover
return subob
@@ -86,11 +79,9 @@ class ZopeTraverser(object):
while path_items:
name = path_items.pop()
- if getattr(object, '__class__', None) == dict:
- object = object[name]
- else:
- object = traversePathElement(object, name, path_items,
- request=request)
+ assert getattr(object, '__class__', None) != dict
+ object = traversePathElement(object, name, path_items,
+ request=request)
return object
zopeTraverser = ZopeTraverser()
@@ -169,7 +160,7 @@ class TestVirtualHosting(unittest.TestCase):
if len(p) == 1:
env['PATH_INFO'] = p[0]
- request = BrowserRequest(StringIO(''), env)
+ request = BrowserRequest(StringIO(u''), env)
request.setPublication(DummyPublication(self.app))
setDefaultSkin(request)
return request
@@ -279,7 +270,7 @@ class TestVirtualHosting(unittest.TestCase):
self.assertEqual(result.getHeader('Location'), location)
-class DummyPublication:
+class DummyPublication(object):
def __init__(self, app):
self.app = app
@@ -307,18 +298,13 @@ class DummyPublication:
Name must be an ASCII string or Unicode object."""
if name == 'index.html':
- from zope.component import queryMultiAdapter
- view = queryMultiAdapter((ob, request), name=name)
- if view is None:
- from zope.publisher.interfaces import NotFound
- import pdb; pdb.set_trace()
- raise NotFound(ob, name)
+ from zope.component import getMultiAdapter
+ view = getMultiAdapter((ob, request), name=name)
return view
- else:
- from zope.traversing.publicationtraverse \
- import PublicationTraverserWithoutProxy
- t = PublicationTraverserWithoutProxy()
- return t.traverseName(request, ob, name)
+
+ from zope.traversing.publicationtraverse import PublicationTraverserWithoutProxy
+ t = PublicationTraverserWithoutProxy()
+ return t.traverseName(request, ob, name)
def afterTraversal(self, request, ob):
"""Post-traversal hook.
@@ -340,7 +326,7 @@ class DummyPublication:
"""Post-callObject hook (if it was successful).
"""
- def handleException(self, ob, request, exc_info, retry_allowed=1):
+ def handleException(self, ob, request, exc_info, retry_allowed=1): # pragma: no cover
"""Handle an exception
Either:
@@ -358,15 +344,4 @@ class DummyPublication:
def getDefaultTraversal(self, request, ob):
if hasattr(ob, 'index'):
return ob, ()
- else:
- return ob, ('index.html',)
-
-
-def test_suite():
- suite = unittest.TestSuite()
- suite.addTest(unittest.makeSuite(TestVirtualHosting))
- return suite
-
-
-if __name__ == '__main__':
- unittest.main()
+ return ob, ('index.html',)
diff --git a/tox.ini b/tox.ini
index 194c2d6..90869b2 100644
--- a/tox.ini
+++ b/tox.ini
@@ -14,7 +14,7 @@ basepython =
python2.7
commands =
coverage run -m zope.testrunner --test-path=src []
- coverage report --fail-under=95
+ coverage report --fail-under=100
deps =
{[testenv]deps}
coverage