summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@agendaless.com>2009-11-25 17:54:40 +0000
committerChris McDonough <chrism@agendaless.com>2009-11-25 17:54:40 +0000
commit103efb5aae885a70590ad1f3c5807af9da8d1ab7 (patch)
tree1b335d2d088b84544f8ef5bad713bc8922674153
parent2aa86c1a5227a675d32c4ede06e0c031ae3edfad (diff)
downloadpyramid-103efb5aae885a70590ad1f3c5807af9da8d1ab7.tar.gz
pyramid-103efb5aae885a70590ad1f3c5807af9da8d1ab7.tar.bz2
pyramid-103efb5aae885a70590ad1f3c5807af9da8d1ab7.zip
- A dependency on the ``repoze.zcml`` package has been removed (its
functionality is replaced internally).
-rw-r--r--CHANGES.txt18
-rw-r--r--repoze/bfg/includes/configure.zcml4
-rw-r--r--repoze/bfg/includes/meta.zcml18
-rw-r--r--repoze/bfg/tests/test_zcml.py273
-rw-r--r--repoze/bfg/zcml.py230
-rw-r--r--setup.py2
6 files changed, 534 insertions, 11 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index c871769d7..ff30b2447 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -126,6 +126,10 @@ Internals
- Instead of depending on the ``martian`` package to do code scanning,
we now just use our own scanning routines.
+- We now no longer have a dependency on ``repoze.zcml`` package;
+ instead, the ``repoze.bfg`` package includes implementations of the
+ ``adapter``, ``subscriber`` and ``utility`` directives.
+
Backwards Incompatibilites
--------------------------
@@ -218,6 +222,15 @@ Backwards Incompatibilites
add an attribute to your unit test request object named
``registry`` with the result.
+- Because the ``repoze.bfg`` package includes implementations of the
+ ``adapter``, ``subscriber`` and ``utility`` ZCML directives, it is
+ now an error to have ``<include package="repoze.zcml"
+ file="meta.zcml"/>`` in the ZCML of a ``repoze.bfg`` application. A
+ ZCML conflict error will be raised if your ZCML does so. This
+ shouldn't be an issue for "normal" installations; it has always been
+ the responsibility of the ``repoze.bfg.includes`` ZCML to include
+ this file in the past; it now just doesn't.
+
Deprecations
------------
@@ -230,7 +243,10 @@ Deprecations
Dependencies
------------
-- The dependency on the ``martian`` package has been removed (its
+- A dependency on the ``martian`` package has been removed (its
+ functionality is replaced internally).
+
+- A dependency on the ``repoze.zcml`` package has been removed (its
functionality is replaced internally).
1.1.1 (2009-11-21)
diff --git a/repoze/bfg/includes/configure.zcml b/repoze/bfg/includes/configure.zcml
index ffabca9a3..6a373fa5d 100644
--- a/repoze/bfg/includes/configure.zcml
+++ b/repoze/bfg/includes/configure.zcml
@@ -1,9 +1,5 @@
<configure xmlns="http://namespaces.repoze.org/bfg">
- <include package="repoze.zcml" file="meta.zcml" />
-
- <!-- traversal adapters -->
-
<include file="meta.zcml" />
</configure>
diff --git a/repoze/bfg/includes/meta.zcml b/repoze/bfg/includes/meta.zcml
index 4c9d18873..feb44f5dd 100644
--- a/repoze/bfg/includes/meta.zcml
+++ b/repoze/bfg/includes/meta.zcml
@@ -76,6 +76,24 @@
handler="repoze.bfg.zcml.renderer"
/>
+ <meta:directive
+ name="adapter"
+ schema="repoze.bfg.zcml.IAdapterDirective"
+ handler="repoze.bfg.zcml.adapter"
+ />
+
+ <meta:directive
+ name="subscriber"
+ schema="repoze.bfg.zcml.ISubscriberDirective"
+ handler="repoze.bfg.zcml.subscriber"
+ />
+
+ <meta:directive
+ name="utility"
+ schema="repoze.bfg.zcml.IUtilityDirective"
+ handler="repoze.bfg.zcml.utility"
+ />
+
</meta:directives>
</configure>
diff --git a/repoze/bfg/tests/test_zcml.py b/repoze/bfg/tests/test_zcml.py
index 34250bdde..2834a8cb1 100644
--- a/repoze/bfg/tests/test_zcml.py
+++ b/repoze/bfg/tests/test_zcml.py
@@ -6,6 +6,9 @@ import unittest
from repoze.bfg import testing
+from zope.interface import Interface
+from zope.interface import implements
+
class TestViewDirective(unittest.TestCase):
def setUp(self):
testing.setUp()
@@ -617,6 +620,269 @@ class TestZCMLScanDirective(unittest.TestCase):
self.assertEqual(action['discriminator'], None)
self.assertEqual(action['args'], (dummy_module, None))
+
+class TestAdapterDirective(unittest.TestCase):
+ def setUp(self):
+ testing.setUp()
+
+ def tearDown(self):
+ testing.tearDown()
+
+ def _callFUT(self, *arg, **kw):
+ from repoze.bfg.zcml import adapter
+ return adapter(*arg, **kw)
+
+ def test_for_is_None_no_adaptedBy(self):
+ context = DummyContext()
+ factory = DummyFactory()
+ self.assertRaises(TypeError, self._callFUT, context, [factory],
+ provides=None, for_=None)
+
+ def test_for_is_None_adaptedBy_still_None(self):
+ context = DummyContext()
+ factory = DummyFactory()
+ factory.__component_adapts__ = None
+ self.assertRaises(TypeError, self._callFUT, context, [factory],
+ provides=None, for_=None)
+
+ def test_for_is_None_adaptedBy_set(self):
+ from repoze.bfg.zcml import handler
+ context = DummyContext()
+ factory = DummyFactory()
+ factory.__component_adapts__ = (IDummy,)
+ self._callFUT(context, [factory], provides=IFactory, for_=None)
+ self.assertEqual(len(context.actions), 1)
+ regadapt = context.actions[0]
+ self.assertEqual(regadapt['discriminator'],
+ ('adapter', (IDummy,), IFactory, ''))
+ self.assertEqual(regadapt['callable'],
+ handler)
+ self.assertEqual(regadapt['args'],
+ ('registerAdapter', factory, (IDummy,), IFactory,
+ '', None))
+
+ def test_provides_missing(self):
+ context = DummyContext()
+ factory = DummyFactory()
+ self.assertRaises(TypeError, self._callFUT, context, [factory],
+ provides=None, for_=(IDummy,))
+
+ def test_provides_obtained_via_implementedBy(self):
+ from repoze.bfg.zcml import handler
+ context = DummyContext()
+ self._callFUT(context, [DummyFactory], for_=(IDummy,))
+ regadapt = context.actions[0]
+ self.assertEqual(regadapt['discriminator'],
+ ('adapter', (IDummy,), IFactory, ''))
+ self.assertEqual(regadapt['callable'],
+ handler)
+ self.assertEqual(regadapt['args'],
+ ('registerAdapter', DummyFactory, (IDummy,), IFactory,
+ '', None))
+
+ def test_multiple_factories_multiple_for(self):
+ context = DummyContext()
+ factory = DummyFactory()
+ self.assertRaises(ValueError, self._callFUT, context,
+ [factory, factory],
+ provides=IFactory,
+ for_=(IDummy, IDummy))
+
+ def test_no_factories_multiple_for(self):
+ context = DummyContext()
+ factory = DummyFactory()
+ self.assertRaises(ValueError, self._callFUT, context,
+ factory=[],
+ provides=IFactory,
+ for_=(IDummy, IDummy))
+
+ def test_rolled_up_factories(self):
+ from repoze.bfg.zcml import handler
+ context = DummyContext()
+ factory = DummyFactory()
+ self._callFUT(context,
+ [factory, factory],
+ provides=IFactory,
+ for_=(IDummy,))
+ regadapt = context.actions[0]
+ self.assertEqual(regadapt['discriminator'],
+ ('adapter', (IDummy,), IFactory, ''))
+ self.assertEqual(regadapt['callable'],
+ handler)
+ self.assertEqual(len(regadapt['args']), 6)
+
+
+class TestSubscriberDirective(unittest.TestCase):
+ def setUp(self):
+ testing.setUp()
+
+ def tearDown(self):
+ testing.tearDown()
+
+ def _callFUT(self, *arg, **kw):
+ from repoze.bfg.zcml import subscriber
+ return subscriber(*arg, **kw)
+
+ def test_no_factory_no_handler(self):
+ context = DummyContext()
+ self.assertRaises(TypeError,
+ self._callFUT, context, for_=None, factory=None,
+ handler=None,
+ provides=None)
+
+ def test_handler_with_provides(self):
+ context = DummyContext()
+ self.assertRaises(TypeError,
+ self._callFUT, context, for_=None, factory=None,
+ handler=1, provides=1)
+
+ def test_handler_and_factory(self):
+ context = DummyContext()
+ self.assertRaises(TypeError,
+ self._callFUT, context, for_=None, factory=1,
+ handler=1, provides=None)
+
+ def test_no_provides_with_factory(self):
+ context = DummyContext()
+ self.assertRaises(TypeError,
+ self._callFUT, context, for_=None, factory=1,
+ handler=None, provides=None)
+
+ def test_adapted_by_as_for_is_None(self):
+ context = DummyContext()
+ factory = DummyFactory()
+ factory.__component_adapts__ = None
+ self.assertRaises(TypeError, self._callFUT, context, for_=None,
+ factory=factory, handler=None, provides=IFactory)
+
+ def test_register_with_factory(self):
+ from repoze.bfg.zcml import handler
+ context = DummyContext()
+ factory = DummyFactory()
+ self._callFUT(context, for_=(IDummy,),
+ factory=factory, handler=None, provides=IFactory)
+ self.assertEqual(len(context.actions), 1)
+ subadapt = context.actions[0]
+ self.assertEqual(subadapt['discriminator'], None)
+ self.assertEqual(subadapt['callable'], handler)
+ self.assertEqual(subadapt['args'],
+ ('registerSubscriptionAdapter', factory,
+ (IDummy,), IFactory, u'', None) )
+
+ def test_register_with_handler(self):
+ from repoze.bfg.zcml import handler
+ context = DummyContext()
+ factory = DummyFactory()
+ self._callFUT(context, for_=(IDummy,),
+ factory=None, handler=factory)
+ self.assertEqual(len(context.actions), 1)
+ subadapt = context.actions[0]
+ self.assertEqual(subadapt['discriminator'], None)
+ self.assertEqual(subadapt['callable'], handler)
+ self.assertEqual(subadapt['args'],
+ ('registerHandler', factory,
+ (IDummy,), u'', None) )
+
+class TestUtilityDirective(unittest.TestCase):
+ def setUp(self):
+ testing.setUp()
+
+ def tearDown(self):
+ testing.tearDown()
+
+ def _callFUT(self, *arg, **kw):
+ from repoze.bfg.zcml import utility
+ return utility(*arg, **kw)
+
+ def test_factory_and_component(self):
+ context = DummyContext()
+ self.assertRaises(TypeError, self._callFUT,
+ context, factory=1, component=1)
+
+ def test_missing_provides(self):
+ context = DummyContext()
+ self.assertRaises(TypeError, self._callFUT, context, provides=None)
+
+ def test_provides_from_factory_implements(self):
+ from repoze.bfg.zcml import handler
+ context = DummyContext()
+ self._callFUT(context, factory=DummyFactory)
+ self.assertEqual(len(context.actions), 1)
+ utility = context.actions[0]
+ self.assertEqual(utility['discriminator'], ('utility', IFactory, ''))
+ self.assertEqual(utility['callable'], handler)
+ self.assertEqual(utility['args'],
+ ('registerUtility', None, IFactory, ''))
+ self.assertEqual(utility['kw'],
+ {'factory': DummyFactory})
+
+ def test_provides_from_component_provides(self):
+ from repoze.bfg.zcml import handler
+ context = DummyContext()
+ component = DummyFactory()
+ self._callFUT(context, component=component)
+ self.assertEqual(len(context.actions), 1)
+ utility = context.actions[0]
+ self.assertEqual(utility['discriminator'], ('utility', IFactory, ''))
+ self.assertEqual(utility['callable'], handler)
+ self.assertEqual(utility['args'],
+ ('registerUtility', component, IFactory, ''))
+ self.assertEqual(utility['kw'], {})
+
+class TestLoadZCML(unittest.TestCase):
+ def setUp(self):
+ testing.setUp()
+
+ def tearDown(self):
+ testing.tearDown()
+
+ def test_it(self):
+ from zope.configuration import xmlconfig
+ import repoze.bfg.includes
+ xmlconfig.file('configure.zcml', package=repoze.bfg.includes)
+
+class TestHandler(unittest.TestCase):
+ def setUp(self):
+ testing.setUp()
+
+ def tearDown(self):
+ testing.tearDown()
+
+ def _callFUT(self, methodName, *arg, **kw):
+ from repoze.bfg.zcml import handler
+ return handler(methodName, *arg, **kw)
+
+ def test_it(self):
+ def foo():
+ pass
+ from zope.interface import Interface
+ class IWhatever(Interface):
+ pass
+ self._callFUT('registerUtility', foo, IWhatever)
+
+class TestRolledUpFactory(unittest.TestCase):
+ def _callFUT(self, *factories):
+ from repoze.bfg.zcml import _rolledUpFactory
+ return _rolledUpFactory(factories)
+
+ def test_it(self):
+ def foo(ob):
+ return ob
+ factory = self._callFUT(foo, foo)
+ result = factory(True)
+ self.assertEqual(result, True)
+
+class IDummy(Interface):
+ pass
+
+class IFactory(Interface):
+ pass
+
+class DummyFactory(object):
+ implements(IFactory)
+ def __call__(self):
+ return 1
+
class DummyModule:
__path__ = "foo"
__name__ = "dummy"
@@ -649,7 +915,8 @@ class DummyContext:
self.actions.append(
{'discriminator':discriminator,
'callable':callable,
- 'args':args}
+ 'args':args,
+ 'kw':kw}
)
def path(self, path):
@@ -664,10 +931,6 @@ class Dummy:
class DummyRoute:
pass
-from zope.interface import Interface
-class IDummy(Interface):
- pass
-
class DummyRequest:
subpath = ()
def __init__(self, environ=None):
diff --git a/repoze/bfg/zcml.py b/repoze/bfg/zcml.py
index b97456668..91252ff53 100644
--- a/repoze/bfg/zcml.py
+++ b/repoze/bfg/zcml.py
@@ -1,9 +1,15 @@
+from zope.component import adaptedBy
+
from zope.configuration.exceptions import ConfigurationError
+from zope.configuration.fields import GlobalInterface
from zope.configuration.fields import GlobalObject
+from zope.configuration.fields import Tokens
from zope.configuration.config import ConfigurationMachine
from zope.configuration import xmlconfig
from zope.interface import Interface
+from zope.interface import implementedBy
+from zope.interface import providedBy
from zope.schema import Bool
from zope.schema import Int
@@ -524,3 +530,227 @@ def zcml_configure(name, package):
file_configure = zcml_configure # backwards compat (>0.8.1)
+def handler(methodName, *args, **kwargs):
+ registry = get_current_registry()
+ method = getattr(registry, methodName)
+ method(*args, **kwargs)
+
+def adapter(_context, factory, provides=None, for_=None, name=''):
+
+ if for_ is None:
+ if len(factory) == 1:
+ for_ = adaptedBy(factory[0])
+
+ if for_ is None:
+ raise TypeError("No for attribute was provided and can't "
+ "determine what the factory adapts.")
+
+ for_ = tuple(for_)
+
+ if provides is None:
+ if len(factory) == 1:
+ p = list(implementedBy(factory[0]))
+ if len(p) == 1:
+ provides = p[0]
+
+ if provides is None:
+ raise TypeError("Missing 'provides' attribute")
+
+ # Generate a single factory from multiple factories:
+ factories = factory
+ if len(factories) == 1:
+ factory = factories[0]
+ elif len(factories) < 1:
+ raise ValueError("No factory specified")
+ elif len(factories) > 1 and len(for_) != 1:
+ raise ValueError("Can't use multiple factories and multiple for")
+ else:
+ factory = _rolledUpFactory(factories)
+
+ _context.action(
+ discriminator = ('adapter', for_, provides, name),
+ callable = handler,
+ args = ('registerAdapter',
+ factory, for_, provides, name, _context.info),
+ )
+
+class IAdapterDirective(Interface):
+ """
+ Register an adapter
+ """
+
+ factory = Tokens(
+ title=u"Adapter factory/factories",
+ description=(u"A list of factories (usually just one) that create"
+ " the adapter instance."),
+ required=True,
+ value_type=GlobalObject()
+ )
+
+ provides = GlobalInterface(
+ title=u"Interface the component provides",
+ description=(u"This attribute specifies the interface the adapter"
+ " instance must provide."),
+ required=False,
+ )
+
+ for_ = Tokens(
+ title=u"Specifications to be adapted",
+ description=u"This should be a list of interfaces or classes",
+ required=False,
+ value_type=GlobalObject(
+ missing_value=object(),
+ ),
+ )
+
+ name = TextLine(
+ title=u"Name",
+ description=(u"Adapters can have names.\n\n"
+ "This attribute allows you to specify the name for"
+ " this adapter."),
+ required=False,
+ )
+
+_handler = handler
+def subscriber(_context, for_=None, factory=None, handler=None, provides=None):
+ if factory is None:
+ if handler is None:
+ raise TypeError("No factory or handler provided")
+ if provides is not None:
+ raise TypeError("Cannot use handler with provides")
+ factory = handler
+ else:
+ if handler is not None:
+ raise TypeError("Cannot use handler with factory")
+ if provides is None:
+ raise TypeError(
+ "You must specify a provided interface when registering "
+ "a factory")
+
+ if for_ is None:
+ for_ = adaptedBy(factory)
+ if for_ is None:
+ raise TypeError("No for attribute was provided and can't "
+ "determine what the factory (or handler) adapts.")
+
+ for_ = tuple(for_)
+
+ if handler is not None:
+ _context.action(
+ discriminator = None,
+ callable = _handler,
+ args = ('registerHandler',
+ handler, for_, u'', _context.info),
+ )
+ else:
+ _context.action(
+ discriminator = None,
+ callable = _handler,
+ args = ('registerSubscriptionAdapter',
+ factory, for_, provides, u'', _context.info),
+ )
+
+class ISubscriberDirective(Interface):
+ """
+ Register a subscriber
+ """
+
+ factory = GlobalObject(
+ title=u"Subscriber factory",
+ description=u"A factory used to create the subscriber instance.",
+ required=False,
+ )
+
+ handler = GlobalObject(
+ title=u"Handler",
+ description=u"A callable object that handles events.",
+ required=False,
+ )
+
+ provides = GlobalInterface(
+ title=u"Interface the component provides",
+ description=(u"This attribute specifies the interface the adapter"
+ " instance must provide."),
+ required=False,
+ )
+
+ for_ = Tokens(
+ title=u"Interfaces or classes that this subscriber depends on",
+ description=u"This should be a list of interfaces or classes",
+ required=False,
+ value_type=GlobalObject(
+ missing_value = object(),
+ ),
+ )
+
+def utility(_context, provides=None, component=None, factory=None, name=''):
+ if factory and component:
+ raise TypeError("Can't specify factory and component.")
+
+ if provides is None:
+ if factory:
+ provides = list(implementedBy(factory))
+ else:
+ provides = list(providedBy(component))
+ if len(provides) == 1:
+ provides = provides[0]
+ else:
+ raise TypeError("Missing 'provides' attribute")
+
+ if factory:
+ kw = dict(factory=factory)
+ else:
+ # older zope.component registries don't accept factory as a kwarg,
+ # so if we don't need it, we don't pass it
+ kw = {}
+
+ _context.action(
+ discriminator = ('utility', provides, name),
+ callable = handler,
+ args = ('registerUtility', component, provides, name),
+ kw = kw,
+ )
+
+class IUtilityDirective(Interface):
+ """Register a utility."""
+
+ component = GlobalObject(
+ title=u"Component to use",
+ description=(u"Python name of the implementation object. This"
+ " must identify an object in a module using the"
+ " full dotted name. If specified, the"
+ " ``factory`` field must be left blank."),
+ required=False,
+ )
+
+ factory = GlobalObject(
+ title=u"Factory",
+ description=(u"Python name of a factory which can create the"
+ " implementation object. This must identify an"
+ " object in a module using the full dotted name."
+ " If specified, the ``component`` field must"
+ " be left blank."),
+ required=False,
+ )
+
+ provides = GlobalInterface(
+ title=u"Provided interface",
+ description=u"Interface provided by the utility.",
+ required=False,
+ )
+
+ name = TextLine(
+ title=u"Name",
+ description=(u"Name of the registration. This is used by"
+ " application code when locating a utility."),
+ required=False,
+ )
+
+def _rolledUpFactory(factories):
+ def factory(ob):
+ for f in factories:
+ ob = f(ob)
+ return ob
+ # Store the original factory for documentation
+ factory.factory = factories[0]
+ return factory
diff --git a/setup.py b/setup.py
index 1d1fd729c..e26ab008a 100644
--- a/setup.py
+++ b/setup.py
@@ -36,8 +36,8 @@ install_requires=[
'WebOb',
'zope.interface >= 3.5.1', # 3.5.0 comment: "allow to bootstrap on jython"
'zope.component >= 3.6.0', # independent of zope.hookable
+ 'zope.configuration',
'zope.deprecation',
- 'repoze.zcml',
'repoze.lru',
]