summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.txt27
-rw-r--r--LICENSE.txt60
-rw-r--r--docs/api/location.rst14
-rw-r--r--docs/index.rst1
-rw-r--r--docs/narr/models.rst21
-rw-r--r--docs/narr/security.rst48
-rw-r--r--repoze/bfg/interfaces.py8
-rw-r--r--repoze/bfg/location.py132
-rw-r--r--repoze/bfg/security.py6
-rw-r--r--repoze/bfg/tests/test_location.py82
-rw-r--r--repoze/bfg/tests/test_traversal.py2
-rw-r--r--repoze/bfg/tests/test_zcml.py6
-rw-r--r--repoze/bfg/traversal.py16
-rw-r--r--repoze/bfg/zcml.py8
-rw-r--r--setup.py16
15 files changed, 402 insertions, 45 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 82156c484..71537b48e 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,3 +1,30 @@
+Next release
+
+ Features
+
+ - A ``repoze.bfg.location`` API module was added.
+
+ Backwards incompatibilities
+
+ - Applications must now use the ``repoze.bfg.interfaces.ILocation``
+ interface rather than ``zope.location.interfaces.ILocation`` to
+ represent that a model object is "location-aware". We've removed
+ a dependency on ``zope.location`` for cleanliness purposes: as
+ new versions of zope libraries are released which have improved
+ dependency information, getting rid of our dependence on
+ ``zope.location`` will prevent a newly installed repoze.bfg
+ application from requiring the ``zope.security``, egg, which not
+ truly used at all in a "stock" repoze.bfg setup. These
+ dependencies are still required by the stack at this time; this
+ is purely a futureproofing move.
+
+ The security and model documentation for previous versions of
+ ``repoze.bfg`` recommended using the
+ ``zope.location.interfaces.ILocation`` interface to represent
+ that a model object is "location-aware". This documentation has
+ been changed to reflect that this interface should now be
+ imported from ``repoze.bfg.interfaces.ILocation`` instead.
+
0.3.8 (08/26/2008)
Docs
diff --git a/LICENSE.txt b/LICENSE.txt
index 5ced96e3b..af4b8ecc6 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,4 +1,4 @@
-License
+The majority of the code in bfg is supplied under this license:
A copyright notice accompanies this license document that identifies
the copyright holders.
@@ -39,3 +39,61 @@ License
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
+Portions of the code in repoze.bfg are supplied under the ZPL (headers
+within individiual files indicate that these portions are licensed
+under the ZPL):
+
+Zope Public License (ZPL) Version 2.1
+-------------------------------------
+
+A copyright notice accompanies this license document that
+identifies the copyright holders.
+
+This license has been certified as open source. It has also
+been designated as GPL compatible by the Free Software
+Foundation (FSF).
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the
+following conditions are met:
+
+1. Redistributions in source code must retain the
+ accompanying copyright notice, this list of conditions,
+ and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the accompanying
+ copyright notice, this list of conditions, and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+3. Names of the copyright holders must not be used to
+ endorse or promote products derived from this software
+ without prior written permission from the copyright
+ holders.
+
+4. The right to distribute this software or to use it for
+ any purpose does not give you the right to use
+ Servicemarks (sm) or Trademarks (tm) of the copyright
+ holders. Use of them is covered by separate agreement
+ with the copyright holders.
+
+5. If any files are modified, you must cause the modified
+ files to carry prominent notices stating that you changed
+ the files and the date of any change.
+
+Disclaimer
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS''
+ AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ NO EVENT SHALL THE COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ DAMAGE.
diff --git a/docs/api/location.rst b/docs/api/location.rst
new file mode 100644
index 000000000..dc7d50a2f
--- /dev/null
+++ b/docs/api/location.rst
@@ -0,0 +1,14 @@
+.. _location_module:
+
+:mod:`repoze.bfg.location`
+--------------------------
+
+.. automodule:: repoze.bfg.location
+
+ .. autofunction:: lineage
+
+ .. autofunction:: inside
+
+ .. autofunction:: locate
+
+
diff --git a/docs/index.rst b/docs/index.rst
index 8c0660246..594add0cb 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -72,6 +72,7 @@ Per-module :mod:`repoze.bfg` API documentation.
api/security
api/template
api/traversal
+ api/location
api/urldispatch
api/view
api/wsgi
diff --git a/docs/narr/models.rst b/docs/narr/models.rst
index 7df265657..e627a682c 100644
--- a/docs/narr/models.rst
+++ b/docs/narr/models.rst
@@ -74,7 +74,7 @@ by via ``__getitem__``.
If you choose not to manage the ``__name__`` and ``__parent__``
attributes of your models "by hand", :mod:`repoze.bfg`` is willing to
help you do this. If your "root" node claims it implements the
-interface ``zope.location.interfaces.ILocation``, you don't need to
+interface ``repoze.bfg.interfaces.ILocation``, you don't need to
manage these attributes by hand. During :term:`traversal`, if the
root node says it implements the ``ILocation`` :term:`interface`,
:mod:`repoze.bfg` will wrap each child in a ``LocationProxy`` which
@@ -84,7 +84,20 @@ recursively.
If you choose to make use of the location-based dynamic assignment of
``__parent__`` and ``__name__``, the root node must have a
``__parent__`` and a ``__name__`` that are both ``None``, and it must
-provide the ``ILocation`` interface. The easiest way to do this is to
-claim that the class representing the root node
-``implements(ILocation)``, as above.
+provide the ``repoze.bfg.interfaces.ILocation`` interface. The
+easiest way to do this is to claim that the class representing the
+root node ``implements(ILocation)``:
+
+.. code-block::
+ :linenos:
+
+ from repoze.bfg.interfaces import ILocation
+ from zope.interface import implements
+
+ class MyRootObject(object):
+ implements(ILocation)
+ __parent__ = None
+ __name__ = ''
+
+
diff --git a/docs/narr/security.rst b/docs/narr/security.rst
index 48c88cbde..76f488f43 100644
--- a/docs/narr/security.rst
+++ b/docs/narr/security.rst
@@ -84,19 +84,17 @@ class:
from repoze.bfg.security import Everyone
from repoze.bfg.security import Allow
- from zope.location.interfaces import ILocation
- from zope.location.location import Location
class IBlog(Interface):
pass
- class Blog(dict, Location):
+ class Blog(dict):
__acl__ = [
(Allow, Everyone, 'view'),
(Allow, 'group:editors', 'add'),
(Allow, 'group:editors', 'edit'),
]
- implements(IBlog, ILocation)
+ implements(IBlog)
The above ACL indicates that the ``Everyone`` principal (a special
system-defined principal indicating, literally, everyone) is allowed
@@ -128,26 +126,44 @@ Location-Awareness
------------------
In order to allow the security machinery to perform ACL inheritance,
-model objects should provide *location-awareness*.
+model objects must provide *location-awareness*. Providing
+location-awareness means two things: the root object in the graph must
+have a ``_name__`` and a ``__parent__`` attribute and the root object
+must be declared to implement the ``repoze.bfg.interfaces.ILocation``
+interface. For example:
-Objects have parents when they define an ``__parent__`` attribute
-which points at their parent object. The root object's ``__parent__``
-is ``None``. An object with a ``__parent__`` attribute and a
-``__name__`` attribute is said to be *location-aware*.
+.. code-block::
+ :linenos:
+
+ from repoze.bfg.interfaces import ILocation
+ from zope.interface import implements
+
+ class Blog(object):
+ implements(ILocation)
+ __name__ = ''
+ __parent__ = None
+
+An object with a ``__parent__`` attribute and a ``__name__`` attribute
+is said to be *location-aware*. Location-aware objects define an
+``__parent__`` attribute which points at their parent object. The
+root object's ``__parent__`` is ``None``.
If the root object in a :mod:`repoze.bfg` application declares that it
-implements the ``ILocation`` interface, it is assumed that the objects
-in the rest of the model are location-aware. Even if they are not
-explictly, if the root object is marked as ``ILocation``, the bfg
-framework will wrap each object during traversal in a *location
-proxy*, which will wrap each object found during traversal in a proxy
-object that has both the ``__name__`` and ``__parent__`` attributes,
-but otherwise acts the same as your model object.
+implements the ``repoze.bfg.interfaces.ILocation`` interface, it is
+assumed that the objects in the rest of the model are location-aware.
+If those objects are not explictly location-aware, if the root object
+is marked as ``ILocation``, the bfg framework will wrap each object
+during traversal in a *location proxy* that has both the ``__name__``
+and ``__parent__`` attributes, but otherwise acts the same as your
+model object.
You can of course supply ``__name__`` and ``__parent__`` attributes
explicitly on all of your model objects, and no location proxying will
be performed.
+See :ref:`location_module` for documentations of functions which use
+location-awareness.
+
Debugging Security Failures
---------------------------
diff --git a/repoze/bfg/interfaces.py b/repoze/bfg/interfaces.py
index 7647facb4..16a4a7b21 100644
--- a/repoze/bfg/interfaces.py
+++ b/repoze/bfg/interfaces.py
@@ -98,3 +98,11 @@ class IWSGIApplicationCreatedEvent(IObjectEvent):
configured."""
app = Attribute(u"Published application")
+
+class ILocation(Interface):
+ """Objects that have a structural location"""
+
+ __parent__ = Attribute("The parent in the location hierarchy")
+
+ __name__ = Attribute("The name within the parent")
+
diff --git a/repoze/bfg/location.py b/repoze/bfg/location.py
new file mode 100644
index 000000000..11f08faf9
--- /dev/null
+++ b/repoze/bfg/location.py
@@ -0,0 +1,132 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+"""Location support borrowed from ``zope.location``, but without
+``zope.security`` support, which is not used by ``repoze.bfg``
+"""
+
+import zope.interface
+from repoze.bfg.interfaces import ILocation
+from zope.proxy import ProxyBase, getProxiedObject, non_overridable
+from zope.proxy.decorator import DecoratorSpecificationDescriptor
+
+def inside(model1, model2):
+ """Is ``model1`` 'inside' ``model2``? Return ``True`` if so, else
+ ``False``.
+
+ ``model1`` is 'inside' ``model2`` if ``model2`` is a `location
+ ancestor` of ``model1``. It is a location ancestor if its parent
+ (or one of its parent's parents, etc.) is an ancestor.
+ """
+ while model1 is not None:
+ if model1 is model2:
+ return True
+ model1 = model1.__parent__
+
+ return False
+
+def locate(model, parent, name=None):
+ """
+ If ``model`` explicitly provides the
+ ``repoze.bfg.interfaces.ILocation`` interface, locate ``model``
+ directly set ``model`` 's ``__parent__`` attribute to the
+ ``parent`` object (also a model), and its ``__name__`` to the
+ supplied ``name`` argument.
+
+ If ``model`` does *not* explicitly provide the
+ ``repoze.bfg.interfaces.ILocation`` interface, return a
+ ``LocationProxy`` object representing ``model`` with its
+ ``__parent__`` attribute assigned to ``parent`` and a ``__name__``
+ attribute assigned to ``__name__``. A ``LocationProxy`` object is
+ an unpickleable proxy that can 'stand in' for arbitrary object
+ instances.
+ """
+ if ILocation.providedBy(model):
+ if parent is not model.__parent__ or name != model.__name__:
+ _locate(model, parent, name)
+ return model
+ return LocationProxy(model, parent, name)
+
+def lineage(model):
+ """
+ Return a generator representing the model lineage. The generator
+ first returns ``model`` unconditionally. Then, if ``model``
+ supplies a ``__parent__`` attribute, return the object represented
+ by ``model.__parent__``. If *that* object has a ``__parent__``
+ attribute, return that object's parent, and so on, until the
+ object being inspected either has no ``__parent__`` attribute or
+ which has a ``__parent__`` attribute of ``None``. For example, if
+ the object tree is::
+
+ thing1 = Thing()
+ thing2 = Thing()
+ thing2.__parent__ = thing1
+
+ Calling ``lineage(thing2)`` will return a generator. When we turn
+ it into a list, we will get::
+
+ list(lineage(thing2))
+ [ <Thing object at thing2>, <Thing object at thing1> ]
+ """
+ while model is not None:
+ yield model
+ model = getattr(model, '__parent__', None)
+
+def _locate(model, parent, name=None):
+ model.__parent__ = parent
+ model.__name__ = name
+
+class ClassAndInstanceDescr(object):
+
+ def __init__(self, *args):
+ self.funcs = args
+
+ def __get__(self, inst, cls):
+ if inst is None:
+ return self.funcs[1](cls)
+ return self.funcs[0](inst)
+
+class LocationProxy(ProxyBase):
+ """Location-object proxy
+
+ This is a non-picklable proxy that can be put around objects that
+ don't implement `ILocation`.
+ """
+
+ zope.interface.implements(ILocation)
+
+ __slots__ = '__parent__', '__name__'
+ __safe_for_unpickling__ = True
+
+ def __new__(self, ob, container=None, name=None):
+ return ProxyBase.__new__(self, ob)
+
+ def __init__(self, ob, container=None, name=None):
+ ProxyBase.__init__(self, ob)
+ self.__parent__ = container
+ self.__name__ = name
+
+ @non_overridable
+ def __reduce__(self, proto=None):
+ raise TypeError("Not picklable")
+
+ __doc__ = ClassAndInstanceDescr(
+ lambda inst: getProxiedObject(inst).__doc__,
+ lambda cls, __doc__ = __doc__: __doc__,
+ )
+
+ __reduce_ex__ = __reduce__
+
+ __providedBy__ = DecoratorSpecificationDescriptor()
+
diff --git a/repoze/bfg/security.py b/repoze/bfg/security.py
index 6c1f262f1..49f47bc89 100644
--- a/repoze/bfg/security.py
+++ b/repoze/bfg/security.py
@@ -5,7 +5,7 @@ import sys
from zope.interface import implements
from zope.component import queryUtility
-from zope.location.location import LocationIterator
+from repoze.bfg.location import lineage
from repoze.bfg.interfaces import ISecurityPolicy
from repoze.bfg.interfaces import IViewPermission
@@ -109,7 +109,7 @@ class ACLSecurityPolicy(object):
""" Return ``Allowed`` if the policy permits access,
``Denied`` if not."""
principals = self.effective_principals(request)
- for location in LocationIterator(context):
+ for location in lineage(context):
authorizer = self.authorizer_factory(location, self.logger)
try:
return authorizer.permits(permission, *principals)
@@ -134,7 +134,7 @@ class ACLSecurityPolicy(object):
return effective_principals
def principals_allowed_by_permission(self, context, permission):
- for location in LocationIterator(context):
+ for location in lineage(context):
acl = getattr(location, '__acl__', None)
if acl is not None:
allowed = {}
diff --git a/repoze/bfg/tests/test_location.py b/repoze/bfg/tests/test_location.py
new file mode 100644
index 000000000..fc8f8c621
--- /dev/null
+++ b/repoze/bfg/tests/test_location.py
@@ -0,0 +1,82 @@
+import unittest
+
+class TestLocation(unittest.TestCase):
+ def test_inside(self):
+ o1 = Location()
+ o2 = Location(); o2.__parent__ = o1
+ o3 = Location(); o3.__parent__ = o2
+ o4 = Location(); o4.__parent__ = o3
+
+ from repoze.bfg.location import inside
+ self.assertEqual(inside(o1, o1), True)
+ self.assertEqual(inside(o2, o1), True)
+ self.assertEqual(inside(o3, o1), True)
+ self.assertEqual(inside(o4, o1), True)
+ self.assertEqual(inside(o1, o4), False)
+ self.assertEqual(inside(o1, None), False)
+
+ def test_locate(self):
+ from repoze.bfg.location import locate
+ from repoze.bfg.location import LocationProxy
+
+ a = Location()
+ parent = Location()
+ a_located = locate(a, parent, 'a')
+ self.failUnless(a_located is a)
+ self.failUnless(a_located.__parent__ is parent)
+ self.assertEqual(a_located.__name__, 'a')
+ # If we locate the object again, nothing special happens:
+
+ a_located_2 = locate(a_located, parent, 'a')
+ self.failUnless(a_located_2 is a_located)
+
+ # If the object does not provide ILocation a LocationProxy is returned:
+
+ l = [1, 2, 3]
+ parent = Location()
+ l_located = locate(l, parent, 'l')
+ self.assertEqual(l_located, [1, 2, 3])
+ self.failUnless(l_located.__parent__ is parent)
+ self.assertEqual(l_located.__name__, 'l')
+ self.failIf(l_located is l)
+ self.assertEqual(type(l_located), LocationProxy)
+
+ l_located_2 = locate(l_located, parent, 'l')
+ self.failUnless(l_located_2 is l_located)
+ # When changing the name, we still do not get a different proxied
+ # object:
+
+ l_located_3 = locate(l_located, parent, 'new-name')
+ self.failUnless(l_located_3 is l_located_2)
+
+ def test_LocationProxy(self):
+ from repoze.bfg.location import LocationProxy
+ from repoze.bfg.interfaces import ILocation
+ l = [1, 2, 3]
+ self.assertEqual(ILocation.providedBy(l), False)
+ p = LocationProxy(l, "Dad", "p")
+ self.assertEqual(p, [1, 2, 3])
+ self.assertEqual(ILocation.providedBy(p), True)
+ self.assertEqual(p.__parent__, 'Dad')
+ self.assertEqual(p.__name__, 'p')
+ import pickle
+ self.assertRaises(TypeError, pickle.dumps, p)
+ # Proxies should get their doc strings from the object they proxy:
+ self.assertEqual(p.__doc__, l.__doc__)
+
+ def test_lineage(self):
+ from repoze.bfg.location import lineage
+ o1 = Location()
+ o2 = Location(); o2.__parent__ = o1
+ o3 = Location(); o3.__parent__ = o2
+ o4 = Location(); o4.__parent__ = o3
+ result = list(lineage(o3))
+ self.assertEqual(result, [o3, o2, o1])
+ result = list(lineage(o1))
+ self.assertEqual(result, [o1])
+
+from repoze.bfg.interfaces import ILocation
+from zope.interface import implements
+class Location(object):
+ implements(ILocation)
+ __name__ = __parent__ = None
diff --git a/repoze/bfg/tests/test_traversal.py b/repoze/bfg/tests/test_traversal.py
index 8034cf39b..4dced41ca 100644
--- a/repoze/bfg/tests/test_traversal.py
+++ b/repoze/bfg/tests/test_traversal.py
@@ -112,7 +112,7 @@ class ModelGraphTraverserTests(unittest.TestCase, PlacelessSetup):
foo = DummyContext(bar)
root = DummyContext(foo)
from zope.interface import directlyProvides
- from zope.location.interfaces import ILocation
+ from repoze.bfg.interfaces import ILocation
directlyProvides(root, ILocation)
root.__name__ = None
root.__parent__ = None
diff --git a/repoze/bfg/tests/test_zcml.py b/repoze/bfg/tests/test_zcml.py
index 158743c64..6d79606ad 100644
--- a/repoze/bfg/tests/test_zcml.py
+++ b/repoze/bfg/tests/test_zcml.py
@@ -32,7 +32,7 @@ class TestViewDirective(unittest.TestCase, PlacelessSetup):
from repoze.bfg.interfaces import IView
from repoze.bfg.interfaces import IViewPermission
from repoze.bfg.security import ViewPermissionFactory
- from zope.component.zcml import handler
+ from repoze.bfg.zcml import handler
from zope.component.interface import provideInterface
self.assertEqual(len(actions), 3)
@@ -79,7 +79,7 @@ class TestViewDirective(unittest.TestCase, PlacelessSetup):
from repoze.bfg.interfaces import IView
from repoze.bfg.interfaces import IViewPermission
from repoze.bfg.security import ViewPermissionFactory
- from zope.component.zcml import handler
+ from repoze.bfg.zcml import handler
from zope.component.interface import provideInterface
self.assertEqual(len(actions), 3)
@@ -125,7 +125,7 @@ class TestViewDirective(unittest.TestCase, PlacelessSetup):
cacheable=False)
actions = context.actions
from repoze.bfg.interfaces import IView
- from zope.component.zcml import handler
+ from repoze.bfg.zcml import handler
from repoze.bfg.zcml import Uncacheable
self.assertEqual(len(actions), 3)
diff --git a/repoze/bfg/traversal.py b/repoze/bfg/traversal.py
index 9783955de..f5d45cd55 100644
--- a/repoze/bfg/traversal.py
+++ b/repoze/bfg/traversal.py
@@ -3,10 +3,10 @@ import urlparse
from zope.interface import classProvides
from zope.interface import implements
-from zope.location.location import located
-from zope.location.location import LocationIterator
-from zope.location.interfaces import ILocation
+from repoze.bfg.location import locate
+from repoze.bfg.location import lineage
+from repoze.bfg.interfaces import ILocation
from repoze.bfg.interfaces import ITraverser
from repoze.bfg.interfaces import ITraverserFactory
@@ -60,7 +60,7 @@ class ModelGraphTraverser(object):
name = segment
break
if self.locatable:
- next = located(next, ob, segment)
+ next = locate(next, ob, segment)
ob = next
return ob, name, path
@@ -68,7 +68,7 @@ class ModelGraphTraverser(object):
def find_root(model):
""" Find the root node in the graph to which ``model``
belongs. Note that ``model`` should be :term:`location`-aware."""
- for location in LocationIterator(model):
+ for location in lineage(model):
if location.__parent__ is None:
model = location
break
@@ -99,7 +99,7 @@ def find_interface(model, interface):
``interface`` in the parent chain of ``model`` or ``None`` if no
object providing ``interface`` can be found in the parent chain.
The ``model`` passed in should be :term:`location`-aware."""
- for location in LocationIterator(model):
+ for location in lineage(model):
if interface.providedBy(location):
return location
@@ -112,7 +112,7 @@ def model_url(model, request, *elements):
URL-quoted. The ``model`` passed in must be
:term:`location`-aware."""
rpath = []
- for location in LocationIterator(model):
+ for location in lineage(model):
if location.__name__:
rpath.append(urllib.quote(location.__name__))
path = list(reversed(rpath))
@@ -127,7 +127,7 @@ def model_path(model, *elements):
will be joined by slashes and appended to the generated path. The
``model`` passed in must be :term:`location`-aware."""
rpath = []
- for location in LocationIterator(model):
+ for location in lineage(model):
if location.__name__:
rpath.append(location.__name__)
path = list(reversed(rpath))
diff --git a/repoze/bfg/zcml.py b/repoze/bfg/zcml.py
index b5de0584a..a38c39ae1 100644
--- a/repoze/bfg/zcml.py
+++ b/repoze/bfg/zcml.py
@@ -4,9 +4,11 @@ from os.path import realpath
import time
from zope.configuration import xmlconfig
+
+from zope.component import getGlobalSiteManager
import zope.configuration.config
-from zope.component.zcml import handler
+
from zope.component.interface import provideInterface
from zope.configuration.exceptions import ConfigurationError
from zope.configuration.fields import GlobalObject
@@ -22,6 +24,10 @@ from repoze.bfg.path import package_path
from repoze.bfg.security import ViewPermissionFactory
+def handler(methodName, *args, **kwargs):
+ method = getattr(getGlobalSiteManager(), methodName)
+ method(*args, **kwargs)
+
class Uncacheable(object):
""" Include in discriminators of actions which are not cacheable """
pass
diff --git a/setup.py b/setup.py
index 3aa1aa78f..7cdc7f8d1 100644
--- a/setup.py
+++ b/setup.py
@@ -26,18 +26,18 @@ README = open(os.path.join(here, 'README.txt')).read()
CHANGES = open(os.path.join(here, 'CHANGES.txt')).read()
install_requires=[
- 'zope.interface',
- 'zope.component',
- 'zope.testing',
- 'zope.hookable',
- 'zope.event',
- 'WebOb',
- 'PasteScript',
'chameleon.core [lxml]',
- 'chameleon.zpt',
'chameleon.genshi',
+ 'chameleon.zpt',
+ 'PasteScript',
'Routes',
'setuptools',
+ 'WebOb',
+ 'zope.component',
+ 'zope.configuration',
+ 'zope.hookable',
+ 'zope.interface',
+ 'zope.proxy',
]
setup(name='repoze.bfg',