diff options
author | Christian Theune <ct@gocept.com> | 2009-01-29 19:39:10 +0000 |
---|---|---|
committer | Christian Theune <ct@gocept.com> | 2009-01-29 19:39:10 +0000 |
commit | fd6cdecea104cb88b12cd28f84a1c9109f2016ac (patch) | |
tree | 2a4f8986c3277612a329abc58c6d14a973dbf7ce /src | |
parent | d4b2ddc96327f624ec69ab901b758fb663027258 (diff) | |
download | zope-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.cfg | 6 | ||||
-rw-r--r-- | src/zope/location/__init__.py | 4 | ||||
-rw-r--r-- | src/zope/location/configure.zcml | 3 | ||||
-rw-r--r-- | src/zope/location/interfaces.py | 112 | ||||
-rw-r--r-- | src/zope/location/location.py | 55 | ||||
-rw-r--r-- | src/zope/location/location.txt | 16 | ||||
-rw-r--r-- | src/zope/location/pickling.py | 24 | ||||
-rw-r--r-- | src/zope/location/tests.py | 10 | ||||
-rw-r--r-- | src/zope/location/traversing.py | 57 |
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() |