summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Theune <ct@gocept.com>2009-01-29 19:39:10 +0000
committerChristian Theune <ct@gocept.com>2009-01-29 19:39:10 +0000
commitfd6cdecea104cb88b12cd28f84a1c9109f2016ac (patch)
tree2a4f8986c3277612a329abc58c6d14a973dbf7ce /src
parentd4b2ddc96327f624ec69ab901b758fb663027258 (diff)
downloadzope-location-fd6cdecea104cb88b12cd28f84a1c9109f2016ac.tar.gz
- Reverse the dependency between zope.location and zope.traversing. This
also causes the dependency to various other packages go away.
Diffstat (limited to 'src')
-rw-r--r--src/zope/location/DEPENDENCIES.cfg6
-rw-r--r--src/zope/location/__init__.py4
-rw-r--r--src/zope/location/configure.zcml3
-rw-r--r--src/zope/location/interfaces.py112
-rw-r--r--src/zope/location/location.py55
-rw-r--r--src/zope/location/location.txt16
-rw-r--r--src/zope/location/pickling.py24
-rw-r--r--src/zope/location/tests.py10
-rw-r--r--src/zope/location/traversing.py57
9 files changed, 198 insertions, 89 deletions
diff --git a/src/zope/location/DEPENDENCIES.cfg b/src/zope/location/DEPENDENCIES.cfg
deleted file mode 100644
index 3b1413d..0000000
--- a/src/zope/location/DEPENDENCIES.cfg
+++ /dev/null
@@ -1,6 +0,0 @@
-zope.component
-zope.interface
-zope.proxy
-zope.schema
-zope.testing
-zope.traversing
diff --git a/src/zope/location/__init__.py b/src/zope/location/__init__.py
index d36b0fe..fdf538b 100644
--- a/src/zope/location/__init__.py
+++ b/src/zope/location/__init__.py
@@ -1,6 +1,6 @@
##############################################################################
#
-# Copyright (c) 2003 Zope Corporation and Contributors.
+# Copyright (c) 2003-2009 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
@@ -17,6 +17,6 @@ $Id$
"""
__docformat__ = 'restructuredtext'
+from zope.location.interfaces import ILocation
from zope.location.location import Location, locate, LocationIterator
from zope.location.location import inside, LocationProxy
-from zope.location.interfaces import ILocation
diff --git a/src/zope/location/configure.zcml b/src/zope/location/configure.zcml
index 3bb7f12..baeffb7 100644
--- a/src/zope/location/configure.zcml
+++ b/src/zope/location/configure.zcml
@@ -1,5 +1,6 @@
<configure xmlns="http://namespaces.zope.org/zope">
- <adapter factory="zope.location.traversing.LocationPhysicallyLocatable" />
+ <adapter factory=".traversing.LocationPhysicallyLocatable" />
+ <adapter factory=".location.LocationProxy" />
</configure>
diff --git a/src/zope/location/interfaces.py b/src/zope/location/interfaces.py
index c8478aa..d4b20d0 100644
--- a/src/zope/location/interfaces.py
+++ b/src/zope/location/interfaces.py
@@ -1,6 +1,6 @@
##############################################################################
#
-# Copyright (c) 2003 Zope Corporation and Contributors.
+# Copyright (c) 2003-2009 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
@@ -17,34 +17,115 @@ $Id$
"""
__docformat__ = 'restructuredtext'
-from zope.interface import Interface, Attribute
-from zope import schema
+import zope.interface
+import zope.schema
+_RAISE_KEYERROR = object()
-class ILocation(Interface):
- """Objects that have a structural location"""
- __parent__ = Attribute("The parent in the location hierarchy")
+class ILocation(zope.interface.Interface):
+ """Objects that can be located in a hierachy.
- __name__ = schema.TextLine(
+ Given a parent and a name an object can be located within that parent. The
+ locatable object's `__name__` and `__parent__` attributes store this
+ information.
+
+ Located objects form a hierarchy that can be used to build file-system-like
+ structures. For example in Zope `ILocation` is used to build URLs and to
+ support security machinery.
+
+ To retrieve an object from its parent using its name, the `ISublocation`
+ interface provides the `sublocations` method to iterate over all objects
+ located within the parent. The object searched for can be found by reading
+ each sublocation's __name__ attribute.
+
+ """
+
+ __parent__ = zope.interface.Attribute("The parent in the location hierarchy.")
+
+ __name__ = zope.schema.TextLine(
title=u"The name within the parent",
- description=u"Traverse the parent with this name to get the object.",
+ description=u"The object can be looked up from the parent's "
+ "sublocations using this name.",
required=False,
default=None)
-class ISublocations(Interface):
- """Provide access to sublocations."""
+class ILocationInfo(zope.interface.Interface):
+ """Provides supplemental information for located objects.
+
+ Requires that the object has been given a location in a hierarchy.
+
+ """
+
+ def getRoot():
+ """Return the root object of the hierarchy."""
+
+ def getPath():
+ """Return the physical path to the object as a string.
+
+ Uses '/' as the path segment separator.
+
+ """
+
+ def getName():
+ """Return the last segment of the physical path."""
+
+ def getNearestSite():
+ """Return the site the object is contained in
+
+ If the object is a site, the object itself is returned.
+
+ """
+
+
+class ISublocations(zope.interface.Interface):
+ """Provide access to sublocations of an object.
+
+ All objects with the same parent object are called the ``sublocations`` of
+ that parent.
+
+ """
def sublocations():
- """Return sublocations
+ """Return an iterable of the object's sublocations."""
+
+
+class IRoot(zope.interface.Interface):
+ """Marker interface to designate root objects within a location hierarchy.
+ """
+
+
+
+
+class ITraverser(zope.interface.Interface):
+ """Provide traverse features"""
+
+ # XXX This is used like a utility but implemented as an adapter: The
+ # traversal policy is only implemented once and repeated for all objects
+ # along the path.
+
+ def traverse(path, default=_RAISE_KEYERROR):
+ """Return an object given a path.
+
+ Path is either an immutable sequence of strings or a slash ('/')
+ delimited string.
+
+ If the first string in the path sequence is an empty string, or the
+ path begins with a '/', start at the root. Otherwise the path is
+ relative to the current context.
+
+ If the object is not found, return 'default' argument.
- An iterable of objects whose __parent__ is the object
- providing the interface is returned.
"""
-class IPossibleSite(Interface):
- """An object that could be a site
+
+class LocationError(KeyError, LookupError):
+ """There is no object for a given location."""
+
+
+class IPossibleSite(zope.interface.Interface):
+ """An object that could be a site.
"""
def setSiteManager(sitemanager):
@@ -57,5 +138,6 @@ class IPossibleSite(Interface):
If there isn't a site manager, raise a component lookup.
"""
+
class ISite(IPossibleSite):
"""Marker interface to indicate that we have a site"""
diff --git a/src/zope/location/location.py b/src/zope/location/location.py
index c80c4b5..920271f 100644
--- a/src/zope/location/location.py
+++ b/src/zope/location/location.py
@@ -1,6 +1,6 @@
##############################################################################
#
-# Copyright (c) 2003 Zope Corporation and Contributors.
+# Copyright (c) 2003-2009 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
@@ -17,66 +17,64 @@ $Id$
"""
__docformat__ = 'restructuredtext'
-
import zope.interface
+import zope.component
+
+# XXX import error when doing import zope.location.interfaces :/
from zope.location.interfaces import ILocation
-from zope.proxy import ProxyBase, getProxiedObject, non_overridable
+from zope.proxy import ProxyBase, non_overridable
from zope.proxy.decorator import DecoratorSpecificationDescriptor
-from zope.security.decorator import DecoratedSecurityCheckerDescriptor
class Location(object):
- """Stupid mix-in that defines `__parent__` and `__name__` attributes."""
+ """Mix-in that implements ILocation.
- zope.interface.implements(ILocation)
-
- __parent__ = __name__ = None
+ It provides the `__parent__` and `__name__` attributes.
+ """
+ zope.interface.implements(ILocation)
-def locate(object, parent, name=None):
- """Locate an object in another
+ __parent__ = None
+ __name__ = None
- This method should only be called from trusted code, because it
- sets attributes that are normally unsettable.
- """
- object.__parent__ = parent
- object.__name__ = name
+def locate(obj, parent, name=None):
+ """Update a location's coordinates."""
+ obj.__parent__ = parent
+ obj.__name__ = name
-def located(object, parent, name=None):
- """Locate an object in another and return it.
+def located(obj, parent, name=None):
+ """Ensure and return the location of an object.
- If the object does not provide ILocation a LocationProxy is returned.
+ Updates the location's coordinates.
"""
- if ILocation.providedBy(object):
- if parent is not object.__parent__ or name != object.__name__:
- locate(object, parent, name)
- return object
- return LocationProxy(object, parent, name)
+ location = zope.location.interfaces.ILocation(obj)
+ locate(location, parent, name)
+ return location
def LocationIterator(object):
+ """Iterate over an object and all of its parents."""
while object is not None:
yield object
object = getattr(object, '__parent__', None)
def inside(l1, l2):
- """Is l1 inside l2
+ """Test whether l1 is a successor of l2.
- L1 is inside l2 if l2 is an ancestor of l1.
+ l1 is a successor of l2 if l2 is in the chain of parents of l1 or l2
+ is l1.
"""
while l1 is not None:
if l1 is l2:
return True
l1 = l1.__parent__
-
return False
-
class ClassAndInstanceDescr(object):
def __init__(self, *args):
@@ -96,6 +94,7 @@ class LocationProxy(ProxyBase):
"""
+ zope.component.adapts(zope.interface.Interface)
zope.interface.implements(ILocation)
__slots__ = '__parent__', '__name__'
@@ -122,5 +121,3 @@ class LocationProxy(ProxyBase):
__reduce_ex__ = __reduce__
__providedBy__ = DecoratorSpecificationDescriptor()
-
- __Security_checker__ = DecoratedSecurityCheckerDescriptor()
diff --git a/src/zope/location/location.txt b/src/zope/location/location.txt
index 4446c09..c7d4afb 100644
--- a/src/zope/location/location.txt
+++ b/src/zope/location/location.txt
@@ -69,8 +69,8 @@ ancestor of l1.
LocationProxy
-------------
-The LocationProxy is a non-picklable proxy that can be put around objects that
-don't implement `ILocation`.
+The LocationProxy is a non-picklable proxy that can be put around
+objects that don't implement `ILocation`.
>>> from zope.location.location import LocationProxy
>>> l = [1, 2, 3]
@@ -121,13 +121,16 @@ If we locate the object again, nothing special happens:
True
-If the object does not provide ILocation a LocationProxy is returned:
+If the object does not provide ILocation an adapter can be provided:
+
+>>> import zope.interface
+>>> import zope.component
+>>> sm = zope.component.getGlobalSiteManager()
+>>> sm.registerAdapter(LocationProxy, required=(zope.interface.Interface,))
>>> l = [1, 2, 3]
>>> parent = Location()
>>> l_located = located(l, parent, 'l')
->>> l_located
-[1, 2, 3]
>>> l_located.__parent__ is parent
True
>>> l_located.__name__
@@ -145,3 +148,6 @@ When changing the name, we still do not get a different proxied object:
>>> l_located_3 = located(l_located, parent, 'new-name')
>>> l_located_3 is l_located_2
True
+
+>>> sm.unregisterAdapter(LocationProxy, required=(zope.interface.Interface,))
+True
diff --git a/src/zope/location/pickling.py b/src/zope/location/pickling.py
index 94fdaf0..8d7459c 100644
--- a/src/zope/location/pickling.py
+++ b/src/zope/location/pickling.py
@@ -1,6 +1,6 @@
##############################################################################
#
-# Copyright (c) 2003 Zope Corporation and Contributors.
+# Copyright (c) 2003-2009 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
@@ -20,8 +20,7 @@ __docformat__ = 'restructuredtext'
import cPickle
import tempfile
import zope.interface
-from zope.traversing.interfaces import IContainmentRoot
-from zope.traversing.interfaces import ITraverser
+import zope.location.interfaces
from zope.location.interfaces import ILocation
from zope.location.location import Location, inside
@@ -195,18 +194,22 @@ class PathPersistent(object):
>>> from zope.location.tests import TLocation
>>> root = TLocation()
- >>> zope.interface.directlyProvides(root, IContainmentRoot)
- >>> o3 = TLocation(); o3.__name__ = 'o3'
- >>> o3.__parent__ = root; root.o3 = o3
+ >>> zope.interface.directlyProvides(
+ ... root, zope.location.interfaces.IRoot)
+ >>> o3 = TLocation()
+ >>> o3.__name__ = 'o3'
+ >>> o3.__parent__ = root
+ >>> root.o3 = o3
>>> persistent.id(o3)
u'/o3'
- >>> o4 = TLocation(); o4.__name__ = 'o4'
- >>> o4.__parent__ = o3; o3.o4 = o4
+ >>> o4 = TLocation()
+ >>> o4.__name__ = 'o4'
+ >>> o4.__parent__ = o3
+ >>> o3.o4 = o4
>>> persistent.id(o4)
u'/o3/o4'
-
We also provide a load function that returns objects by traversing
given paths. It has to find the root based on the object given to
the constructor. Therefore, that object must also be rooted:
@@ -226,11 +229,10 @@ class PathPersistent(object):
if ILocation.providedBy(object):
if not inside(object, self.location):
return LocationPhysicallyLocatable(object).getPath()
-
return None
def load(self, path):
if path[:1] != u'/':
raise ValueError("ZPersistent paths must be absolute", path)
root = LocationPhysicallyLocatable(self.location).getRoot()
- return ITraverser(root).traverse(path[1:])
+ return zope.location.interfaces.ITraverser(root).traverse(path[1:])
diff --git a/src/zope/location/tests.py b/src/zope/location/tests.py
index e340667..89cc50a 100644
--- a/src/zope/location/tests.py
+++ b/src/zope/location/tests.py
@@ -1,6 +1,6 @@
##############################################################################
#
-# Copyright (c) 2003 Zope Corporation and Contributors.
+# Copyright (c) 2003-2009 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
@@ -15,13 +15,15 @@
$Id$
"""
+
import unittest
import zope.interface
import zope.testing.doctest
-from zope.traversing.interfaces import ITraverser
from zope.testing.doctestunit import DocTestSuite
+from zope.location.interfaces import ITraverser
from zope.location.location import Location
+
class TLocation(Location):
"""Simple traversable location used in examples."""
@@ -33,12 +35,10 @@ class TLocation(Location):
o = getattr(o, name)
return o
+
def test_suite():
return unittest.TestSuite((
zope.testing.doctest.DocFileSuite('location.txt'),
DocTestSuite('zope.location.traversing'),
DocTestSuite('zope.location.pickling'),
))
-
-if __name__ == '__main__':
- unittest.main(defaultTest='test_suite')
diff --git a/src/zope/location/traversing.py b/src/zope/location/traversing.py
index f3c9c04..25cd6e2 100644
--- a/src/zope/location/traversing.py
+++ b/src/zope/location/traversing.py
@@ -1,6 +1,6 @@
##############################################################################
#
-# Copyright (c) 2003 Zope Corporation and Contributors.
+# Copyright (c) 2003-2009 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
@@ -19,17 +19,18 @@ __docformat__ = 'restructuredtext'
import zope.component
import zope.interface
-from zope.traversing.interfaces import IPhysicallyLocatable
-from zope.traversing.interfaces import IContainmentRoot, ITraverser
-from zope.traversing.api import getParents
+from zope.location.interfaces import ILocationInfo
+from zope.location.interfaces import IRoot, ITraverser
from zope.location.interfaces import ILocation, ISite
from zope.location.location import Location
+
class LocationPhysicallyLocatable(object):
"""Provide location information for location objects
"""
+
zope.component.adapts(ILocation)
- zope.interface.implements(IPhysicallyLocatable)
+ zope.interface.implements(ILocationInfo)
def __init__(self, context):
self.context = context
@@ -37,13 +38,13 @@ class LocationPhysicallyLocatable(object):
def getRoot(self):
"""Get the root location for a location.
- See IPhysicallyLocatable
+ See ILocationInfo
The root location is a location that contains the given
location and that implements IContainmentRoot.
>>> root = Location()
- >>> zope.interface.directlyProvides(root, IContainmentRoot)
+ >>> zope.interface.directlyProvides(root, IRoot)
>>> LocationPhysicallyLocatable(root).getRoot() is root
1
@@ -80,7 +81,7 @@ class LocationPhysicallyLocatable(object):
context = self.context
max = 9999
while context is not None:
- if IContainmentRoot.providedBy(context):
+ if IRoot.providedBy(context):
return context
context = context.__parent__
max -= 1
@@ -93,12 +94,12 @@ class LocationPhysicallyLocatable(object):
def getPath(self):
"""Get the path of a location.
- See IPhysicallyLocatable
+ See ILocationInfo
This is an "absolute path", rooted at a root object.
>>> root = Location()
- >>> zope.interface.directlyProvides(root, IContainmentRoot)
+ >>> zope.interface.directlyProvides(root, IRoot)
>>> LocationPhysicallyLocatable(root).getPath()
u'/'
@@ -138,7 +139,7 @@ class LocationPhysicallyLocatable(object):
context = self.context
max = 9999
while context is not None:
- if IContainmentRoot.providedBy(context):
+ if IRoot.providedBy(context):
if path:
path.append('')
path.reverse()
@@ -154,10 +155,36 @@ class LocationPhysicallyLocatable(object):
raise TypeError("Not enough context to determine location root")
+ def getParents(self):
+ """Returns a list starting with the object's parent followed by
+ each of its parents.
+
+ Raises a TypeError if the object is not connected to a containment
+ root.
+
+ """
+ # XXX Merge this implementation with getPath. This was refactored
+ # from zope.traversing.
+ if IRoot.providedBy(self.context):
+ return []
+
+ parents = []
+ w = self.context
+ while 1:
+ w = w.__parent__
+ if w is None:
+ break
+ parents.append(w)
+
+ if parents and IRoot.providedBy(parents[-1]):
+ return parents
+
+ raise TypeError("Not enough context information to get all parents")
+
def getName(self):
"""Get a location name
- See IPhysicallyLocatable.
+ See ILocationInfo
>>> o1 = Location(); o1.__name__ = 'o1'
>>> LocationPhysicallyLocatable(o1).getName()
@@ -167,7 +194,7 @@ class LocationPhysicallyLocatable(object):
return self.context.__name__
def getNearestSite(self):
- """return the nearest site, see IPhysicallyLocatable
+ """return the nearest site, see ILocationInfo
>>> o1 = Location()
>>> o1.__name__ = 'o1'
@@ -177,7 +204,7 @@ class LocationPhysicallyLocatable(object):
TypeError: Not enough context information to get all parents
>>> root = Location()
- >>> zope.interface.directlyProvides(root, IContainmentRoot)
+ >>> zope.interface.directlyProvides(root, IRoot)
>>> o1 = Location()
>>> o1.__name__ = 'o1'
>>> o1.__parent__ = root
@@ -186,7 +213,7 @@ class LocationPhysicallyLocatable(object):
"""
if ISite.providedBy(self.context):
return self.context
- for parent in getParents(self.context):
+ for parent in self.getParents():
if ISite.providedBy(parent):
return parent
return self.getRoot()