diff options
| author | Tres Seaver <tseaver@palladion.com> | 2012-06-29 14:47:56 +0000 |
|---|---|---|
| committer | Tres Seaver <tseaver@palladion.com> | 2012-06-29 14:47:56 +0000 |
| commit | 2e39b26054c8c1da36886b7ba8ba0d346d62d2d7 (patch) | |
| tree | 370e3c664f4bec9fb91effde230daf4cd56c6688 /src | |
| parent | 33d9fc041301b310e4cf28f8e28b0f915c58d4fd (diff) | |
| download | zope-component-2e39b26054c8c1da36886b7ba8ba0d346d62d2d7.tar.gz | |
Merge tseaver-test_cleanup branch.
Diffstat (limited to 'src')
49 files changed, 5078 insertions, 6650 deletions
diff --git a/src/zope/component/README.txt b/src/zope/component/README.txt deleted file mode 100644 index ed43ad2..0000000 --- a/src/zope/component/README.txt +++ /dev/null @@ -1,402 +0,0 @@ -Zope Component Architecture -=========================== - -This package, together with `zope.interface`, provides facilities for -defining, registering and looking up components. There are two basic -kinds of components: adapters and utilities. - -Utilities ---------- - -Utilities are just components that provide an interface and that are -looked up by an interface and a name. Let's look at a trivial utility -definition: - - >>> from zope import interface - - >>> class IGreeter(interface.Interface): - ... def greet(): - ... "say hello" - - >>> class Greeter: - ... interface.implements(IGreeter) - ... - ... def __init__(self, other="world"): - ... self.other = other - ... - ... def greet(self): - ... print "Hello", self.other - -We can register an instance this class using `provideUtility` [1]_: - - >>> from zope import component - >>> greet = Greeter('bob') - >>> component.provideUtility(greet, IGreeter, 'robert') - -In this example we registered the utility as providing the `IGreeter` -interface with a name of 'bob'. We can look the interface up with -either `queryUtility` or `getUtility`: - - >>> component.queryUtility(IGreeter, 'robert').greet() - Hello bob - - >>> component.getUtility(IGreeter, 'robert').greet() - Hello bob - -`queryUtility` and `getUtility` differ in how failed lookups are handled: - - >>> component.queryUtility(IGreeter, 'ted') - >>> component.queryUtility(IGreeter, 'ted', 42) - 42 - >>> component.getUtility(IGreeter, 'ted') - ... # doctest: +ELLIPSIS - Traceback (most recent call last): - ... - ComponentLookupError: (<InterfaceClass ...IGreeter>, 'ted') - -If a component provides only one interface, as in the example above, -then we can omit the provided interface from the call to `provideUtility`: - - >>> ted = Greeter('ted') - >>> component.provideUtility(ted, name='ted') - >>> component.queryUtility(IGreeter, 'ted').greet() - Hello ted - -The name defaults to an empty string: - - >>> world = Greeter() - >>> component.provideUtility(world) - >>> component.queryUtility(IGreeter).greet() - Hello world - -Adapters --------- - -Adapters are components that are computed from other components to -adapt them to some interface. Because they are computed from other -objects, they are provided as factories, usually classes. Here, we'll -create a greeter for persons, so we can provide personalized greetings -for different people: - - >>> class IPerson(interface.Interface): - ... name = interface.Attribute("Name") - - >>> class PersonGreeter: - ... - ... component.adapts(IPerson) - ... interface.implements(IGreeter) - ... - ... def __init__(self, person): - ... self.person = person - ... - ... def greet(self): - ... print "Hello", self.person.name - -The class defines a constructor that takes an argument for every -object adapted. - -We used `component.adapts` to declare what we adapt. We can find -out if an object declares that it adapts anything using adaptedBy: - - >>> list(component.adaptedBy(PersonGreeter)) == [IPerson] - True - -If an object makes no declaration, then None is returned: - - >>> component.adaptedBy(Greeter()) is None - True - - -If we declare the interfaces adapted and if we provide only one -interface, as in the example above, then we can provide the adapter -very simply [1]_: - - >>> component.provideAdapter(PersonGreeter) - -For adapters that adapt a single interface to a single interface -without a name, we can get the adapter by simply calling the -interface: - - >>> class Person: - ... interface.implements(IPerson) - ... - ... def __init__(self, name): - ... self.name = name - - >>> IGreeter(Person("Sally")).greet() - Hello Sally - -We can also provide arguments to be very specific about what -how to register the adapter. - - >>> class BobPersonGreeter(PersonGreeter): - ... name = 'Bob' - ... def greet(self): - ... print "Hello", self.person.name, "my name is", self.name - - >>> component.provideAdapter( - ... BobPersonGreeter, [IPerson], IGreeter, 'bob') - -The arguments can also be provided as keyword arguments: - - >>> class TedPersonGreeter(BobPersonGreeter): - ... name = "Ted" - - >>> component.provideAdapter( - ... factory=TedPersonGreeter, adapts=[IPerson], - ... provides=IGreeter, name='ted') - -For named adapters, use `queryAdapter`, or `getAdapter`: - - >>> component.queryAdapter(Person("Sally"), IGreeter, 'bob').greet() - Hello Sally my name is Bob - - >>> component.getAdapter(Person("Sally"), IGreeter, 'ted').greet() - Hello Sally my name is Ted - -If an adapter can't be found, `queryAdapter` returns a default value -and `getAdapter` raises an error: - - >>> component.queryAdapter(Person("Sally"), IGreeter, 'frank') - >>> component.queryAdapter(Person("Sally"), IGreeter, 'frank', 42) - 42 - >>> component.getAdapter(Person("Sally"), IGreeter, 'frank') - ... # doctest: +ELLIPSIS - Traceback (most recent call last): - ... - ComponentLookupError: (...Person...>, <...IGreeter>, 'frank') - -Adapters can adapt multiple objects: - - >>> class TwoPersonGreeter: - ... - ... component.adapts(IPerson, IPerson) - ... interface.implements(IGreeter) - ... - ... def __init__(self, person, greeter): - ... self.person = person - ... self.greeter = greeter - ... - ... def greet(self): - ... print "Hello", self.person.name - ... print "my name is", self.greeter.name - - >>> component.provideAdapter(TwoPersonGreeter) - -Note that the declaration-order of the Interfaces beeing adapted to is -important for adapter look up. It must be the the same as the order of -parameters given to the adapter and used to query the adapter. This is -especially the case when different Interfaces are adapt to (opposed to -this example). - -To look up a multi-adapter, use either `queryMultiAdapter` or -`getMultiAdapter`: - - >>> component.queryMultiAdapter((Person("Sally"), Person("Bob")), - ... IGreeter).greet() - Hello Sally - my name is Bob - -Adapters need not be classes. Any callable will do. We use the -adapter decorator (in the Python 2.4 decorator sense) to declare that -a callable object adapts some interfaces (or classes): - - >>> class IJob(interface.Interface): - ... "A job" - - >>> class Job: - ... interface.implements(IJob) - - >>> def personJob(person): - ... return getattr(person, 'job', None) - >>> personJob = interface.implementer(IJob)(personJob) - >>> personJob = component.adapter(IPerson)(personJob) - -In Python 2.4, the example can be written: - - >>> @interface.implementer(IJob) - ... @component.adapter(IPerson) - ... def personJob(person): - ... return getattr(person, 'job', None) - -which looks a bit nicer. - -In this example, the personJob function simply returns the person's -`job` attribute if present, or None if it's not present. An adapter -factory can return None to indicate that adaptation wasn't possible. -Let's register this adapter and try it out: - - >>> component.provideAdapter(personJob) - >>> sally = Person("Sally") - >>> IJob(sally) # doctest: +ELLIPSIS - Traceback (most recent call last): - ... - TypeError: ('Could not adapt', ... - -The adaptation failed because sally didn't have a job. Let's give her -one: - - >>> job = Job() - >>> sally.job = job - >>> IJob(sally) is job - True - -Subscription Adapters ---------------------- - -Unlike regular adapters, subscription adapters are used when we want -all of the adapters that adapt an object to a particular adapter. - -Consider a validation problem. We have objects and we want to assess -whether they meet some sort of standards. We define a validation -interface: - - >>> class IValidate(interface.Interface): - ... def validate(ob): - ... """Determine whether the object is valid - ... - ... Return a string describing a validation problem. - ... An empty string is returned to indicate that the - ... object is valid. - ... """ - -Perhaps we have documents: - - >>> class IDocument(interface.Interface): - ... summary = interface.Attribute("Document summary") - ... body = interface.Attribute("Document text") - - >>> class Document: - ... interface.implements(IDocument) - ... def __init__(self, summary, body): - ... self.summary, self.body = summary, body - -Now, we may want to specify various validation rules for -documents. For example, we might require that the summary be a single -line: - - >>> class SingleLineSummary: - ... component.adapts(IDocument) - ... interface.implements(IValidate) - ... - ... def __init__(self, doc): - ... self.doc = doc - ... - ... def validate(self): - ... if '\n' in self.doc.summary: - ... return 'Summary should only have one line' - ... else: - ... return '' - -Or we might require the body to be at least 1000 characters in length: - - >>> class AdequateLength: - ... component.adapts(IDocument) - ... interface.implements(IValidate) - ... - ... def __init__(self, doc): - ... self.doc = doc - ... - ... def validate(self): - ... if len(self.doc.body) < 1000: - ... return 'too short' - ... else: - ... return '' - -We can register these as subscription adapters [1]_: - - >>> component.provideSubscriptionAdapter(SingleLineSummary) - >>> component.provideSubscriptionAdapter(AdequateLength) - -We can then use the subscribers to validate objects: - - >>> doc = Document("A\nDocument", "blah") - >>> [adapter.validate() - ... for adapter in component.subscribers([doc], IValidate) - ... if adapter.validate()] - ['Summary should only have one line', 'too short'] - - >>> doc = Document("A\nDocument", "blah" * 1000) - >>> [adapter.validate() - ... for adapter in component.subscribers([doc], IValidate) - ... if adapter.validate()] - ['Summary should only have one line'] - - >>> doc = Document("A Document", "blah") - >>> [adapter.validate() - ... for adapter in component.subscribers([doc], IValidate) - ... if adapter.validate()] - ['too short'] - -Handlers --------- - -Handlers are subscription adapter factories that don't produce -anything. They do all of their work when called. Handlers -are typically used to handle events. - -Event subscribers are different from other subscription adapters in -that the caller of event subscribers doesn't expect to interact with -them in any direct way. For example, an event publisher doesn't -expect to get any return value. Because subscribers don't need to -provide an API to their callers, it is more natural to define them -with functions, rather than classes. For example, in a -document-management system, we might want to record creation times for -documents: - - >>> import datetime - - >>> def documentCreated(event): - ... event.doc.created = datetime.datetime.utcnow() - -In this example, we have a function that takes an event and performs -some processing. It doesn't actually return anything. This is a -special case of a subscription adapter that adapts an event to -nothing. All of the work is done when the adapter "factory" is -called. We call subscribers that don't actually create anything -"handlers". There are special APIs for registering and calling -them. - -To register the subscriber above, we define a document-created event: - - >>> class IDocumentCreated(interface.Interface): - ... doc = interface.Attribute("The document that was created") - - >>> class DocumentCreated: - ... interface.implements(IDocumentCreated) - ... - ... def __init__(self, doc): - ... self.doc = doc - -We'll also change our handler definition to: - - >>> def documentCreated(event): - ... event.doc.created = datetime.datetime.utcnow() - - >>> documentCreated = component.adapter(IDocumentCreated)(documentCreated) - -Note that in Python 2.4, this can be written: - - >>> @component.adapter(IDocumentCreated) - ... def documentCreated(event): - ... event.doc.created = datetime.datetime.utcnow() - -This marks the handler as an adapter of `IDocumentCreated` events. - -Now we'll register the handler [1]_: - - >>> component.provideHandler(documentCreated) - -Now, if we can create an event and use the `handle` function to call -handlers registered for the event: - - >>> component.handle(DocumentCreated(doc)) - >>> doc.created.__class__.__name__ - 'datetime' - - - -.. [1] CAUTION: This API should only be used from test or - application-setup code. This API shouldn't be used by regular - library modules, as component registration is a configuration - activity. diff --git a/src/zope/component/_api.py b/src/zope/component/_api.py index 4cf573c..0ceba76 100644 --- a/src/zope/component/_api.py +++ b/src/zope/component/_api.py @@ -25,6 +25,7 @@ from zope.component.interfaces import IComponentRegistrationConvenience from zope.component.interfaces import IFactory from zope.component.interfaces import ComponentLookupError from zope.component.interfaces import IComponentLookup +from zope.component._compat import _BLANK from zope.component._declaration import adaptedBy from zope.component._declaration import adapter from zope.component._declaration import adapts @@ -33,7 +34,7 @@ from zope.component._declaration import adapts # to our Python version if not. try: from zope.hookable import hookable -except ImportError: +except ImportError: #pragma NO COVER from zope.component.hookable import hookable # getSiteManager() returns a component registry. Although the term @@ -42,6 +43,8 @@ except ImportError: base = None @hookable def getSiteManager(context=None): + """ See IComponentArchitecture. + """ global base if context is None: if base is None: @@ -52,7 +55,7 @@ def getSiteManager(context=None): # to avoid the recursion implied by using a local `getAdapter()` call. try: return IComponentLookup(context) - except TypeError, error: + except TypeError as error: raise ComponentLookupError(*error.args) # Adapter API @@ -90,26 +93,26 @@ def queryAdapterInContext(object, interface, context, default=None): return getSiteManager(context).queryAdapter(object, interface, '', default) -def getAdapter(object, interface=Interface, name=u'', context=None): +def getAdapter(object, interface=Interface, name=_BLANK, context=None): adapter = queryAdapter(object, interface, name, None, context) if adapter is None: raise ComponentLookupError(object, interface, name) return adapter -def queryAdapter(object, interface=Interface, name=u'', default=None, +def queryAdapter(object, interface=Interface, name=_BLANK, default=None, context=None): if context is None: return adapter_hook(interface, object, name, default) return getSiteManager(context).queryAdapter(object, interface, name, default) -def getMultiAdapter(objects, interface=Interface, name=u'', context=None): +def getMultiAdapter(objects, interface=Interface, name=_BLANK, context=None): adapter = queryMultiAdapter(objects, interface, name, context=context) if adapter is None: raise ComponentLookupError(objects, interface, name) return adapter -def queryMultiAdapter(objects, interface=Interface, name=u'', default=None, +def queryMultiAdapter(objects, interface=Interface, name=_BLANK, default=None, context=None): try: sitemanager = getSiteManager(context) @@ -136,10 +139,7 @@ def subscribers(objects, interface, context=None): return sitemanager.subscribers(objects, interface) def handle(*objects): - sitemanager = getSiteManager(None) - # iterating over subscribers assures they get executed - for ignored in sitemanager.subscribers(objects, None): - pass + getSiteManager(None).subscribers(objects, None) ############################################################################# # Register the component architectures adapter hook, with the adapter hook @@ -150,7 +150,7 @@ def handle(*objects): def adapter_hook(interface, object, name='', default=None): try: sitemanager = getSiteManager() - except ComponentLookupError: + except ComponentLookupError: #pragma NO COVER w/o context, cannot test # Oh blast, no site manager. This should *never* happen! return None return sitemanager.queryAdapter(object, interface, name, default) @@ -216,13 +216,23 @@ def getNextUtility(context, interface, name=''): # Factories def createObject(__factory_name, *args, **kwargs): + """Invoke the named factory and return the result. + + ``__factory_name`` is a positional-only argument. + """ context = kwargs.pop('context', None) return getUtility(IFactory, __factory_name, context)(*args, **kwargs) def getFactoryInterfaces(name, context=None): + """Return the interface provided by the named factory's objects + + Result might be a single interface. XXX + """ return getUtility(IFactory, name, context).getInterfaces() def getFactoriesFor(interface, context=None): + """Return info on all factories implementing the given interface. + """ utils = getSiteManager(context) for (name, factory) in utils.getUtilitiesFor(IFactory): interfaces = factory.getInterfaces() diff --git a/src/zope/component/_compat.py b/src/zope/component/_compat.py new file mode 100644 index 0000000..e889aae --- /dev/null +++ b/src/zope/component/_compat.py @@ -0,0 +1,42 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Foundation 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. +# +############################################################################## + +import sys +import types + +if sys.version_info[0] < 3: #pragma NO COVER + + import cPickle as _pickle + + def _u(s): + return unicode(s, 'unicode_escape') + + CLASS_TYPES = (type, types.ClassType) + + PYTHON3 = False + PYTHON2 = True + +else: #pragma NO COVER + + import pickle as _pickle + + def _u(s): + return s + + CLASS_TYPES = (type,) + + PYTHON3 = True + PYTHON2 = False + +_BLANK = _u('') diff --git a/src/zope/component/_declaration.py b/src/zope/component/_declaration.py index 0a8b44d..b434d75 100644 --- a/src/zope/component/_declaration.py +++ b/src/zope/component/_declaration.py @@ -13,16 +13,17 @@ ############################################################################## """Adapter declarations """ -import types import sys -class adapter: +from zope.component._compat import CLASS_TYPES + +class adapter(object): def __init__(self, *interfaces): self.interfaces = interfaces def __call__(self, ob): - if isinstance(ob, _class_types): + if isinstance(ob, CLASS_TYPES): ob.__component_adapts__ = _adapts_descr(self.interfaces) else: ob.__component_adapts__ = self.interfaces @@ -33,11 +34,8 @@ def adapts(*interfaces): frame = sys._getframe(1) locals = frame.f_locals - # Try to make sure we were called from a class def. In 2.2.0 we can't - # check for __module__ since it doesn't seem to be added to the locals - # until later on. - if (locals is frame.f_globals) or ( - ('__module__' not in locals) and sys.version_info[:3] > (2, 2, 0)): + # Ensure we were called from a class def. + if locals is frame.f_globals or '__module__' not in locals: raise TypeError("adapts can be used only from a class definition.") if '__component_adapts__' in locals: @@ -48,8 +46,6 @@ def adapts(*interfaces): def adaptedBy(ob): return getattr(ob, '__component_adapts__', None) -_class_types = type, types.ClassType - class _adapts_descr(object): def __init__(self, interfaces): self.interfaces = interfaces diff --git a/src/zope/component/configure.txt b/src/zope/component/configure.txt deleted file mode 100644 index 3088ab8..0000000 --- a/src/zope/component/configure.txt +++ /dev/null @@ -1,13 +0,0 @@ -Package configuration -===================== - -The ``zope.component`` package provides a ZCML file that configures some basic -components: - - >>> from zope.configuration.xmlconfig import XMLConfig - >>> import zope.component - - >>> XMLConfig('configure.zcml', zope.component)() - - >>> len(list(zope.component.getGlobalSiteManager().registeredHandlers())) - 5 diff --git a/src/zope/component/event.py b/src/zope/component/event.py index 360507e..850e4fc 100644 --- a/src/zope/component/event.py +++ b/src/zope/component/event.py @@ -11,22 +11,26 @@ # FOR A PARTICULAR PURPOSE. # ############################################################################## -"""Implement Component Architecture-specific event dispatching, based -on subscription adapters / handlers. +"""Component Architecture-specific event dispatching + +Based on subscription adapters / handlers. """ -__docformat__ = 'restructuredtext' -import zope.component.interfaces -import zope.event +from zope.event import subscribers as event_subscribers + +from zope.component.interfaces import IObjectEvent +from zope.component._api import subscribers as component_subscribers +from zope.component._declaration import adapter def dispatch(*event): - zope.component.subscribers(event, None) + component_subscribers(event, None) -zope.event.subscribers.append(dispatch) +event_subscribers.append(dispatch) -@zope.component.adapter(zope.component.interfaces.IObjectEvent) +@adapter(IObjectEvent) def objectEventNotify(event): - """Event subscriber to dispatch ObjectEvents to interested adapters.""" - zope.component.subscribers((event.object, event), None) + """Dispatch ObjectEvents to interested adapters. + """ + component_subscribers((event.object, event), None) diff --git a/src/zope/component/event.txt b/src/zope/component/event.txt deleted file mode 100644 index c79cd21..0000000 --- a/src/zope/component/event.txt +++ /dev/null @@ -1,142 +0,0 @@ -Events -====== - -The Component Architecture provides a way to dispatch events to event -handlers. Event handlers are registered as *subscribers* -a.k.a. *handlers*. - -Before we can start we need to import ``zope.component.event`` to make -the dispatching effective: - - >>> import zope.component.event - -Consider two event classes: - - >>> class Event1(object): - ... pass - - >>> class Event2(Event1): - ... pass - -Now consider two handlers for these event classes: - - >>> called = [] - - >>> import zope.component - >>> @zope.component.adapter(Event1) - ... def handler1(event): - ... called.append(1) - - >>> @zope.component.adapter(Event2) - ... def handler2(event): - ... called.append(2) - -We can register them with the Component Architecture: - - >>> zope.component.provideHandler(handler1) - >>> zope.component.provideHandler(handler2) - -Now let's go through the events. We'll see that the handlers have been -called accordingly: - - >>> from zope.event import notify - >>> notify(Event1()) - >>> called - [1] - - >>> del called[:] - >>> notify(Event2()) - >>> called.sort() - >>> called - [1, 2] - - - -Object events -------------- - - -The ``objectEventNotify`` function is a subscriber to dispatch -ObjectEvents to interested adapters. - -First create an object class: - - >>> class IUseless(zope.interface.Interface): - ... """Useless object""" - - >>> class UselessObject(object): - ... """Useless object""" - ... zope.interface.implements(IUseless) - -Then create an event class: - - >>> class IObjectThrownEvent(zope.component.interfaces.IObjectEvent): - ... """An object has been thrown away""" - - >>> class ObjectThrownEvent(zope.component.interfaces.ObjectEvent): - ... """An object has been thrown away""" - ... zope.interface.implements(IObjectThrownEvent) - -Create an object and an event: - - >>> hammer = UselessObject() - >>> event = ObjectThrownEvent(hammer) - -Then notify the event to the subscribers. -Since the subscribers list is empty, nothing happens. - - >>> zope.component.event.objectEventNotify(event) - -Now create an handler for the event: - - >>> events = [] - >>> def record(*args): - ... events.append(args) - - >>> zope.component.provideHandler(record, [IUseless, IObjectThrownEvent]) - -The event is notified to the subscriber: - - >>> zope.component.event.objectEventNotify(event) - >>> events == [(hammer, event)] - True - -Following test demonstrates how a subscriber can raise an exception -to prevent an action. - - >>> zope.component.provideHandler(zope.component.event.objectEventNotify) - -Let's create a container: - - >>> class ToolBox(dict): - ... def __delitem__(self, key): - ... notify(ObjectThrownEvent(self[key])) - ... return super(ToolBox,self).__delitem__(key) - - >>> container = ToolBox() - -And put the object into the container: - - >>> container['Red Hammer'] = hammer - -Create an handler function that will raise an error when called: - - >>> class Veto(Exception): - ... pass - - >>> def callback(item, event): - ... assert(item == event.object) - ... raise Veto - -Register the handler: - - >>> zope.component.provideHandler(callback, [IUseless, IObjectThrownEvent]) - -Then if we try to remove the object, an ObjectThrownEvent is fired: - - >>> del container['Red Hammer'] - ... # doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - raise Veto - Veto diff --git a/src/zope/component/eventtesting.py b/src/zope/component/eventtesting.py index 1f0c109..0e0b757 100644 --- a/src/zope/component/eventtesting.py +++ b/src/zope/component/eventtesting.py @@ -20,10 +20,14 @@ from zope.component.registry import dispatchAdapterRegistrationEvent from zope.component.registry import ( dispatchSubscriptionAdapterRegistrationEvent) from zope.component.registry import dispatchHandlerRegistrationEvent -from zope.testing import cleanup +try: + from zope.testing.cleanup import addCleanUp +except ImportError: #pragma NO COVER + def addCleanUp(x): + pass events = [] -def getEvents(event_type=None, filter=None): +def getEvents(event_type=None, filter=None): #pragma NO COVER going aaway r = [] for event in events: if event_type is not None and not event_type.providedBy(event): @@ -34,11 +38,11 @@ def getEvents(event_type=None, filter=None): return r -def clearEvents(): +def clearEvents(): #pragma NO COVER going aaway del events[:] -cleanup.addCleanUp(clearEvents) +addCleanUp(clearEvents) -class PlacelessSetup: +class PlacelessSetup: #pragma NO COVER going aaway def setUp(self): provideHandler(objectEventNotify) @@ -48,5 +52,5 @@ class PlacelessSetup: provideHandler(dispatchHandlerRegistrationEvent) provideHandler(events.append, (None,)) -def setUp(test=None): +def setUp(test=None): #pragma NO COVER going aaway PlacelessSetup().setUp() diff --git a/src/zope/component/factory.py b/src/zope/component/factory.py index 0d27c4d..d9edce2 100644 --- a/src/zope/component/factory.py +++ b/src/zope/component/factory.py @@ -13,17 +13,19 @@ ############################################################################## """Factory object """ -from zope.interface import implements, implementedBy +from zope.interface import implementer +from zope.interface import implementedBy from zope.interface.declarations import Implements from zope.component.interfaces import IFactory + +@implementer(IFactory) class Factory(object): """Generic factory implementation. The purpose of this implementation is to provide a quick way of creating factories for classes, functions and other objects. """ - implements(IFactory) def __init__(self, callable, title='', description='', interfaces=None): self._callable = callable @@ -41,5 +43,5 @@ class Factory(object): return spec return implementedBy(self._callable) - def __repr__(self): + def __repr__(self): #pragma NO COVER return '<%s for %s>' % (self.__class__.__name__, repr(self._callable)) diff --git a/src/zope/component/factory.txt b/src/zope/component/factory.txt deleted file mode 100644 index 1067fef..0000000 --- a/src/zope/component/factory.txt +++ /dev/null @@ -1,131 +0,0 @@ -Factories -========= - - -The Factory Class ------------------ - - >>> from zope.interface import Interface - >>> class IFunction(Interface): - ... pass - - >>> class IKlass(Interface): - ... pass - - >>> from zope.interface import implements - >>> class Klass(object): - ... implements(IKlass) - ... - ... def __init__(self, *args, **kw): - ... self.args = args - ... self.kw = kw - - >>> from zope.component.factory import Factory - >>> factory = Factory(Klass, 'Klass', 'Klassier') - >>> factory2 = Factory(lambda x: x, 'Func', 'Function') - >>> factory3 = Factory(lambda x: x, 'Func', 'Function', (IFunction,)) - -Calling a Factory -~~~~~~~~~~~~~~~~~ - -Here we test whether the factory correctly creates the objects and -including the correct handling of constructor elements. - -First we create a factory that creates instanace of the `Klass` class: - - >>> factory = Factory(Klass, 'Klass', 'Klassier') - -Now we use the factory to create the instance - - >>> kl = factory(1, 2, foo=3, bar=4) - -and make sure that the correct class was used to create the object: - - >>> kl.__class__ - <class 'Klass'> - -Since we passed in a couple positional and keyword arguments - - >>> kl.args - (1, 2) - >>> kl.kw - {'foo': 3, 'bar': 4} - - >>> factory2(3) - 3 - >>> factory3(3) - 3 - - -Title and Description -~~~~~~~~~~~~~~~~~~~~~ - - >>> factory.title - 'Klass' - >>> factory.description - 'Klassier' - >>> factory2.title - 'Func' - >>> factory2.description - 'Function' - >>> factory3.title - 'Func' - >>> factory3.description - 'Function' - - -Provided Interfaces -~~~~~~~~~~~~~~~~~~~ - - >>> implemented = factory.getInterfaces() - >>> implemented.isOrExtends(IKlass) - True - >>> list(implemented) - [<InterfaceClass __builtin__.IKlass>] - - >>> implemented2 = factory2.getInterfaces() - >>> list(implemented2) - [] - - >>> implemented3 = factory3.getInterfaces() - >>> list(implemented3) - [<InterfaceClass __builtin__.IFunction>] - - -The Component Architecture Factory API --------------------------------------- - - >>> import zope.component - >>> factory = Factory(Klass, 'Klass', 'Klassier') - >>> gsm = zope.component.getGlobalSiteManager() - - >>> from zope.component.interfaces import IFactory - >>> gsm.registerUtility(factory, IFactory, 'klass') - -Creating an Object -~~~~~~~~~~~~~~~~~~ - - >>> kl = zope.component.createObject('klass', 1, 2, foo=3, bar=4) - >>> isinstance(kl, Klass) - True - >>> kl.args - (1, 2) - >>> kl.kw - {'foo': 3, 'bar': 4} - -Accessing Provided Interfaces -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - >>> implemented = zope.component.getFactoryInterfaces('klass') - >>> implemented.isOrExtends(IKlass) - True - >>> [iface for iface in implemented] - [<InterfaceClass __builtin__.IKlass>] - -List of All Factories -~~~~~~~~~~~~~~~~~~~~~ - - >>> [(name, fac.__class__) for name, fac in - ... zope.component.getFactoriesFor(IKlass)] - [(u'klass', <class 'zope.component.factory.Factory'>)] - diff --git a/src/zope/component/globalregistry.py b/src/zope/component/globalregistry.py index 4c2f3bd..0fae7fc 100644 --- a/src/zope/component/globalregistry.py +++ b/src/zope/component/globalregistry.py @@ -13,10 +13,12 @@ ############################################################################## """Global components support """ -from zope.interface import implements +from zope.interface import implementer from zope.interface.adapter import AdapterRegistry from zope.interface.registry import Components + from zope.component.interfaces import IComponentLookup +from zope.component._compat import _BLANK def GAR(components, registryName): return getattr(components, registryName) @@ -35,8 +37,8 @@ class GlobalAdapterRegistry(AdapterRegistry): def __reduce__(self): return GAR, (self.__parent__, self.__name__) +@implementer(IComponentLookup) class BaseGlobalComponents(Components): - implements(IComponentLookup) def _init_registries(self): self.adapters = GlobalAdapterRegistry(self, 'adapters') @@ -50,7 +52,7 @@ base = BaseGlobalComponents('base') try: from zope.testing.cleanup import addCleanUp -except ImportError: +except ImportError: #pragma NO COVER pass else: addCleanUp(lambda: base.__init__('base')) @@ -64,10 +66,10 @@ def getGlobalSiteManager(): # We eventually want to deprecate these in favor of using the global # component registry directly. -def provideUtility(component, provides=None, name=u''): +def provideUtility(component, provides=None, name=_BLANK): base.registerUtility(component, provides, name, event=False) -def provideAdapter(factory, adapts=None, provides=None, name=''): +def provideAdapter(factory, adapts=None, provides=None, name=_BLANK): base.registerAdapter(factory, adapts, provides, name, event=False) def provideSubscriptionAdapter(factory, adapts=None, provides=None): @@ -75,12 +77,3 @@ def provideSubscriptionAdapter(factory, adapts=None, provides=None): def provideHandler(factory, adapts=None): base.registerHandler(factory, adapts, event=False) - -import zope.component._api # see http://www.zope.org/Collectors/Zope3-dev/674 -# Ideally, we will switch to an explicit adapter hook registration. For now, -# if you provide an adapter, we want to make sure that the adapter hook is -# registered, and that registration depends on code in _api, which itself -# depends on code in this module. So, for now, we do another of these nasty -# circular import workarounds. See also standalonetests.py, as run by -# tests.py in StandaloneTests, for a test that fails without this hack, and -# succeeds with it. diff --git a/src/zope/component/hooks.py b/src/zope/component/hooks.py index 39dbb2c..32bb641 100644 --- a/src/zope/component/hooks.py +++ b/src/zope/component/hooks.py @@ -17,17 +17,24 @@ __docformat__ = 'restructuredtext' import contextlib import threading -import zope.component try: - import zope.security.proxy -except ImportError: - SECURITY_SUPPORT = False -else: - SECURITY_SUPPORT = True + from zope.security.proxy import removeSecurityProxy +except ImportError: #pragma NO COVER + def removeSecurityProxy(x): + return x + +from zope.component.globalregistry import getGlobalSiteManager +from zope.component.interfaces import ComponentLookupError +from zope.component.interfaces import IComponentLookup class read_property(object): + """Descriptor for property-like computed attributes. + + Unlike the standard 'property', this descriptor allows assigning a + value to the instance, shadowing the property getter function. + """ def __init__(self, func): self.func = func @@ -39,20 +46,19 @@ class read_property(object): class SiteInfo(threading.local): site = None - sm = zope.component.getGlobalSiteManager() + sm = getGlobalSiteManager() + @read_property def adapter_hook(self): adapter_hook = self.sm.adapters.adapter_hook self.adapter_hook = adapter_hook return adapter_hook - adapter_hook = read_property(adapter_hook) - siteinfo = SiteInfo() def setSite(site=None): if site is None: - sm = zope.component.getGlobalSiteManager() + sm = getGlobalSiteManager() else: # We remove the security proxy because there's no way for @@ -62,8 +68,7 @@ def setSite(site=None): # once site managers do less. There's probably no good reason why # they can't be proxied. Well, except maybe for performance. - if SECURITY_SUPPORT: - site = zope.security.proxy.removeSecurityProxy(site) + site = removeSecurityProxy(site) # The getSiteManager method is defined by IPossibleSite. sm = site.getSiteManager() @@ -103,34 +108,35 @@ def getSiteManager(context=None): # We should really look look at this again though, especially # once site managers do less. There's probably no good reason why # they can't be proxied. Well, except maybe for performance. - sm = zope.component.interfaces.IComponentLookup( - context, zope.component.getGlobalSiteManager()) - if SECURITY_SUPPORT: - sm = zope.security.proxy.removeSecurityProxy(sm) + sm = IComponentLookup( + context, getGlobalSiteManager()) + sm = removeSecurityProxy(sm) return sm def adapter_hook(interface, object, name='', default=None): try: return siteinfo.adapter_hook(interface, object, name, default) - except zope.component.interfaces.ComponentLookupError: + except ComponentLookupError: return default def setHooks(): - zope.component.adapter_hook.sethook(adapter_hook) - zope.component.getSiteManager.sethook(getSiteManager) + from zope.component import _api + _api.adapter_hook.sethook(adapter_hook) + _api.getSiteManager.sethook(getSiteManager) def resetHooks(): # Reset hookable functions to original implementation. - zope.component.adapter_hook.reset() - zope.component.getSiteManager.reset() + from zope.component import _api + _api.adapter_hook.reset() + _api.getSiteManager.reset() # Clear the site thread global clearSite = setSite try: from zope.testing.cleanup import addCleanUp -except ImportError: +except ImportError: #pragma NO COVER pass else: addCleanUp(resetHooks) diff --git a/src/zope/component/hooks.txt b/src/zope/component/hooks.txt deleted file mode 100644 index 10a8bff..0000000 --- a/src/zope/component/hooks.txt +++ /dev/null @@ -1,97 +0,0 @@ -============================== -The current component registry -============================== - -There can be any number of component registries in an application. One of them -is the global component registry, and there is also the concept of a currently -used component registry. Component registries other than the global one are -associated with objects called sites. The ``zope.component.hooks`` module -provides an API to set and access the current site as well as manipulate the -adapter hook associated with it. - -As long as we haven't set a site, none is being considered current: - ->>> from zope.component.hooks import getSite ->>> print getSite() -None - -We can also ask for the current component registry (aka site manager -historically); it will return the global one if no current site is set: - ->>> from zope.component.hooks import getSiteManager ->>> getSiteManager() -<BaseGlobalComponents base> - -Let's set a site now. A site has to be an object that provides the -``getSiteManager`` method, which is specified by -``zope.component.interfaces.IPossibleSite``: - ->>> from zope.interface.registry import Components ->>> class Site(object): -... def __init__(self): -... self.registry = Components('components') -... def getSiteManager(self): -... return self.registry - ->>> from zope.component.hooks import setSite ->>> site1 = Site() ->>> setSite(site1) - -After this, the newly set site is considered the currently active one: - ->>> getSite() is site1 -True ->>> getSiteManager() is site1.registry -True - -If we set another site, that one will be considered current: - ->>> site2 = Site() ->>> site2.registry is not site1.registry -True ->>> setSite(site2) - ->>> getSite() is site2 -True ->>> getSiteManager() is site2.registry -True - -Finally we can unset the site and the global component registry is used again: - ->>> setSite() ->>> print getSite() -None ->>> getSiteManager() -<BaseGlobalComponents base> - - -Context manager -=============== - -There also is a context manager for setting the site, which is especially -useful when writing tests: - ->>> import zope.component.hooks ->>> dummy = with_statement # support for Python-2.5 ->>> print getSite() -None ->>> with zope.component.hooks.site(site2): -... getSite() is site2 -True ->>> print getSite() -None - -The site is properly restored even if the body of the with statement -raises an exception: - ->>> print getSite() -None ->>> with zope.component.hooks.site(site2): -... getSite() is site2 -... raise ValueError('An error in the body') -Traceback (most recent call last): - ... -ValueError: An error in the body ->>> print getSite() -None - diff --git a/src/zope/component/index.txt b/src/zope/component/index.txt deleted file mode 100644 index 5528b93..0000000 --- a/src/zope/component/index.txt +++ /dev/null @@ -1,22 +0,0 @@ -Welcome to zope.component's documentation! -========================================== - -Contents: - -.. toctree:: - :maxdepth: 2 - - README - socketexample - event - factory - registry - persistentregistry - zcml - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/src/zope/component/interface.py b/src/zope/component/interface.py index 416544c..bd44eee 100644 --- a/src/zope/component/interface.py +++ b/src/zope/component/interface.py @@ -13,78 +13,22 @@ ############################################################################## """Interface utility functions """ -__docformat__ = 'restructuredtext' - -from types import ClassType - -import zope.component -from zope.component.interfaces import ComponentLookupError from zope.interface import alsoProvides from zope.interface.interfaces import IInterface -def provideInterface(id, interface, iface_type=None, info=''): - """register Interface with global site manager as utility - - >>> gsm = zope.component.getGlobalSiteManager() - - >>> from zope.interface import Interface - >>> from zope.interface.interfaces import IInterface - >>> from zope.component.tests import ITestType - - >>> class I(Interface): - ... pass - >>> IInterface.providedBy(I) - True - >>> ITestType.providedBy(I) - False - >>> interfaces = gsm.getUtilitiesFor(ITestType) - >>> list(interfaces) - [] - - # provide first interface type - >>> provideInterface('', I, ITestType) - >>> ITestType.providedBy(I) - True - >>> interfaces = list(gsm.getUtilitiesFor(ITestType)) - >>> [name for (name, iface) in interfaces] - [u'zope.component.interface.I'] - >>> [iface.__name__ for (name, iface) in interfaces] - ['I'] - - # provide second interface type - >>> class IOtherType(IInterface): - ... pass - >>> provideInterface('', I, IOtherType) - - >>> ITestType.providedBy(I) - True - >>> IOtherType.providedBy(I) - True - >>> interfaces = list(gsm.getUtilitiesFor(ITestType)) - >>> [name for (name, iface) in interfaces] - [u'zope.component.interface.I'] - >>> interfaces = list(gsm.getUtilitiesFor(IOtherType)) - >>> [name for (name, iface) in interfaces] - [u'zope.component.interface.I'] +from zope.component.globalregistry import getGlobalSiteManager +from zope.component.interfaces import ComponentLookupError +from zope.component._api import queryUtility +from zope.component._compat import CLASS_TYPES - >>> class I1(Interface): - ... pass - >>> provideInterface('', I1) - >>> IInterface.providedBy(I1) - True - >>> ITestType.providedBy(I1) - False - >>> interfaces = list(gsm.getUtilitiesFor(ITestType)) - >>> [name for (name, iface) in interfaces] - [u'zope.component.interface.I'] - >>> [iface.__name__ for (name, iface) in interfaces] - ['I'] +def provideInterface(id, interface, iface_type=None, info=''): + """ Mark 'interface' as a named utilty providing 'iface_type'. """ if not id: id = "%s.%s" % (interface.__module__, interface.__name__) if not IInterface.providedBy(interface): - if not isinstance(interface, (type, ClassType)): + if not isinstance(interface, CLASS_TYPES): raise TypeError(id, "is not an interface or class") return @@ -95,33 +39,12 @@ def provideInterface(id, interface, iface_type=None, info=''): else: iface_type = IInterface - gsm = zope.component.getGlobalSiteManager() + gsm = getGlobalSiteManager() gsm.registerUtility(interface, iface_type, id, info) def getInterface(context, id): """Return interface or raise ComponentLookupError - - >>> from zope.interface import Interface - >>> from zope.component.tests import ITestType - - >>> class I4(Interface): - ... pass - >>> IInterface.providedBy(I4) - True - >>> ITestType.providedBy(I4) - False - >>> getInterface(None, 'zope.component.interface.I4') - Traceback (most recent call last): - ... - ComponentLookupError: zope.component.interface.I4 - >>> provideInterface('', I4, ITestType) - >>> ITestType.providedBy(I4) - True - >>> iface = queryInterface( """\ - """ 'zope.component.interface.I4') - >>> iface.__name__ - 'I4' """ iface = queryInterface(id, None) if iface is None: @@ -130,51 +53,13 @@ def getInterface(context, id): def queryInterface(id, default=None): - """return interface or ``None`` - - >>> from zope.interface import Interface - >>> from zope.interface.interfaces import IInterface - >>> from zope.component.tests import ITestType - - >>> class I3(Interface): - ... pass - >>> IInterface.providedBy(I3) - True - >>> ITestType.providedBy(I3) - False - >>> queryInterface('zope.component.interface.I3') - - >>> provideInterface('', I3, ITestType) - >>> ITestType.providedBy(I3) - True - >>> iface = queryInterface('zope.component.interface.I3') - >>> iface.__name__ - 'I3' + """Return an interface or ``None`` """ - return zope.component.queryUtility(IInterface, id, default) + return queryUtility(IInterface, id, default) def searchInterface(context, search_string=None, base=None): """Interfaces search - - >>> from zope.interface import Interface - >>> from zope.interface.interfaces import IInterface - >>> from zope.component.tests import ITestType - - >>> class I5(Interface): - ... pass - >>> IInterface.providedBy(I5) - True - >>> ITestType.providedBy(I5) - False - >>> searchInterface(None, 'zope.component.interface.I5') - [] - >>> provideInterface('', I5, ITestType) - >>> ITestType.providedBy(I5) - True - >>> iface = searchInterface(None, 'zope.component.interface.I5') - >>> iface[0].__name__ - 'I5' """ return [iface_util[1] for iface_util in searchInterfaceUtilities(context, search_string, base)] @@ -182,32 +67,13 @@ def searchInterface(context, search_string=None, base=None): def searchInterfaceIds(context, search_string=None, base=None): """Interfaces search - - >>> from zope.interface import Interface - >>> from zope.interface.interfaces import IInterface - >>> from zope.component.tests import ITestType - - >>> class I5(Interface): - ... pass - >>> IInterface.providedBy(I5) - True - >>> ITestType.providedBy(I5) - False - >>> searchInterface(None, 'zope.component.interface.I5') - [] - >>> provideInterface('', I5, ITestType) - >>> ITestType.providedBy(I5) - True - >>> iface = searchInterfaceIds(None, 'zope.component.interface.I5') - >>> iface - [u'zope.component.interface.I5'] """ return [iface_util[0] for iface_util in searchInterfaceUtilities(context, search_string, base)] def searchInterfaceUtilities(context, search_string=None, base=None): - gsm = zope.component.getGlobalSiteManager() + gsm = getGlobalSiteManager() iface_utilities = gsm.getUtilitiesFor(IInterface) if search_string: @@ -217,7 +83,7 @@ def searchInterfaceUtilities(context, search_string=None, base=None): find(search_string) >= 0)] if base: res = [iface_util for iface_util in iface_utilities - if iface_util[1].extends(base)] + if iface_util[1].isOrExtends(base)] else: res = [iface_util for iface_util in iface_utilities] return res @@ -245,6 +111,8 @@ def nameToInterface(context, id): def interfaceToName(context, interface): if interface is None: return 'None' + # XXX this search is pointless: we are always going to return the + # same value whether or not we find anything. items = searchInterface(context, base=interface) ids = [('%s.%s' %(iface.__module__, iface.__name__)) for iface in items diff --git a/src/zope/component/interfaces.py b/src/zope/component/interfaces.py index a13300f..90df100 100644 --- a/src/zope/component/interfaces.py +++ b/src/zope/component/interfaces.py @@ -38,6 +38,9 @@ from zope.interface.interfaces import Unregistered from zope.interface.interfaces import IComponentRegistry from zope.interface.interfaces import IComponents +from zope.component._compat import _BLANK + + class IComponentArchitecture(Interface): """The Component Architecture is defined by two key components: Adapters and Utiltities. Both are managed by site managers. All other components @@ -112,7 +115,7 @@ class IComponentArchitecture(Interface): # Adapter API def getAdapter(object, - interface=Interface, name=u'', + interface=Interface, name=_BLANK, context=None): """Get a named adapter to an interface for an object @@ -167,7 +170,7 @@ class IComponentArchitecture(Interface): named adapter methods with an empty string for a name. """ - def queryAdapter(object, interface=Interface, name=u'', + def queryAdapter(object, interface=Interface, name=_BLANK, default=None, context=None): """Look for a named adapter to an interface for an object @@ -204,7 +207,7 @@ class IComponentArchitecture(Interface): """ def queryMultiAdapter(objects, - interface=Interface, name=u'', + interface=Interface, name=_BLANK, default=None, context=None): """Look for a multi-adapter to an interface for objects @@ -316,7 +319,7 @@ class IComponentRegistrationConvenience(Interface): activity. """ - def provideUtility(component, provides=None, name=u''): + def provideUtility(component, provides=None, name=_BLANK): """Register a utility globally A utility is registered to provide an interface with a @@ -332,7 +335,7 @@ class IComponentRegistrationConvenience(Interface): """ - def provideAdapter(factory, adapts=None, provides=None, name=u''): + def provideAdapter(factory, adapts=None, provides=None, name=_BLANK): """Register an adapter globally An adapter is registered to provide an interface with a name diff --git a/src/zope/component/nexttesting.py b/src/zope/component/nexttesting.py deleted file mode 100644 index 8295c50..0000000 --- a/src/zope/component/nexttesting.py +++ /dev/null @@ -1,104 +0,0 @@ -############################################################################## -# -# Copyright (c) 2009 Zope Foundation 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. -# -############################################################################## -"""Helper functions for testing utilities that use get/queryNextUtility. -""" -import zope.interface -from zope.component.interfaces import IComponentLookup, IComponents - - -class SiteManagerStub(object): - zope.interface.implements(IComponents) - - __bases__ = () - - def __init__(self): - self._utils = {} - - def setNext(self, next): - self.__bases__ = (next, ) - - def provideUtility(self, iface, util, name=''): - self._utils[(iface, name)] = util - - def queryUtility(self, iface, name='', default=None): - return self._utils.get((iface, name), default) - - -def testingNextUtility(utility, nextutility, interface, name='', - sitemanager=None, nextsitemanager=None): - """Provide a next utility for testing. - - This function sets up two utilities, so the get/queryNextUtility functions - will see the second one as the "next" to the first one. - - To test it, we need to create a utility interface and implementation: - - >>> from zope.interface import Interface, implements - >>> class IAnyUtility(Interface): - ... pass - - >>> class AnyUtility(object): - ... implements(IAnyUtility) - ... def __init__(self, id): - ... self.id = id - - >>> any1 = AnyUtility(1) - >>> any1next = AnyUtility(2) - - Now, we can make the "any1next" be next to "any1". - - >>> testingNextUtility(any1, any1next, IAnyUtility) - - >>> from zope.component import getNextUtility - >>> getNextUtility(any1, IAnyUtility) is any1next - True - - It will work for named utilities as well. - - >>> testingNextUtility(any1, any1next, IAnyUtility, 'any') - >>> getNextUtility(any1, IAnyUtility, 'any') is any1next - True - - We can also provide our custom component registries: - - >>> sm = SiteManagerStub() - >>> nextsm = SiteManagerStub() - - >>> testingNextUtility(any1, any1next, IAnyUtility, - ... sitemanager=sm, nextsitemanager=nextsm) - - >>> IComponentLookup(any1) is sm - True - >>> IComponentLookup(any1next) is nextsm - True - >>> getNextUtility(any1, IAnyUtility) is any1next - True - - """ - if sitemanager is None: - sitemanager = SiteManagerStub() - if nextsitemanager is None: - nextsitemanager = SiteManagerStub() - sitemanager.setNext(nextsitemanager) - - sitemanager.provideUtility(interface, utility, name) - utility.__conform__ = ( - lambda iface: - iface.isOrExtends(IComponentLookup) and sitemanager or None - ) - nextsitemanager.provideUtility(interface, nextutility, name) - nextutility.__conform__ = ( - lambda iface: - iface.isOrExtends(IComponentLookup) and nextsitemanager or None - ) diff --git a/src/zope/component/persistentregistry.py b/src/zope/component/persistentregistry.py index d185680..b3edfa1 100644 --- a/src/zope/component/persistentregistry.py +++ b/src/zope/component/persistentregistry.py @@ -13,16 +13,13 @@ ############################################################################## """Persistent component managers """ -import persistent.mapping -import persistent.list -import zope.interface.adapter - +from persistent import Persistent +from persistent.mapping import PersistentMapping +from persistent.list import PersistentList +from zope.interface.adapter import VerifyingAdapterRegistry from zope.interface.registry import Components -class PersistentAdapterRegistry( - zope.interface.adapter.VerifyingAdapterRegistry, - persistent.Persistent, - ): +class PersistentAdapterRegistry(VerifyingAdapterRegistry, Persistent): def changed(self, originally_changed): if originally_changed is self: @@ -48,7 +45,7 @@ class PersistentComponents(Components): self.utilities = PersistentAdapterRegistry() def _init_registrations(self): - self._utility_registrations = persistent.mapping.PersistentMapping() - self._adapter_registrations = persistent.mapping.PersistentMapping() - self._subscription_registrations = persistent.list.PersistentList() - self._handler_registrations = persistent.list.PersistentList() + self._utility_registrations = PersistentMapping() + self._adapter_registrations = PersistentMapping() + self._subscription_registrations = PersistentList() + self._handler_registrations = PersistentList() diff --git a/src/zope/component/persistentregistry.txt b/src/zope/component/persistentregistry.txt deleted file mode 100644 index bf0de9e..0000000 --- a/src/zope/component/persistentregistry.txt +++ /dev/null @@ -1,6 +0,0 @@ -Persistent Component Management -=============================== - -Persistent component management allows persistent management of -components. From a usage point of view, there shouldn't be any new -behavior beyond what's described in registry.txt. diff --git a/src/zope/component/registry.txt b/src/zope/component/registry.txt deleted file mode 100644 index 85933c8..0000000 --- a/src/zope/component/registry.txt +++ /dev/null @@ -1,1117 +0,0 @@ -Component-Management objects -============================ - -Component-management objects provide a higher-level -component-management API over the basic adapter-registration API -provided by the zope.interface package. In particular, it provides: - -- utilities - -- support for computing adapters, rather than just looking up adapter - factories. - -- management of registration comments - -The zope.component.registry.Components class provides an -implementation of zope.component.interfaces.IComponents that provides -these features. - - >>> from zope.component import registry - >>> from zope.component import tests - >>> components = registry.Components('comps') - -As components are registered, events are generated. Let's register -an event subscriber, so we can see the events generated: - - >>> import zope.event - >>> def logevent(event): - ... print event - >>> zope.event.subscribers.append(logevent) - -Utilities ---------- - -You can register Utilities using registerUtility: - - >>> components.registerUtility(tests.U1(1)) - Registered event: - UtilityRegistration(<Components comps>, I1, u'', 1, None, u'') - -Here we didn't specify an interface or name. An unnamed utility was -registered for interface I1, since that is only interface implemented -by the U1 class: - - >>> components.getUtility(tests.I1) - U1(1) - -You can also register a utility using a factory instead of a utility instance: - - >>> def factory(): - ... return tests.U1(1) - >>> components.registerUtility(factory=factory) - Unregistered event: - UtilityRegistration(<Components comps>, I1, u'', 1, None, u'') - Registered event: - UtilityRegistration(<Components comps>, I1, u'', 1, <function factory at <SOME ADDRESS>>, u'') - - -If a component implements other than one interface or no interface, -then an error will be raised: - - >>> components.registerUtility(tests.U12(2)) - ... # doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - TypeError: The utility doesn't provide a single interface and - no provided interface was specified. - - >>> components.registerUtility(tests.A) - ... # doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - TypeError: The utility doesn't provide a single interface and - no provided interface was specified. - - -We can provide an interface if desired: - - >>> components.registerUtility(tests.U12(2), tests.I2) - Registered event: - UtilityRegistration(<Components comps>, I2, u'', 2, None, u'') - -and we can specify a name: - - >>> components.registerUtility(tests.U12(3), tests.I2, u'three') - Registered event: - UtilityRegistration(<Components comps>, I2, u'three', 3, None, u'') - - >>> components.getUtility(tests.I2) - U12(2) - - >>> components.getUtility(tests.I2, 'three') - U12(3) - -If you try to get a utility that doesn't exist, you'll get a component -lookup error: - - >>> components.getUtility(tests.I3) - ... # doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - ComponentLookupError: - (<InterfaceClass zope.component.tests.I3>, u'') - -Unless you use queryUtility: - - >>> components.queryUtility(tests.I3) - >>> components.queryUtility(tests.I3, default=42) - 42 - -You can get information about registered utilities with the -registeredUtilities method: - - >>> for registration in sorted(components.registeredUtilities()): - ... print registration.provided, registration.name - ... print registration.component, registration.info - <InterfaceClass zope.component.tests.I1> - U1(1) - <InterfaceClass zope.component.tests.I2> - U12(2) - <InterfaceClass zope.component.tests.I2> three - U12(3) - -Duplicate registrations replace existing ones: - - >>> components.registerUtility(tests.U1(4), info=u'use 4 now') - Unregistered event: - UtilityRegistration(<Components comps>, I1, u'', 1, <function factory at <SOME ADDRESS>>, u'') - Registered event: - UtilityRegistration(<Components comps>, I1, u'', 4, None, u'use 4 now') - >>> components.getUtility(tests.I1) - U1(4) - - >>> for registration in sorted(components.registeredUtilities()): - ... print registration.provided, registration.name - ... print registration.component, registration.info - <InterfaceClass zope.component.tests.I1> - U1(4) use 4 now - <InterfaceClass zope.component.tests.I2> - U12(2) - <InterfaceClass zope.component.tests.I2> three - U12(3) - -As shown in the this example, you can provide an "info" argumemnt when -registering utilities. This provides extra documentation about the -registration itself that is shown when listing registrations. - -You can also unregister utilities: - - >>> components.unregisterUtility(provided=tests.I1) - Unregistered event: - UtilityRegistration(<Components comps>, I1, u'', 4, None, u'use 4 now') - True - -A boolean is returned indicating whether anything changed: - - >>> components.queryUtility(tests.I1) - >>> for registration in sorted(components.registeredUtilities()): - ... print registration.provided, registration.name - ... print registration.component, registration.info - <InterfaceClass zope.component.tests.I2> - U12(2) - <InterfaceClass zope.component.tests.I2> three - U12(3) - -When you unregister, you can specify a component. If the component -doesn't match the one registered, then nothing happens: - - >>> u5 = tests.U1(5) - >>> components.registerUtility(u5) - Registered event: - UtilityRegistration(<Components comps>, I1, u'', 5, None, u'') - >>> components.unregisterUtility(tests.U1(6)) - False - >>> components.queryUtility(tests.I1) - U1(5) - >>> components.unregisterUtility(u5) - Unregistered event: - UtilityRegistration(<Components comps>, I1, u'', 5, None, u'') - True - >>> components.queryUtility(tests.I1) - -You can get the name and utility for all of the utilities that provide -an interface using getUtilitiesFor: - - >>> sorted(components.getUtilitiesFor(tests.I2)) - [(u'', U12(2)), (u'three', U12(3))] - -getAllUtilitiesRegisteredFor is similar to getUtilitiesFor except that -it includes utilities that are overridden. For example, we'll -register a utility that for an extending interface of I2: - - >>> util = tests.U('ext') - >>> components.registerUtility(util, tests.I2e) - Registered event: - UtilityRegistration(<Components comps>, I2e, u'', ext, None, u'') - -We don't get the new utility for getUtilitiesFor: - - >>> sorted(components.getUtilitiesFor(tests.I2)) - [(u'', U12(2)), (u'three', U12(3))] - -but we do get it from getAllUtilitiesRegisteredFor: - - >>> sorted(map(str, components.getAllUtilitiesRegisteredFor(tests.I2))) - ['U(ext)', 'U12(2)', 'U12(3)'] - -Removing a utility also makes it disappear from getUtilitiesFor: - - >>> components.unregisterUtility(util, tests.I2e) - Unregistered event: - UtilityRegistration(<Components comps>, I2e, u'', ext, None, u'') - True - >>> list(components.getAllUtilitiesRegisteredFor(tests.I2e)) - [] - -Adapters --------- - -You can register adapters with registerAdapter: - - >>> components.registerAdapter(tests.A12_1) - Registered event: - AdapterRegistration(<Components comps>, [I1, I2], IA1, u'', A12_1, u'') - -Here, we didn't specify required interfaces, a provided interface, or -a name. The required interfaces were determined from the factory -s __component_adapts__ attribute and the provided interface was -determined by introspecting what the factory implements. - - >>> components.getMultiAdapter((tests.U1(6), tests.U12(7)), tests.IA1) - A12_1(U1(6), U12(7)) - -If a factory implements more than one interface, an exception will be -raised: - - >>> components.registerAdapter(tests.A1_12) - ... # doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - TypeError: The adapter factory doesn't implement a single - interface and no provided interface was specified. - -Unless the provided interface is specified: - - >>> components.registerAdapter(tests.A1_12, provided=tests.IA2) - Registered event: - AdapterRegistration(<Components comps>, [I1], IA2, u'', A1_12, u'') - -If a factory doesn't declare an implemented interface, an exception will be -raised: - - >>> components.registerAdapter(tests.A12_) - ... # doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - TypeError: The adapter factory doesn't implement a single - interface and no provided interface was specified. - -Unless the provided interface is specified: - - >>> components.registerAdapter(tests.A12_, provided=tests.IA2) - Registered event: - AdapterRegistration(<Components comps>, [I1, I2], IA2, u'', A12_, u'') - -The required interface needs to be specified in the registration if -the factory doesn't have a __component_adapts__ attribute: - - >>> components.registerAdapter(tests.A_2) - ... # doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - TypeError: The adapter factory doesn't have a __component_adapts__ - attribute and no required specifications were specified - -Unless the required specifications specified: - - >>> components.registerAdapter(tests.A_2, required=[tests.I3]) - Registered event: - AdapterRegistration(<Components comps>, [I3], IA2, u'', A_2, u'') - -Classes can be specified in place of specifications, in which case the -implementedBy specification for the class is used: - - >>> components.registerAdapter(tests.A_3, required=[tests.U], - ... info="Really class specific") - ... # doctest: +NORMALIZE_WHITESPACE - Registered event: - AdapterRegistration(<Components comps>, [zope.component.tests.U], IA3, u'', - A_3, 'Really class specific') - -We can see the adapters that have been registered using the -registeredAdapters method: - - >>> for registration in sorted(components.registeredAdapters()): - ... print registration.required - ... print registration.provided, registration.name - ... print registration.factory, registration.info - ... # doctest: +NORMALIZE_WHITESPACE - (<InterfaceClass zope.component.tests.I1>, - <InterfaceClass zope.component.tests.I2>) - <InterfaceClass zope.component.tests.IA1> - zope.component.tests.A12_1 - (<InterfaceClass zope.component.tests.I1>, - <InterfaceClass zope.component.tests.I2>) - <InterfaceClass zope.component.tests.IA2> - zope.component.tests.A12_ - (<InterfaceClass zope.component.tests.I1>,) - <InterfaceClass zope.component.tests.IA2> - zope.component.tests.A1_12 - (<InterfaceClass zope.component.tests.I3>,) - <InterfaceClass zope.component.tests.IA2> - zope.component.tests.A_2 - (<implementedBy zope.component.tests.U>,) - <InterfaceClass zope.component.tests.IA3> - zope.component.tests.A_3 Really class specific - -As with utilities, we can provide registration information when -registering adapters. - -If you try to fetch an adapter that isn't registered, you'll get a -component-lookup error: - - >>> components.getMultiAdapter((tests.U(8), ), tests.IA1) - ... # doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - ComponentLookupError: ((U(8),), - <InterfaceClass zope.component.tests.IA1>, u'') - -unless you use queryAdapter: - - >>> components.queryMultiAdapter((tests.U(8), ), tests.IA1) - >>> components.queryMultiAdapter((tests.U(8), ), tests.IA1, default=42) - 42 - -When looking up an adapter for a single object, you can use the -slightly simpler getAdapter and queryAdapter calls: - - >>> components.getAdapter(tests.U1(9), tests.IA2) - A1_12(U1(9)) - - >>> components.queryAdapter(tests.U1(9), tests.IA2) - A1_12(U1(9)) - - >>> components.getAdapter(tests.U(8), tests.IA1) - ... # doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - ComponentLookupError: (U(8), - <InterfaceClass zope.component.tests.IA1>, u'') - - >>> components.queryAdapter(tests.U(8), tests.IA2) - >>> components.queryAdapter(tests.U(8), tests.IA2, default=42) - 42 - -You can unregister an adapter. If a factory is provided and if the -rewuired and provided interfaces, can be infered, then they need not -be provided: - - >>> components.unregisterAdapter(tests.A12_1) - Unregistered event: - AdapterRegistration(<Components comps>, [I1, I2], IA1, u'', A12_1, u'') - True - - >>> for registration in sorted(components.registeredAdapters()): - ... print registration.required - ... print registration.provided, registration.name - ... print registration.factory, registration.info - ... # doctest: +NORMALIZE_WHITESPACE - (<InterfaceClass zope.component.tests.I1>, - <InterfaceClass zope.component.tests.I2>) - <InterfaceClass zope.component.tests.IA2> - zope.component.tests.A12_ - (<InterfaceClass zope.component.tests.I1>,) - <InterfaceClass zope.component.tests.IA2> - zope.component.tests.A1_12 - (<InterfaceClass zope.component.tests.I3>,) - <InterfaceClass zope.component.tests.IA2> - zope.component.tests.A_2 - (<implementedBy zope.component.tests.U>,) - <InterfaceClass zope.component.tests.IA3> - zope.component.tests.A_3 Really class specific - -A boolean is returned indicating whether a change was made. - -If a factory implements more than one interface, an exception will be -raised: - - >>> components.unregisterAdapter(tests.A1_12) - ... # doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - TypeError: The adapter factory doesn't implement a single - interface and no provided interface was specified. - -Unless the provided interface is specified: - - >>> components.unregisterAdapter(tests.A1_12, provided=tests.IA2) - Unregistered event: - AdapterRegistration(<Components comps>, [I1], IA2, u'', A1_12, u'') - True - -If a factory doesn't declare an implemented interface, an exception will be -raised: - - >>> components.unregisterAdapter(tests.A12_) - ... # doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - TypeError: The adapter factory doesn't implement a single - interface and no provided interface was specified. - -Unless the provided interface is specified: - - >>> components.unregisterAdapter(tests.A12_, provided=tests.IA2) - Unregistered event: - AdapterRegistration(<Components comps>, [I1, I2], IA2, u'', A12_, u'') - True - -The required interface needs to be specified if the factory doesn't -have a __component_adapts__ attribute: - - >>> components.unregisterAdapter(tests.A_2) - ... # doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - TypeError: The adapter factory doesn't have a __component_adapts__ - attribute and no required specifications were specified - - >>> components.unregisterAdapter(tests.A_2, required=[tests.I3]) - Unregistered event: - AdapterRegistration(<Components comps>, [I3], IA2, u'', A_2, u'') - True - - >>> for registration in sorted(components.registeredAdapters()): - ... print registration.required - ... print registration.provided, registration.name - ... print registration.factory, registration.info - ... # doctest: +NORMALIZE_WHITESPACE - (<implementedBy zope.component.tests.U>,) - <InterfaceClass zope.component.tests.IA3> - zope.component.tests.A_3 Really class specific - -If a factory is unregistered that is not registered, False is -returned: - - - >>> components.unregisterAdapter(tests.A_2, required=[tests.I3]) - False - >>> components.unregisterAdapter(tests.A12_1, required=[tests.U]) - False - -The factory can be omitted, to unregister *any* factory that matches -specified required and provided interfaces: - - >>> components.unregisterAdapter(required=[tests.U], provided=tests.IA3) - ... # doctest: +NORMALIZE_WHITESPACE - Unregistered event: - AdapterRegistration(<Components comps>, [zope.component.tests.U], - IA3, u'', A_3, 'Really class specific') - True - - >>> for registration in sorted(components.registeredAdapters()): - ... print registration - -Adapters can be named: - - >>> components.registerAdapter(tests.A1_12, provided=tests.IA2, - ... name=u'test') - Registered event: - AdapterRegistration(<Components comps>, [I1], IA2, u'test', A1_12, u'') - - >>> components.queryMultiAdapter((tests.U1(9), ), tests.IA2) - >>> components.queryMultiAdapter((tests.U1(9), ), tests.IA2, name=u'test') - A1_12(U1(9)) - - >>> components.queryAdapter(tests.U1(9), tests.IA2) - >>> components.queryAdapter(tests.U1(9), tests.IA2, name=u'test') - A1_12(U1(9)) - >>> components.getAdapter(tests.U1(9), tests.IA2, name=u'test') - A1_12(U1(9)) - -It is possible to look up all of the adapters that provide an -interface: - - >>> components.registerAdapter(tests.A1_23, provided=tests.IA2, - ... name=u'test 2') - Registered event: - AdapterRegistration(<Components comps>, [I1], IA2, u'test 2', A1_23, u'') - - >>> components.registerAdapter(tests.A1_12, provided=tests.IA2) - Registered event: - AdapterRegistration(<Components comps>, [I1], IA2, u'', A1_12, u'') - - >>> for name, adapter in sorted(components.getAdapters((tests.U1(9), ), - ... tests.IA2)): - ... print name, adapter - A1_12(U1(9)) - test A1_12(U1(9)) - test 2 A1_23(U1(9)) - - -getAdapters is most commonly used as the basis of menu systems. - -If an adapter factory returns None, it is equivalent to there being no -factory: - - >>> components.registerAdapter(tests.noop, - ... required=[tests.IA1], provided=tests.IA2, - ... name=u'test noop') - ... # doctest: +NORMALIZE_WHITESPACE - Registered event: - AdapterRegistration(<Components comps>, [IA1], IA2, u'test noop', - noop, u'') - >>> components.queryAdapter(tests.U1(9), tests.IA2, name=u'test noop') - - >>> components.registerAdapter(tests.A1_12, provided=tests.IA2) - Registered event: - AdapterRegistration(<Components comps>, [I1], IA2, u'', A1_12, u'') - - >>> for name, adapter in sorted(components.getAdapters((tests.U1(9), ), - ... tests.IA2)): - ... print name, adapter - A1_12(U1(9)) - test A1_12(U1(9)) - test 2 A1_23(U1(9)) - - - >>> components.unregisterAdapter(tests.A1_12, provided=tests.IA2, - ... name=u'test') - Unregistered event: - AdapterRegistration(<Components comps>, [I1], IA2, u'test', A1_12, u'') - True - >>> components.unregisterAdapter(tests.A1_12, provided=tests.IA2) - Unregistered event: - AdapterRegistration(<Components comps>, [I1], IA2, u'', A1_12, u'') - True - >>> for registration in sorted(components.registeredAdapters()): - ... print registration.required - ... print registration.provided, registration.name - ... print registration.factory, registration.info - ... # doctest: +NORMALIZE_WHITESPACE - (<InterfaceClass zope.component.tests.I1>,) - <InterfaceClass zope.component.tests.IA2> test 2 - zope.component.tests.A1_23 - (<InterfaceClass zope.component.tests.IA1>,) - <InterfaceClass zope.component.tests.IA2> test noop - <function noop at 0xb79a1064> - - -Subscribers ------------ - -Subscribers provide a way to get multiple adapters of a given type. -In this regard, subscribers are like named adapters, except that there -isn't any concept of the most specific adapter for a given name. - -Subscribers are registered by calling registerSubscriptionAdapter: - - >>> components.registerSubscriptionAdapter(tests.A1_2) - ... # doctest: +NORMALIZE_WHITESPACE - Registered event: - SubscriptionRegistration(<Components comps>, [I1], IA2, u'', A1_2, u'') - - >>> components.registerSubscriptionAdapter( - ... tests.A1_12, provided=tests.IA2) - ... # doctest: +NORMALIZE_WHITESPACE - Registered event: - SubscriptionRegistration(<Components comps>, [I1], IA2, u'', A1_12, u'') - - >>> components.registerSubscriptionAdapter( - ... tests.A, [tests.I1], tests.IA2, - ... info='a sample comment') - ... # doctest: +NORMALIZE_WHITESPACE - Registered event: - SubscriptionRegistration(<Components comps>, [I1], IA2, u'', - A, 'a sample comment') - -The same rules, with regard to when required and provided interfaces -have to be specified apply as with adapters: - - >>> components.registerSubscriptionAdapter(tests.A1_12) - ... # doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - TypeError: The adapter factory doesn't implement a single - interface and no provided interface was specified. - - >>> components.registerSubscriptionAdapter(tests.A) - ... # doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - TypeError: The adapter factory doesn't implement a single interface and - no provided interface was specified. - - >>> components.registerSubscriptionAdapter(tests.A, required=[tests.IA1]) - ... # doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - TypeError: The adapter factory doesn't implement a single interface - and no provided interface was specified. - -Note that we provided the info argument as a keyword argument above. -That's because there is a name argument that's reserved for future -use. We can give a name, as long as it is an empty string: - - >>> components.registerSubscriptionAdapter( - ... tests.A, [tests.I1], tests.IA2, u'', 'a sample comment') - ... # doctest: +NORMALIZE_WHITESPACE - Registered event: - SubscriptionRegistration(<Components comps>, [I1], IA2, u'', - A, 'a sample comment') - - >>> components.registerSubscriptionAdapter( - ... tests.A, [tests.I1], tests.IA2, u'oops', 'a sample comment') - Traceback (most recent call last): - ... - TypeError: Named subscribers are not yet supported - -Subscribers are looked up using the subscribers method: - - >>> for s in components.subscribers((tests.U1(1), ), tests.IA2): - ... print s - A1_2(U1(1)) - A1_12(U1(1)) - A(U1(1),) - A(U1(1),) - -Note that, because we created multiple subscriptions for A, we got multiple -subscriber instances. - -As with normal adapters, if a factory returns None, the result is skipped: - - >>> components.registerSubscriptionAdapter( - ... tests.noop, [tests.I1], tests.IA2) - Registered event: - SubscriptionRegistration(<Components comps>, [I1], IA2, u'', noop, u'') - - >>> for s in components.subscribers((tests.U1(1), ), tests.IA2): - ... print s - A1_2(U1(1)) - A1_12(U1(1)) - A(U1(1),) - A(U1(1),) - -We can get registration information for subscriptions: - - >>> for registration in sorted( - ... components.registeredSubscriptionAdapters()): - ... print registration.required - ... print registration.provided, registration.name - ... print registration.factory, registration.info - (<InterfaceClass zope.component.tests.I1>,) - <InterfaceClass zope.component.tests.IA2> - zope.component.tests.A a sample comment - (<InterfaceClass zope.component.tests.I1>,) - <InterfaceClass zope.component.tests.IA2> - zope.component.tests.A a sample comment - (<InterfaceClass zope.component.tests.I1>,) - <InterfaceClass zope.component.tests.IA2> - zope.component.tests.A1_12 - (<InterfaceClass zope.component.tests.I1>,) - <InterfaceClass zope.component.tests.IA2> - zope.component.tests.A1_2 - (<InterfaceClass zope.component.tests.I1>,) - <InterfaceClass zope.component.tests.IA2> - <function noop at 0xb796ff7c> - -We can also unregister subscriptions in much the same way we can for adapters: - - >>> components.unregisterSubscriptionAdapter(tests.A1_2) - ... # doctest: +NORMALIZE_WHITESPACE - Unregistered event: - SubscriptionRegistration(<Components comps>, [I1], IA2, u'', A1_2, '') - True - - >>> for s in components.subscribers((tests.U1(1), ), tests.IA2): - ... print s - A1_12(U1(1)) - A(U1(1),) - A(U1(1),) - - >>> for registration in sorted( - ... components.registeredSubscriptionAdapters()): - ... print registration.required - ... print registration.provided, registration.name - ... print registration.factory, registration.info - (<InterfaceClass zope.component.tests.I1>,) - <InterfaceClass zope.component.tests.IA2> - zope.component.tests.A a sample comment - (<InterfaceClass zope.component.tests.I1>,) - <InterfaceClass zope.component.tests.IA2> - zope.component.tests.A a sample comment - (<InterfaceClass zope.component.tests.I1>,) - <InterfaceClass zope.component.tests.IA2> - zope.component.tests.A1_12 - (<InterfaceClass zope.component.tests.I1>,) - <InterfaceClass zope.component.tests.IA2> - <function noop at 0xb796ff7c> - - >>> components.unregisterSubscriptionAdapter( - ... tests.A, [tests.I1], tests.IA2) - Unregistered event: - SubscriptionRegistration(<Components comps>, [I1], IA2, u'', A, '') - True - - >>> for s in components.subscribers((tests.U1(1), ), tests.IA2): - ... print s - A1_12(U1(1)) - - >>> for registration in sorted( - ... components.registeredSubscriptionAdapters()): - ... print registration.required - ... print registration.provided, registration.name - ... print registration.factory, registration.info - (<InterfaceClass zope.component.tests.I1>,) - <InterfaceClass zope.component.tests.IA2> - zope.component.tests.A1_12 - (<InterfaceClass zope.component.tests.I1>,) - <InterfaceClass zope.component.tests.IA2> - <function noop at 0xb796ff7c> - -Note here that both registrations for A were removed. - -If we omit the factory, we must specify the required and provided interfaces: - - >>> components.unregisterSubscriptionAdapter(required=[tests.I1]) - Traceback (most recent call last): - ... - TypeError: Must specify one of factory and provided - - >>> components.unregisterSubscriptionAdapter(provided=tests.IA2) - Traceback (most recent call last): - ... - TypeError: Must specify one of factory and required - - >>> components.unregisterSubscriptionAdapter( - ... required=[tests.I1], provided=tests.IA2) - Unregistered event: - SubscriptionRegistration(<Components comps>, [I1], IA2, u'', None, '') - True - - >>> for s in components.subscribers((tests.U1(1), ), tests.IA2): - ... print s - - >>> for registration in sorted( - ... components.registeredSubscriptionAdapters()): - ... print registration.factory - -As when registering, an error is raised if the registration -information can't be determined from the factory and isn't specified: - - >>> components.unregisterSubscriptionAdapter(tests.A1_12) - ... # doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - TypeError: The adapter factory doesn't implement a single - interface and no provided interface was specified. - - >>> components.unregisterSubscriptionAdapter(tests.A) - ... # doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - TypeError: The adapter factory doesn't implement a single interface and - no provided interface was specified. - - >>> components.unregisterSubscriptionAdapter(tests.A, required=[tests.IA1]) - ... # doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - TypeError: The adapter factory doesn't implement a single interface - and no provided interface was specified. - -If you unregister something that's not registered, nothing will be -changed and False will be returned: - - - >>> components.unregisterSubscriptionAdapter( - ... required=[tests.I1], provided=tests.IA2) - False - -Handlers --------- - -Handlers are used when you want to perform some function in response -to an event. Handlers aren't expected to return anything when called -and are not registered to provide any interface. - - >>> from zope import component - >>> @component.adapter(tests.I1) - ... def handle1(x): - ... print 'handle1', x - - >>> components.registerHandler(handle1, info="First handler") - ... # doctest: +NORMALIZE_WHITESPACE - Registered event: - HandlerRegistration(<Components comps>, [I1], u'', - handle1, 'First handler') - >>> components.handle(tests.U1(1)) - handle1 U1(1) - - >>> @component.adapter(tests.I1, tests.I2) - ... def handle12(x, y): - ... print 'handle12', x, y - - >>> components.registerHandler(handle12) - Registered event: - HandlerRegistration(<Components comps>, [I1, I2], u'', handle12, u'') - >>> components.handle(tests.U1(1), tests.U12(2)) - handle12 U1(1) U12(2) - -If a handler doesn't document interfaces it handles, then -the required interfaces must be specified: - - >>> def handle(*objects): - ... print 'handle', objects - - >>> components.registerHandler(handle) - ... # doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - TypeError: The adapter factory doesn't have a __component_adapts__ - attribute and no required specifications were specified - - >>> components.registerHandler(handle, required=[tests.I1], - ... info="a comment") - Registered event: - HandlerRegistration(<Components comps>, [I1], u'', handle, 'a comment') - -Handlers can also be registered for classes: - - >>> components.registerHandler(handle, required=[tests.U], - ... info="handle a class") - ... # doctest: +NORMALIZE_WHITESPACE - Registered event: - HandlerRegistration(<Components comps>, [zope.component.tests.U], u'', - handle, 'handle a class') - - - >>> components.handle(tests.U1(1)) - handle (U1(1),) - handle1 U1(1) - handle (U1(1),) - -We can list the handler registrations: - - >>> for registration in components.registeredHandlers(): - ... print registration.required - ... print registration.handler, registration.info - ... # doctest: +NORMALIZE_WHITESPACE - (<InterfaceClass zope.component.tests.I1>,) - <function handle1 at 0xb78f5bfc> First handler - (<InterfaceClass zope.component.tests.I1>, - <InterfaceClass zope.component.tests.I2>) - <function handle12 at 0xb78f5c34> - (<InterfaceClass zope.component.tests.I1>,) - <function handle at 0xb78f5ca4> a comment - (<implementedBy zope.component.tests.U>,) - <function handle at 0xb78f5ca4> handle a class - -and we can unregister handlers: - - >>> components.unregisterHandler(required=[tests.U]) - ... # doctest: +NORMALIZE_WHITESPACE - Unregistered event: - HandlerRegistration(<Components comps>, [zope.component.tests.U], u'', - None, '') - True - - >>> for registration in components.registeredHandlers(): - ... print registration.required - ... print registration.handler, registration.info - ... # doctest: +NORMALIZE_WHITESPACE - (<InterfaceClass zope.component.tests.I1>,) - <function handle1 at 0xb78f5bfc> First handler - (<InterfaceClass zope.component.tests.I1>, - <InterfaceClass zope.component.tests.I2>) - <function handle12 at 0xb78f5c34> - (<InterfaceClass zope.component.tests.I1>,) - <function handle at 0xb78f5ca4> a comment - - >>> components.unregisterHandler(handle12) - Unregistered event: - HandlerRegistration(<Components comps>, [I1, I2], u'', handle12, '') - True - - >>> for registration in components.registeredHandlers(): - ... print registration.required - ... print registration.handler, registration.info - (<InterfaceClass zope.component.tests.I1>,) - <function handle1 at 0xb78f5bfc> First handler - (<InterfaceClass zope.component.tests.I1>,) - <function handle at 0xb78f5ca4> a comment - - >>> components.unregisterHandler(handle12) - False - - >>> components.unregisterHandler() - Traceback (most recent call last): - ... - TypeError: Must specify one of factory and required - - >>> components.registerHandler(handle) - ... # doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - TypeError: The adapter factory doesn't have a __component_adapts__ - attribute and no required specifications were specified - -Extending ---------- - -Component-management objects can extend other component-management -objects. - - >>> c1 = registry.Components('1') - >>> c1.__bases__ - () - - >>> c2 = registry.Components('2', (c1, )) - >>> c2.__bases__ == (c1, ) - True - - >>> c1.registerUtility(tests.U1(1)) - Registered event: - UtilityRegistration(<Components 1>, I1, u'', 1, None, u'') - - >>> c1.queryUtility(tests.I1) - U1(1) - >>> c2.queryUtility(tests.I1) - U1(1) - >>> c1.registerUtility(tests.U1(2)) - Unregistered event: - UtilityRegistration(<Components 1>, I1, u'', 1, None, u'') - Registered event: - UtilityRegistration(<Components 1>, I1, u'', 2, None, u'') - - >>> c2.queryUtility(tests.I1) - U1(2) - -We can use multiple inheritence: - - >>> c3 = registry.Components('3', (c1, )) - >>> c4 = registry.Components('4', (c2, c3)) - >>> c4.queryUtility(tests.I1) - U1(2) - - >>> c1.registerUtility(tests.U12(1), tests.I2) - Registered event: - UtilityRegistration(<Components 1>, I2, u'', 1, None, u'') - - >>> c4.queryUtility(tests.I2) - U12(1) - - >>> c3.registerUtility(tests.U12(3), tests.I2) - Registered event: - UtilityRegistration(<Components 3>, I2, u'', 3, None, u'') - >>> c4.queryUtility(tests.I2) - U12(3) - - >>> c1.registerHandler(handle1, info="First handler") - Registered event: - HandlerRegistration(<Components 1>, [I1], u'', handle1, 'First handler') - - >>> c2.registerHandler(handle, required=[tests.U]) - ... # doctest: +NORMALIZE_WHITESPACE - Registered event: - HandlerRegistration(<Components 2>, [zope.component.tests.U], u'', - handle, u'') - - >>> @component.adapter(tests.I1) - ... def handle3(x): - ... print 'handle3', x - >>> c3.registerHandler(handle3) - Registered event: - HandlerRegistration(<Components 3>, [I1], u'', handle3, u'') - - >>> @component.adapter(tests.I1) - ... def handle4(x): - ... print 'handle4', x - >>> c4.registerHandler(handle4) - Registered event: - HandlerRegistration(<Components 4>, [I1], u'', handle4, u'') - - >>> c4.handle(tests.U1(1)) - handle1 U1(1) - handle3 U1(1) - handle (U1(1),) - handle4 U1(1) - -Redispatch of registration events ---------------------------------- - -Some handlers are available that, if registered, redispatch -registration events to the objects being registered. They depend on -being dispatched to by the object-event dispatcher: - - >>> from zope import component - >>> import zope.component.event - >>> zope.component.getGlobalSiteManager().registerHandler( - ... zope.component.event.objectEventNotify) - ... # doctest: +NORMALIZE_WHITESPACE - Registered event: - HandlerRegistration(<BaseGlobalComponents base>, - [IObjectEvent], u'', objectEventNotify, u'') - -To see this, we'll first register a multi-handler to show is when -handlers are called on 2 objects: - - >>> @zope.component.adapter(None, None) - ... def double_handler(o1, o2): - ... print 'Double dispatch:' - ... print ' ', o1 - ... print ' ', o2 - >>> zope.component.getGlobalSiteManager().registerHandler(double_handler) - ... # doctest: +NORMALIZE_WHITESPACE - Double dispatch: - HandlerRegistration(<BaseGlobalComponents base>, - [Interface, Interface], u'', double_handler, u'') - Registered event: - HandlerRegistration(<BaseGlobalComponents base>, - [Interface, Interface], u'', double_handler, u'') - Registered event: - HandlerRegistration(<BaseGlobalComponents base>, - [Interface, Interface], u'', double_handler, u'') - -In the example above, the double_handler reported it's own registration. :) - -Now we'll register our handlers: - - >>> zope.component.getGlobalSiteManager().registerHandler( - ... registry.dispatchUtilityRegistrationEvent) - ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS - Double dispatch: - ... - - >>> zope.component.getGlobalSiteManager().registerHandler( - ... registry.dispatchAdapterRegistrationEvent) - ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS - Double dispatch: - ... - - >>> zope.component.getGlobalSiteManager().registerHandler( - ... registry.dispatchSubscriptionAdapterRegistrationEvent) - ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS - Double dispatch: - ... - - >>> zope.component.getGlobalSiteManager().registerHandler( - ... registry.dispatchHandlerRegistrationEvent) - ... # doctest: +NORMALIZE_WHITESPACE - Double dispatch: - HandlerRegistration(<BaseGlobalComponents base>, - [IHandlerRegistration, IRegistrationEvent], u'', - dispatchHandlerRegistrationEvent, u'') - Registered event: - HandlerRegistration(<BaseGlobalComponents base>, - [IHandlerRegistration, IRegistrationEvent], u'', - dispatchHandlerRegistrationEvent, u'') - Double dispatch: - <function dispatchHandlerRegistrationEvent at 0xb799f72c> - Registered event: - HandlerRegistration(<BaseGlobalComponents base>, - [IHandlerRegistration, IRegistrationEvent], u'', - dispatchHandlerRegistrationEvent, u'') - Registered event: - HandlerRegistration(<BaseGlobalComponents base>, - [IHandlerRegistration, IRegistrationEvent], u'', - dispatchHandlerRegistrationEvent, u'') - -In the last example above, we can see that the registration of -dispatchHandlerRegistrationEvent was handled by -dispatchHandlerRegistrationEvent and redispatched. This can be seen -in the second double-dispatch output, where the first argument is the -object being registered, which is dispatchHandlerRegistrationEvent. - -If we change some other registrations, we can the double dispatch -taking place: - - >>> components.registerUtility(u5) - ... # doctest: +NORMALIZE_WHITESPACE - Double dispatch: - UtilityRegistration(<Components comps>, I1, u'', 5, None, u'') - Registered event: - UtilityRegistration(<Components comps>, I1, u'', 5, None, u'') - Double dispatch: - U1(5) - Registered event: - UtilityRegistration(<Components comps>, I1, u'', 5, None, u'') - Registered event: - UtilityRegistration(<Components comps>, I1, u'', 5, None, u'') - - >>> components.registerAdapter(tests.A12_1) - ... # doctest: +NORMALIZE_WHITESPACE - Double dispatch: - AdapterRegistration(<Components comps>, [I1, I2], IA1, u'', A12_1, u'') - Registered event: - AdapterRegistration(<Components comps>, [I1, I2], IA1, u'', A12_1, u'') - Double dispatch: - zope.component.tests.A12_1 - Registered event: - AdapterRegistration(<Components comps>, [I1, I2], IA1, u'', A12_1, u'') - Registered event: - AdapterRegistration(<Components comps>, [I1, I2], IA1, u'', A12_1, u'') - - >>> components.registerSubscriptionAdapter(tests.A1_2) - ... # doctest: +NORMALIZE_WHITESPACE - Double dispatch: - SubscriptionRegistration(<Components comps>, [I1], IA2, u'', A1_2, u'') - Registered event: - SubscriptionRegistration(<Components comps>, [I1], IA2, u'', A1_2, u'') - Double dispatch: - zope.component.tests.A1_2 - Registered event: - SubscriptionRegistration(<Components comps>, [I1], IA2, u'', A1_2, u'') - Registered event: - SubscriptionRegistration(<Components comps>, [I1], IA2, u'', A1_2, u'') diff --git a/src/zope/component/security.py b/src/zope/component/security.py index 85230fe..5c9707e 100644 --- a/src/zope/component/security.py +++ b/src/zope/component/security.py @@ -13,13 +13,15 @@ ############################################################################## """zope.security support for the configuration handlers """ -__docformat__ = "reStructuredText" - from zope.interface import providedBy -from zope.proxy import ProxyBase, getProxiedObject -from zope.security.adapter import LocatingTrustedAdapterFactory, \ - LocatingUntrustedAdapterFactory, TrustedAdapterFactory -from zope.security.checker import Checker, CheckerPublic, InterfaceChecker +from zope.proxy import ProxyBase +from zope.proxy import getProxiedObject +from zope.security.adapter import LocatingTrustedAdapterFactory +from zope.security.adapter import LocatingUntrustedAdapterFactory +from zope.security.adapter import TrustedAdapterFactory +from zope.security.checker import Checker +from zope.security.checker import CheckerPublic +from zope.security.checker import InterfaceChecker from zope.security.proxy import Proxy @@ -60,7 +62,8 @@ def proxify(ob, checker=None, provides=None, permission=None): if checker is None: if provides is None or permission is None: - raise ValueError, 'Required arguments: checker or both provides and permissions' + raise ValueError('Required arguments: ' + 'checker or both provides and permissions') if permission == PublicPermission: permission = CheckerPublic checker = InterfaceChecker(provides, permission) @@ -84,82 +87,6 @@ def protectedFactory(original_factory, provides, permission): return factory def securityAdapterFactory(factory, permission, locate, trusted): - """ - If a permission is provided when wrapping the adapter, it will be - wrapped in a LocatingAdapterFactory. - - >>> class Factory: - ... pass - - If both locate and trusted are False and a non-public - permission is provided, then the factory is wrapped into a - LocatingUntrustedAdapterFactory: - - >>> factory = securityAdapterFactory(Factory, 'zope.AnotherPermission', - ... locate=False, trusted=False) - - >>> isinstance(factory, LocatingUntrustedAdapterFactory) - True - - If a PublicPermission is provided, then the factory is not touched. - - >>> factory = securityAdapterFactory(Factory, PublicPermission, - ... locate=False, trusted=False) - - >>> factory is Factory - True - - Same for CheckerPublic: - - >>> factory = securityAdapterFactory(Factory, CheckerPublic, - ... locate=False, trusted=False) - - >>> factory is Factory - True - - If the permission is None, the factory isn't touched: - - >>> factory = securityAdapterFactory(Factory, None, - ... locate=False, trusted=False) - - >>> factory is Factory - True - - If the factory is trusted and a no permission is provided then the - adapter is wrapped into a TrustedAdapterFactory: - - >>> factory = securityAdapterFactory(Factory, None, - ... locate=False, trusted=True) - - >>> isinstance(factory, TrustedAdapterFactory) - True - - Same for PublicPermission: - - >>> factory = securityAdapterFactory(Factory, PublicPermission, - ... locate=False, trusted=True) - - >>> isinstance(factory, TrustedAdapterFactory) - True - - Same for CheckerPublic: - - >>> factory = securityAdapterFactory(Factory, CheckerPublic, - ... locate=False, trusted=True) - - >>> isinstance(factory, TrustedAdapterFactory) - True - - If the factory is trusted and a locate is true, then the - adapter is wrapped into a LocatingTrustedAdapterFactory: - - >>> factory = securityAdapterFactory(Factory, 'zope.AnotherPermission', - ... locate=True, trusted=True) - - >>> isinstance(factory, LocatingTrustedAdapterFactory) - True - - """ if permission == PublicPermission: permission = CheckerPublic if locate or (permission is not None and permission is not CheckerPublic): diff --git a/src/zope/component/socketexample.txt b/src/zope/component/socketexample.txt deleted file mode 100644 index 11bb6fd..0000000 --- a/src/zope/component/socketexample.txt +++ /dev/null @@ -1,597 +0,0 @@ -The Zope 3 Component Architecture (Socket Example) -================================================== - -The component architecture provides an application framework that provides its -functionality through loosely-connected components. A *component* can be any -Python object and has a particular purpose associated with it. Thus, in a -component-based applications you have many small component in contrast to -classical object-oriented development, where you have a few big objects. - -Components communicate via specific APIs, which are formally defined by -interfaces, which are provided by the `zope.interface` package. *Interfaces* -describe the methods and properties that a component is expected to -provide. They are also used as a primary mean to provide developer-level -documentation for the components. For more details about interfaces see -`zope/interface/README.txt`. - -The two main types of components are *adapters* and *utilities*. They will be -discussed in detail later in this document. Both component types are managed -by the *site manager*, with which you can register and access these -components. However, most of the site manager's functionality is hidden behind -the component architecture's public API, which is documented in -`IComponentArchitecture`. - - -Adapters --------- - -Adapters are a well-established pattern. An *adapter* uses an object providing -one interface to produce an object that provides another interface. Here an -example: Imagine that you purchased an electric shaver in the US, and thus -you require the US socket type. You are now traveling in Germany, where another -socket style is used. You will need a device, an adapter, that converts from -the German to the US socket style. - -The functionality of adapters is actually natively provided by the -`zope.interface` package and is thus well documented there. The `human.txt` -file provides a gentle introduction to adapters, whereby `adapter.txt` is -aimed at providing a comprehensive insight into adapters, but is too abstract -for many as an initial read. Thus, we will only explain adapters in the context -of the component architecture's API. - -So let's say that we have a German socket - - >>> from zope.interface import Interface, implements - - >>> class IGermanSocket(Interface): - ... pass - - >>> class Socket(object): - ... def __repr__(self): - ... return '<instance of %s>' %self.__class__.__name__ - - >>> class GermanSocket(Socket): - ... """German wall socket.""" - ... implements(IGermanSocket) - -and we want to convert it to an US socket - - >>> class IUSSocket(Interface): - ... pass - -so that our shaver can be used in Germany. So we go to a German electronics -store to look for an adapter that we can plug in the wall: - - >>> class GermanToUSSocketAdapter(Socket): - ... implements(IUSSocket) - ... __used_for__ = IGermanSocket - ... - ... def __init__(self, socket): - ... self.context = socket - -Note that I could have called the passed in socket any way I like, but -`context` is the standard name accepted. - - -Single Adapters -~~~~~~~~~~~~~~~ - -Before we can use the adapter, we have to buy it and make it part of our -inventory. In the component architecture we do this by registering the adapter -with the framework, more specifically with the global site manager: - - >>> import zope.component - >>> gsm = zope.component.getGlobalSiteManager() - >>> gsm.registerAdapter(GermanToUSSocketAdapter, (IGermanSocket,), IUSSocket) - -`zope.component` is the component architecture API that is being -presented by this file. You registered an adapter from `IGermanSocket` -to `IUSSocket` having no name (thus the empty string). - -Anyways, you finally get back to your hotel room and shave, since you have not -been able to shave in the plane. In the bathroom you discover a socket: - - >>> bathroomDE = GermanSocket() - >>> IGermanSocket.providedBy(bathroomDE) - True - -You now insert the adapter in the German socket - - >>> bathroomUS = zope.component.getAdapter(bathroomDE, IUSSocket, '') - -so that the socket now provides the US version: - - >>> IUSSocket.providedBy(bathroomUS) - True - -Now you can insert your shaver and get on with your day. - -After a week you travel for a couple of days to the Prague and you notice that -the Czech have yet another socket type: - - >>> class ICzechSocket(Interface): - ... pass - - >>> class CzechSocket(Socket): - ... implements(ICzechSocket) - - >>> czech = CzechSocket() - -You try to find an adapter for your shaver in your bag, but you fail, since -you do not have one: - - >>> zope.component.getAdapter(czech, IUSSocket, '') \ - ... #doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - ComponentLookupError: (<instance of CzechSocket>, - <InterfaceClass __builtin__.IUSSocket>, - '') - -or the more graceful way: - - >>> marker = object() - >>> socket = zope.component.queryAdapter(czech, IUSSocket, '', marker) - >>> socket is marker - True - -In the component architecture API any `get*` method will fail with a specific -exception, if a query failed, whereby methods starting with `query*` will -always return a `default` value after a failure. - - -Named Adapters -~~~~~~~~~~~~~~ - -You are finally back in Germany. You also brought your DVD player and a couple -DVDs with you, which you would like to watch. Your shaver was able to convert -automatically from 110 volts to 240 volts, but your DVD player cannot. So you -have to buy another adapter that also handles converting the voltage and the -frequency of the AC current: - - >>> class GermanToUSSocketAdapterAndTransformer(object): - ... implements(IUSSocket) - ... __used_for__ = IGermanSocket - ... - ... def __init__(self, socket): - ... self.context = socket - -Now, we need a way to keep the two adapters apart. Thus we register them with -a name: - - >>> gsm.registerAdapter(GermanToUSSocketAdapter, - ... (IGermanSocket,), IUSSocket, 'shaver',) - >>> gsm.registerAdapter(GermanToUSSocketAdapterAndTransformer, - ... (IGermanSocket,), IUSSocket, 'dvd') - -Now we simply look up the adapters using their labels (called *name*): - - >>> socket = zope.component.getAdapter(bathroomDE, IUSSocket, 'shaver') - >>> socket.__class__ is GermanToUSSocketAdapter - True - - >>> socket = zope.component.getAdapter(bathroomDE, IUSSocket, 'dvd') - >>> socket.__class__ is GermanToUSSocketAdapterAndTransformer - True - -Clearly, we do not have an adapter for the MP3 player - - >>> zope.component.getAdapter(bathroomDE, IUSSocket, 'mp3') \ - ... #doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - ComponentLookupError: (<instance of GermanSocket>, - <InterfaceClass __builtin__.IUSSocket>, - 'mp3') - -but you could use the 'dvd' adapter in this case of course. ;) - -Sometimes you want to know all adapters that are available. Let's say you want -to know about all the adapters that convert a German to a US socket type: - - >>> sockets = list(zope.component.getAdapters((bathroomDE,), IUSSocket)) - >>> len(sockets) - 3 - >>> names = [name for name, socket in sockets] - >>> names.sort() - >>> names - [u'', u'dvd', u'shaver'] - -`zope.component.getAdapters()` returns a list of tuples. The first -entry of the tuple is the name of the adapter and the second is the -adapter itself. - - -Multi-Adapters -~~~~~~~~~~~~~~ - -After watching all the DVDs you brought at least twice, you get tired of them -and you want to listen to some music using your MP3 player. But darn, the MP3 -player plug has a ground pin and all the adapters you have do not support -that: - - >>> class IUSGroundedSocket(IUSSocket): - ... pass - -So you go out another time to buy an adapter. This time, however, you do not -buy yet another adapter, but a piece that provides the grounding plug: - - >>> class IGrounder(Interface): - ... pass - - >>> class Grounder(object): - ... implements(IGrounder) - ... def __repr__(self): - ... return '<instance of Grounder>' - - -Then together they will provided a grounded us socket: - - >>> class GroundedGermanToUSSocketAdapter(object): - ... implements(IUSGroundedSocket) - ... __used_for__ = (IGermanSocket, IGrounder) - ... def __init__(self, socket, grounder): - ... self.socket, self.grounder = socket, grounder - -You now register the combination, so that you know you can create a -`IUSGroundedSocket`: - - >>> gsm.registerAdapter(GroundedGermanToUSSocketAdapter, - ... (IGermanSocket, IGrounder), IUSGroundedSocket, 'mp3') - -Given the grounder - - >>> grounder = Grounder() - -and a German socket - - >>> livingroom = GermanSocket() - -we can now get a grounded US socket: - - >>> socket = zope.component.getMultiAdapter((livingroom, grounder), - ... IUSGroundedSocket, 'mp3') - - >>> socket.__class__ is GroundedGermanToUSSocketAdapter - True - >>> socket.socket is livingroom - True - >>> socket.grounder is grounder - True - -Of course, you do not have a 'dvd' grounded US socket available: - - >>> zope.component.getMultiAdapter((livingroom, grounder), - ... IUSGroundedSocket, 'dvd') \ - ... #doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - ComponentLookupError: ((<instance of GermanSocket>, - <instance of Grounder>), - <InterfaceClass __builtin__.IUSGroundedSocket>, - 'dvd') - - - >>> socket = zope.component.queryMultiAdapter( - ... (livingroom, grounder), IUSGroundedSocket, 'dvd', marker) - >>> socket is marker - True - -Again, you might want to read `adapter.txt` in `zope.interface` for a more -comprehensive coverage of multi-adapters. - -Subscribers ------------ - -While subscribers are directly supported by the adapter registry and are -adapters for all theoretical purposes, practically it might be better to think -of them as separate components. Subscribers are particularly useful for -events. - -Let's say one of our adapters overheated and caused a small fire: - - >>> class IFire(Interface): - ... pass - - >>> class Fire(object): - ... implements(IFire) - - >>> fire = Fire() - -We want to use all available objects to put out the fire: - - >>> class IFireExtinguisher(Interface): - ... def extinguish(): - ... pass - - >>> class FireExtinguisher(object): - ... def __init__(self, fire): - ... pass - ... def extinguish(self): - ... "Place extinguish code here." - ... print 'Used ' + self.__class__.__name__ + '.' - -Here some specific methods to put out the fire: - - >>> class PowderExtinguisher(FireExtinguisher): - ... pass - >>> gsm.registerSubscriptionAdapter(PowderExtinguisher, - ... (IFire,), IFireExtinguisher) - - >>> class Blanket(FireExtinguisher): - ... pass - >>> gsm.registerSubscriptionAdapter(Blanket, (IFire,), IFireExtinguisher) - - >>> class SprinklerSystem(FireExtinguisher): - ... pass - >>> gsm.registerSubscriptionAdapter(SprinklerSystem, - ... (IFire,), IFireExtinguisher) - -Now let use all these things to put out the fire: - - >>> extinguishers = zope.component.subscribers((fire,), IFireExtinguisher) - >>> extinguishers.sort() - >>> for extinguisher in extinguishers: - ... extinguisher.extinguish() - Used Blanket. - Used PowderExtinguisher. - Used SprinklerSystem. - -If no subscribers are found for a particular object, then an empty list is -returned: - - >>> zope.component.subscribers((object(),), IFireExtinguisher) - [] - - -Utilities ---------- - -Utilities are the second type of component, the component architecture -implements. *Utilities* are simply components that provide an interface. When -you register an utility, you always register an instance (in contrast to a -factory for adapters) since the initialization and setup process of a utility -might be complex and is not well defined. In some ways a utility is much more -fundamental than an adapter, because an adapter cannot be used without another -component, but a utility is always self-contained. I like to think of -utilities as the foundation of your application and adapters as components -extending beyond this foundation. - -Back to our story... - -After your vacation is over you fly back home to Tampa, Florida. But it is -August now, the middle of the Hurricane season. And, believe it or not, you are -worried that you will not be able to shave when the power goes out for several -days. (You just hate wet shavers.) - -So you decide to go to your favorite hardware store and by a Diesel-powered -electric generator. The generator provides of course a US-style socket: - - >>> class Generator(object): - ... implements(IUSSocket) - ... def __repr__(self): - ... return '<instance of Generator>' - - >>> generator = Generator() - -Like for adapters, we now have to add the newly-acquired generator to our -inventory by registering it as a utility: - - >>> gsm.registerUtility(generator, IUSSocket) - -We can now get the utility using - - >>> utility = zope.component.getUtility(IUSSocket) - >>> utility is generator - True - -As you can see, it is very simple to register and retrieve utilities. If a -utility does not exist for a particular interface, such as the German socket, -then the lookup fails - - >>> zope.component.getUtility(IGermanSocket) - Traceback (most recent call last): - ... - ComponentLookupError: (<InterfaceClass __builtin__.IGermanSocket>, '') - -or more gracefully when specifying a default value: - - >>> default = object() - >>> utility = zope.component.queryUtility(IGermanSocket, default=default) - >>> utility is default - True - -Note: The only difference between `getUtility()` and `queryUtility()` is the -fact that you can specify a default value for the latter function, so that it -will never cause a `ComponentLookupError`. - - -Named Utilities -~~~~~~~~~~~~~~~ - -It is often desirable to have several utilities providing the same interface -per site. This way you can implement any sort of registry using utilities. For -this reason, utilities -- like adapters -- can be named. - -In the context of our story, we might want to do the following: You really do -not trust gas stations either. What if the roads are blocked after a hurricane -and the gas stations run out of oil. So you look for another renewable power -source. Then you think about solar panels! After a storm there is usually very -nice weather, so why not? Via the Web you order a set of 110V/120W solar -panels that provide a regular US-style socket as output: - - >>> class SolarPanel(object): - ... implements(IUSSocket) - ... def __repr__(self): - ... return '<instance of Solar Panel>' - - >>> panel = SolarPanel() - -Once it arrives, we add it to our inventory: - - >>> gsm.registerUtility(panel, IUSSocket, 'Solar Panel') - -You can now access the solar panel using - - >>> utility = zope.component.getUtility(IUSSocket, 'Solar Panel') - >>> utility is panel - True - -Of course, if a utility is not available, then the lookup will simply fail - - >>> zope.component.getUtility(IUSSocket, 'Wind Mill') - Traceback (most recent call last): - ... - ComponentLookupError: (<InterfaceClass __builtin__.IUSSocket>, 'Wind Mill') - -or more gracefully when specifying a default value: - - >>> default = object() - >>> utility = zope.component.queryUtility(IUSSocket, 'Wind Mill', - ... default=default) - >>> utility is default - True - -Now you want to look at all the utilities you have for a particular kind. The -following API function will return a list of name/utility pairs: - - >>> utils = list(zope.component.getUtilitiesFor(IUSSocket)) - >>> utils.sort() - >>> utils #doctest: +NORMALIZE_WHITESPACE - [(u'', <instance of Generator>), - (u'Solar Panel', <instance of Solar Panel>)] - -Another method of looking up all utilities is by using -`getAllUtilitiesRegisteredFor(iface)`. This function will return an iterable -of utilities (without names); however, it will also return overridden -utilities. If you are not using multiple site managers, you will not actually -need this method. - - >>> utils = list(zope.component.getAllUtilitiesRegisteredFor(IUSSocket)) - >>> utils.sort() - >>> utils - [<instance of Generator>, <instance of Solar Panel>] - - -Factories -~~~~~~~~~ - -A *factory* is a special kind of utility that exists to create other -components. A factory is always identified by a name. It also provides a title -and description and is able to tell the developer what interfaces the created -object will provide. The advantage of using a factory to create an object -instead of directly instantiating a class or executing any other callable is -that we can refer to the factory by name. As long as the name stays fixed, the -implementation of the callable can be renamed or moved without a breakage in -code. - -Let's say that our solar panel comes in parts and they have to be -assembled. This assembly would be done by a factory, so let's create one for -the solar panel. To do this, we can use a standard implementation of the -`IFactory` interface: - - >>> from zope.component.factory import Factory - >>> factory = Factory(SolarPanel, - ... 'Solar Panel', - ... 'This factory creates a solar panel.') - -Optionally, I could have also specified the interfaces that the created object -will provide, but the factory class is smart enough to determine the -implemented interface from the class. We now register the factory: - - >>> from zope.component.interfaces import IFactory - >>> gsm.registerUtility(factory, IFactory, 'SolarPanel') - -We can now get a list of interfaces the produced object will provide: - - >>> ifaces = zope.component.getFactoryInterfaces('SolarPanel') - >>> IUSSocket in ifaces - True - -By the way, this is equivalent to - - >>> ifaces2 = factory.getInterfaces() - >>> ifaces is ifaces2 - True - -Of course you can also just create an object: - - >>> panel = zope.component.createObject('SolarPanel') - >>> panel.__class__ is SolarPanel - True - -Note: Ignore the first argument (`None`) for now; it is the context of the -utility lookup, which is usually an optional argument, but cannot be in this -case, since all other arguments beside the `name` are passed in as arguments -to the specified callable. - -Once you register several factories - - >>> gsm.registerUtility(Factory(Generator), IFactory, 'Generator') - -you can also determine, which available factories will create objects -providing a certain interface: - - >>> factories = zope.component.getFactoriesFor(IUSSocket) - >>> factories = [(name, factory.__class__) for name, factory in factories] - >>> factories.sort() - >>> factories #doctest: +NORMALIZE_WHITESPACE - [(u'Generator', <class 'zope.component.factory.Factory'>), - (u'SolarPanel', <class 'zope.component.factory.Factory'>)] - - -Site Managers -------------- - -Why do we need site managers? Why is the component architecture API not -sufficient? Some applications, including Zope 3, have a concept of -locations. It is often desirable to have different configurations for these -location; this can be done by overwriting existing or adding new component -registrations. Site managers in locations below the root location, should be -able to delegate requests to their parent locations. The root site manager is -commonly known as *global site manager*, since it is always available. You can -always get the global site manager using the API: - - >>> gsm = zope.component.getGlobalSiteManager() - - >>> from zope.component import globalSiteManager - >>> gsm is globalSiteManager - True - >>> from zope.component.interfaces import IComponentLookup - >>> IComponentLookup.providedBy(gsm) - True - >>> from zope.component.interfaces import IComponents - >>> IComponents.providedBy(gsm) - True - -You can also lookup at site manager in a given context. The only requirement -is that the context can be adapted to a site manager. So let's create a -special site manager: - - >>> from zope.component.globalregistry import BaseGlobalComponents - >>> sm = BaseGlobalComponents() - -Now we create a context that adapts to the site manager via the `__conform__` -method as specified in PEP 246. - - >>> class Context(object): - ... def __init__(self, sm): - ... self.sm = sm - ... def __conform__(self, interface): - ... if interface.isOrExtends(IComponentLookup): - ... return self.sm - -We now instantiate the `Context` with our special site manager: - - >>> context = Context(sm) - >>> context.sm is sm - True - -We can now ask for the site manager of this context: - - >>> lsm = zope.component.getSiteManager(context) - >>> lsm is sm - True - -The site manager instance `lsm` is formally known as a *local site manager* of -`context`. diff --git a/src/zope/component/standalonetests.py b/src/zope/component/standalonetests.py index 9b8385a..3b3c8fe 100644 --- a/src/zope/component/standalonetests.py +++ b/src/zope/component/standalonetests.py @@ -1,54 +1,49 @@ """ -Standalone Tests +See: https://bugs.launchpad.net/zope3/+bug/98401 """ -import unittest -import doctest import sys import pickle -if __name__ == "__main__": - sys.path = pickle.loads(sys.stdin.read()) - -from zope import interface -from zope.component.testing import setUp, tearDown - -class I1(interface.Interface): - pass - -class I2(interface.Interface): - pass - -class Ob(object): - interface.implements(I1) - def __repr__(self): - return '<instance Ob>' - -ob = Ob() - -class Comp(object): - interface.implements(I2) - def __init__(self, context): - self.context = context - -def providing_adapter_sets_adapter_hook(): - """ - A side effect of importing installs the adapter hook. See - http://www.zope.org/Collectors/Zope3-dev/674. - - >>> import zope.component - >>> zope.component.provideAdapter(Comp, (I1,), I2) - >>> adapter = I2(ob) - >>> adapter.__class__ is Comp - True - >>> adapter.context is ob - True - """ - - -def test_suite(): - return unittest.TestSuite(( - doctest.DocTestSuite(setUp=setUp, tearDown=tearDown), - )) - -if __name__ == "__main__": - unittest.main(defaultTest='test_suite') +def write(x): + sys.stdout.write('%s\n' % x) + +if __name__ == "__main__": #pragma NO COVER (runs in subprocess) + #sys.path = pickle.loads(sys.stdin.read()) + write('XXXXXXXXXX') + for p in sys.path: + write('- %s' % p) + write('XXXXXXXXXX') + + import zope + from zope.interface import Interface + from zope.interface import implementer + + class I1(Interface): + pass + + class I2(Interface): + pass + + @implementer(I1) + class Ob(object): + def __repr__(self): + return '<instance Ob>' + + ob = Ob() + + @implementer(I2) + class Comp(object): + def __init__(self, context): + self.context = context + + write('YYYYYYYYY') + for p in zope.__path__: + write('- %s' % p) + write('YYYYYYYYY') + import zope.component + + zope.component.provideAdapter(Comp, (I1,), I2) + adapter = I2(ob) + write('ZZZZZZZZ') + assert adapter.__class__ is Comp + assert adapter.context is ob diff --git a/src/zope/component/testfiles/adapter.py b/src/zope/component/testfiles/adapter.py index 1fcdaf9..6728db1 100644 --- a/src/zope/component/testfiles/adapter.py +++ b/src/zope/component/testfiles/adapter.py @@ -13,22 +13,25 @@ ############################################################################## """Sample adapter class for testing """ -import zope.interface -import zope.component -import components -class I1(zope.interface.Interface): +from zope.interface import Interface +from zope.interface import implementer + +from zope.component import adapter +from zope.component.testfiles import components + +class I1(Interface): pass -class I2(zope.interface.Interface): +class I2(Interface): pass -class I3(zope.interface.Interface): +class I3(Interface): def f1(): pass def f2(): pass def f3(): pass -class IS(zope.interface.Interface): +class IS(Interface): pass @@ -36,23 +39,27 @@ class Adapter(object): def __init__(self, *args): self.context = args +@implementer(I1) class A1(Adapter): - zope.interface.implements(I1) + pass +@implementer(I2) class A2(Adapter): - zope.interface.implements(I2) + pass +@adapter(components.IContent, I1, I2) +@implementer(I3) class A3(Adapter): - zope.component.adapts(components.IContent, I1, I2) - zope.interface.implements(I3) + pass class A4: pass a4 = A4() +@implementer(I1, I2) class A5: - zope.interface.implements(I1, I2) + pass a5 = A5() diff --git a/src/zope/component/testfiles/components.py b/src/zope/component/testfiles/components.py index 3967b1e..efbd2fb 100644 --- a/src/zope/component/testfiles/components.py +++ b/src/zope/component/testfiles/components.py @@ -13,8 +13,10 @@ ############################################################################## """Components for testing """ -from zope.interface import Interface, Attribute, implements -from zope.component import adapts +from zope.interface import Interface +from zope.interface import Attribute +from zope.interface import implementer +from zope.component import adapter class IAppb(Interface): a = Attribute('test attribute') @@ -31,12 +33,14 @@ class IApp3(IAppb): class IContent(Interface): pass +@implementer(IContent) class Content(object): - implements(IContent) + pass +@adapter(IContent) +@implementer(IApp) class Comp(object): - adapts(IContent) - implements(IApp) + pass def __init__(self, *args): # Ignore arguments passed to constructor diff --git a/src/zope/component/testfiles/views.py b/src/zope/component/testfiles/views.py index d395f9f..a017b5f 100644 --- a/src/zope/component/testfiles/views.py +++ b/src/zope/component/testfiles/views.py @@ -13,7 +13,10 @@ ############################################################################## """Views test. """ -from zope.interface import Interface, implements, directlyProvides + +from zope.interface import Interface +from zope.interface import implementer +from zope.interface import directlyProvides class Request(object): @@ -29,8 +32,8 @@ class IV(Interface): class IC(Interface): pass +@implementer(IV) class V1(object): - implements(IV) def __init__(self, context, request): self.context = context @@ -46,6 +49,7 @@ class VZMI(V1): def index(self): return 'ZMI here' +@implementer(IV) class R1(object): def index(self): @@ -57,7 +61,6 @@ class R1(object): def __init__(self, request): pass - implements(IV) class RZMI(R1): pass diff --git a/src/zope/component/testing.py b/src/zope/component/testing.py index 5fde361..d49f1cb 100644 --- a/src/zope/component/testing.py +++ b/src/zope/component/testing.py @@ -18,7 +18,17 @@ import zope.component.event # we really don't need special setup now: -from zope.testing.cleanup import CleanUp as PlacelessSetup +try: + from zope.testing.cleanup import CleanUp as PlacelessSetup +except ImportError: + class PlacelessSetup(object): + def cleanUp(self): + from zope.component.globalregistry import base + base.__init__('base') + def setUp(self): + self.cleanUp() + def tearDown(self): + self.cleanUp() def setUp(test=None): PlacelessSetup().setUp() diff --git a/src/zope/component/testlayer.py b/src/zope/component/testlayer.py index fb036d2..c73c959 100644 --- a/src/zope/component/testlayer.py +++ b/src/zope/component/testlayer.py @@ -15,7 +15,12 @@ import os from zope.configuration import xmlconfig, config -from zope.testing.cleanup import cleanUp +try: + from zope.testing.cleanup import cleanUp +except ImportError: + def cleanUp(): + pass + from zope.component import provideHandler from zope.component.hooks import setHooks from zope.component.eventtesting import events, clearEvents diff --git a/src/zope/component/testlayer.txt b/src/zope/component/testlayer.txt deleted file mode 100644 index 1246ba7..0000000 --- a/src/zope/component/testlayer.txt +++ /dev/null @@ -1,100 +0,0 @@ -Layers -====== - -zope.component.testlayer defines two things: - -* a LayerBase that makes it easier and saner to use zope.testing's - test layers. - -* a ZCMLLayer which lets you implement a layer that loads up some - ZCML. - -LayerBase ---------- - -We check whether our LayerBase can be used to create layers of our -own. We do this simply by subclassing: - - >>> from zope.component.testlayer import LayerBase - >>> class OurLayer(LayerBase): - ... def setUp(self): - ... super(OurLayer, self).setUp() - ... print "setUp called" - ... def tearDown(self): - ... super(OurLayer, self).tearDown() - ... print "tearDown called" - ... def testSetUp(self): - ... super(OurLayer, self).testSetUp() - ... print "testSetUp called" - ... def testTearDown(self): - ... super(OurLayer, self).testTearDown() - ... print "testTearDown called" - -Note that if we wanted to ensure that the methods of the superclass -were called we have to use super(). In this case we actually wouldn't -need to, as these methods do nothing at all, but we just ensure that -they are there in the first place. - -Let's instantiate our layer. We need to supply it with the package the -layer is defined in:: - - >>> import zope.component - >>> layer = OurLayer(zope.component) - -Now we run some tests with this layer: - - >>> import unittest - >>> class TestCase(unittest.TestCase): - ... layer = layer - ... - ... def testFoo(self): - ... print "testFoo" - >>> suite = unittest.TestSuite() - >>> suite.addTest(unittest.makeSuite(TestCase)) - >>> from zope.testrunner.runner import Runner - >>> runner = Runner(args=[], found_suites=[suite]) - >>> succeeded = runner.run() - Running zope.component.OurLayer tests: - Set up zope.component.OurLayer setUp called - in ... seconds. - testSetUp called - testFoo - testTearDown called - Ran 1 tests with 0 failures and 0 errors in ... seconds. - Tearing down left over layers: - Tear down zope.component.OurLayer tearDown called - in ... seconds. - -ZCMLLayer ---------- - -We now want a layer that loads up some ZCML from a file. The default -is ``ftesting.zcml``, but here we'll load a test ``testlayer.zcml``. - - >>> from zope.component.testlayer import ZCMLFileLayer - >>> zcml_file_layer = ZCMLFileLayer( - ... zope.component.testfiles, - ... 'testlayer.zcml') - - >>> class TestCase(unittest.TestCase): - ... layer = zcml_file_layer - ... - ... def testFoo(self): - ... # we should now have the adapter registered - ... from zope import component - ... from zope.component.testfiles import components - ... self.assert_(isinstance( - ... components.IApp2(components.content), components.Comp2)) - -Since the ZCML sets up an adapter, we expect the tests to pass:: - - >>> suite = unittest.TestSuite() - >>> suite.addTest(unittest.makeSuite(TestCase)) - >>> runner = Runner(args=[], found_suites=[suite]) - >>> succeeded = runner.run() - Running zope.component.testfiles.ZCMLFileLayer tests: - Set up zope.component.testfiles.ZCMLFileLayer in ... seconds. - Ran 1 tests with 0 failures and 0 errors in ... seconds. - Tearing down left over layers: - Tear down zope.component.testfiles.ZCMLFileLayer in ... seconds. - diff --git a/src/zope/component/tests.py b/src/zope/component/tests.py deleted file mode 100644 index c8c2906..0000000 --- a/src/zope/component/tests.py +++ /dev/null @@ -1,1748 +0,0 @@ -############################################################################## -# -# Copyright (c) 2001, 2002, 2009 Zope Foundation 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. -# -############################################################################## -"""Component Architecture Tests -""" - -import __future__ - -import doctest -import persistent -import re -import sys -import unittest -import transaction -from cStringIO import StringIO - -from zope import interface, component -from zope.interface.verify import verifyObject -from zope.interface.interfaces import IInterface -from zope.testing import renormalizing -from zope.testrunner.layer import UnitTests - -from zope.component.interfaces import ComponentLookupError -from zope.component.interfaces import IComponentArchitecture -from zope.component.interfaces import IComponentLookup -from zope.component.testing import setUp, tearDown, PlacelessSetup -import zope.component.persistentregistry -import zope.component.globalregistry - -from zope.configuration.xmlconfig import XMLConfig, xmlconfig -from zope.configuration.exceptions import ConfigurationError -from zope.security.checker import ProxyFactory - -from zope.component.testfiles.adapter import A1, A2, A3 -from zope.component.testfiles.components import IContent, Content -from zope.component.testfiles.components import IApp -from zope.component.testfiles.views import Request, IC, IV, V1, R1, IR - -# side effect gets component-based event dispatcher installed. -# we should obviously make this more explicit -import zope.component.event - -class I1(interface.Interface): - pass -class I2(interface.Interface): - pass -class I2e(I2): - pass -class I3(interface.Interface): - pass - -class ITestType(IInterface): - pass - -class U: - - def __init__(self, name): - self.__name__ = name - - def __repr__(self): - return "%s(%s)" % (self.__class__.__name__, self.__name__) - -class U1(U): - interface.implements(I1) - -class U12(U): - interface.implements(I1, I2) - -class IA1(interface.Interface): - pass - -class IA2(interface.Interface): - pass - -class IA3(interface.Interface): - pass - -class A: - - def __init__(self, *context): - self.context = context - - def __repr__(self): - return "%s%r" % (self.__class__.__name__, self.context) - -class A12_1(A): - component.adapts(I1, I2) - interface.implements(IA1) - -class A12_(A): - component.adapts(I1, I2) - -class A_2(A): - interface.implements(IA2) - -class A_3(A): - interface.implements(IA3) - -class A1_12(U): - component.adapts(I1) - interface.implements(IA1, IA2) - -class A1_2(U): - component.adapts(I1) - interface.implements(IA2) - -class A1_23(U): - component.adapts(I1) - interface.implements(IA1, IA3) - -def noop(*args): - pass - -@component.adapter(I1) -def handle1(x): - print 'handle1', x - -def handle(*objects): - print 'handle', objects - -@component.adapter(I1) -def handle3(x): - print 'handle3', x - -@component.adapter(I1) -def handle4(x): - print 'handle4', x - -class Ob(object): - interface.implements(I1) - def __repr__(self): - return '<instance Ob>' - - -ob = Ob() - -class Ob2(object): - interface.implements(I2) - def __repr__(self): - return '<instance Ob2>' - -class Comp(object): - interface.implements(I2) - def __init__(self, context): - self.context = context - -comp = Comp(1) - -class Comp2(object): - interface.implements(I3) - def __init__(self, context): - self.context = context - - -class ConformsToIComponentLookup(object): - """This object allows the sitemanager to conform/adapt to - `IComponentLookup` and thus to itself.""" - - def __init__(self, sitemanager): - self.sitemanager = sitemanager - - def __conform__(self, interface): - """This method is specified by the adapter PEP to do the adaptation.""" - if interface is IComponentLookup: - return self.sitemanager - - -def testInterfaces(): - """Ensure that the component architecture API is provided by - `zope.component`. - - >>> verifyObject(IComponentArchitecture, component) - True - """ - -def test_getGlobalSiteManager(): - """One of the most important functions is to get the global site manager. - - >>> from zope.component.interfaces import IComponentLookup - >>> from zope.component.globalregistry import base - - Get the global site manager via the CA API function: - - >>> gsm = component.getGlobalSiteManager() - - Make sure that the global site manager implements the correct interface - and is the global site manager instance we expect to get. - - >>> IComponentLookup.providedBy(gsm) - True - >>> base is gsm - True - - Finally, ensure that we always get the same global site manager, otherwise - our component registry will always be reset. - - >>> component.getGlobalSiteManager() is gsm - True - """ - -def test_getSiteManager(): - """Make sure that `getSiteManager()` always returns the correct site - manager instance. - - We don't know anything about the default service manager, except that it - is an `IComponentLookup`. - - >>> from zope.component.interfaces import IComponentLookup - >>> IComponentLookup.providedBy(component.getSiteManager()) - True - - Calling `getSiteManager()` with no args is equivalent to calling it with a - context of `None`. - - >>> component.getSiteManager() is component.getSiteManager(None) - True - - If the context passed to `getSiteManager()` is not `None`, it is - adapted to `IComponentLookup` and this adapter returned. So, we - create a context that can be adapted to `IComponentLookup` using - the `__conform__` API. - - Let's create the simplest stub-implementation of a site manager possible: - - >>> sitemanager = object() - - Now create a context that knows how to adapt to our newly created site - manager. - - >>> context = ConformsToIComponentLookup(sitemanager) - - Now make sure that the `getSiteManager()` API call returns the correct - site manager. - - >>> component.getSiteManager(context) is sitemanager - True - - Using a context that is not adaptable to `IComponentLookup` should fail. - - >>> component.getSiteManager(ob) #doctest: +NORMALIZE_WHITESPACE, +ELLIPSIS - Traceback (most recent call last): - ... - ComponentLookupError: ('Could not adapt', <instance Ob>, - <InterfaceClass zope...interfaces.IComponentLookup>) - """ - -def testAdapterInContext(self): - """The `getAdapterInContext()` and `queryAdapterInContext()` API functions - do not only use the site manager to look up the adapter, but first tries - to use the `__conform__()` method of the object to find an adapter as - specified by PEP 246. - - Let's start by creating a component that support's the PEP 246's - `__conform__()` method: - - >>> class Component(object): - ... interface.implements(I1) - ... def __conform__(self, iface, default=None): - ... if iface == I2: - ... return 42 - ... def __repr__(self): - ... return '''<Component implementing 'I1'>''' - - >>> ob = Component() - - We also gave the component a custom representation, so it will be easier - to use in these tests. - - We now have to create a site manager (other than the default global one) - with which we can register adapters for `I1`. - - >>> from zope.component.globalregistry import BaseGlobalComponents - >>> sitemanager = BaseGlobalComponents() - - Now we create a new `context` that knows how to get to our custom site - manager. - - >>> context = ConformsToIComponentLookup(sitemanager) - - We now register an adapter from `I1` to `I3`: - - >>> sitemanager.registerAdapter(lambda x: 43, (I1,), I3, '') - - If an object implements the interface you want to adapt to, - `getAdapterInContext()` should simply return the object. - - >>> component.getAdapterInContext(ob, I1, context) - <Component implementing 'I1'> - >>> component.queryAdapterInContext(ob, I1, context) - <Component implementing 'I1'> - - If an object conforms to the interface you want to adapt to, - `getAdapterInContext()` should simply return the conformed object. - - >>> component.getAdapterInContext(ob, I2, context) - 42 - >>> component.queryAdapterInContext(ob, I2, context) - 42 - - If an adapter isn't registered for the given object and interface, and you - provide no default, raise ComponentLookupError... - - >>> class I4(interface.Interface): - ... pass - - >>> component.getAdapterInContext(ob, I4, context) \\ - ... #doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - ComponentLookupError: (<Component implementing 'I1'>, - <InterfaceClass zope.component.tests.I4>) - - ...otherwise, you get the default: - - >>> component.queryAdapterInContext(ob, I4, context, 44) - 44 - - If you ask for an adapter for which something's registered you get the - registered adapter - - >>> component.getAdapterInContext(ob, I3, context) - 43 - >>> component.queryAdapterInContext(ob, I3, context) - 43 - """ - -def testAdapter(): - """The `getAdapter()` and `queryAdapter()` API functions are similar to - `{get|query}AdapterInContext()` functions, except that they do not care - about the `__conform__()` but also handle named adapters. (Actually, the - name is a required argument.) - - If an adapter isn't registered for the given object and interface, and you - provide no default, raise `ComponentLookupError`... - - >>> component.getAdapter(ob, I2, '') #doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - ComponentLookupError: (<instance Ob>, - <InterfaceClass zope.component.tests.I2>, - '') - - ...otherwise, you get the default - - >>> component.queryAdapter(ob, I2, '', '<default>') - '<default>' - - Now get the global site manager and register an adapter from `I1` to `I2` - without a name: - - >>> component.getGlobalSiteManager().registerAdapter( - ... Comp, (I1,), I2, '') - - You should get a sensible error message if you forget that the 'requires' - argument is supposed to be a sequence - - >>> component.getGlobalSiteManager().registerAdapter( - ... Comp, I1, I2, '') - Traceback (most recent call last): - ... - TypeError: the required argument should be a list of interfaces, not a single interface - - You can now simply access the adapter using the `getAdapter()` API - function: - - >>> adapter = component.getAdapter(ob, I2, '') - >>> adapter.__class__ is Comp - True - >>> adapter.context is ob - True - """ - -def testInterfaceCall(): - """Here we test the `adapter_hook()` function that we registered with the - `zope.interface` adapter hook registry, so that we can call interfaces to - do adaptation. - - First, we need to register an adapter: - - >>> component.getGlobalSiteManager().registerAdapter( - ... Comp, [I1], I2, '') - - Then we try to adapt `ob` to provide an `I2` interface by calling the `I2` - interface with the obejct as first argument: - - >>> adapter = I2(ob) - >>> adapter.__class__ is Comp - True - >>> adapter.context is ob - True - - If no adapter is found, a `TypeError is raised... - - >>> I1(Ob2()) #doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - TypeError: ('Could not adapt', <instance Ob2>, - <InterfaceClass zope.component.tests.I1>) - - ...unless we specify an alternative adapter: - - >>> marker = object() - >>> I2(object(), marker) is marker - True - """ - -def testNamedAdapter(): - """Make sure that adapters with names are correctly selected from the - registry. - - First we register some named adapter: - - >>> component.getGlobalSiteManager().registerAdapter( - ... lambda x: 0, [I1], I2, 'foo') - - If an adapter isn't registered for the given object and interface, - and you provide no default, raise `ComponentLookupError`... - - >>> component.getAdapter(ob, I2, 'bar') \\ - ... #doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - ComponentLookupError: - (<instance Ob>, <InterfaceClass zope.component.tests.I2>, 'bar') - - ...otherwise, you get the default - - >>> component.queryAdapter(ob, I2, 'bar', '<default>') - '<default>' - - But now we register an adapter for the object having the correct name - - >>> component.getGlobalSiteManager().registerAdapter( - ... Comp, [I1], I2, 'bar') - - so that the lookup succeeds: - - >>> adapter = component.getAdapter(ob, I2, 'bar') - >>> adapter.__class__ is Comp - True - >>> adapter.context is ob - True - """ - -def testMultiAdapter(): - """Adapting a combination of 2 objects to an interface - - Multi-adapters adapt one or more objects to another interface. To make - this demonstration non-trivial, we need to create a second object to be - adapted: - - >>> ob2 = Ob2() - - Like for regular adapters, if an adapter isn't registered for the given - objects and interface, and you provide no default, raise - `ComponentLookupError`... - - >>> component.getMultiAdapter((ob, ob2), I3) \\ - ... #doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - ComponentLookupError: - ((<instance Ob>, <instance Ob2>), - <InterfaceClass zope.component.tests.I3>, - u'') - - ...otherwise, you get the default - - >>> component.queryMultiAdapter((ob, ob2), I3, default='<default>') - '<default>' - - Note that the name is not a required attribute here. - - To test multi-adapters, we also have to create an adapter class that - handles to context objects: - - >>> class DoubleAdapter(object): - ... interface.implements(I3) - ... def __init__(self, first, second): - ... self.first = first - ... self.second = second - - Now we can register the multi-adapter using - - >>> component.getGlobalSiteManager().registerAdapter( - ... DoubleAdapter, (I1, I2), I3, '') - - Notice how the required interfaces are simply provided by a tuple. Now we - can get the adapter: - - >>> adapter = component.getMultiAdapter((ob, ob2), I3) - >>> adapter.__class__ is DoubleAdapter - True - >>> adapter.first is ob - True - >>> adapter.second is ob2 - True - """ - -def testAdapterForInterfaceNone(): - """Providing an adapter for None says that your adapter can adapt anything - to `I2`. - - >>> component.getGlobalSiteManager().registerAdapter( - ... Comp, (None,), I2, '') - - >>> adapter = I2(ob) - >>> adapter.__class__ is Comp - True - >>> adapter.context is ob - True - - It can really adapt any arbitrary object: - - >>> something = object() - >>> adapter = I2(something) - >>> adapter.__class__ is Comp - True - >>> adapter.context is something - True - """ - -def testGetAdapters(): - """It is sometimes desireable to get a list of all adapters that are - registered for a particular output interface, given a set of - objects. - - Let's register some adapters first: - - >>> component.getGlobalSiteManager().registerAdapter( - ... Comp, [I1], I2, '') - >>> component.getGlobalSiteManager().registerAdapter( - ... Comp, [None], I2, 'foo') - - Now we get all the adapters that are registered for `ob` that provide - `I2`: - - >>> adapters = sorted(component.getAdapters((ob,), I2)) - >>> [(name, adapter.__class__.__name__) for name, adapter in adapters] - [(u'', 'Comp'), (u'foo', 'Comp')] - - Note that the output doesn't include None values. If an adapter - factory returns None, it is as if it wasn't present. - - >>> component.getGlobalSiteManager().registerAdapter( - ... lambda context: None, [I1], I2, 'nah') - >>> adapters = sorted(component.getAdapters((ob,), I2)) - >>> [(name, adapter.__class__.__name__) for name, adapter in adapters] - [(u'', 'Comp'), (u'foo', 'Comp')] - - """ - -def testUtility(): - """Utilities are components that simply provide an interface. They are - instantiated at the time or before they are registered. Here we test the - simple query interface. - - Before we register any utility, there is no utility available, of - course. The pure instatiation of an object does not make it a utility. If - you do not specify a default, you get a `ComponentLookupError`... - - >>> component.getUtility(I1) #doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - ComponentLookupError: \ - (<InterfaceClass zope.component.tests.I1>, '') - - ...otherwise, you get the default - - >>> component.queryUtility(I1, default='<default>') - '<default>' - >>> component.queryUtility(I2, default='<default>') - '<default>' - - Now we declare `ob` to be the utility providing `I1` - - >>> component.getGlobalSiteManager().registerUtility(ob, I1) - - so that the component is now available: - - >>> component.getUtility(I1) is ob - True - """ - -def testNamedUtility(): - """Like adapters, utilities can be named. - - Just because you register an utility having no name - - >>> component.getGlobalSiteManager().registerUtility(ob, I1) - - does not mean that they are available when you specify a name: - - >>> component.getUtility(I1, name='foo') \\ - ... #doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - ComponentLookupError: - (<InterfaceClass zope.component.tests.I1>, 'foo') - - - ...otherwise, you get the default - - >>> component.queryUtility(I1, name='foo', default='<default>') - '<default>' - - Registering the utility under the correct name - - >>> component.getGlobalSiteManager().registerUtility( - ... ob, I1, name='foo') - - really helps: - - >>> component.getUtility(I1, 'foo') is ob - True - """ - -def test_getAllUtilitiesRegisteredFor(): - """Again, like for adapters, it is often useful to get a list of all - utilities that have been registered for a particular interface. Utilities - providing a derived interface are also listed. - - Thus, let's create a derivative interface of `I1`: - - >>> class I11(I1): - ... pass - - >>> class Ob11(Ob): - ... interface.implements(I11) - - >>> ob11 = Ob11() - >>> ob_bob = Ob() - - Now we register the new utilities: - - >>> gsm = component.getGlobalSiteManager() - >>> gsm.registerUtility(ob, I1) - >>> gsm.registerUtility(ob11, I11) - >>> gsm.registerUtility(ob_bob, I1, name='bob') - >>> gsm.registerUtility(Comp(2), I2) - - We can now get all the utilities that provide interface `I1`: - - >>> uts = list(component.getAllUtilitiesRegisteredFor(I1)) - >>> uts = sorted([util.__class__.__name__ for util in uts]) - >>> uts - ['Ob', 'Ob', 'Ob11'] - - Note that `getAllUtilitiesRegisteredFor()` does not return the names of - the utilities. - """ - -def testNotBrokenWhenNoSiteManager(): - """Make sure that the adapter lookup is not broken, when no site manager - is available. - - Both of those things emit `DeprecationWarnings`. - - >>> I2(ob) #doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - TypeError: ('Could not adapt', - <instance Ob>, - <InterfaceClass zope.component.tests.I2>) - - - >>> I2(ob, 42) - 42 - """ - - -def testNo__component_adapts__leakage(): - """ - We want to make sure that an `adapts()` call in a class definition - doesn't affect instances. - - >>> class C: - ... component.adapts() - - >>> C.__component_adapts__ - () - >>> C().__component_adapts__ - Traceback (most recent call last): - ... - AttributeError: __component_adapts__ - """ - -def test_ability_to_pickle_globalsitemanager(): - """ - We need to make sure that it is possible to pickle the global site manager - and its two global adapter registries. - - >>> from zope.component import globalSiteManager - >>> import cPickle - >>> pickle = cPickle.dumps(globalSiteManager) - >>> sm = cPickle.loads(pickle) - >>> sm is globalSiteManager - True - - Now let's ensure that the registries themselves can be pickled as well: - - >>> pickle = cPickle.dumps(globalSiteManager.adapters) - >>> adapters = cPickle.loads(pickle) - >>> adapters is globalSiteManager.adapters - True - """ - -def test_persistent_component_managers(): - """ -Here, we'll demonstrate that changes work even when data are stored in -a database and when accessed from multiple connections. - -Start by setting up a database and creating two transaction -managers and database connections to work with. - - >>> import ZODB.tests.util - >>> db = ZODB.tests.util.DB() - >>> import transaction - >>> t1 = transaction.TransactionManager() - >>> c1 = db.open(transaction_manager=t1) - >>> r1 = c1.root() - >>> t2 = transaction.TransactionManager() - >>> c2 = db.open(transaction_manager=t2) - >>> r2 = c2.root() - -Create a set of components registries in the database, alternating -connections. - - >>> from zope.component.persistentregistry import PersistentComponents - - >>> _ = t1.begin() - >>> r1[1] = PersistentComponents('1') - >>> t1.commit() - - >>> _ = t2.begin() - >>> r2[2] = PersistentComponents('2', (r2[1], )) - >>> t2.commit() - - >>> _ = t1.begin() - >>> r1[3] = PersistentComponents('3', (r1[1], )) - >>> t1.commit() - - >>> _ = t2.begin() - >>> r2[4] = PersistentComponents('4', (r2[2], r2[3])) - >>> t2.commit() - - >>> _ = t1.begin() - >>> r1[1].__bases__ - () - >>> r1[2].__bases__ == (r1[1], ) - True - - >>> r1[1].registerUtility(U1(1)) - >>> r1[1].queryUtility(I1) - U1(1) - >>> r1[2].queryUtility(I1) - U1(1) - >>> t1.commit() - - >>> _ = t2.begin() - >>> r2[1].registerUtility(U1(2)) - >>> r2[2].queryUtility(I1) - U1(2) - - >>> r2[4].queryUtility(I1) - U1(2) - >>> t2.commit() - - - >>> _ = t1.begin() - >>> r1[1].registerUtility(U12(1), I2) - >>> r1[4].queryUtility(I2) - U12(1) - >>> t1.commit() - - - >>> _ = t2.begin() - >>> r2[3].registerUtility(U12(3), I2) - >>> r2[4].queryUtility(I2) - U12(3) - >>> t2.commit() - - >>> _ = t1.begin() - - >>> r1[1].registerHandler(handle1, info="First handler") - >>> r1[2].registerHandler(handle, required=[U]) - - >>> r1[3].registerHandler(handle3) - - >>> r1[4].registerHandler(handle4) - - >>> r1[4].handle(U1(1)) - handle1 U1(1) - handle3 U1(1) - handle (U1(1),) - handle4 U1(1) - - >>> t1.commit() - - >>> _ = t2.begin() - >>> r2[4].handle(U1(1)) - handle1 U1(1) - handle3 U1(1) - handle (U1(1),) - handle4 U1(1) - >>> t2.abort() - - >>> db.close() - """ - -def persistent_registry_doesnt_scew_up_subsribers(): - """ - >>> import ZODB.tests.util - >>> db = ZODB.tests.util.DB() - >>> import transaction - >>> t1 = transaction.TransactionManager() - >>> c1 = db.open(transaction_manager=t1) - >>> r1 = c1.root() - >>> t2 = transaction.TransactionManager() - >>> c2 = db.open(transaction_manager=t2) - >>> r2 = c2.root() - - >>> from zope.component.persistentregistry import PersistentComponents - - >>> _ = t1.begin() - >>> r1[1] = PersistentComponents('1') - >>> r1[1].registerHandler(handle1) - >>> r1[1].registerSubscriptionAdapter(handle1, provided=I2) - >>> _ = r1[1].unregisterHandler(handle1) - >>> _ = r1[1].unregisterSubscriptionAdapter(handle1, provided=I2) - >>> t1.commit() - >>> _ = t1.begin() - >>> r1[1].registerHandler(handle1) - >>> r1[1].registerSubscriptionAdapter(handle1, provided=I2) - >>> t1.commit() - - >>> _ = t2.begin() - >>> len(list(r2[1].registeredHandlers())) - 1 - >>> len(list(r2[1].registeredSubscriptionAdapters())) - 1 - >>> t2.abort() - - """ - - - -class GlobalRegistry: - pass - -base = zope.component.globalregistry.GlobalAdapterRegistry( - GlobalRegistry, 'adapters') -GlobalRegistry.adapters = base -def clear_base(): - base.__init__(GlobalRegistry, 'adapters') - -class IFoo(interface.Interface): - pass -class Foo(persistent.Persistent): - interface.implements(IFoo) - name = '' - def __init__(self, name=''): - self.name = name - - def __repr__(self): - return 'Foo(%r)' % self.name - -def test_deghostification_of_persistent_adapter_registries(): - """ - -We want to make sure that we see updates corrextly. - - >>> len(base._v_subregistries) - 0 - - >>> import ZODB.tests.util - >>> db = ZODB.tests.util.DB() - >>> tm1 = transaction.TransactionManager() - >>> c1 = db.open(transaction_manager=tm1) - >>> r1 = zope.component.persistentregistry.PersistentAdapterRegistry( - ... (base,)) - >>> r2 = zope.component.persistentregistry.PersistentAdapterRegistry((r1,)) - >>> c1.root()[1] = r1 - >>> c1.root()[2] = r2 - >>> tm1.commit() - >>> r1._p_deactivate() - - >>> len(base._v_subregistries) - 0 - - >>> tm2 = transaction.TransactionManager() - >>> c2 = db.open(transaction_manager=tm2) - >>> r1 = c2.root()[1] - >>> r2 = c2.root()[2] - - >>> r1.lookup((), IFoo, '') - - >>> base.register((), IFoo, '', Foo('')) - >>> r1.lookup((), IFoo, '') - Foo('') - - >>> r2.lookup((), IFoo, '1') - - >>> r1.register((), IFoo, '1', Foo('1')) - - >>> r2.lookup((), IFoo, '1') - Foo('1') - - >>> r1.lookup((), IFoo, '2') - >>> r2.lookup((), IFoo, '2') - - >>> base.register((), IFoo, '2', Foo('2')) - - >>> r1.lookup((), IFoo, '2') - Foo('2') - - >>> r2.lookup((), IFoo, '2') - Foo('2') - -Cleanup: - - >>> db.close() - >>> clear_base() - - """ - - -def test_multi_handler_unregistration(): - """ - There was a bug where multiple handlers for the same required - specification would all be removed when one of them was - unregistered: - - >>> class I(zope.interface.Interface): - ... pass - >>> def factory1(event): - ... print "| Factory 1 is here" - >>> def factory2(event): - ... print "| Factory 2 is here" - >>> class Event(object): - ... zope.interface.implements(I) - >>> from zope.interface.registry import Components - >>> registry = Components() - >>> registry.registerHandler(factory1, [I,]) - >>> registry.registerHandler(factory2, [I,]) - >>> registry.handle(Event()) - | Factory 1 is here - | Factory 2 is here - >>> registry.unregisterHandler(factory1, [I,]) - True - >>> registry.handle(Event()) - | Factory 2 is here - """ - -def test_next_utilities(): - """ - It is common for a utility to delegate its answer to a utility - providing the same interface in one of the component registry's - bases. Let's first create a global utility:: - - >>> import zope.interface - >>> class IMyUtility(zope.interface.Interface): - ... pass - - >>> class MyUtility(ConformsToIComponentLookup): - ... zope.interface.implements(IMyUtility) - ... def __init__(self, id, sm): - ... self.id = id - ... self.sitemanager = sm - ... def __repr__(self): - ... return "%s('%s')" % (self.__class__.__name__, self.id) - - >>> from zope.component import getGlobalSiteManager - >>> gsm = getGlobalSiteManager() - - >>> gutil = MyUtility('global', gsm) - >>> gsm.registerUtility(gutil, IMyUtility, 'myutil') - - Now, let's create two registries and set up the bases hierarchy:: - - >>> from zope.interface.registry import Components - >>> sm1 = Components('sm1', bases=(gsm, )) - >>> sm1_1 = Components('sm1_1', bases=(sm1, )) - - Now we create two utilities and insert them in our folder hierarchy: - - >>> util1 = MyUtility('one', sm1) - >>> sm1.registerUtility(util1, IMyUtility, 'myutil') - >>> IComponentLookup(util1) is sm1 - True - - >>> util1_1 = MyUtility('one-one', sm1_1) - >>> sm1_1.registerUtility(util1_1, IMyUtility, 'myutil') - >>> IComponentLookup(util1_1) is sm1_1 - True - - Now, if we ask `util1_1` for its next available utility we get the - ``one`` utility:: - - >>> from zope.component import getNextUtility - >>> getNextUtility(util1_1, IMyUtility, 'myutil') - MyUtility('one') - - Next we ask `util1` for its next utility and we should get the global version: - - >>> getNextUtility(util1, IMyUtility, 'myutil') - MyUtility('global') - - However, if we ask the global utility for the next one, an error is raised - - >>> getNextUtility(gutil, IMyUtility, - ... 'myutil') #doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - ComponentLookupError: - No more utilities for <InterfaceClass zope.component.tests.IMyUtility>, - 'myutil' have been found. - - You can also use `queryNextUtility` and specify a default: - - >>> from zope.component import queryNextUtility - >>> queryNextUtility(gutil, IMyUtility, 'myutil', 'default') - 'default' - - Let's now ensure that the function also works with multiple registries. First - we create another base registry: - - >>> myregistry = Components() - - We now set up another utility into that registry: - - >>> custom_util = MyUtility('my_custom_util', myregistry) - >>> myregistry.registerUtility(custom_util, IMyUtility, 'my_custom_util') - - We add it as a base to the local site manager: - - >>> sm1.__bases__ = (myregistry,) + sm1.__bases__ - - Both the ``myregistry`` and global utilities should be available: - - >>> queryNextUtility(sm1, IMyUtility, 'my_custom_util') - MyUtility('my_custom_util') - >>> queryNextUtility(sm1, IMyUtility, 'myutil') - MyUtility('global') - - Note, if the context cannot be converted to a site manager, the default is - retruned: - - >>> queryNextUtility(object(), IMyUtility, 'myutil', 'default') - 'default' - """ - -def dont_leak_utility_registrations_in__subscribers(): - """ - - We've observed utilities getting left in _subscribers when they - get unregistered. - - >>> import zope.interface.registry - >>> reg = zope.interface.registry.Components() - >>> class C: - ... def __init__(self, name): - ... self.name = name - ... def __repr__(self): - ... return "C(%s)" % self.name - - >>> c1 = C(1) - >>> reg.registerUtility(c1, I1) - >>> reg.registerUtility(c1, I1) - >>> list(reg.getAllUtilitiesRegisteredFor(I1)) - [C(1)] - - >>> reg.unregisterUtility(provided=I1) - True - >>> list(reg.getAllUtilitiesRegisteredFor(I1)) - [] - - >>> reg.registerUtility(c1, I1) - >>> reg.registerUtility(C(2), I1) - - >>> list(reg.getAllUtilitiesRegisteredFor(I1)) - [C(2)] - - """ - -def test_zcml_handler_site_manager(): - """ - The ZCML directives provided by zope.component use the ``getSiteManager`` - method to get the registry where to register the components. This makes - possible to hook ``getSiteManager`` before loading a ZCML file: - - >>> from zope.interface.registry import Components - >>> registry = Components() - >>> def dummy(context=None): - ... return registry - >>> from zope.component import getSiteManager - >>> ignore = getSiteManager.sethook(dummy) - - >>> from zope.component.testfiles.components import comp, IApp - >>> from zope.component.zcml import handler - >>> handler('registerUtility', comp, IApp, u'') - >>> registry.getUtility(IApp) is comp - True - >>> ignore = getSiteManager.reset() - - """ - -class StandaloneTests(unittest.TestCase): - def testStandalone(self): - import subprocess - import sys - import os - import tempfile - import pickle - - executable = os.path.abspath(sys.executable) - program = os.path.join(os.path.dirname(__file__), 'standalonetests.py') - process = subprocess.Popen([executable, program], - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - stdin=subprocess.PIPE) - pickle.dump(sys.path, process.stdin) - process.stdin.close() - - try: - process.wait() - except OSError, e: - if e.errno != 4: # MacIntel raises apparently unimportant EINTR? - raise # TODO verify sanity of a pass on EINTR :-/ - lines = process.stdout.readlines() - process.stdout.close() - success = True - # Interpret the result: We scan the output from the end backwards - # until we find either a line that says 'OK' (which means the tests - # ran successfully) or a line that starts with quite a few dashes - # (which means we didn't find a line that says 'OK' within the summary - # of the test runner and the tests did not run successfully.) - for l in reversed(lines): - l = l.strip() - if not l: - continue - if l.startswith('-----'): - break - if l.endswith('OK'): - sucess = True - if not success: - self.fail(''.join(lines)) - -class HookableTests(unittest.TestCase): - - def test_ctor_no_func(self): - from zope.component.hookable import hookable - self.assertRaises(TypeError, hookable) - - def test_ctor_simple(self): - from zope.component.hookable import hookable - def foo(): - pass - hooked = hookable(foo) - self.failUnless(hooked.original is foo) - self.failUnless(hooked.implementation is foo) - - def test_ctor_extra_arg(self): - from zope.component.hookable import hookable - def foo(): - pass - self.assertRaises(TypeError, hookable, foo, foo) - - def test_ctor_extra_arg(self): - from zope.component.hookable import hookable - def foo(): - pass - self.assertRaises(TypeError, hookable, foo, nonesuch=foo) - - def test_sethook(self): - from zope.component.hookable import hookable - def foo(): - pass - def bar(): - pass - hooked = hookable(foo) - hooked.sethook(bar) - self.failUnless(hooked.original is foo) - self.failUnless(hooked.implementation is bar) - - def test_reset(self): - from zope.component.hookable import hookable - def foo(): - pass - def bar(): - pass - hooked = hookable(foo) - hooked.sethook(bar) - hooked.reset() - self.failUnless(hooked.original is foo) - self.failUnless(hooked.implementation is foo) - - def test_cant_assign_original(self): - from zope.component.hookable import hookable - def foo(): - pass - def bar(): - pass - hooked = hookable(foo) - try: - hooked.original = bar - except TypeError: - pass - except AttributeError: - pass - else: - self.fail('Assigned original') - - def test_cant_delete_original(self): - from zope.component.hookable import hookable - def foo(): - pass - hooked = hookable(foo) - try: - del hooked.original - except TypeError: - pass - except AttributeError: - pass - else: - self.fail('Deleted original') - - def test_cant_assign_original(self): - from zope.component.hookable import hookable - def foo(): - pass - def bar(): - pass - hooked = hookable(foo) - try: - hooked.implementation = bar - except TypeError: - pass - except AttributeError: - pass - else: - self.fail('Assigned implementation') - - def test_readonly_original(self): - from zope.component.hookable import hookable - def foo(): - pass - hooked = hookable(foo) - try: - del hooked.implementation - except TypeError: - pass - except AttributeError: - pass - else: - self.fail('Deleted implementation') - -class Ob3(object): - interface.implements(IC) - -template = """<configure - xmlns='http://namespaces.zope.org/zope' - i18n_domain='zope'> - %s - </configure>""" - - -class ResourceViewTests(PlacelessSetup, unittest.TestCase): - - def setUp(self): - super(ResourceViewTests, self).setUp() - XMLConfig('meta.zcml', zope.component)() - XMLConfig('meta.zcml', zope.security)() - - def testView(self): - ob = Ob3() - request = Request(IV) - self.assertEqual( - zope.component.queryMultiAdapter((ob, request), name=u'test'), None) - - xmlconfig(StringIO(template % - ''' - <view name="test" - factory="zope.component.testfiles.views.V1" - for="zope.component.testfiles.views.IC" - type="zope.component.testfiles.views.IV"/> - ''' - )) - - self.assertEqual( - zope.component.queryMultiAdapter((ob, request), - name=u'test').__class__, - V1) - - - def testMultiView(self): - xmlconfig(StringIO(template % - ''' - <view name="test" - factory="zope.component.testfiles.adapter.A3" - for="zope.component.testfiles.views.IC - zope.component.testfiles.adapter.I1 - zope.component.testfiles.adapter.I2" - type="zope.component.testfiles.views.IV"/> - ''' - )) - - - ob = Ob3() - a1 = A1() - a2 = A2() - request = Request(IV) - view = zope.component.queryMultiAdapter((ob, a1, a2, request), - name=u'test') - self.assertEqual(view.__class__, A3) - self.assertEqual(view.context, (ob, a1, a2, request)) - - - def testMultiView_fails_w_multiple_factories(self): - self.assertRaises( - ConfigurationError, - xmlconfig, - StringIO(template % - ''' - <view name="test" - factory="zope.component.testfiles.adapter.A3 - zope.component.testfiles.adapter.A2" - for="zope.component.testfiles.views.IC - zope.component.testfiles.adapter.I1 - zope.component.testfiles.adapter.I2" - type="zope.component.testfiles.views.IV"/> - ''' - ) - ) - - def testView_w_multiple_factories(self): - xmlconfig(StringIO(template % - ''' - <view name="test" - factory="zope.component.testfiles.adapter.A1 - zope.component.testfiles.adapter.A2 - zope.component.testfiles.adapter.A3 - zope.component.testfiles.views.V1" - for="zope.component.testfiles.views.IC" - type="zope.component.testfiles.views.IV"/> - ''' - )) - - ob = Ob3() - - # The view should be a V1 around an A3, around an A2, around - # an A1, anround ob: - view = zope.component.queryMultiAdapter((ob, Request(IV)), name=u'test') - self.assertEqual(view.__class__, V1) - a3 = view.context - self.assertEqual(a3.__class__, A3) - a2 = a3.context[0] - self.assertEqual(a2.__class__, A2) - a1 = a2.context[0] - self.assertEqual(a1.__class__, A1) - self.assertEqual(a1.context[0], ob) - - def testView_fails_w_no_factories(self): - self.assertRaises(ConfigurationError, - xmlconfig, - StringIO(template % - ''' - <view name="test" - factory="" - for="zope.component.testfiles.views.IC" - type="zope.component.testfiles.views.IV"/> - ''' - ), - ) - - - def testViewThatProvidesAnInterface(self): - ob = Ob3() - self.assertEqual( - zope.component.queryMultiAdapter((ob, Request(IR)), IV, u'test'), - None) - - xmlconfig(StringIO(template % - ''' - <view name="test" - factory="zope.component.testfiles.views.V1" - for="zope.component.testfiles.views.IC" - type="zope.component.testfiles.views.IR" - /> - ''' - )) - - self.assertEqual( - zope.component.queryMultiAdapter((ob, Request(IR)), IV, u'test'), - None) - - xmlconfig(StringIO(template % - ''' - <view name="test" - factory="zope.component.testfiles.views.V1" - for="zope.component.testfiles.views.IC" - type="zope.component.testfiles.views.IR" - provides="zope.component.testfiles.views.IV" - /> - ''' - )) - - v = zope.component.queryMultiAdapter((ob, Request(IR)), IV, u'test') - self.assertEqual(v.__class__, V1) - - - def testUnnamedViewThatProvidesAnInterface(self): - ob = Ob3() - self.assertEqual( - zope.component.queryMultiAdapter((ob, Request(IR)), IV), None) - - xmlconfig(StringIO(template % - ''' - <view factory="zope.component.testfiles.views.V1" - for="zope.component.testfiles.views.IC" - type="zope.component.testfiles.views.IR" - /> - ''' - )) - - v = zope.component.queryMultiAdapter((ob, Request(IR)), IV) - self.assertEqual(v, None) - - xmlconfig(StringIO(template % - ''' - <view factory="zope.component.testfiles.views.V1" - for="zope.component.testfiles.views.IC" - type="zope.component.testfiles.views.IR" - provides="zope.component.testfiles.views.IV" - /> - ''' - )) - - v = zope.component.queryMultiAdapter((ob, Request(IR)), IV) - self.assertEqual(v.__class__, V1) - - def testViewHavingARequiredClass(self): - xmlconfig(StringIO(template % ( - ''' - <view - for="zope.component.testfiles.components.Content" - type="zope.component.testfiles.views.IR" - factory="zope.component.testfiles.adapter.A1" - /> - ''' - ))) - - content = Content() - a1 = zope.component.getMultiAdapter((content, Request(IR))) - self.assert_(isinstance(a1, A1)) - - class MyContent: - interface.implements(IContent) - - self.assertRaises(ComponentLookupError, zope.component.getMultiAdapter, - (MyContent(), Request(IR))) - - def testInterfaceProtectedView(self): - xmlconfig(StringIO(template % - ''' - <view name="test" - factory="zope.component.testfiles.views.V1" - for="zope.component.testfiles.views.IC" - type="zope.component.testfiles.views.IV" - permission="zope.Public" - allowed_interface="zope.component.testfiles.views.IV" - /> - ''' - )) - - v = ProxyFactory(zope.component.getMultiAdapter((Ob3(), Request(IV)), - name='test')) - self.assertEqual(v.index(), 'V1 here') - self.assertRaises(Exception, getattr, v, 'action') - - def testAttributeProtectedView(self): - xmlconfig(StringIO(template % - ''' - <view name="test" - factory="zope.component.testfiles.views.V1" - for="zope.component.testfiles.views.IC" - type="zope.component.testfiles.views.IV" - permission="zope.Public" - allowed_attributes="action" - /> - ''' - )) - - v = ProxyFactory(zope.component.getMultiAdapter((Ob3(), Request(IV)), - name='test')) - self.assertEqual(v.action(), 'done') - self.assertRaises(Exception, getattr, v, 'index') - - def testInterfaceAndAttributeProtectedView(self): - xmlconfig(StringIO(template % - ''' - <view name="test" - factory="zope.component.testfiles.views.V1" - for="zope.component.testfiles.views.IC" - type="zope.component.testfiles.views.IV" - permission="zope.Public" - allowed_attributes="action" - allowed_interface="zope.component.testfiles.views.IV" - /> - ''' - )) - - v = zope.component.getMultiAdapter((Ob3(), Request(IV)), name='test') - self.assertEqual(v.index(), 'V1 here') - self.assertEqual(v.action(), 'done') - - def testDuplicatedInterfaceAndAttributeProtectedView(self): - xmlconfig(StringIO(template % - ''' - <view name="test" - factory="zope.component.testfiles.views.V1" - for="zope.component.testfiles.views.IC" - type="zope.component.testfiles.views.IV" - permission="zope.Public" - allowed_attributes="action index" - allowed_interface="zope.component.testfiles.views.IV" - /> - ''' - )) - - v = zope.component.getMultiAdapter((Ob3(), Request(IV)), name='test') - self.assertEqual(v.index(), 'V1 here') - self.assertEqual(v.action(), 'done') - - def testIncompleteProtectedViewNoPermission(self): - self.assertRaises( - ConfigurationError, - xmlconfig, - StringIO(template % - ''' - <view name="test" - factory="zope.component.testfiles.views.V1" - for="zope.component.testfiles.views.IC" - type="zope.component.testfiles.views.IV" - allowed_attributes="action index" - /> - ''' - )) - - def testViewUndefinedPermission(self): - config = StringIO(template % ( - ''' - <view name="test" - factory="zope.component.testfiles.views.V1" - for="zope.component.testfiles.views.IC" - type="zope.component.testfiles.views.IV" - permission="zope.UndefinedPermission" - allowed_attributes="action index" - allowed_interface="zope.component.testfiles.views.IV" - /> - ''' - )) - self.assertRaises(ValueError, xmlconfig, config, testing=1) - - def testResource(self): - ob = Ob3() - self.assertEqual( - zope.component.queryAdapter(Request(IV), name=u'test'), None) - xmlconfig(StringIO(template % ( - ''' - <resource name="test" - factory="zope.component.testfiles.views.R1" - type="zope.component.testfiles.views.IV"/> - ''' - ))) - - self.assertEqual( - zope.component.queryAdapter(Request(IV), name=u'test').__class__, - R1) - - def testResourceThatProvidesAnInterface(self): - ob = Ob3() - self.assertEqual(zope.component.queryAdapter(Request(IR), IV, u'test'), - None) - - xmlconfig(StringIO(template % - ''' - <resource - name="test" - factory="zope.component.testfiles.views.R1" - type="zope.component.testfiles.views.IR" - /> - ''' - )) - - v = zope.component.queryAdapter(Request(IR), IV, name=u'test') - self.assertEqual(v, None) - - xmlconfig(StringIO(template % - ''' - <resource - name="test" - factory="zope.component.testfiles.views.R1" - type="zope.component.testfiles.views.IR" - provides="zope.component.testfiles.views.IV" - /> - ''' - )) - - v = zope.component.queryAdapter(Request(IR), IV, name=u'test') - self.assertEqual(v.__class__, R1) - - def testUnnamedResourceThatProvidesAnInterface(self): - ob = Ob3() - self.assertEqual(zope.component.queryAdapter(Request(IR), IV), None) - - xmlconfig(StringIO(template % - ''' - <resource - factory="zope.component.testfiles.views.R1" - type="zope.component.testfiles.views.IR" - /> - ''' - )) - - v = zope.component.queryAdapter(Request(IR), IV) - self.assertEqual(v, None) - - xmlconfig(StringIO(template % - ''' - <resource - factory="zope.component.testfiles.views.R1" - type="zope.component.testfiles.views.IR" - provides="zope.component.testfiles.views.IV" - /> - ''' - )) - - v = zope.component.queryAdapter(Request(IR), IV) - self.assertEqual(v.__class__, R1) - - def testResourceUndefinedPermission(self): - - config = StringIO(template % ( - ''' - <resource name="test" - factory="zope.component.testfiles.views.R1" - type="zope.component.testfiles.views.IV" - permission="zope.UndefinedPermission"/> - ''' - )) - self.assertRaises(ValueError, xmlconfig, config, testing=1) - - -class ConditionalSecurityLayer(UnitTests): - - __name__ = 'ConditionalSecurity' - __bases__ = () - - def setUp(self): - setUp() - self.modules = {} - for m in ('zope.security', 'zope.proxy'): - self.modules[m] = sys.modules[m] - sys.modules[m] = None - import zope.component.zcml - reload(zope.component.zcml) - - def tearDown(self): - tearDown() - for m in ('zope.security', 'zope.proxy'): - sys.modules[m] = self.modules[m] - import zope.component.zcml - reload(zope.component.zcml) - - -def setUpRegistryTests(tests): - setUp() - -def tearDownRegistryTests(tests): - tearDown() - import zope.event - zope.event.subscribers.pop() - -def clearZCML(test=None): - tearDown() - setUp() - XMLConfig('meta.zcml', component)() - -def test_suite(): - checker = renormalizing.RENormalizing([ - (re.compile('at 0x[0-9a-fA-F]+'), 'at <SOME ADDRESS>'), - (re.compile(r"<type 'exceptions.(\w+)Error'>:"), - r'exceptions.\1Error:'), - ]) - - zcml_conditional = doctest.DocFileSuite('zcml_conditional.txt', checker=checker) - zcml_conditional.layer = ConditionalSecurityLayer() - - with_globs = dict(with_statement=__future__.with_statement) - hooks_conditional = doctest.DocFileSuite( - 'hooks.txt', checker=checker, globs=with_globs) - hooks_conditional.layer = ConditionalSecurityLayer() - - return unittest.TestSuite(( - doctest.DocTestSuite(setUp=setUp, tearDown=tearDown), - unittest.makeSuite(HookableTests), - doctest.DocTestSuite('zope.component.interface', - setUp=setUp, tearDown=tearDown), - doctest.DocTestSuite('zope.component.nexttesting'), - doctest.DocFileSuite('README.txt', - setUp=setUp, tearDown=tearDown), - doctest.DocFileSuite('socketexample.txt', - setUp=setUp, tearDown=tearDown), - doctest.DocFileSuite('factory.txt', - setUp=setUp, tearDown=tearDown), - doctest.DocFileSuite('hooks.txt', checker=checker, - setUp=setUp, tearDown=tearDown, - globs=with_globs), - doctest.DocFileSuite('event.txt', - setUp=setUp, tearDown=tearDown), - doctest.DocTestSuite('zope.component.security'), - doctest.DocFileSuite('zcml.txt', checker=checker, - setUp=setUp, tearDown=tearDown), - doctest.DocFileSuite('configure.txt', - setUp=setUp, tearDown=tearDown), - doctest.DocFileSuite('testlayer.txt', - optionflags=(doctest.ELLIPSIS + - doctest.NORMALIZE_WHITESPACE + - doctest.REPORT_NDIFF)), - zcml_conditional, - hooks_conditional, - unittest.makeSuite(StandaloneTests), - unittest.makeSuite(ResourceViewTests), - )) - -if __name__ == "__main__": - unittest.main(defaultTest='test_suite') diff --git a/src/zope/component/tests/__init__.py b/src/zope/component/tests/__init__.py new file mode 100644 index 0000000..65140f2 --- /dev/null +++ b/src/zope/component/tests/__init__.py @@ -0,0 +1 @@ +# tests package diff --git a/src/zope/component/tests/examples.py b/src/zope/component/tests/examples.py new file mode 100644 index 0000000..2b5e08c --- /dev/null +++ b/src/zope/component/tests/examples.py @@ -0,0 +1,150 @@ +############################################################################## +# +# Copyright (c) 2001, 2002, 2009 Zope Foundation 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. +# +############################################################################## +"""Examples supporting Sphinx doctest snippets. +""" +import sys + +from zope.interface import Interface +from zope.interface import implementer +from zope.interface.interfaces import IInterface + +from zope.component._declaration import adapter +from zope.component.testfiles.views import IC + +def write(x): + sys.stdout.write('%s\n' % x) + +class ITestType(IInterface): + pass + + +class I1(Interface): + pass + +class I2(Interface): + pass + +class I3(Interface): + pass + +class I4(Interface): + pass + +class IGI(Interface): + pass + +class IQI(Interface): + pass + +class ISI(Interface): + pass + +class ISII(Interface): + pass + +def noop(*args): + pass + +class U(object): + + def __init__(self, name): + self.__name__ = name + + def __repr__(self): + return "%s(%s)" % (self.__class__.__name__, self.__name__) + +@implementer(I1) +class U1(U): + pass + +@implementer(I1, I2) +class U12(U): + pass + +@adapter(I1) +def handle1(x): + write('handle1 %s' % x) + +def handle2(*objects): + write( 'handle2 ' + repr(objects)) + +@adapter(I1) +def handle3(x): + write( 'handle3 %s' % x) + +@adapter(I1) +def handle4(x): + write( 'handle4 %s' % x) + +class GlobalRegistry: + pass + +from zope.component.globalregistry import GlobalAdapterRegistry +base = GlobalAdapterRegistry(GlobalRegistry, 'adapters') +GlobalRegistry.adapters = base +def clear_base(): + base.__init__(GlobalRegistry, 'adapters') + + +@implementer(I1) +class Ob(object): + def __repr__(self): + return '<instance Ob>' + + +ob = Ob() + +@implementer(I2) +class Ob2(object): + def __repr__(self): + return '<instance Ob2>' + +@implementer(IC) +class Ob3(object): + pass + +@implementer(I2) +class Comp(object): + def __init__(self, context): + self.context = context + +comp = Comp(1) + +@implementer(I3) +class Comp2(object): + def __init__(self, context): + self.context = context + + +class ConformsToIComponentLookup(object): + """Allow a dummy sitemanager to conform/adapt to `IComponentLookup`.""" + + def __init__(self, sitemanager): + self.sitemanager = sitemanager + + def __conform__(self, interface): + """This method is specified by the adapter PEP to do the adaptation.""" + from zope.component.interfaces import IComponentLookup + if interface is IComponentLookup: + return self.sitemanager + + +def clearZCML(test=None): + from zope.configuration.xmlconfig import XMLConfig + import zope.component + from zope.component.testing import setUp + from zope.component.testing import tearDown + tearDown() + setUp() + XMLConfig('meta.zcml', zope.component)() diff --git a/src/zope/component/tests/test___init__.py b/src/zope/component/tests/test___init__.py new file mode 100644 index 0000000..f9553e6 --- /dev/null +++ b/src/zope/component/tests/test___init__.py @@ -0,0 +1,92 @@ +############################################################################## +# +# Copyright (c) 2012 Zope Foundation 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. +# +############################################################################## + +import unittest + +class Test_package(unittest.TestCase): + + def test_module_conforms_to_IComponentArchitecture(self): + from zope.interface.verify import verifyObject + from zope.component.interfaces import IComponentArchitecture + import zope.component as zc + verifyObject(IComponentArchitecture, zc) + + def test_module_conforms_to_IComponentRegistrationConvenience(self): + from zope.interface.verify import verifyObject + from zope.component.interfaces import IComponentRegistrationConvenience + import zope.component as zc + verifyObject(IComponentRegistrationConvenience, zc) + + +class Test_Interface_call(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def test_miss(self): + from zope.interface import Interface + class IFoo(Interface): + pass + self.assertRaises(TypeError, IFoo, object()) + + def test_miss_w_default(self): + from zope.interface import Interface + class IFoo(Interface): + pass + marker = object() + self.assertTrue(IFoo(object(), marker) is marker) + + def test_hit(self): + from zope.interface import Interface + from zope.interface import implementer + from zope.component import getGlobalSiteManager + class IFoo(Interface): + pass + class IBar(Interface): + pass + @implementer(IBar) + class Bar(object): + pass + @implementer(IFoo) + class Baz(object): + def __init__(self, context): + self.context = context + getGlobalSiteManager().registerAdapter(Baz, (IBar,), IFoo, '') + bar = Bar() + adapted = IFoo(bar) + self.assertTrue(adapted.__class__ is Baz) + self.assertTrue(adapted.context is bar) + + def test_hit_registered_for_None(self): + from zope.interface import Interface + from zope.interface import implementer + from zope.component import getGlobalSiteManager + class IFoo(Interface): + pass + @implementer(IFoo) + class Baz(object): + def __init__(self, context): + self.context = context + getGlobalSiteManager().registerAdapter(Baz, (None,), IFoo, '') + ctx = object() + adapted = IFoo(ctx) + self.assertTrue(adapted.__class__ is Baz) + self.assertTrue(adapted.context is ctx) + + + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(Test_package), + unittest.makeSuite(Test_Interface_call), + )) diff --git a/src/zope/component/tests/test__api.py b/src/zope/component/tests/test__api.py new file mode 100644 index 0000000..edcef69 --- /dev/null +++ b/src/zope/component/tests/test__api.py @@ -0,0 +1,1204 @@ +############################################################################## +# +# Copyright (c) 2012 Zope Foundation 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. +# +############################################################################## +""" Tests for z.c._api +""" +import unittest + + +class Test_getSiteManager(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def _callFUT(self, *args, **kw): + from zope.component._api import getSiteManager + return getSiteManager(*args, **kw) + + def test_sm_is_IComponentLookup(self): + from zope.component.interfaces import IComponentLookup + sm = self._callFUT() + self.assertTrue(IComponentLookup.providedBy(sm)) + + def test_sm_is_singleton(self): + from zope.component.globalregistry import base + sm = self._callFUT() + self.assertTrue(sm is base) + self.assertTrue(self._callFUT() is sm) + + def test_w_None(self): + self.assertTrue(self._callFUT(None) is self._callFUT()) + + def test_getSiteManager_w_conforming_context(self): + from zope.component.tests.examples import ConformsToIComponentLookup + sitemanager = object() + context = ConformsToIComponentLookup(sitemanager) + self.assertTrue(self._callFUT(context) is sitemanager) + + def test_getSiteManager_w_invalid_context_no_adapter(self): + from zope.component.interfaces import ComponentLookupError + self.assertRaises(ComponentLookupError, self._callFUT, object()) + + def test_getSiteManager_w_invalid_context_w_adapter(self): + from zope.interface import Interface + from zope.component.globalregistry import getGlobalSiteManager + from zope.component.interfaces import IComponentLookup + gsm = getGlobalSiteManager() + sm = object() + def _adapt(x): + return sm + gsm.registerAdapter(_adapt, (Interface,), IComponentLookup, '') + self.assertTrue(self._callFUT(object()) is sm) + + +class Test_getAdapterInContext(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def _callFUT(self, *args, **kw): + from zope.component import getAdapterInContext + return getAdapterInContext(*args, **kw) + + def test_miss(self): + from zope.interface import Interface + from zope.component.interfaces import ComponentLookupError + class IFoo(Interface): + pass + self.assertRaises(ComponentLookupError, + self._callFUT, object(), IFoo, context=None) + + def test_hit_via_sm(self): + from zope.interface import Interface + from zope.interface import implementer + from zope.interface.registry import Components + from zope.component import getGlobalSiteManager + from zope.component.tests.examples import ConformsToIComponentLookup + class IFoo(Interface): + pass + class IBar(Interface): + pass + @implementer(IFoo) + class Global(object): + def __init__(self, context): + self.context = context + @implementer(IFoo) + class Local(object): + def __init__(self, context): + self.context = context + @implementer(IBar) + class Bar(object): + pass + class Context(ConformsToIComponentLookup): + def __init__(self, sm): + self.sitemanager = sm + gsm = getGlobalSiteManager() + gsm.registerAdapter(Global, (IBar,), IFoo, '') + sm1 = Components('sm1', bases=(gsm, )) + sm1.registerAdapter(Local, (IBar,), IFoo, '') + bar = Bar() + adapted = self._callFUT(bar, IFoo, context=Context(sm1)) + self.assertTrue(adapted.__class__ is Local) + self.assertTrue(adapted.context is bar) + + +class Test_queryAdapterInContext(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def _callFUT(self, *args, **kw): + from zope.component import queryAdapterInContext + return queryAdapterInContext(*args, **kw) + + def test_miss(self): + from zope.interface import Interface + from zope.component import queryAdapterInContext + class IFoo(Interface): + pass + self.assertEqual( + queryAdapterInContext(object(), IFoo, context=None), None) + + def test_w_object_conforming(self): + from zope.interface import Interface + from zope.component import queryAdapterInContext + class IFoo(Interface): + pass + _adapted = object() + class Foo(object): + def __conform__(self, iface, default=None): + if iface is IFoo: + return _adapted + return default + self.assertTrue( + queryAdapterInContext(Foo(), IFoo, context=None) is _adapted) + + def test___conform___raises_TypeError_via_class(self): + from zope.interface import Interface + from zope.component import queryAdapterInContext + class IFoo(Interface): + pass + _adapted = object() + class Foo(object): + def __conform__(self, iface, default=None): + if iface is IFoo: + return _adapted + return default + # call via class, triggering TypeError + self.assertEqual(queryAdapterInContext(Foo, IFoo, context=None), None) + + def test___conform___raises_TypeError_via_inst(self): + from zope.interface import Interface + from zope.component import queryAdapterInContext + class IFoo(Interface): + pass + _adapted = object() + class Foo(object): + def __conform__(self, iface, default=None): + raise TypeError + self.assertRaises(TypeError, + queryAdapterInContext, Foo(), IFoo, context=None) + + def test_w_object_implementing(self): + from zope.interface import Interface + from zope.interface import implementer + from zope.component import queryAdapterInContext + class IFoo(Interface): + pass + @implementer(IFoo) + class Foo(object): + pass + foo = Foo() + self.assertTrue( + queryAdapterInContext(foo, IFoo, context=None) is foo) + + +class Test_getAdapter(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def _callFUT(self, *args, **kw): + from zope.component import getAdapter + return getAdapter(*args, **kw) + + def test_anonymous_nonesuch(self): + from zope.interface import Interface + from zope.component.interfaces import ComponentLookupError + class IFoo(Interface): + pass + self.assertRaises(ComponentLookupError, + self._callFUT, object(), IFoo, '') + + def test_named_nonesuch(self): + from zope.interface import Interface + from zope.component.interfaces import ComponentLookupError + class IFoo(Interface): + pass + self.assertRaises(ComponentLookupError, + self._callFUT, object(), IFoo, 'bar') + + def test_anonymous_hit(self): + from zope.interface import Interface + from zope.interface import implementer + from zope.component import getGlobalSiteManager + class IFoo(Interface): + pass + class IBar(Interface): + pass + @implementer(IBar) + class Bar(object): + pass + @implementer(IFoo) + class Baz(object): + def __init__(self, context): + self.context = context + getGlobalSiteManager().registerAdapter(Baz, (IBar,), IFoo, '') + bar = Bar() + adapted = self._callFUT(bar, IFoo, '') + self.assertTrue(adapted.__class__ is Baz) + self.assertTrue(adapted.context is bar) + + def test_anonymous_hit_registered_for_None(self): + from zope.interface import Interface + from zope.interface import implementer + from zope.component import getGlobalSiteManager + class IFoo(Interface): + pass + @implementer(IFoo) + class Baz(object): + def __init__(self, context): + self.context = context + getGlobalSiteManager().registerAdapter(Baz, (None,), IFoo, '') + ctx = object() + adapted = self._callFUT(ctx, IFoo, '') + self.assertTrue(adapted.__class__ is Baz) + self.assertTrue(adapted.context is ctx) + + def test_named_hit(self): + from zope.interface import Interface + from zope.interface import implementer + from zope.component import getGlobalSiteManager + class IFoo(Interface): + pass + class IBar(Interface): + pass + @implementer(IBar) + class Bar(object): + pass + @implementer(IFoo) + class Baz(object): + def __init__(self, context): + self.context = context + getGlobalSiteManager().registerAdapter(Baz, (IBar,), IFoo, 'named') + bar = Bar() + adapted = self._callFUT(bar, IFoo, 'named') + self.assertTrue(adapted.__class__ is Baz) + self.assertTrue(adapted.context is bar) + + +class Test_queryAdapter(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def _callFUT(self, *args, **kw): + from zope.component import queryAdapter + return queryAdapter(*args, **kw) + + def test_anonymous_nonesuch(self): + from zope.interface import Interface + class IFoo(Interface): + pass + self.assertEqual(self._callFUT(object(), IFoo, '', '<default>'), + '<default>') + + def test_named_nonesuch(self): + from zope.interface import Interface + class IFoo(Interface): + pass + self.assertEqual(self._callFUT(object(), IFoo, 'bar'), None) + + def test_anonymous_hit(self): + from zope.interface import Interface + from zope.interface import implementer + from zope.component import getGlobalSiteManager + class IFoo(Interface): + pass + class IBar(Interface): + pass + @implementer(IBar) + class Bar(object): + pass + @implementer(IFoo) + class Baz(object): + def __init__(self, context): + self.context = context + getGlobalSiteManager().registerAdapter(Baz, (IBar,), IFoo, '') + bar = Bar() + adapted = self._callFUT(bar, IFoo, '') + self.assertTrue(adapted.__class__ is Baz) + self.assertTrue(adapted.context is bar) + + def test_named_hit(self): + from zope.interface import Interface + from zope.interface import implementer + from zope.component import getGlobalSiteManager + class IFoo(Interface): + pass + class IBar(Interface): + pass + @implementer(IBar) + class Bar(object): + pass + @implementer(IFoo) + class Baz(object): + def __init__(self, context): + self.context = context + getGlobalSiteManager().registerAdapter(Baz, (IBar,), IFoo, 'named') + bar = Bar() + adapted = self._callFUT(bar, IFoo, 'named') + self.assertTrue(adapted.__class__ is Baz) + self.assertTrue(adapted.context is bar) + + def test_nested(self): + from zope.interface import Interface + from zope.interface import implementer + from zope.interface.registry import Components + from zope.component import getGlobalSiteManager + from zope.component.tests.examples import ConformsToIComponentLookup + class IFoo(Interface): + pass + class IBar(Interface): + pass + @implementer(IFoo) + class Global(object): + def __init__(self, context): + self.context = context + @implementer(IFoo) + class Local(object): + def __init__(self, context): + self.context = context + @implementer(IBar) + class Bar(object): + pass + class Context(ConformsToIComponentLookup): + def __init__(self, sm): + self.sitemanager = sm + gsm = getGlobalSiteManager() + gsm.registerAdapter(Global, (IBar,), IFoo, '') + sm1 = Components('sm1', bases=(gsm, )) + sm1.registerAdapter(Local, (IBar,), IFoo, '') + bar = Bar() + adapted = self._callFUT(bar, IFoo, '', context=Context(sm1)) + self.assertTrue(adapted.__class__ is Local) + self.assertTrue(adapted.context is bar) + + +class Test_getMultiAdapter(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def _callFUT(self, *args, **kw): + from zope.component import getMultiAdapter + return getMultiAdapter(*args, **kw) + + def test_anonymous_nonesuch(self): + from zope.interface import Interface + from zope.component.interfaces import ComponentLookupError + class IFoo(Interface): + pass + self.assertRaises(ComponentLookupError, + self._callFUT, (object(), object()), IFoo, '') + + def test_named_nonesuch(self): + from zope.interface import Interface + from zope.component.interfaces import ComponentLookupError + class IFoo(Interface): + pass + self.assertRaises(ComponentLookupError, + self._callFUT, (object(), object()), IFoo, 'bar') + + def test_anonymous_hit(self): + from zope.interface import Interface + from zope.interface import implementer + from zope.component import getGlobalSiteManager + class IFoo(Interface): + pass + class IBar(Interface): + pass + class IBaz(Interface): + pass + @implementer(IBar) + class Bar(object): + pass + @implementer(IBaz) + class Baz(object): + pass + @implementer(IFoo) + class FooAdapter(object): + def __init__(self, first, second): + self.first, self.second = first, second + getGlobalSiteManager().registerAdapter( + FooAdapter, (IBar, IBaz), IFoo, '') + bar = Bar() + baz = Baz() + adapted = self._callFUT((bar, baz), IFoo, '') + self.assertTrue(adapted.__class__ is FooAdapter) + self.assertTrue(adapted.first is bar) + self.assertTrue(adapted.second is baz) + + def test_anonymous_hit_registered_for_None(self): + from zope.interface import Interface + from zope.interface import implementer + from zope.component import getGlobalSiteManager + class IFoo(Interface): + pass + class IBar(Interface): + pass + class IBaz(Interface): + pass + @implementer(IBar) + class Bar(object): + pass + @implementer(IFoo) + class FooAdapter(object): + def __init__(self, first, second): + self.first, self.second = first, second + getGlobalSiteManager().registerAdapter( + FooAdapter, (IBar, None), IFoo, '') + bar = Bar() + baz = object() + adapted = self._callFUT((bar, baz), IFoo, '') + self.assertTrue(adapted.__class__ is FooAdapter) + self.assertTrue(adapted.first is bar) + self.assertTrue(adapted.second is baz) + + def test_named_hit(self): + from zope.interface import Interface + from zope.interface import implementer + from zope.component import getGlobalSiteManager + class IFoo(Interface): + pass + class IBar(Interface): + pass + class IBaz(Interface): + pass + @implementer(IBar) + class Bar(object): + pass + @implementer(IBaz) + class Baz(object): + pass + @implementer(IFoo) + class FooAdapter(object): + def __init__(self, first, second): + self.first, self.second = first, second + getGlobalSiteManager().registerAdapter( + FooAdapter, (IBar, IBaz), IFoo, 'named') + bar = Bar() + baz = Baz() + adapted = self._callFUT((bar, baz), IFoo, 'named') + self.assertTrue(adapted.__class__ is FooAdapter) + self.assertTrue(adapted.first is bar) + self.assertTrue(adapted.second is baz) + + +class Test_queryMultiAdapter(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def _callFUT(self, *args, **kw): + from zope.component import queryMultiAdapter + return queryMultiAdapter(*args, **kw) + + def test_anonymous_nonesuch(self): + from zope.interface import Interface + class IFoo(Interface): + pass + self.assertEqual(self._callFUT((object(), object()), IFoo, '', + '<default>'), + '<default>') + + def test_named_nonesuch(self): + from zope.interface import Interface + class IFoo(Interface): + pass + self.assertEqual(self._callFUT((object(), object()), IFoo, 'bar'), + None) + + def test_anonymous_hit(self): + from zope.interface import Interface + from zope.interface import implementer + from zope.component import getGlobalSiteManager + class IFoo(Interface): + pass + class IBar(Interface): + pass + class IBaz(Interface): + pass + @implementer(IBar) + class Bar(object): + pass + @implementer(IBaz) + class Baz(object): + pass + @implementer(IFoo) + class FooAdapter(object): + def __init__(self, first, second): + self.first, self.second = first, second + getGlobalSiteManager().registerAdapter( + FooAdapter, (IBar, IBaz), IFoo, '') + bar = Bar() + baz = Baz() + adapted = self._callFUT((bar, baz), IFoo, '') + self.assertTrue(adapted.__class__ is FooAdapter) + self.assertTrue(adapted.first is bar) + self.assertTrue(adapted.second is baz) + + def test_named_hit(self): + from zope.interface import Interface + from zope.interface import implementer + from zope.component import getGlobalSiteManager + class IFoo(Interface): + pass + class IBar(Interface): + pass + class IBaz(Interface): + pass + @implementer(IBar) + class Bar(object): + pass + @implementer(IBaz) + class Baz(object): + pass + @implementer(IFoo) + class FooAdapter(object): + def __init__(self, first, second): + self.first, self.second = first, second + getGlobalSiteManager().registerAdapter( + FooAdapter, (IBar, IBaz), IFoo, 'named') + bar = Bar() + baz = Baz() + adapted = self._callFUT((bar, baz), IFoo, 'named') + self.assertTrue(adapted.__class__ is FooAdapter) + self.assertTrue(adapted.first is bar) + self.assertTrue(adapted.second is baz) + + def test_nested(self): + from zope.interface import Interface + from zope.interface import implementer + from zope.interface.registry import Components + from zope.component import getGlobalSiteManager + from zope.component.tests.examples import ConformsToIComponentLookup + class IFoo(Interface): + pass + class IBar(Interface): + pass + class IBaz(Interface): + pass + @implementer(IBar) + class Bar(object): + pass + @implementer(IBaz) + class Baz(object): + pass + @implementer(IFoo) + class Global(object): + def __init__(self, first, second): + self.first, self.second = first, second + @implementer(IFoo) + class Local(object): + def __init__(self, first, second): + self.first, self.second = first, second + class Context(ConformsToIComponentLookup): + def __init__(self, sm): + self.sitemanager = sm + gsm = getGlobalSiteManager() + gsm.registerAdapter(Global, (IBar, IBaz), IFoo, '') + sm1 = Components('sm1', bases=(gsm, )) + sm1.registerAdapter(Local, (IBar, IBaz), IFoo, '') + bar = Bar() + baz = Baz() + adapted = self._callFUT((bar, baz), IFoo, '', context=Context(sm1)) + self.assertTrue(adapted.__class__ is Local) + self.assertTrue(adapted.first is bar) + self.assertTrue(adapted.second is baz) + + def test_wo_sitemanager(self): + from zope.interface import Interface + from zope.interface import implementer + from zope.component.interfaces import ComponentLookupError + class IFoo(Interface): + pass + class IBar(Interface): + pass + class IBaz(Interface): + pass + @implementer(IBar) + class Bar(object): + pass + @implementer(IBaz) + class Baz(object): + pass + class Context(object): + def __conform__(self, iface): + raise ComponentLookupError + bar = Bar() + baz = Baz() + adapted = self._callFUT((bar, baz), IFoo, '', context=Context()) + self.assertTrue(adapted is None) + + +class Test_getAdapters(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def _callFUT(self, *args, **kw): + from zope.component import getAdapters + return getAdapters(*args, **kw) + + def test_nonesuch(self): + from zope.interface import Interface + class IFoo(Interface): + pass + self.assertEqual(list(self._callFUT((object(),), IFoo)), []) + + def test_hit(self): + from zope.interface import Interface + from zope.component import getGlobalSiteManager + class IFoo(Interface): + pass + class BarAdapter(object): + def __init__(self, context): + self.context = context + class BazAdapter(object): + def __init__(self, context): + self.context = context + gsm = getGlobalSiteManager() + gsm.registerAdapter(BarAdapter, (None,), IFoo) + gsm.registerAdapter(BazAdapter, (None,), IFoo, name='bar') + tuples = list(self._callFUT((object(),), IFoo)) + self.assertEqual(len(tuples), 2) + names = [(x, y.__class__.__name__) for x, y in tuples] + self.assertTrue(('', 'BarAdapter') in names) + self.assertTrue(('bar', 'BazAdapter') in names) + + def test_wo_sitemanager(self): + from zope.interface import Interface + from zope.interface import implementer + from zope.component.interfaces import ComponentLookupError + class IFoo(Interface): + pass + class IBar(Interface): + pass + class IBaz(Interface): + pass + @implementer(IBar) + class Bar(object): + pass + @implementer(IBaz) + class Baz(object): + pass + class Context(object): + def __conform__(self, iface): + raise ComponentLookupError + bar = Bar() + baz = Baz() + adapted = self._callFUT((bar, baz), IFoo, context=Context()) + self.assertEqual(adapted, []) + + +class Test_subscribers(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def _callFUT(self, *args, **kw): + from zope.component import subscribers + return subscribers(*args, **kw) + + def test_nonesuch(self): + from zope.interface import Interface + class IFoo(Interface): + pass + subscribers = self._callFUT((object,), IFoo) + self.assertEqual(subscribers, []) + + def test_hit(self): + from zope.interface import Interface + from zope.component import getGlobalSiteManager + class IFoo(Interface): + pass + class BarAdapter(object): + def __init__(self, context): + self.context = context + class BazAdapter(object): + def __init__(self, context): + self.context = context + gsm = getGlobalSiteManager() + gsm.registerSubscriptionAdapter(BarAdapter, (None,), IFoo) + gsm.registerSubscriptionAdapter(BazAdapter, (None,), IFoo) + subscribers = self._callFUT((object(),), IFoo) + self.assertEqual(len(subscribers), 2) + names = [(x.__class__.__name__) for x in subscribers] + self.assertTrue('BarAdapter' in names) + self.assertTrue('BazAdapter' in names) + + def test_wo_sitemanager(self): + from zope.interface import Interface + from zope.component.interfaces import ComponentLookupError + class IFoo(Interface): + pass + class Context(object): + def __conform__(self, iface): + raise ComponentLookupError + subscribers = self._callFUT((object,), IFoo, context=Context()) + self.assertEqual(subscribers, []) + + +class Test_handle(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def _callFUT(self, *args, **kw): + from zope.component import handle + return handle(*args, **kw) + + def test_nonesuch(self): + from zope.interface import Interface + class IFoo(Interface): + pass + subscribers = self._callFUT((object,), IFoo) #doesn't raise + + def test_hit(self): + from zope.component import getGlobalSiteManager + from zope.interface import Interface + from zope.interface import implementer + class IFoo(Interface): + pass + @implementer(IFoo) + class Foo(object): + pass + _called = [] + def _bar(context): + _called.append('_bar') + def _baz(context): + _called.append('_baz') + gsm = getGlobalSiteManager() + gsm.registerHandler(_bar, (IFoo,)) + gsm.registerHandler(_baz, (IFoo,)) + self._callFUT(Foo()) + self.assertEqual(len(_called), 2, _called) + self.assertTrue('_bar' in _called) + self.assertTrue('_baz' in _called) + + +class Test_getUtility(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def _callFUT(self, *args, **kw): + from zope.component._api import getUtility + return getUtility(*args, **kw) + + def test_anonymous_nonesuch(self): + from zope.interface import Interface + from zope.component.interfaces import ComponentLookupError + class IFoo(Interface): + pass + self.assertRaises(ComponentLookupError, self._callFUT, IFoo) + + def test_named_nonesuch(self): + from zope.interface import Interface + from zope.component.interfaces import ComponentLookupError + class IFoo(Interface): + pass + self.assertRaises(ComponentLookupError, + self._callFUT, IFoo, name='bar') + + def test_anonymous_hit(self): + from zope.interface import Interface + from zope.component import getGlobalSiteManager + class IFoo(Interface): + pass + obj = object() + getGlobalSiteManager().registerUtility(obj, IFoo) + self.assertTrue(self._callFUT(IFoo) is obj) + + def test_named_hit(self): + from zope.interface import Interface + from zope.component import getGlobalSiteManager + class IFoo(Interface): + pass + obj = object() + getGlobalSiteManager().registerUtility(obj, IFoo, name='bar') + self.assertTrue(self._callFUT(IFoo, name='bar') is obj) + + def test_w_conforming_context(self): + from zope.interface import Interface + from zope.component import getGlobalSiteManager + from zope.component.tests.examples import ConformsToIComponentLookup + class SM(object): + def __init__(self, obj): + self._obj = obj + def queryUtility(self, interface, name, default): + return self._obj + class IFoo(Interface): + pass + obj1 = object() + obj2 = object() + sm = SM(obj2) + context = ConformsToIComponentLookup(sm) + getGlobalSiteManager().registerUtility(obj1, IFoo) + self.assertTrue(self._callFUT(IFoo, context=context) is obj2) + + +class Test_queryUtility(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def _callFUT(self, *args, **kw): + from zope.component._api import queryUtility + return queryUtility(*args, **kw) + + def test_anonymous_nonesuch(self): + from zope.interface import Interface + class IFoo(Interface): + pass + self.assertEqual(self._callFUT(IFoo), None) + + def test_anonymous_nonesuch_w_default(self): + from zope.interface import Interface + class IFoo(Interface): + pass + obj = object() + self.assertTrue(self._callFUT(IFoo, default=obj) is obj) + + def test_named_nonesuch(self): + from zope.interface import Interface + class IFoo(Interface): + pass + self.assertEqual(self._callFUT(IFoo, name='bar'), None) + + def test_named_nonesuch_w_default(self): + from zope.interface import Interface + class IFoo(Interface): + pass + obj = object() + self.assertTrue(self._callFUT(IFoo, name='bar', default=obj) is obj) + + def test_anonymous_hit(self): + from zope.interface import Interface + from zope.component import getGlobalSiteManager + class IFoo(Interface): + pass + obj = object() + getGlobalSiteManager().registerUtility(obj, IFoo) + self.assertTrue(self._callFUT(IFoo) is obj) + + def test_named_hit(self): + from zope.interface import Interface + from zope.component import getGlobalSiteManager + class IFoo(Interface): + pass + obj = object() + getGlobalSiteManager().registerUtility(obj, IFoo, name='bar') + self.assertTrue(self._callFUT(IFoo, name='bar') is obj) + + def test_w_conforming_context(self): + from zope.interface import Interface + from zope.component import getGlobalSiteManager + from zope.component.tests.examples import ConformsToIComponentLookup + class SM(object): + def __init__(self, obj): + self._obj = obj + def queryUtility(self, interface, name, default): + return self._obj + class IFoo(Interface): + pass + obj1 = object() + obj2 = object() + sm = SM(obj2) + context = ConformsToIComponentLookup(sm) + getGlobalSiteManager().registerUtility(obj1, IFoo) + self.assertTrue(self._callFUT(IFoo, context=context) is obj2) + + +class Test_getUtilitiesFor(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def _callFUT(self, *args, **kw): + from zope.component._api import getUtilitiesFor + return getUtilitiesFor(*args, **kw) + + def test_nonesuch(self): + from zope.interface import Interface + class IFoo(Interface): + pass + self.assertEqual(list(self._callFUT(IFoo)), []) + + def test_hit(self): + from zope.interface import Interface + from zope.component import getGlobalSiteManager + class IFoo(Interface): + pass + obj = object() + obj1 = object() + getGlobalSiteManager().registerUtility(obj, IFoo) + getGlobalSiteManager().registerUtility(obj1, IFoo, name='bar') + tuples = list(self._callFUT(IFoo)) + self.assertEqual(len(tuples), 2) + self.assertTrue(('', obj) in tuples) + self.assertTrue(('bar', obj1) in tuples) + + +class Test_getAllUtilitiesRegisteredFor(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def _callFUT(self, *args, **kw): + from zope.component import getAllUtilitiesRegisteredFor + return getAllUtilitiesRegisteredFor(*args, **kw) + + def test_nonesuch(self): + from zope.interface import Interface + class IFoo(Interface): + pass + self.assertEqual(list(self._callFUT(IFoo)), []) + + def test_hit(self): + from zope.interface import Interface + from zope.component import getGlobalSiteManager + class IFoo(Interface): + pass + class IBar(IFoo): + pass + obj = object() + obj1 = object() + obj2 = object() + getGlobalSiteManager().registerUtility(obj, IFoo) + getGlobalSiteManager().registerUtility(obj1, IFoo, name='bar') + getGlobalSiteManager().registerUtility(obj2, IBar) + uts = list(self._callFUT(IFoo)) + self.assertEqual(len(uts), 3) + self.assertTrue(obj in uts) + self.assertTrue(obj1 in uts) + self.assertTrue(obj2 in uts) + + +class Test_getNextUtility(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def _callFUT(self, *args, **kw): + from zope.component import getNextUtility + return getNextUtility(*args, **kw) + + def test_global(self): + from zope.component import getGlobalSiteManager + from zope.component.interface import ComponentLookupError + gsm = getGlobalSiteManager() + gutil = _makeMyUtility('global', gsm) + gsm.registerUtility(gutil, IMyUtility, 'myutil') + self.assertRaises(ComponentLookupError, + self._callFUT, gutil, IMyUtility, 'myutil') + + def test_nested(self): + from zope.component import getGlobalSiteManager + from zope.component.interfaces import IComponentLookup + from zope.interface.registry import Components + gsm = getGlobalSiteManager() + gutil = _makeMyUtility('global', gsm) + gsm.registerUtility(gutil, IMyUtility, 'myutil') + sm1 = Components('sm1', bases=(gsm, )) + sm1_1 = Components('sm1_1', bases=(sm1, )) + util1 = _makeMyUtility('one', sm1) + sm1.registerUtility(util1, IMyUtility, 'myutil') + self.assertTrue(IComponentLookup(util1) is sm1) + self.assertTrue(self._callFUT(util1, IMyUtility, 'myutil') is gutil) + util1_1 = _makeMyUtility('one-one', sm1_1) + sm1_1.registerUtility(util1_1, IMyUtility, 'myutil') + self.assertTrue(IComponentLookup(util1_1) is sm1_1) + self.assertTrue(self._callFUT(util1_1, IMyUtility, 'myutil') is util1) + + +class Test_queryNextUtility(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def _callFUT(self, *args, **kw): + from zope.component import queryNextUtility + return queryNextUtility(*args, **kw) + + def test_global(self): + from zope.component import getGlobalSiteManager + gsm = getGlobalSiteManager() + gutil = _makeMyUtility('global', gsm) + gsm.registerUtility(gutil, IMyUtility, 'myutil') + self.assertEqual(self._callFUT(gutil, IMyUtility, 'myutil'), None) + + def test_nested(self): + from zope.component import getGlobalSiteManager + from zope.interface.registry import Components + gsm = getGlobalSiteManager() + gutil = _makeMyUtility('global', gsm) + gsm.registerUtility(gutil, IMyUtility, 'myutil') + sm1 = Components('sm1', bases=(gsm, )) + sm1_1 = Components('sm1_1', bases=(sm1, )) + util1 = _makeMyUtility('one', sm1) + sm1.registerUtility(util1, IMyUtility, 'myutil') + util1_1 = _makeMyUtility('one-one', sm1_1) + sm1_1.registerUtility(util1_1, IMyUtility, 'myutil') + myregistry = Components() + custom_util = _makeMyUtility('my_custom_util', myregistry) + myregistry.registerUtility(custom_util, IMyUtility, 'my_custom_util') + sm1.__bases__ = (myregistry,) + sm1.__bases__ + # Both the ``myregistry`` and global utilities should be available: + self.assertTrue(self._callFUT(sm1, IMyUtility, 'my_custom_util') + is custom_util) + self.assertTrue(self._callFUT(sm1, IMyUtility, 'myutil') is gutil) + + def test_wo_sitemanager(self): + from zope.interface import Interface + from zope.component.interfaces import ComponentLookupError + class IFoo(Interface): + pass + class Context(object): + def __conform__(self, iface): + raise ComponentLookupError + self.assertEqual(self._callFUT(Context(), IFoo, 'myutil'), None) + + +class Test_createObject(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def _callFUT(self, *args, **kw): + from zope.component import createObject + return createObject(*args, **kw) + + def test_miss(self): + from zope.component.interfaces import ComponentLookupError + self.assertRaises(ComponentLookupError, self._callFUT, 'nonesuch') + + def test_hit(self): + from zope.component.interfaces import IFactory + _object = object() + _factory_called = [] + def _factory(*args, **kw): + _factory_called.append((args, kw)) + return _object + class Context(object): + def __conform__(self, iface): + return self + def queryUtility(self, iface, name, default): + if iface is IFactory and name == 'test': + return _factory + return default + context = Context() + self.assertTrue(self._callFUT('test', context=context) is _object) + self.assertEqual(_factory_called, [((), {})]) + + +class Test_getFactoryInterfaces(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def _callFUT(self, *args, **kw): + from zope.component import getFactoryInterfaces + return getFactoryInterfaces(*args, **kw) + + def test_miss(self): + from zope.component.interfaces import ComponentLookupError + self.assertRaises(ComponentLookupError, self._callFUT, 'nonesuch') + + def test_hit(self): + from zope.component.interfaces import IFactory + from zope.interface import Interface + class IFoo(Interface): + pass + class _Factory(object): + def getInterfaces(self): + return [IFoo] + class Context(object): + def __conform__(self, iface): + return self + def queryUtility(self, iface, name, default): + if iface is IFactory and name == 'test': + return _Factory() + return default + context = Context() + self.assertEqual(self._callFUT('test', context=context), [IFoo]) + + +class Test_getFactoriesFor(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def _callFUT(self, *args, **kw): + from zope.component import getFactoriesFor + return getFactoriesFor(*args, **kw) + + def test_no_factories_registered(self): + from zope.interface import Interface + class IFoo(Interface): + pass + self.assertEqual(list(self._callFUT(IFoo)), []) + + def test_w_factory_returning_spec(self): + from zope.interface import Interface + from zope.interface import implementer + from zope.interface import providedBy + from zope.component.interfaces import IFactory + class IFoo(Interface): + pass + class IBar(Interface): + pass + @implementer(IFoo, IBar) + class _Factory(object): + def getInterfaces(self): + return providedBy(self) + _factory = _Factory() + class Context(object): + def __conform__(self, iface): + return self + def getUtilitiesFor(self, iface): + if iface is IFactory: + return [('test', _factory)] + self.assertEqual(list(self._callFUT(IFoo, context=Context())), + [('test', _factory)]) + self.assertEqual(list(self._callFUT(IBar, context=Context())), + [('test', _factory)]) + + def test_w_factory_returning_list_of_interfaces(self): + from zope.interface import Interface + from zope.component.interfaces import IFactory + class IFoo(Interface): + pass + class IBar(Interface): + pass + class _Factory(object): + def getInterfaces(self): + return [IFoo, IBar] + _factory = _Factory() + class Context(object): + def __conform__(self, iface): + return self + def getUtilitiesFor(self, iface): + if iface is IFactory: + return [('test', _factory)] + self.assertEqual(list(self._callFUT(IFoo, context=Context())), + [('test', _factory)]) + self.assertEqual(list(self._callFUT(IBar, context=Context())), + [('test', _factory)]) + + +IMyUtility = None +def _makeMyUtility(name, sm): + global IMyUtility + from zope.interface import Interface + from zope.interface import implementer + from zope.component.tests.examples import ConformsToIComponentLookup + + if IMyUtility is None: + class IMyUtility(Interface): + pass + + @implementer(IMyUtility) + class MyUtility(ConformsToIComponentLookup): + def __init__(self, id, sm): + self.id = id + self.sitemanager = sm + + return MyUtility(name, sm) + + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(Test_getSiteManager), + unittest.makeSuite(Test_getAdapterInContext), + unittest.makeSuite(Test_queryAdapterInContext), + unittest.makeSuite(Test_getAdapter), + unittest.makeSuite(Test_queryAdapter), + unittest.makeSuite(Test_getMultiAdapter), + unittest.makeSuite(Test_queryMultiAdapter), + unittest.makeSuite(Test_getAdapters), + unittest.makeSuite(Test_subscribers), + unittest.makeSuite(Test_handle), + unittest.makeSuite(Test_getUtility), + unittest.makeSuite(Test_queryUtility), + unittest.makeSuite(Test_getUtilitiesFor), + unittest.makeSuite(Test_getAllUtilitiesRegisteredFor), + unittest.makeSuite(Test_getNextUtility), + unittest.makeSuite(Test_queryNextUtility), + unittest.makeSuite(Test_createObject), + unittest.makeSuite(Test_getFactoryInterfaces), + unittest.makeSuite(Test_getFactoriesFor), + )) + diff --git a/src/zope/component/tests/test__declaration.py b/src/zope/component/tests/test__declaration.py new file mode 100644 index 0000000..b6c0767 --- /dev/null +++ b/src/zope/component/tests/test__declaration.py @@ -0,0 +1,221 @@ +############################################################################## +# +# Copyright (c) 2012 Zope Foundation 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. +# +############################################################################## +"""Test adapter declaration helpers +""" +import unittest + +class Test_adapter(unittest.TestCase): + + def _getTargetClass(self): + from zope.component._declaration import adapter + return adapter + + def _makeOne(self, *interfaces): + return self._getTargetClass()(*interfaces) + + def test_ctor_no_interfaces(self): + deco = self._makeOne() + self.assertEqual(list(deco.interfaces), []) + + def test_ctor_w_interfaces(self): + from zope.interface import Interface + class IFoo(Interface): + pass + class IBar(Interface): + pass + deco = self._makeOne(IFoo, IBar) + self.assertEqual(list(deco.interfaces), [IFoo, IBar]) + + def test__call___w_class(self): + from zope.interface import Interface + class IFoo(Interface): + pass + class IBar(Interface): + pass + @self._makeOne(IFoo, IBar) + class Baz(object): + pass + self.assertEqual(Baz.__component_adapts__, (IFoo, IBar)) + + def test__call___w_inst_of_decorated_class(self): + from zope.interface import Interface + class IFoo(Interface): + pass + class IBar(Interface): + pass + @self._makeOne(IFoo, IBar) + class Baz(object): + pass + baz = Baz() + self.assertRaises(AttributeError, + getattr, baz, '__component_adapts_') + + def test__call___w_non_class(self): + from zope.interface import Interface + class IFoo(Interface): + pass + class IBar(Interface): + pass + class Baz(object): + pass + deco = self._makeOne(IFoo, IBar) + baz = deco(Baz()) + self.assertEqual(baz.__component_adapts__, (IFoo, IBar)) + + +class Test_adapts(unittest.TestCase): + + def _run_generated_code(self, code, globs, locs, + fails_under_py3k=True, + ): + import warnings + #from zope.component._compat import PYTHON3 + PYTHON3 = False + with warnings.catch_warnings(record=True) as log: + warnings.resetwarnings() + if not PYTHON3: + exec(code, globs, locs) + self.assertEqual(len(log), 0) # no longer warn + return True + else: + try: + exec(code, globs, locs) + except TypeError: + return False + else: + if fails_under_py3k: + self.fail("Didn't raise TypeError") + + def test_instances_not_affected(self): + from zope.component._declaration import adapts + class C(object): + adapts() + + self.assertEqual(C.__component_adapts__, ()) + def _try(): + return C().__component_adapts__ + self.assertRaises(AttributeError, _try) + + def test_called_from_function(self): + import warnings + from zope.component._declaration import adapts + from zope.interface import Interface + class IFoo(Interface): + pass + globs = {'adapts': adapts, 'IFoo': IFoo} + locs = {} + CODE = "\n".join([ + 'def foo():', + ' adapts(IFoo)' + ]) + if self._run_generated_code(CODE, globs, locs, False): + foo = locs['foo'] + with warnings.catch_warnings(record=True) as log: + warnings.resetwarnings() + self.assertRaises(TypeError, foo) + self.assertEqual(len(log), 0) # no longer warn + + def test_called_twice_from_class(self): + import warnings + from zope.component._declaration import adapts + from zope.interface import Interface + from zope.interface._compat import PYTHON3 + class IFoo(Interface): + pass + class IBar(Interface): + pass + globs = {'adapts': adapts, 'IFoo': IFoo, 'IBar': IBar} + locs = {} + CODE = "\n".join([ + 'class Foo(object):', + ' adapts(IFoo)', + ' adapts(IBar)', + ]) + with warnings.catch_warnings(record=True) as log: + warnings.resetwarnings() + try: + exec(CODE, globs, locs) + except TypeError: + if not PYTHON3: + self.assertEqual(len(log), 0) # no longer warn + else: + self.fail("Didn't raise TypeError") + + def test_called_once_from_class(self): + from zope.component._declaration import adapts + from zope.interface import Interface + class IFoo(Interface): + pass + globs = {'adapts': adapts, 'IFoo': IFoo} + locs = {} + CODE = "\n".join([ + 'class Foo(object):', + ' adapts(IFoo)', + ]) + if self._run_generated_code(CODE, globs, locs): + Foo = locs['Foo'] + spec = Foo.__component_adapts__ + self.assertEqual(list(spec), [IFoo]) + + +class Test_adaptedBy(unittest.TestCase): + + def _callFUT(self, obj): + from zope.component._declaration import adaptedBy + return adaptedBy(obj) + + def test_obj_w_no_attr(self): + self.assertEqual(self._callFUT(object()), None) + + def test__call___w_class(self): + from zope.interface import Interface + class IFoo(Interface): + pass + class IBar(Interface): + pass + class Baz(object): + __component_adapts__ = (IFoo, IBar) + self.assertEqual(self._callFUT(Baz), (IFoo, IBar)) + + def test__call___w_inst_of_decorated_class(self): + from zope.interface import Interface + from zope.component._declaration import _adapts_descr + class IFoo(Interface): + pass + class IBar(Interface): + pass + class Baz(object): + __component_adapts__ = _adapts_descr((IFoo, IBar)) + baz = Baz() + self.assertEqual(self._callFUT(baz), None) + + def test__call___w_non_class(self): + from zope.interface import Interface + class IFoo(Interface): + pass + class IBar(Interface): + pass + class Baz(object): + pass + baz = Baz() + baz.__component_adapts__ = (IFoo, IBar) + self.assertEqual(self._callFUT(baz), (IFoo, IBar)) + + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(Test_adapter), + unittest.makeSuite(Test_adapts), + unittest.makeSuite(Test_adaptedBy), + )) diff --git a/src/zope/component/tests/test_event.py b/src/zope/component/tests/test_event.py new file mode 100644 index 0000000..0b250dc --- /dev/null +++ b/src/zope/component/tests/test_event.py @@ -0,0 +1,66 @@ +############################################################################## +# +# Copyright (c) 2012 Zope Foundation 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. +# +############################################################################## +"""Test adapter declaration helpers +""" +import unittest + +class Test_dispatch(unittest.TestCase): + + def test_it(self): + from zope.interface import Interface + from zope.component.globalregistry import getGlobalSiteManager + from zope.component.event import dispatch + _adapted = [] + def _adapter(context): + _adapted.append(context) + return object() + gsm = getGlobalSiteManager() + gsm.registerHandler(_adapter, (Interface,)) + del _adapted[:] # clear handler reg + event = object() + dispatch(event) + self.assertEqual(_adapted, [event]) + +class Test_objectEventNotify(unittest.TestCase): + + def test_it(self): + from zope.interface import Interface + from zope.interface import implementer + from zope.component.globalregistry import getGlobalSiteManager + from zope.component.interfaces import IObjectEvent + from zope.component.event import objectEventNotify + _adapted = [] + def _adapter(context, event): + _adapted.append((context, event)) + return object() + gsm = getGlobalSiteManager() + gsm.registerHandler(_adapter, (Interface, IObjectEvent)) + del _adapted[:] # clear handler reg + @implementer(IObjectEvent) + class _ObjectEvent(object): + def __init__(self, object): + self.object = object + context = object() + event = _ObjectEvent(context) + objectEventNotify(event) + self.assertEqual(_adapted, [(context, event)]) + + + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(Test_dispatch), + unittest.makeSuite(Test_objectEventNotify), + )) + diff --git a/src/zope/component/tests/test_factory.py b/src/zope/component/tests/test_factory.py new file mode 100644 index 0000000..cda0191 --- /dev/null +++ b/src/zope/component/tests/test_factory.py @@ -0,0 +1,111 @@ +############################################################################## +# +# Copyright (c) 2012 Zope Foundation 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. +# +############################################################################## +"""Tests for z.c.factory +""" +import unittest + + +class FactoryTests(unittest.TestCase): + + def _getTargetClass(self): + from zope.component.factory import Factory + return Factory + + def _makeOne(self, callable=None, *args, **kw): + if callable is None: + callable = _test_callable + return self._getTargetClass()(callable, *args, **kw) + + def test_class_conforms_to_IFactory(self): + from zope.interface.verify import verifyClass + from zope.component.interfaces import IFactory + verifyClass(IFactory, self._getTargetClass()) + + def test_instance_conforms_to_IFactory(self): + from zope.interface.verify import verifyObject + from zope.component.interfaces import IFactory + verifyObject(IFactory, self._makeOne()) + + def test_ctor_defaults(self): + factory = self._makeOne() + self.assertEqual(factory._callable, _test_callable) + self.assertEqual(factory.title, '') + self.assertEqual(factory.description, '') + self.assertEqual(factory._interfaces, None) + + def test_ctor_expclit(self): + factory = self._makeOne(_test_callable, 'TITLE', 'DESCRIPTION') + self.assertEqual(factory.title, 'TITLE') + self.assertEqual(factory.description, 'DESCRIPTION') + + def test___call___no_args(self): + _called = [] + def _callable(*args, **kw): + _called.append((args, kw)) + factory = self._makeOne(_callable) + factory() + self.assertEqual(_called, [((), {})]) + + def test___call___positional_args(self): + _called = [] + def _callable(*args, **kw): + _called.append((args, kw)) + factory = self._makeOne(_callable) + factory('one', 'two') + self.assertEqual(_called, [(('one', 'two'), {})]) + + def test___call___keyword_args(self): + _called = [] + def _callable(*args, **kw): + _called.append((args, kw)) + factory = self._makeOne(_callable) + factory(foo='bar') + self.assertEqual(_called, [((), {'foo': 'bar'})]) + + def test_getInterfaces_explicit(self): + from zope.interface import Interface + from zope.interface import implementer + class IFoo(Interface): + pass + class IBar(Interface): + pass + class IBaz(Interface): + pass + @implementer(IBaz) + def _callable(): + pass + factory = self._makeOne(_callable, interfaces=(IFoo, IBar)) + spec = factory.getInterfaces() + self.assertEqual(spec.__name__, '_callable') + self.assertEqual(list(spec), [IFoo, IBar]) + + def test_getInterfaces_implicit(self): + from zope.interface import Interface + from zope.interface import implementer + class IBaz(Interface): + pass + @implementer(IBaz) + def _callable(): + pass + factory = self._makeOne(_callable) + spec = factory.getInterfaces() + self.assertEqual(list(spec), [IBaz]) + +def _test_callable(*args, **kw): + pass + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(FactoryTests), + )) diff --git a/src/zope/component/tests/test_globalregistry.py b/src/zope/component/tests/test_globalregistry.py new file mode 100644 index 0000000..d55b4cb --- /dev/null +++ b/src/zope/component/tests/test_globalregistry.py @@ -0,0 +1,251 @@ +############################################################################## +# +# Copyright (c) 2012 Zope Foundation 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. +# +############################################################################## +""" Tests for z.c._api +""" +import unittest + +class Test_getGlobalSiteManager(unittest.TestCase): + + def _callFUT(self): + from zope.component.globalregistry import getGlobalSiteManager + return getGlobalSiteManager() + + def test_gsm_is_IComponentLookup(self): + from zope.component.globalregistry import base + from zope.component.interfaces import IComponentLookup + gsm = self._callFUT() + self.assertTrue(gsm is base) + self.assertTrue(IComponentLookup.providedBy(gsm)) + + def test_gsm_is_singleton(self): + gsm = self._callFUT() + self.assertTrue(self._callFUT() is gsm) + + def test_gsm_pickling(self): + from zope.component._compat import _pickle + gsm = self._callFUT() + dumped = _pickle.dumps(gsm) + loaded = _pickle.loads(dumped) + self.assertTrue(loaded is gsm) + + dumped_utilities = _pickle.dumps(gsm.utilities) + loaded_utilities = _pickle.loads(dumped_utilities) + self.assertTrue(loaded_utilities is gsm.utilities) + + dumped_adapters = _pickle.dumps(gsm.adapters) + loaded_adapters = _pickle.loads(dumped_adapters) + self.assertTrue(loaded_adapters is gsm.adapters) + + +class Test_provideUtility(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def _callFUT(self, *args, **kw): + from zope.component.globalregistry import provideUtility + return provideUtility(*args, **kw) + + def test_anonymous_no_provides(self): + from zope.interface import Interface + from zope.interface import implementer + from zope.component.globalregistry import getGlobalSiteManager + class IFoo(Interface): + pass + @implementer(IFoo) + class Foo(object): + pass + foo = Foo() + self._callFUT(foo) + gsm = getGlobalSiteManager() + self.assertTrue(gsm.getUtility(IFoo, '') is foo) + + def test_named_w_provides(self): + from zope.interface import Interface + from zope.component.globalregistry import getGlobalSiteManager + class IFoo(Interface): + pass + class Foo(object): + pass + foo = Foo() + self._callFUT(foo, IFoo, 'named') + gsm = getGlobalSiteManager() + self.assertTrue(gsm.getUtility(IFoo, 'named') is foo) + + +class Test_provideAdapter(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def _callFUT(self, *args, **kw): + from zope.component.globalregistry import provideAdapter + return provideAdapter(*args, **kw) + + def test_anonymous_no_provides_no_adapts(self): + from zope.interface import Interface + from zope.interface import implementer + from zope.component.globalregistry import getGlobalSiteManager + from zope.component._api import adapter + class IFoo(Interface): + pass + class IBar(Interface): + pass + @implementer(IFoo) + class Foo(object): + pass + @adapter(IFoo) + @implementer(IBar) + class Bar(object): + def __init__(self, context): + self.context = context + self._callFUT(Bar) + gsm = getGlobalSiteManager() + foo = Foo() + adapted = gsm.getAdapter(foo, IBar) + self.assertTrue(isinstance(adapted, Bar)) + self.assertTrue(adapted.context is foo) + + def test_named_w_provides_w_adapts(self): + from zope.interface import Interface + from zope.interface import implementer + from zope.component.globalregistry import getGlobalSiteManager + class IFoo(Interface): + pass + class IBar(Interface): + pass + @implementer(IFoo) + class Foo(object): + pass + class Bar(object): + def __init__(self, context): + self.context = context + self._callFUT(Bar, (IFoo,), IBar, 'test') + gsm = getGlobalSiteManager() + foo = Foo() + adapted = gsm.getAdapter(foo, IBar, name='test') + self.assertTrue(isinstance(adapted, Bar)) + self.assertTrue(adapted.context is foo) + + +class Test_provideSubscriptionAdapter(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def _callFUT(self, *args, **kw): + from zope.component.globalregistry import provideSubscriptionAdapter + return provideSubscriptionAdapter(*args, **kw) + + def test_no_provides_no_adapts(self): + from zope.interface import Interface + from zope.interface import implementer + from zope.component.globalregistry import getGlobalSiteManager + from zope.component._api import adapter + class IFoo(Interface): + pass + class IBar(Interface): + pass + @implementer(IFoo) + class Foo(object): + pass + @adapter(IFoo) + @implementer(IBar) + class Bar(object): + def __init__(self, context): + self.context = context + self._callFUT(Bar) + gsm = getGlobalSiteManager() + foo = Foo() + adapted = gsm.subscribers((foo,), IBar) + self.assertEqual(len(adapted), 1) + self.assertTrue(isinstance(adapted[0], Bar)) + self.assertTrue(adapted[0].context is foo) + + def test_w_provides_w_adapts(self): + from zope.interface import Interface + from zope.interface import implementer + from zope.component.globalregistry import getGlobalSiteManager + class IFoo(Interface): + pass + class IBar(Interface): + pass + @implementer(IFoo) + class Foo(object): + pass + class Bar(object): + def __init__(self, context): + self.context = context + self._callFUT(Bar, (IFoo,), IBar) + gsm = getGlobalSiteManager() + foo = Foo() + adapted = gsm.subscribers((foo,), IBar) + self.assertEqual(len(adapted), 1) + self.assertTrue(isinstance(adapted[0], Bar)) + self.assertTrue(adapted[0].context is foo) + + +class Test_provideHandler(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def _callFUT(self, *args, **kw): + from zope.component.globalregistry import provideHandler + return provideHandler(*args, **kw) + + def test_no_adapts(self): + from zope.interface import Interface + from zope.interface import implementer + from zope.interface import providedBy + from zope.component.globalregistry import getGlobalSiteManager + from zope.component._api import adapter + class IFoo(Interface): + pass + @implementer(IFoo) + class Foo(object): + pass + @adapter(IFoo) + def _handler(context): + assert 0, "DON'T GO HERE" + self._callFUT(_handler) + gsm = getGlobalSiteManager() + regs = list(gsm.registeredHandlers()) + self.assertEqual(len(regs), 1) + hr = regs[0] + self.assertEqual(list(hr.required), list(providedBy(Foo()))) + self.assertEqual(hr.name, '') + self.assertTrue(hr.factory is _handler) + + def test_w_adapts(self): + from zope.interface import Interface + from zope.component.globalregistry import getGlobalSiteManager + class IFoo(Interface): + pass + def _handler(context): + assert 0, "DON'T GO HERE" + self._callFUT(_handler, (IFoo,)) + gsm = getGlobalSiteManager() + regs = list(gsm.registeredHandlers()) + self.assertEqual(len(regs), 1) + hr = regs[0] + self.assertEqual(list(hr.required), [IFoo]) + self.assertEqual(hr.name, '') + self.assertTrue(hr.factory is _handler) + + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(Test_getGlobalSiteManager), + unittest.makeSuite(Test_provideUtility), + unittest.makeSuite(Test_provideAdapter), + unittest.makeSuite(Test_provideSubscriptionAdapter), + unittest.makeSuite(Test_provideHandler), + )) diff --git a/src/zope/component/tests/test_hookable.py b/src/zope/component/tests/test_hookable.py new file mode 100644 index 0000000..1201561 --- /dev/null +++ b/src/zope/component/tests/test_hookable.py @@ -0,0 +1,141 @@ +############################################################################## +# +# Copyright (c) 2012 Zope Foundation 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. +# +############################################################################## +"""Pure-Python hookable tests +""" +import unittest + + +class HookableTests(unittest.TestCase): + + def test_ctor_no_func(self): + from zope.component.hookable import hookable + self.assertRaises(TypeError, hookable) + + def test_ctor_simple(self): + from zope.component.hookable import hookable + def foo(): + pass + hooked = hookable(foo) + self.failUnless(hooked.original is foo) + self.failUnless(hooked.implementation is foo) + + def test_ctor_extra_arg(self): + from zope.component.hookable import hookable + def foo(): + pass + self.assertRaises(TypeError, hookable, foo, foo) + + def test_ctor_extra_arg_miss(self): + from zope.component.hookable import hookable + def foo(): + pass + self.assertRaises(TypeError, hookable, foo, nonesuch=foo) + + def test_sethook(self): + from zope.component.hookable import hookable + def foo(): + pass + def bar(): + pass + hooked = hookable(foo) + hooked.sethook(bar) + self.failUnless(hooked.original is foo) + self.failUnless(hooked.implementation is bar) + + def test_reset(self): + from zope.component.hookable import hookable + def foo(): + pass + def bar(): + pass + hooked = hookable(foo) + hooked.sethook(bar) + hooked.reset() + self.failUnless(hooked.original is foo) + self.failUnless(hooked.implementation is foo) + + def test_cant_assign_original(self): + from zope.component.hookable import hookable + def foo(): + pass + def bar(): + pass + hooked = hookable(foo) + try: + hooked.original = bar + except TypeError: + pass + except AttributeError: + pass + else: + self.fail('Assigned original') + + def test_cant_delete_original(self): + from zope.component.hookable import hookable + def foo(): + pass + hooked = hookable(foo) + try: + del hooked.original + except TypeError: + pass + except AttributeError: + pass + else: + self.fail('Deleted original') + + def test_cant_assign_implementation(self): + from zope.component.hookable import hookable + def foo(): + pass + def bar(): + pass + hooked = hookable(foo) + try: + hooked.implementation = bar + except TypeError: + pass + except AttributeError: + pass + else: + self.fail('Assigned implementation') + + def test_cant_delete_implementation(self): + from zope.component.hookable import hookable + def foo(): + pass + hooked = hookable(foo) + try: + del hooked.implementation + except TypeError: + pass + except AttributeError: + pass + else: + self.fail('Deleted implementation') + + def test_ctor___call__(self): + from zope.component.hookable import hookable + _called = [] + def foo(*args, **kw): + _called.append((args, kw)) + hooked = hookable(foo) + hooked('one', 'two', bar='baz') + self.assertEqual(_called, [(('one', 'two'), {'bar': 'baz'})]) + + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(HookableTests), + )) diff --git a/src/zope/component/tests/test_hooks.py b/src/zope/component/tests/test_hooks.py new file mode 100644 index 0000000..b52b1c7 --- /dev/null +++ b/src/zope/component/tests/test_hooks.py @@ -0,0 +1,344 @@ +############################################################################## +# +# Copyright (c) 2012 Zope Foundation 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. +# +############################################################################## +"""Tests for z.c.hooks +""" +import unittest + + +class Test_read_property(unittest.TestCase): + + def _getTargetClass(self): + from zope.component.hooks import read_property + return read_property + + def test_via_instance(self): + class Foo(object): + @self._getTargetClass() + def bar(self): + return 'BAR' + foo = Foo() + self.assertEqual(foo.bar, 'BAR') + foo.bar = 'BAZ' + self.assertEqual(foo.bar, 'BAZ') + del foo.bar + self.assertEqual(foo.bar, 'BAR') + + def test_via_class(self): + class Foo(object): + @self._getTargetClass() + def bar(self): + return 'BAR' + bar = Foo.bar + self.assertTrue(isinstance(bar, self._getTargetClass())) + self.assertEqual(bar.func(object()), 'BAR') + + +class SiteInfoTests(unittest.TestCase): + + def _getTargetClass(self): + from zope.component.hooks import SiteInfo + return SiteInfo + + def _makeOne(self): + return self._getTargetClass()() + + def test_initial(self): + from zope.component.globalregistry import getGlobalSiteManager + gsm = getGlobalSiteManager() + si = self._makeOne() + self.assertEqual(si.site, None) + self.assertTrue(si.sm is gsm) + + def test_adapter_hook(self): + _hook = object() + class _Registry(object): + adapter_hook = _hook + class _SiteManager(object): + adapters = _Registry() + si = self._makeOne() + si.sm = _SiteManager() + self.assertFalse('adapter_hook' in si.__dict__) + self.assertTrue(si.adapter_hook is _hook) + self.assertTrue('adapter_hook' in si.__dict__) + del si.adapter_hook + self.assertFalse('adapter_hook' in si.__dict__) + + +class Test_setSite(unittest.TestCase): + + def _callFUT(self, site): + from zope.component.hooks import setSite + return setSite(site) + + def test_w_None(self): + from zope.component import hooks + from zope.component.globalregistry import getGlobalSiteManager + gsm = getGlobalSiteManager() + _SM2 = object() + _SITE = object() + _HOOK = object() + siteinfo = _DummySiteInfo() + siteinfo.sm = _SM2 + siteinfo.site = _SITE + siteinfo.adapterhook = _HOOK + with _Monkey(hooks, siteinfo=siteinfo): + self._callFUT(None) + self.assertTrue(siteinfo.sm is gsm) + self.assertTrue(siteinfo.site is None) + self.assertFalse('adapter_hook' in siteinfo.__dict__) + + def test_w_site(self): + from zope.component import hooks + _SM2 = object() + class _Site(object): + def getSiteManager(self): + return _SM2 + siteinfo = _DummySiteInfo() + _site = _Site() + with _Monkey(hooks, siteinfo=siteinfo): + self._callFUT(_site) + self.assertTrue(siteinfo.sm is _SM2) + self.assertTrue(siteinfo.site is _site) + self.assertFalse('adapter_hook' in siteinfo.__dict__) + + +class Test_getSite(unittest.TestCase): + + def _callFUT(self): + from zope.component.hooks import getSite + return getSite() + + def test_w_None(self): + from zope.component import hooks + from zope.component.globalregistry import getGlobalSiteManager + gsm = getGlobalSiteManager() + siteinfo = _DummySiteInfo() + with _Monkey(hooks, siteinfo=siteinfo): + self.assertTrue(self._callFUT() is None) + + def test_w_site(self): + from zope.component import hooks + from zope.component.globalregistry import getGlobalSiteManager + gsm = getGlobalSiteManager() + _SM2 = object() + _SITE = object() + siteinfo = _DummySiteInfo() + siteinfo.sm = _SM2 + siteinfo.site = _SITE + with _Monkey(hooks, siteinfo=siteinfo): + self.assertTrue(self._callFUT() is _SITE) + + +class Test_site(unittest.TestCase): + + def _callFUT(self, new_site): + from zope.component.hooks import site + return site(new_site) + + def test_it(self): + from zope.component import hooks + from zope.component.globalregistry import getGlobalSiteManager + gsm = getGlobalSiteManager() + _SM2 = object() + class _Site(object): + def getSiteManager(self): + return _SM2 + _site = _Site() + siteinfo = _DummySiteInfo() + self.assertTrue(siteinfo.site is None) + self.assertTrue(siteinfo.sm is _SM) + with _Monkey(hooks, siteinfo=siteinfo): + with self._callFUT(_site): + self.assertTrue(siteinfo.site is _site) + self.assertTrue(siteinfo.sm is _SM2) + self.assertTrue(siteinfo.site is None) + self.assertTrue(siteinfo.sm is gsm) + + +class Test_getSiteManager(unittest.TestCase): + + def _callFUT(self, context=None): + from zope.component.hooks import getSiteManager + return getSiteManager(context) + + def test_default(self): + from zope.component import hooks + from zope.component.globalregistry import getGlobalSiteManager + gsm = getGlobalSiteManager() + _SM2 = object() + siteinfo = _DummySiteInfo() + siteinfo.sm = _SM2 + with _Monkey(hooks, siteinfo=siteinfo): + self.assertTrue(self._callFUT() is _SM2) + + def test_w_explicit_context_no_IComponentLookup(self): + from zope.component import hooks + from zope.component.globalregistry import getGlobalSiteManager + gsm = getGlobalSiteManager() + _SM2 = object() + siteinfo = _DummySiteInfo() + siteinfo.sm = _SM2 + with _Monkey(hooks, siteinfo=siteinfo): + self.assertTrue(self._callFUT(object()) is gsm) + + def test_w_explicit_context_w_IComponentLookup(self): + from zope.interface import Interface + from zope.component import hooks + from zope.component.globalregistry import getGlobalSiteManager + from zope.component.interfaces import IComponentLookup + class _Lookup(object): + def __init__(self, context): + self.context = context + gsm = getGlobalSiteManager() + gsm.registerAdapter(_Lookup, (Interface,), IComponentLookup, '') + _SM2 = object() + siteinfo = _DummySiteInfo() + siteinfo.sm = _SM2 + context = object() + with _Monkey(hooks, siteinfo=siteinfo): + sm = self._callFUT(context) + self.assertTrue(isinstance(sm, _Lookup)) + self.assertTrue(sm.context is context) + + +class Test_adapter_hook(unittest.TestCase): + + def _callFUT(self, interface, object, name='', default=None): + from zope.component.hooks import adapter_hook + return adapter_hook(interface, object, name, default) + + def test_success(self): + from zope.interface import Interface + from zope.component import hooks + from zope.component.globalregistry import getGlobalSiteManager + class IFoo(Interface): + pass + gsm = getGlobalSiteManager() + _ADAPTER = object() + _DEFAULT = object() + _CONTEXT = object() + _called = [] + def _adapter_hook(interface, object, name, default): + _called.append((interface, object, name, default)) + return _ADAPTER + siteinfo = _DummySiteInfo() + siteinfo.adapter_hook = _adapter_hook + with _Monkey(hooks, siteinfo=siteinfo): + adapter = self._callFUT(IFoo, _CONTEXT, 'bar', _DEFAULT) + self.assertTrue(adapter is _ADAPTER) + self.assertEqual(_called, [(IFoo, _CONTEXT, 'bar', _DEFAULT)]) + + def test_hook_raises(self): + from zope.interface import Interface + from zope.component import hooks + from zope.component.globalregistry import getGlobalSiteManager + from zope.component.interfaces import ComponentLookupError + class IFoo(Interface): + pass + gsm = getGlobalSiteManager() + _DEFAULT = object() + _CONTEXT = object() + _called = [] + def _adapter_hook(interface, object, name, default): + _called.append((interface, object, name, default)) + raise ComponentLookupError('testing') + siteinfo = _DummySiteInfo() + siteinfo.adapter_hook = _adapter_hook + with _Monkey(hooks, siteinfo=siteinfo): + adapter = self._callFUT(IFoo, _CONTEXT, 'bar', _DEFAULT) + self.assertTrue(adapter is _DEFAULT) + self.assertEqual(_called, [(IFoo, _CONTEXT, 'bar', _DEFAULT)]) + + +class Test_setHooks(unittest.TestCase): + + def _callFUT(self): + from zope.component.hooks import setHooks + return setHooks() + + def test_it(self): + import zope.component._api + from zope.component import hooks + class _Hook(object): + def __init__(self): + self._hooked = None + def sethook(self, value): + self._hooked = value + adapter_hook = _Hook() + getSiteManager = _Hook() + with _Monkey(zope.component._api, + adapter_hook=adapter_hook, + getSiteManager=getSiteManager): + self._callFUT() + self.assertEqual(adapter_hook._hooked, hooks.adapter_hook) + self.assertEqual(getSiteManager._hooked, hooks.getSiteManager) + + +class Test_resetHooks(unittest.TestCase): + + def _callFUT(self): + from zope.component.hooks import resetHooks + return resetHooks() + + def test_it(self): + import zope.component._api + class _Hook(object): + def __init__(self): + self._reset = False + def reset(self): + self._reset = True + adapter_hook = _Hook() + getSiteManager = _Hook() + with _Monkey(zope.component._api, + adapter_hook=adapter_hook, + getSiteManager=getSiteManager): + self._callFUT() + self.assertTrue(adapter_hook._reset) + self.assertTrue(getSiteManager._reset) + + +_SM = object() +class _DummySiteInfo(object): + sm = _SM + site = None + +class _Monkey(object): + # context-manager for replacing module names in the scope of a test. + def __init__(self, module, **kw): + self.module = module + self.to_restore = dict([(key, getattr(module, key)) for key in kw]) + for key, value in kw.items(): + setattr(module, key, value) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + for key, value in self.to_restore.items(): + setattr(self.module, key, value) + + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(Test_read_property), + unittest.makeSuite(SiteInfoTests), + unittest.makeSuite(Test_setSite), + unittest.makeSuite(Test_getSite), + unittest.makeSuite(Test_site), + unittest.makeSuite(Test_getSiteManager), + unittest.makeSuite(Test_adapter_hook), + unittest.makeSuite(Test_setHooks), + unittest.makeSuite(Test_resetHooks), + )) + diff --git a/src/zope/component/tests/test_interface.py b/src/zope/component/tests/test_interface.py new file mode 100644 index 0000000..211974a --- /dev/null +++ b/src/zope/component/tests/test_interface.py @@ -0,0 +1,397 @@ +############################################################################## +# +# Copyright (c) 2012 Zope Foundation 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. +# +############################################################################## +"""Tests for z.c.interface +""" +import unittest + + +class Test_provideInterface(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def _callFUT(self, *args, **kw): + from zope.component.interface import provideInterface + return provideInterface(*args, **kw) + + def test_w_interface_not_IInterface(self): + self.assertRaises(TypeError, self._callFUT, 'xxx', object()) + + def test_w_iface_type_not_IInterface(self): + from zope.interface import Interface + from zope.interface.interface import InterfaceClass + class IFoo(Interface): + pass + IBar = InterfaceClass('IBar') + self.assertRaises(TypeError, self._callFUT, 'xxx', IFoo, IBar) + + def test_w_class(self): + from zope.interface.interfaces import IInterface + from zope.component.globalregistry import getGlobalSiteManager + gsm = getGlobalSiteManager() + class IBar(IInterface): + pass + class Foo(object): + pass + self._callFUT('', Foo, IBar) + self.assertFalse(IBar.providedBy(Foo)) + self.assertEqual(len(list(gsm.getUtilitiesFor(IBar))), 0) + + def test_wo_name_w_iface_type(self): + from zope.interface import Interface + from zope.interface.interfaces import IInterface + from zope.component.globalregistry import getGlobalSiteManager + gsm = getGlobalSiteManager() + class IFoo(Interface): + pass + class IBar(IInterface): + pass + self._callFUT('', IFoo, IBar) + self.assertTrue(IBar.providedBy(IFoo)) + nm = 'zope.component.tests.test_interface.IFoo' + self.assertTrue(gsm.getUtility(IBar, nm) is IFoo) + + def test_w_name_wo_ifact_type(self): + from zope.interface import Interface + from zope.interface.interfaces import IInterface + from zope.component.globalregistry import getGlobalSiteManager + gsm = getGlobalSiteManager() + class IFoo(Interface): + pass + self._callFUT('foo', IFoo) + self.assertTrue(IInterface.providedBy(IFoo)) + registered = gsm.getUtility(IInterface, name='foo') + self.assertTrue(registered is IFoo) + + +class Test_getInterface(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def _callFUT(self, *args, **kw): + from zope.component.interface import getInterface + return getInterface(*args, **kw) + + def test_miss(self): + from zope.component.interfaces import ComponentLookupError + self.assertRaises(ComponentLookupError, + self._callFUT, object(), 'nonesuch') + + def test_hit(self): + from zope.interface import Interface + from zope.interface.interfaces import IInterface + from zope.component.globalregistry import getGlobalSiteManager + gsm = getGlobalSiteManager() + class IFoo(Interface): + pass + gsm.registerUtility(IFoo, IInterface, 'foo') + self.assertTrue(self._callFUT(object(), 'foo') is IFoo) + + +class Test_queryInterface(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def _callFUT(self, *args, **kw): + from zope.component.interface import queryInterface + return queryInterface(*args, **kw) + + def test_miss(self): + _DEFAULT = object() + self.assertTrue( + self._callFUT('nonesuch', default=_DEFAULT) is _DEFAULT) + + def test_hit(self): + from zope.interface import Interface + from zope.interface.interfaces import IInterface + from zope.component.globalregistry import getGlobalSiteManager + gsm = getGlobalSiteManager() + class IFoo(Interface): + pass + gsm.registerUtility(IFoo, IInterface, 'foo') + self.assertTrue(self._callFUT('foo') is IFoo) + + +class Test_searchInterface(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def _callFUT(self, *args, **kw): + from zope.component.interface import searchInterface + return searchInterface(*args, **kw) + + def test_empty(self): + self.assertEqual(self._callFUT(object()), []) + + def test_no_search_string_no_base(self): + from zope.interface import Interface + from zope.interface.interfaces import IInterface + from zope.component.globalregistry import getGlobalSiteManager + gsm = getGlobalSiteManager() + class IFoo(Interface): + pass + gsm.registerUtility(IFoo, IInterface, 'foo') + self.assertEqual(self._callFUT(object()), [IFoo]) + + def test_w_search_string_no_base(self): + from zope.interface import Interface + from zope.interface.interfaces import IInterface + from zope.component.globalregistry import getGlobalSiteManager + gsm = getGlobalSiteManager() + class IFoo(Interface): + pass + class IBar(Interface): + pass + gsm.registerUtility(IFoo, IInterface, 'foo') + gsm.registerUtility(IBar, IInterface, 'bar') + self.assertEqual(self._callFUT(object(), 'IFoo'), [IFoo]) + + def test_no_search_string_w_base(self): + from zope.interface import Interface + from zope.interface.interfaces import IInterface + from zope.component.globalregistry import getGlobalSiteManager + gsm = getGlobalSiteManager() + class IBase(Interface): + pass + class IFoo(IBase): + pass + class IBar(Interface): + pass + gsm.registerUtility(IFoo, IInterface, 'foo') + gsm.registerUtility(IBar, IInterface, 'bar') + self.assertEqual(self._callFUT(object(), base=IBase), [IFoo]) + + +class Test_searchInterfaceIds(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def _callFUT(self, *args, **kw): + from zope.component.interface import searchInterfaceIds + return searchInterfaceIds(*args, **kw) + + def test_empty(self): + self.assertEqual(self._callFUT(object()), []) + + def test_no_search_string_no_base(self): + from zope.interface import Interface + from zope.interface.interfaces import IInterface + from zope.component.globalregistry import getGlobalSiteManager + gsm = getGlobalSiteManager() + class IFoo(Interface): + pass + gsm.registerUtility(IFoo, IInterface, 'foo') + self.assertEqual(self._callFUT(object()), ['foo']) + + def test_w_search_string_no_base(self): + from zope.interface import Interface + from zope.interface.interfaces import IInterface + from zope.component.globalregistry import getGlobalSiteManager + gsm = getGlobalSiteManager() + class IFoo(Interface): + pass + class IBar(Interface): + pass + gsm.registerUtility(IFoo, IInterface, 'foo') + gsm.registerUtility(IBar, IInterface, 'bar') + self.assertEqual(self._callFUT(object(), 'IFoo'), ['foo']) + + def test_no_search_string_w_base(self): + from zope.interface import Interface + from zope.interface.interfaces import IInterface + from zope.component.globalregistry import getGlobalSiteManager + gsm = getGlobalSiteManager() + class IBase(Interface): + pass + class IFoo(IBase): + pass + class IBar(Interface): + pass + gsm.registerUtility(IFoo, IInterface, 'foo') + gsm.registerUtility(IBar, IInterface, 'bar') + self.assertEqual(self._callFUT(object(), base=IBase), ['foo']) + + +class Test_searchInterfaceUtilities(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def _callFUT(self, *args, **kw): + from zope.component.interface import searchInterfaceUtilities + return searchInterfaceUtilities(*args, **kw) + + def test_empty(self): + self.assertEqual(self._callFUT(object()), []) + + def test_no_search_string_no_base(self): + from zope.interface import Interface + from zope.interface.interfaces import IInterface + from zope.component.globalregistry import getGlobalSiteManager + gsm = getGlobalSiteManager() + class IFoo(Interface): + pass + gsm.registerUtility(IFoo, IInterface, 'foo') + self.assertEqual(self._callFUT(object()), [('foo', IFoo)]) + + def test_w_search_string_no_base(self): + from zope.interface import Interface + from zope.interface.interfaces import IInterface + from zope.component.globalregistry import getGlobalSiteManager + gsm = getGlobalSiteManager() + class IFoo(Interface): + pass + class IBar(Interface): + pass + gsm.registerUtility(IFoo, IInterface, 'foo') + gsm.registerUtility(IBar, IInterface, 'bar') + self.assertEqual(self._callFUT(object(), 'IFoo'), [('foo', IFoo)]) + + def test_no_search_string_w_base(self): + from zope.interface import Interface + from zope.interface.interfaces import IInterface + from zope.component.globalregistry import getGlobalSiteManager + gsm = getGlobalSiteManager() + class IBase(Interface): + pass + class IFoo(IBase): + pass + class IBar(Interface): + pass + gsm.registerUtility(IFoo, IInterface, 'foo') + gsm.registerUtility(IBar, IInterface, 'bar') + self.assertEqual(self._callFUT(object(), base=IBase), [('foo', IFoo)]) + + def test_no_search_string_w_base_is_same(self): + from zope.interface import Interface + from zope.interface.interfaces import IInterface + from zope.component.globalregistry import getGlobalSiteManager + gsm = getGlobalSiteManager() + class IFoo(Interface): + pass + class IBar(Interface): + pass + gsm.registerUtility(IFoo, IInterface, 'foo') + gsm.registerUtility(IBar, IInterface, 'bar') + self.assertEqual(self._callFUT(object(), base=IFoo), [('foo', IFoo)]) + + +class Test_getInterfaceAllDocs(unittest.TestCase): + + def _callFUT(self, *args, **kw): + from zope.component.interface import getInterfaceAllDocs + return getInterfaceAllDocs(*args, **kw) + + def test_w_class(self): + class Foo(object): + """DOCSTRING""" + bar = None + def baz(self): + """BAZ""" + self.assertEqual(self._callFUT(Foo), + 'zope.component.tests.test_interface.foo\n' + + 'docstring') + + def test_w_interface_no_members(self): + from zope.interface import Interface + class IFoo(Interface): + """DOCSTRING""" + self.assertEqual(self._callFUT(IFoo), + 'zope.component.tests.test_interface.ifoo\n' + + 'docstring') + + def test_w_interface_w_members(self): + from zope.interface import Attribute + from zope.interface import Interface + class IFoo(Interface): + """DOCSTRING""" + bar = Attribute('bar', 'Do bar') + def baz(self): + """BAZ""" + self.assertEqual(self._callFUT(IFoo), + 'zope.component.tests.test_interface.ifoo\n' + + 'docstring\n' + + 'do bar\n' + + 'baz') + + +class Test_nameToInterface(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def _callFUT(self, *args, **kw): + from zope.component.interface import nameToInterface + return nameToInterface(*args, **kw) + + def test_w_None(self): + self.assertTrue(self._callFUT(object(), 'None') is None) + + def test_miss(self): + from zope.component.interfaces import ComponentLookupError + self.assertRaises(ComponentLookupError, + self._callFUT, object(), 'nonesuch') + + def test_hit(self): + from zope.interface import Interface + from zope.interface.interfaces import IInterface + from zope.component.globalregistry import getGlobalSiteManager + gsm = getGlobalSiteManager() + class IFoo(Interface): + pass + gsm.registerUtility(IFoo, IInterface, 'foo') + found = self._callFUT(object(), 'foo') + self.assertTrue(found is IFoo) + + +class Test_interfaceToName(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def _callFUT(self, *args, **kw): + from zope.component.interface import interfaceToName + return interfaceToName(*args, **kw) + + def test_w_None(self): + self.assertEqual(self._callFUT(object(), None), 'None') + + def test_w_unregistered(self): + from zope.interface import Interface + class IFoo(Interface): + pass + self.assertEqual(self._callFUT(object(), IFoo), + 'zope.component.tests.test_interface.IFoo') + + def test_w_registered(self): + from zope.interface import Interface + from zope.interface.interfaces import IInterface + from zope.component.globalregistry import getGlobalSiteManager + gsm = getGlobalSiteManager() + class IFoo(Interface): + pass + gsm.registerUtility(IFoo, IInterface, 'foo') + self.assertEqual(self._callFUT(object(), IFoo), + 'zope.component.tests.test_interface.IFoo') + + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(Test_provideInterface), + unittest.makeSuite(Test_getInterface), + unittest.makeSuite(Test_queryInterface), + unittest.makeSuite(Test_searchInterface), + unittest.makeSuite(Test_searchInterfaceIds), + unittest.makeSuite(Test_searchInterfaceUtilities), + unittest.makeSuite(Test_getInterfaceAllDocs), + unittest.makeSuite(Test_nameToInterface), + unittest.makeSuite(Test_interfaceToName), + )) + diff --git a/src/zope/component/tests/test_persistentregistry.py b/src/zope/component/tests/test_persistentregistry.py new file mode 100644 index 0000000..4cf162f --- /dev/null +++ b/src/zope/component/tests/test_persistentregistry.py @@ -0,0 +1,151 @@ +############################################################################## +# +# Copyright (c) 2012 Zope Foundation 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. +# +############################################################################## +"""Tests for z.c.hooks +""" +import unittest + + +class PersistentAdapterRegistryTests(unittest.TestCase): + + def _getTargetClass(self): + from zope.component.persistentregistry import PersistentAdapterRegistry + return PersistentAdapterRegistry + + def _makeOne(self, *args, **kw): + return self._getTargetClass()(*args, **kw) + + def _makeCache(self, jar): + # Borrowed from persistent.tests.test_pyPersistence. + + class _Cache(object): + def __init__(self, jar): + self._jar = jar + self._mru = [] + def mru(self, oid): + self._mru.append(oid) + def new_ghost(self, oid, obj): + obj._p_jar = self._jar + obj._p_oid = oid + + return _Cache(jar) + + def _makeJar(self): + # Borrowed from persistent.tests.test_pyPersistence. + from zope.interface import implements + from persistent.interfaces import IPersistentDataManager + + class _Jar(object): + implements(IPersistentDataManager) + def __init__(self): + self._loaded = [] + self._registered = [] + def setstate(self, obj): + self._loaded.append(obj._p_oid) + def register(self, obj): + self._registered.append(obj._p_oid) + + jar = _Jar() + jar._cache = self._makeCache(jar) + return jar + + def _makeOneWithJar(self, dirty=False): + # Borrowed from persistent.tests.test_pyPersistence. + OID = _makeOctets('\x01' * 8) + inst = self._makeOne() + jar = self._makeJar() + jar._cache.new_ghost(OID, inst) # assigns _p_jar, _p_oid + return inst, jar, OID + + def test_changed_original_is_not_us(self): + registry, jar, OID = self._makeOneWithJar() + self.assertEqual(registry._generation, 1) + registry.changed(object()) + # 'originally_changed' is not us, but we are still dirty because + # '_generation' gets bumped. + self.assertEqual(registry._p_changed, True) + # base class gets called + self.assertEqual(registry._generation, 2) + + def test_changed_original_is_us(self): + registry, jar, OID = self._makeOneWithJar() + self.assertEqual(registry._generation, 1) + registry.changed(registry) + # 'originally_changed' is not us, so not dirty + self.assertEqual(registry._p_changed, True) + # base class gets called + self.assertEqual(registry._generation, 2) + + def test___getstate___simple(self): + registry, jar, OID = self._makeOneWithJar() + state = registry.__getstate__() + self.assertEqual(state['__bases__'], ()) + self.assertEqual(state['_generation'], 1) + self.assertEqual(state['_provided'], {}) + self.assertEqual(state['_adapters'], []) + self.assertEqual(state['_subscribers'], []) + self.assertEqual(state['ro'], [registry]) + + def test___getstate___skips_delegated_names(self): + registry, jar, OID = self._makeOneWithJar() + registry.names = lambda *args: ['a', 'b', 'c'] + self.assertFalse('names' in registry.__getstate__()) + + def test___setstate___rebuilds__v_lookup(self): + registry, jar, OID = self._makeOneWithJar() + state = registry.__getstate__() + self.assertTrue('_v_lookup' in registry.__dict__) + registry._p_changed = None # clears volatile '_v_lookup' + self.assertFalse('_v_lookup' in registry.__dict__) + registry.__setstate__(state) + self.assertTrue('_v_lookup' in registry.__dict__) + + +class PersistentComponentsTests(unittest.TestCase): + + def _getTargetClass(self): + from zope.component.persistentregistry import PersistentComponents + return PersistentComponents + + def _makeOne(self, *args, **kw): + return self._getTargetClass()(*args, **kw) + + def test_ctor_initializes_registries_and_registrations(self): + from persistent.mapping import PersistentMapping + from persistent.list import PersistentList + from zope.component.persistentregistry import PersistentAdapterRegistry + registry = self._makeOne() + self.assertTrue(isinstance(registry.adapters, + PersistentAdapterRegistry)) + self.assertTrue(isinstance(registry.utilities, + PersistentAdapterRegistry)) + self.assertTrue(isinstance(registry._adapter_registrations, + PersistentMapping)) + self.assertTrue(isinstance(registry._utility_registrations, + PersistentMapping)) + self.assertTrue(isinstance(registry._subscription_registrations, + PersistentList)) + self.assertTrue(isinstance(registry._handler_registrations, + PersistentList)) + +def _makeOctets(s): + import sys + if sys.version_info < (3,): + return bytes(s) + return bytes(s, 'ascii') #pragma NO COVERAGE + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(PersistentAdapterRegistryTests), + unittest.makeSuite(PersistentComponentsTests), + )) diff --git a/src/zope/component/tests/test_registry.py b/src/zope/component/tests/test_registry.py new file mode 100644 index 0000000..0ba6bd2 --- /dev/null +++ b/src/zope/component/tests/test_registry.py @@ -0,0 +1,131 @@ +############################################################################## +# +# Copyright (c) 2012 Zope Foundation 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. +# +############################################################################## +"""Tests for z.c.registry +""" +import unittest + + +class Test_dispatchUtilityRegistrationEvent(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def _callFUT(self, *args, **kw): + from zope.component.registry import dispatchUtilityRegistrationEvent + return dispatchUtilityRegistrationEvent(*args, **kw) + + def test_it(self): + from zope.component import registry + class _Registration(object): + component = object() + _EVENT = object() + _handled = [] + def _handle(*args): + _handled.append(args) + with _Monkey(registry, handle=_handle): + self._callFUT(_Registration(), _EVENT) + self.assertEqual(_handled, [(_Registration.component, _EVENT)]) + + +class Test_dispatchAdapterRegistrationEvent(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def _callFUT(self, *args, **kw): + from zope.component.registry import dispatchAdapterRegistrationEvent + return dispatchAdapterRegistrationEvent(*args, **kw) + + def test_it(self): + from zope.component import registry + class _Registration(object): + def factory(self, *args, **kw): + pass + _registration = _Registration() + _EVENT = object() + _handled = [] + def _handle(*args): + _handled.append(args) + with _Monkey(registry, handle=_handle): + self._callFUT(_registration, _EVENT) + self.assertEqual(_handled, [(_registration.factory, _EVENT)]) + + +class Test_dispatchSubscriptionAdapterRegistrationEvent(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def _callFUT(self, *args, **kw): + from zope.component.registry \ + import dispatchSubscriptionAdapterRegistrationEvent + return dispatchSubscriptionAdapterRegistrationEvent(*args, **kw) + + def test_it(self): + from zope.component import registry + class _Registration(object): + def factory(self, *args, **kw): + pass + _registration = _Registration() + _EVENT = object() + _handled = [] + def _handle(*args): + _handled.append(args) + with _Monkey(registry, handle=_handle): + self._callFUT(_registration, _EVENT) + self.assertEqual(_handled, [(_registration.factory, _EVENT)]) + + +class Test_dispatchHandlerRegistrationEvent(unittest.TestCase): + + from zope.component.testing import setUp, tearDown + + def _callFUT(self, *args, **kw): + from zope.component.registry import dispatchHandlerRegistrationEvent + return dispatchHandlerRegistrationEvent(*args, **kw) + + def test_it(self): + from zope.component import registry + class _Registration(object): + def handler(self, *args, **kw): + pass + _registration = _Registration() + _EVENT = object() + _handled = [] + def _handle(*args): + _handled.append(args) + with _Monkey(registry, handle=_handle): + self._callFUT(_registration, _EVENT) + self.assertEqual(_handled, [(_registration.handler, _EVENT)]) + + +class _Monkey(object): + # context-manager for replacing module names in the scope of a test. + def __init__(self, module, **kw): + self.module = module + self.to_restore = dict([(key, getattr(module, key)) for key in kw]) + for key, value in kw.items(): + setattr(module, key, value) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + for key, value in self.to_restore.items(): + setattr(self.module, key, value) + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(Test_dispatchUtilityRegistrationEvent), + unittest.makeSuite(Test_dispatchAdapterRegistrationEvent), + unittest.makeSuite(Test_dispatchSubscriptionAdapterRegistrationEvent), + unittest.makeSuite(Test_dispatchHandlerRegistrationEvent), + )) diff --git a/src/zope/component/tests/test_security.py b/src/zope/component/tests/test_security.py new file mode 100644 index 0000000..df85129 --- /dev/null +++ b/src/zope/component/tests/test_security.py @@ -0,0 +1,249 @@ +############################################################################## +# +# Copyright (c) 2012 Zope Foundation 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. +# +############################################################################## +"""Pure-Python hookable tests +""" +import unittest + + +class PermissionProxyTests(unittest.TestCase): + + def _getTargetClass(self): + from zope.component.security import PermissionProxy + return PermissionProxy + + def _makeOne(self, wrapped): + return self._getTargetClass()(wrapped) + + def test_proxy_delegates___provided_by__(self): + from zope.interface import Interface + from zope.interface import implementer + from zope.interface import providedBy + class IFoo(Interface): + pass + @implementer(IFoo) + class Foo(object): + pass + foo = Foo() + proxy = self._makeOne(foo) + self.assertEqual(providedBy(proxy), providedBy(foo)) + + +class Test__checker(unittest.TestCase): + + def _callFUT(self, *args, **kw): + from zope.component.security import _checker + return _checker(*args, **kw) + + def test_no_allowed_attributes_no_allowed_interfaces(self): + from zope.security.checker import CheckerPublic + checker = self._callFUT(object(), 'zope.Public', (), ()) + self.assertEqual(checker.get_permissions, {'__call__': CheckerPublic}) + self.assertFalse(checker.set_permissions) + + def test_w_allowed_interfaces(self): + from zope.interface import Interface + class IFoo(Interface): + def bar(self): + pass + def baz(self): + pass + class ISpam(Interface): + def qux(self): + pass + checker = self._callFUT(object(), 'testing', (IFoo, ISpam), ()) + self.assertEqual(checker.get_permissions, + {'bar': 'testing', 'baz': 'testing', 'qux': 'testing'}) + self.assertFalse(checker.set_permissions) + + def test_w_allowed_attributes(self): + checker = self._callFUT(object(), 'testing', (), ('foo', 'bar')) + self.assertEqual(checker.get_permissions, + {'foo': 'testing', 'bar': 'testing'}) + self.assertFalse(checker.set_permissions) + + +class Test_proxify(unittest.TestCase): + + def _callFUT(self, *args, **kw): + from zope.component.security import proxify + return proxify(*args, **kw) + + def _makeContext(self): + class _Context(object): + def bar(self): + pass + return _Context() + + def test_no_checker_no_provides(self): + ctx = self._makeContext() + self.assertRaises(ValueError, self._callFUT, ctx, permission='testing') + + def test_no_checker_no_permission(self): + from zope.interface import Interface + class IFoo(Interface): + def bar(self): + pass + ctx = self._makeContext() + self.assertRaises(ValueError, self._callFUT, ctx, provides=IFoo) + + def test_no_checker_w_provides_and_permission_public(self): + from zope.interface import Interface + from zope.security.checker import CheckerPublic + from zope.proxy import getProxiedObject + class IFoo(Interface): + def bar(self): + pass + ctx = self._makeContext() + proxy = self._callFUT(ctx, provides=IFoo, permission='zope.Public') + self.assertTrue(getProxiedObject(proxy) is ctx) + checker = proxy.__Security_checker__ + self.assertEqual(checker.get_permissions, {'bar': CheckerPublic}) + self.assertFalse(checker.set_permissions) + + def test_no_checker_w_provides_and_permission_protected(self): + from zope.interface import Interface + from zope.proxy import getProxiedObject + class IFoo(Interface): + def bar(self): + pass + ctx = self._makeContext() + proxy = self._callFUT(ctx, provides=IFoo, permission='testing') + self.assertTrue(getProxiedObject(proxy) is ctx) + checker = proxy.__Security_checker__ + self.assertEqual(checker.get_permissions, {'bar': 'testing'}) + self.assertFalse(checker.set_permissions) + + def test_w_checker(self): + from zope.proxy import getProxiedObject + _CHECKER = object() + ctx = self._makeContext() + proxy = self._callFUT(ctx, _CHECKER) + self.assertTrue(getProxiedObject(proxy) is ctx) + self.assertTrue(proxy.__Security_checker__ is _CHECKER) + + +class Test_protectedFactory(unittest.TestCase): + + def _callFUT(self, *args, **kw): + from zope.component.security import protectedFactory + return protectedFactory(*args, **kw) + + def test_public_not_already_proxied(self): + from zope.interface import Interface + from zope.security.checker import CheckerPublic + class IFoo(Interface): + def bar(self): + pass + class _Factory(object): + def bar(self): + pass + protected = self._callFUT(_Factory, IFoo, 'zope.Public') + self.assertTrue(protected.factory is _Factory) + foo = protected() + self.assertEqual(foo.__Security_checker__.get_permissions, + {'bar': CheckerPublic}) + + def test_nonpublic_already_proxied(self): + from zope.interface import Interface + from zope.security.proxy import getTestProxyItems + class IFoo(Interface): + def bar(self): + pass + class _Factory(object): + __slots__ = ('one',) + def bar(self): + pass + protected = self._callFUT(_Factory, IFoo, 'testing') + self.assertTrue(protected.factory is _Factory) + foo = protected() + self.assertEqual(getTestProxyItems(foo), [('bar', 'testing')]) + + +class Test_securityAdapterFactory(unittest.TestCase): + + def _callFUT(self, *args, **kw): + from zope.component.security import securityAdapterFactory + return securityAdapterFactory(*args, **kw) + + def test_no_permission_untrusted_no_location(self): + class _Factory(object): + pass + self.assertTrue(self._callFUT(_Factory, None, False, False) + is _Factory) + + def test_public_untrusted_no_location(self): + class _Factory(object): + pass + self.assertTrue(self._callFUT(_Factory, 'zope.Public', False, False) + is _Factory) + + def test_CheckerPublic_untrusted_no_location(self): + from zope.security.checker import CheckerPublic + class _Factory(object): + pass + self.assertTrue(self._callFUT(_Factory, CheckerPublic, False, False) + is _Factory) + + def test_protected_untrusted_no_location(self): + from zope.security.adapter import LocatingUntrustedAdapterFactory + class _Factory(object): + pass + proxy = self._callFUT(_Factory, 'testing', False, False) + self.assertTrue(isinstance(proxy, LocatingUntrustedAdapterFactory)) + + def test_no_permission_trusted_no_location(self): + from zope.security.adapter import LocatingTrustedAdapterFactory + class _Factory(object): + pass + proxy = self._callFUT(_Factory, None, False, True) + self.assertTrue(isinstance(proxy, LocatingTrustedAdapterFactory)) + + def test_public_trusted_no_location(self): + from zope.security.adapter import LocatingTrustedAdapterFactory + class _Factory(object): + pass + proxy = self._callFUT(_Factory, 'zope.Public', False, True) + self.assertTrue(isinstance(proxy, LocatingTrustedAdapterFactory)) + + def test_CheckerPublic_trusted_no_location(self): + from zope.security.adapter import LocatingTrustedAdapterFactory + from zope.security.checker import CheckerPublic + class _Factory(object): + pass + proxy = self._callFUT(_Factory, CheckerPublic, False, True) + self.assertTrue(isinstance(proxy, LocatingTrustedAdapterFactory)) + + def test_protected_trusted_no_location(self): + from zope.security.adapter import LocatingTrustedAdapterFactory + class _Factory(object): + pass + proxy = self._callFUT(_Factory, 'testing', False, True) + self.assertTrue(isinstance(proxy, LocatingTrustedAdapterFactory)) + + def test_protected_trusted_w_location(self): + from zope.security.adapter import LocatingTrustedAdapterFactory + class _Factory(object): + pass + proxy = self._callFUT(_Factory, 'testing', True, True) + self.assertTrue(isinstance(proxy, LocatingTrustedAdapterFactory)) + + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(PermissionProxyTests), + unittest.makeSuite(Test__checker), + unittest.makeSuite(Test_proxify), + unittest.makeSuite(Test_protectedFactory), + unittest.makeSuite(Test_securityAdapterFactory), + )) diff --git a/src/zope/component/tests/test_standalone.py b/src/zope/component/tests/test_standalone.py new file mode 100644 index 0000000..76ee068 --- /dev/null +++ b/src/zope/component/tests/test_standalone.py @@ -0,0 +1,52 @@ +############################################################################## +# +# Copyright (c) 2001, 2002, 2009 Zope Foundation 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. +# +############################################################################## +"""Component Architecture Tests +""" +import unittest + + +class StandaloneTests(unittest.TestCase): + def testStandalone(self): + # See: https://bugs.launchpad.net/zope3/+bug/98401 + import subprocess + import sys + import os + import pickle + + executable = os.path.abspath(sys.executable) + where = os.path.dirname(os.path.dirname(__file__)) + program = os.path.join(where, 'standalonetests.py') + process = subprocess.Popen([executable, program], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + stdin=subprocess.PIPE) + pickle.dump(sys.path, process.stdin) + process.stdin.close() + + try: + rc = process.wait() + except OSError as e: + if e.errno != 4: # MacIntel raises apparently unimportant EINTR? + raise # TODO verify sanity of a pass on EINTR :-/ + if rc != 0: + output = process.stdout.read() + sys.stderr.write('#' * 80 + '\n') + sys.stdout.write(output) + sys.stderr.write('#' * 80 + '\n') + self.fail('Output code: %d' % rc) + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(StandaloneTests), + )) diff --git a/src/zope/component/tests/test_zcml.py b/src/zope/component/tests/test_zcml.py new file mode 100644 index 0000000..f64e822 --- /dev/null +++ b/src/zope/component/tests/test_zcml.py @@ -0,0 +1,1141 @@ +############################################################################## +# +# Copyright (c) 2012 Zope Foundation 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. +# +############################################################################## +"""Tests for ZCML directives. +""" +import unittest + + +def skipIfNoSecurity(testfunc): + try: + import zope.security + except ImportError: + return lambda self: None + return testfunc + +class Test_handler(unittest.TestCase): + + def _callFUT(self, *args, **kw): + from zope.component.zcml import handler + return handler(*args, **kw) + + def test_uses_configured_site_manager(self): + from zope.interface.registry import Components + from zope.component import getSiteManager + from zope.component.testfiles.components import comp, IApp + from zope.component._compat import _BLANK + + registry = Components() + def dummy(context=None): + return registry + getSiteManager.sethook(dummy) + + try: + self._callFUT('registerUtility', comp, IApp, _BLANK) + self.assertTrue(registry.getUtility(IApp) is comp) + finally: + getSiteManager.reset() + + +class Test__rolledUpFactory(unittest.TestCase): + + def _callFUT(self, *args, **kw): + from zope.component.zcml import _rolledUpFactory + return _rolledUpFactory(*args, **kw) + + def test_with_one(self): + _OBJ = object() + _CREATED = object() + def _factory(obj): + return _CREATED + rolled = self._callFUT([_factory]) + self.assertTrue(rolled.factory is _factory) + self.assertTrue(rolled(_OBJ) is _CREATED) + + def test_with_multiple(self): + _OBJ = object() + _CREATED1 = object() + _CREATED2 = object() + _CREATED3 = object() + def _factory1(obj): + return _CREATED1 + def _factory2(obj): + return _CREATED2 + def _factory3(obj): + return _CREATED3 + rolled = self._callFUT([_factory1, _factory2, _factory3]) + self.assertTrue(rolled.factory is _factory1) + self.assertTrue(rolled(_OBJ) is _CREATED3) + + +class Test_adapter(unittest.TestCase): + + def _callFUT(self, *args, **kw): + from zope.component.zcml import adapter + return adapter(*args, **kw) + + def test_empty_factory(self): + from zope.interface import Interface + from zope.component.zcml import ComponentConfigurationError + class IFoo(Interface): + pass + _cfg_ctx = _makeConfigContext() + self.assertRaises(ComponentConfigurationError, + self._callFUT, _cfg_ctx, [], [Interface], IFoo) + + def test_multiple_factory_multiple_for_(self): + from zope.interface import Interface + from zope.component.zcml import ComponentConfigurationError + class IFoo(Interface): + pass + class IBar(Interface): + pass + class Foo(object): + pass + class Bar(object): + pass + _cfg_ctx = _makeConfigContext() + self.assertRaises(ComponentConfigurationError, + self._callFUT, _cfg_ctx, [Foo, Bar], + [Interface, IBar], IFoo) + + def test_no_for__factory_not_adapts(self): + #@adapter(IFoo) + class _Factory(object): + def __init__(self, context): + self.context = context + _cfg_ctx = _makeConfigContext() + self.assertRaises(TypeError, self._callFUT, _cfg_ctx, [_Factory]) + + def test_no_for__factory_adapts_no_provides_factory_not_implements(self): + from zope.interface import Interface + from zope.component._declaration import adapter + @adapter(Interface) + class _Factory(object): + def __init__(self, context): + self.context = context + _cfg_ctx = _makeConfigContext() + self.assertRaises(TypeError, self._callFUT, _cfg_ctx, [_Factory]) + + def test_multiple_factory_single_for__w_name(self): + from zope.interface import Interface + from zope.component.interface import provideInterface + from zope.component.zcml import handler + class IFoo(Interface): + pass + class Foo(object): + pass + class Bar(object): + pass + _cfg_ctx = _makeConfigContext() + self._callFUT(_cfg_ctx, [Foo, Bar], IFoo, [Interface], name='test') + self.assertEqual(len(_cfg_ctx._actions), 3) + self.assertEqual(_cfg_ctx._actions[0][0], ()) + # Register the adapter + action =_cfg_ctx._actions[0][1] + self.assertEqual(action['callable'], handler) + self.assertEqual(action['discriminator'], + ('adapter', (Interface,), IFoo, 'test')) + self.assertEqual(action['args'][0], 'registerAdapter') + self.assertEqual(action['args'][1].factory, Foo) #rolled up + self.assertEqual(action['args'][2], (Interface,)) + self.assertEqual(action['args'][3], IFoo) + self.assertEqual(action['args'][4], 'test') + self.assertEqual(action['args'][5], 'TESTING') + # Register the provided interface + self.assertEqual(_cfg_ctx._actions[1][0], ()) + action =_cfg_ctx._actions[1][1] + self.assertEqual(action['callable'], provideInterface) + self.assertEqual(action['discriminator'], None) + self.assertEqual(action['args'], ('', IFoo)) + # Register the required interface(s) + self.assertEqual(_cfg_ctx._actions[2][0], ()) + action =_cfg_ctx._actions[2][1] + self.assertEqual(action['callable'], provideInterface) + self.assertEqual(action['discriminator'], None) + self.assertEqual(action['args'], ('', Interface)) + + @skipIfNoSecurity + def test_single_factory_single_for_w_permission(self): + from zope.interface import Interface + from zope.security.adapter import LocatingUntrustedAdapterFactory + from zope.component.zcml import handler + class IFoo(Interface): + pass + class Foo(object): + pass + _cfg_ctx = _makeConfigContext() + self._callFUT(_cfg_ctx, [Foo], IFoo, [Interface], permission='testing') + self.assertEqual(len(_cfg_ctx._actions), 3) + self.assertEqual(_cfg_ctx._actions[0][0], ()) + # Register the adapter + action =_cfg_ctx._actions[0][1] + self.assertEqual(action['callable'], handler) + self.assertEqual(action['discriminator'], + ('adapter', (Interface,), IFoo, '')) + self.assertEqual(action['args'][0], 'registerAdapter') + factory_proxy = action['args'][1] + # Foo wraped by 'protected_factory' plus + # 'LocatingUntrustedAdapterFactory' + self.assertTrue(isinstance(factory_proxy, + LocatingUntrustedAdapterFactory)) + self.assertTrue(factory_proxy.factory.factory is Foo) + self.assertEqual(action['args'][2], (Interface,)) + self.assertEqual(action['args'][3], IFoo) + self.assertEqual(action['args'][4], '') + self.assertEqual(action['args'][5], 'TESTING') + + @skipIfNoSecurity + def test_single_factory_single_for_w_locate_no_permission(self): + from zope.interface import Interface + from zope.security.adapter import LocatingUntrustedAdapterFactory + from zope.component.zcml import handler + class IFoo(Interface): + pass + class Foo(object): + pass + _cfg_ctx = _makeConfigContext() + self._callFUT(_cfg_ctx, [Foo], IFoo, [Interface], locate=True) + self.assertEqual(len(_cfg_ctx._actions), 3) + self.assertEqual(_cfg_ctx._actions[0][0], ()) + # Register the adapter + action =_cfg_ctx._actions[0][1] + self.assertEqual(action['callable'], handler) + self.assertEqual(action['discriminator'], + ('adapter', (Interface,), IFoo, '')) + self.assertEqual(action['args'][0], 'registerAdapter') + factory_proxy = action['args'][1] + # Foo wraped by 'LocatingUntrustedAdapterFactory' + self.assertTrue(isinstance(factory_proxy, + LocatingUntrustedAdapterFactory)) + self.assertTrue(factory_proxy.factory is Foo) + self.assertEqual(action['args'][2], (Interface,)) + self.assertEqual(action['args'][3], IFoo) + self.assertEqual(action['args'][4], '') + self.assertEqual(action['args'][5], 'TESTING') + + @skipIfNoSecurity + def test_single_factory_single_for_w_trusted_no_permission(self): + from zope.interface import Interface + from zope.security.adapter import TrustedAdapterFactory + from zope.component.zcml import handler + class IFoo(Interface): + pass + class Foo(object): + pass + _cfg_ctx = _makeConfigContext() + self._callFUT(_cfg_ctx, [Foo], IFoo, [Interface], trusted=True) + self.assertEqual(len(_cfg_ctx._actions), 3) + self.assertEqual(_cfg_ctx._actions[0][0], ()) + # Register the adapter + action =_cfg_ctx._actions[0][1] + self.assertEqual(action['callable'], handler) + self.assertEqual(action['discriminator'], + ('adapter', (Interface,), IFoo, '')) + self.assertEqual(action['args'][0], 'registerAdapter') + factory_proxy = action['args'][1] + # Foo wraped by 'LocatingUntrustedAdapterFactory' + self.assertTrue(isinstance(factory_proxy, TrustedAdapterFactory)) + self.assertTrue(factory_proxy.factory is Foo) + self.assertEqual(action['args'][2], (Interface,)) + self.assertEqual(action['args'][3], IFoo) + self.assertEqual(action['args'][4], '') + self.assertEqual(action['args'][5], 'TESTING') + + def test_no_for__no_provides_factory_adapts_factory_implements(self): + from zope.interface import Interface + from zope.interface import implementer + from zope.component._declaration import adapter + from zope.component.zcml import handler + class IFoo(Interface): + pass + @adapter(Interface) + @implementer(IFoo) + class _Factory(object): + def __init__(self, context): + self.context = context + _cfg_ctx = _makeConfigContext() + self._callFUT(_cfg_ctx, [_Factory]) + self.assertEqual(len(_cfg_ctx._actions), 3) + self.assertEqual(_cfg_ctx._actions[0][0], ()) + # Register the adapter + action =_cfg_ctx._actions[0][1] + self.assertEqual(action['callable'], handler) + self.assertEqual(action['discriminator'], + ('adapter', (Interface,), IFoo, '')) + self.assertEqual(action['args'], + ('registerAdapter', _Factory, (Interface,), IFoo, + '', 'TESTING')) + + +class Test_subscriber(unittest.TestCase): + + def _callFUT(self, *args, **kw): + from zope.component.zcml import subscriber + return subscriber(*args, **kw) + + def test_no_factory_no_handler(self): + from zope.interface import Interface + _cfg_ctx = _makeConfigContext() + self.assertRaises(TypeError, + self._callFUT, _cfg_ctx, (Interface,)) + + def test_no_factory_w_handler_w_provides(self): + from zope.interface import Interface + class IFoo(Interface): + pass + def _handler(*args): + pass + _cfg_ctx = _makeConfigContext() + self.assertRaises(TypeError, + self._callFUT, _cfg_ctx, (Interface,), + handler=_handler, provides=IFoo) + + def test_w_factory_w_handler(self): + from zope.interface import Interface + class Foo(object): + pass + def _handler(*args): + pass + _cfg_ctx = _makeConfigContext() + self.assertRaises(TypeError, + self._callFUT, _cfg_ctx, (Interface,), Foo, + handler=_handler) + + def test_w_factory_no_provides(self): + from zope.interface import Interface + class Foo(object): + pass + _cfg_ctx = _makeConfigContext() + self.assertRaises(TypeError, + self._callFUT, _cfg_ctx, (Interface,), Foo) + + def test_w_factory_w_provides_no_for_factory_wo_adapter(self): + from zope.interface import Interface + class IFoo(Interface): + pass + class Foo(object): + pass + _cfg_ctx = _makeConfigContext() + self.assertRaises(TypeError, + self._callFUT, _cfg_ctx, + factory=Foo, provides=IFoo) + + def test_no_factory_w_handler_no_provides(self): + from zope.interface import Interface + from zope.component.interface import provideInterface + from zope.component.zcml import handler + def _handler(*args): + pass + _cfg_ctx = _makeConfigContext() + self._callFUT(_cfg_ctx, (Interface,), handler=_handler) + self.assertEqual(len(_cfg_ctx._actions), 2) + self.assertEqual(_cfg_ctx._actions[0][0], ()) + # Register the adapter + action =_cfg_ctx._actions[0][1] + self.assertEqual(action['callable'], handler) + self.assertEqual(action['discriminator'], None) + self.assertEqual(action['args'][0], 'registerHandler') + self.assertEqual(action['args'][1], _handler) + self.assertEqual(action['args'][2], (Interface,)) + self.assertEqual(action['args'][3], '') + self.assertEqual(action['args'][4], 'TESTING') + # Register the required interface(s) + self.assertEqual(_cfg_ctx._actions[1][0], ()) + action =_cfg_ctx._actions[1][1] + self.assertEqual(action['callable'], provideInterface) + self.assertEqual(action['discriminator'], None) + self.assertEqual(action['args'], ('', Interface)) + + def test_w_factory_w_provides(self): + from zope.interface import Interface + from zope.component.interface import provideInterface + from zope.component.zcml import handler + class IFoo(Interface): + pass + class Foo(object): + pass + def _handler(*args): + pass + _cfg_ctx = _makeConfigContext() + self._callFUT(_cfg_ctx, (Interface,), Foo, provides=IFoo) + self.assertEqual(len(_cfg_ctx._actions), 3) + self.assertEqual(_cfg_ctx._actions[0][0], ()) + # Register the adapter + action =_cfg_ctx._actions[0][1] + self.assertEqual(action['callable'], handler) + self.assertEqual(action['discriminator'], None) + self.assertEqual(action['args'][0], 'registerSubscriptionAdapter') + self.assertEqual(action['args'][1], Foo) + self.assertEqual(action['args'][2], (Interface,)) + self.assertEqual(action['args'][3], IFoo) + self.assertEqual(action['args'][4], '') + self.assertEqual(action['args'][5], 'TESTING') + # Register the provided interface + self.assertEqual(_cfg_ctx._actions[1][0], ()) + action =_cfg_ctx._actions[1][1] + self.assertEqual(action['callable'], provideInterface) + self.assertEqual(action['discriminator'], None) + self.assertEqual(action['args'], ('', IFoo)) + # Register the required interface(s) + self.assertEqual(_cfg_ctx._actions[2][0], ()) + action =_cfg_ctx._actions[2][1] + self.assertEqual(action['callable'], provideInterface) + self.assertEqual(action['discriminator'], None) + self.assertEqual(action['args'], ('', Interface)) + + @skipIfNoSecurity + def test_w_factory_w_provides_w_permission(self): + from zope.interface import Interface + from zope.security.adapter import LocatingUntrustedAdapterFactory + from zope.component.interface import provideInterface + from zope.component.zcml import handler + class IFoo(Interface): + pass + class Foo(object): + pass + _cfg_ctx = _makeConfigContext() + self._callFUT(_cfg_ctx, (Interface,), Foo, + provides=IFoo, permission='testing') + self.assertEqual(len(_cfg_ctx._actions), 3) + self.assertEqual(_cfg_ctx._actions[0][0], ()) + # Register the adapter + action =_cfg_ctx._actions[0][1] + self.assertEqual(action['callable'], handler) + self.assertEqual(action['discriminator'], None) + self.assertEqual(action['args'][0], 'registerSubscriptionAdapter') + factory_proxy = action['args'][1] + # Foo wraped by 'protected_factory' plus + # 'LocatingUntrustedAdapterFactory' + self.assertTrue(isinstance(factory_proxy, + LocatingUntrustedAdapterFactory)) + self.assertTrue(factory_proxy.factory.factory is Foo) + self.assertEqual(action['args'][2], (Interface,)) + self.assertEqual(action['args'][3], IFoo) + self.assertEqual(action['args'][4], '') + self.assertEqual(action['args'][5], 'TESTING') + # Register the provided interface + self.assertEqual(_cfg_ctx._actions[1][0], ()) + action =_cfg_ctx._actions[1][1] + self.assertEqual(action['callable'], provideInterface) + self.assertEqual(action['discriminator'], None) + self.assertEqual(action['args'], ('', IFoo)) + # Register the required interface(s) + self.assertEqual(_cfg_ctx._actions[2][0], ()) + action =_cfg_ctx._actions[2][1] + self.assertEqual(action['callable'], provideInterface) + self.assertEqual(action['discriminator'], None) + self.assertEqual(action['args'], ('', Interface)) + + @skipIfNoSecurity + def test_w_factory_w_provides_wo_permission_w_locate(self): + from zope.interface import Interface + from zope.security.adapter import LocatingUntrustedAdapterFactory + from zope.component.interface import provideInterface + from zope.component.zcml import handler + class IFoo(Interface): + pass + class Foo(object): + pass + _cfg_ctx = _makeConfigContext() + self._callFUT(_cfg_ctx, (Interface,), Foo, provides=IFoo, locate=True) + self.assertEqual(len(_cfg_ctx._actions), 3) + self.assertEqual(_cfg_ctx._actions[0][0], ()) + # Register the adapter + action =_cfg_ctx._actions[0][1] + self.assertEqual(action['callable'], handler) + self.assertEqual(action['discriminator'], None) + self.assertEqual(action['args'][0], 'registerSubscriptionAdapter') + factory_proxy = action['args'][1] + # Foo wraped by 'protected_factory' plus + # 'LocatingUntrustedAdapterFactory' + self.assertTrue(isinstance(factory_proxy, + LocatingUntrustedAdapterFactory)) + self.assertTrue(factory_proxy.factory is Foo) + self.assertEqual(action['args'][2], (Interface,)) + self.assertEqual(action['args'][3], IFoo) + self.assertEqual(action['args'][4], '') + self.assertEqual(action['args'][5], 'TESTING') + # Register the provided interface + self.assertEqual(_cfg_ctx._actions[1][0], ()) + action =_cfg_ctx._actions[1][1] + self.assertEqual(action['callable'], provideInterface) + self.assertEqual(action['discriminator'], None) + self.assertEqual(action['args'], ('', IFoo)) + # Register the required interface(s) + self.assertEqual(_cfg_ctx._actions[2][0], ()) + action =_cfg_ctx._actions[2][1] + self.assertEqual(action['callable'], provideInterface) + self.assertEqual(action['discriminator'], None) + self.assertEqual(action['args'], ('', Interface)) + + @skipIfNoSecurity + def test_w_factory_w_provides_wo_permission_w_trusted(self): + from zope.interface import Interface + from zope.security.adapter import TrustedAdapterFactory + from zope.component.interface import provideInterface + from zope.component.zcml import handler + class IFoo(Interface): + pass + class Foo(object): + pass + _cfg_ctx = _makeConfigContext() + self._callFUT(_cfg_ctx, (Interface,), Foo, provides=IFoo, trusted=True) + self.assertEqual(len(_cfg_ctx._actions), 3) + self.assertEqual(_cfg_ctx._actions[0][0], ()) + # Register the adapter + action =_cfg_ctx._actions[0][1] + self.assertEqual(action['callable'], handler) + self.assertEqual(action['discriminator'], None) + self.assertEqual(action['args'][0], 'registerSubscriptionAdapter') + factory_proxy = action['args'][1] + # Foo wraped by 'protected_factory' plus + # 'TrustedAdapterFactory' + self.assertTrue(isinstance(factory_proxy, + TrustedAdapterFactory)) + self.assertTrue(factory_proxy.factory is Foo) + self.assertEqual(action['args'][2], (Interface,)) + self.assertEqual(action['args'][3], IFoo) + self.assertEqual(action['args'][4], '') + self.assertEqual(action['args'][5], 'TESTING') + # Register the provided interface + self.assertEqual(_cfg_ctx._actions[1][0], ()) + action =_cfg_ctx._actions[1][1] + self.assertEqual(action['callable'], provideInterface) + self.assertEqual(action['discriminator'], None) + self.assertEqual(action['args'], ('', IFoo)) + # Register the required interface(s) + self.assertEqual(_cfg_ctx._actions[2][0], ()) + action =_cfg_ctx._actions[2][1] + self.assertEqual(action['callable'], provideInterface) + self.assertEqual(action['discriminator'], None) + self.assertEqual(action['args'], ('', Interface)) + + +class Test_utility(unittest.TestCase): + + def _callFUT(self, *args, **kw): + from zope.component.zcml import utility + return utility(*args, **kw) + + def test_w_factory_w_component(self): + class _Factory(object): + pass + _COMPONENT = object + _cfg_ctx = _makeConfigContext() + self.assertRaises(TypeError, self._callFUT, _cfg_ctx, + factory=_Factory, + component=_COMPONENT) + + def test_w_factory_wo_provides_factory_no_implements(self): + class _Factory(object): + pass + _cfg_ctx = _makeConfigContext() + self.assertRaises(TypeError, + self._callFUT, _cfg_ctx, factory=_Factory) + + def test_w_component_wo_provides_component_no_provides(self): + _COMPONENT = object + _cfg_ctx = _makeConfigContext() + self.assertRaises(TypeError, + self._callFUT, _cfg_ctx, component=_COMPONENT) + + def test_w_factory_w_provides(self): + from zope.interface import Interface + from zope.component.interface import provideInterface + from zope.component.zcml import handler + class IFoo(Interface): + pass + class Foo(object): + pass + _cfg_ctx = _makeConfigContext() + self._callFUT(_cfg_ctx, factory=Foo, provides=IFoo) + self.assertEqual(len(_cfg_ctx._actions), 2) + self.assertEqual(_cfg_ctx._actions[0][0], ()) + # Register the utility + action =_cfg_ctx._actions[0][1] + self.assertEqual(action['callable'], handler) + self.assertEqual(action['discriminator'], ('utility', IFoo, '')) + self.assertEqual(action['args'][0], 'registerUtility') + self.assertEqual(action['args'][1], None) + self.assertEqual(action['args'][2], IFoo) + self.assertEqual(action['args'][3], '') + self.assertEqual(action['args'][4], 'TESTING') + self.assertEqual(action['kw'], {'factory': Foo}) + # Register the provided interface + self.assertEqual(_cfg_ctx._actions[1][0], ()) + action =_cfg_ctx._actions[1][1] + self.assertEqual(action['callable'], provideInterface) + self.assertEqual(action['discriminator'], None) + self.assertEqual(action['args'], ('', IFoo)) + + def test_w_factory_wo_provides_factory_implements(self): + from zope.interface import Interface + from zope.interface import implementer + from zope.component.interface import provideInterface + from zope.component.zcml import handler + class IFoo(Interface): + pass + @implementer(IFoo) + class Foo(object): + pass + _cfg_ctx = _makeConfigContext() + self._callFUT(_cfg_ctx, factory=Foo) + self.assertEqual(len(_cfg_ctx._actions), 2) + self.assertEqual(_cfg_ctx._actions[0][0], ()) + # Register the utility + action =_cfg_ctx._actions[0][1] + self.assertEqual(action['callable'], handler) + self.assertEqual(action['discriminator'], ('utility', IFoo, '')) + self.assertEqual(action['args'][0], 'registerUtility') + self.assertEqual(action['args'][1], None) + self.assertEqual(action['args'][2], IFoo) + self.assertEqual(action['args'][3], '') + self.assertEqual(action['args'][4], 'TESTING') + self.assertEqual(action['kw'], {'factory': Foo}) + # Register the provided interface + self.assertEqual(_cfg_ctx._actions[1][0], ()) + action =_cfg_ctx._actions[1][1] + self.assertEqual(action['callable'], provideInterface) + self.assertEqual(action['discriminator'], None) + self.assertEqual(action['args'], ('', IFoo)) + + def test_w_component_w_provides_w_naem(self): + from zope.interface import Interface + from zope.component.interface import provideInterface + from zope.component.zcml import handler + class IFoo(Interface): + pass + _COMPONENT = object() + _cfg_ctx = _makeConfigContext() + self._callFUT(_cfg_ctx, component=_COMPONENT, + name='test', provides=IFoo) + self.assertEqual(len(_cfg_ctx._actions), 2) + self.assertEqual(_cfg_ctx._actions[0][0], ()) + # Register the utility + action =_cfg_ctx._actions[0][1] + self.assertEqual(action['callable'], handler) + self.assertEqual(action['discriminator'], ('utility', IFoo, 'test')) + self.assertEqual(action['args'][0], 'registerUtility') + self.assertEqual(action['args'][1], _COMPONENT) + self.assertEqual(action['args'][2], IFoo) + self.assertEqual(action['args'][3], 'test') + self.assertEqual(action['args'][4], 'TESTING') + # Register the provided interface + self.assertEqual(_cfg_ctx._actions[1][0], ()) + action =_cfg_ctx._actions[1][1] + self.assertEqual(action['callable'], provideInterface) + self.assertEqual(action['discriminator'], None) + self.assertEqual(action['args'], ('', IFoo)) + + def test_w_component_wo_provides_component_provides(self): + from zope.interface import Interface + from zope.interface import directlyProvides + from zope.component.interface import provideInterface + from zope.component.zcml import handler + class IFoo(Interface): + pass + class Foo(object): + pass + _COMPONENT = Foo() + directlyProvides(_COMPONENT, IFoo) + _cfg_ctx = _makeConfigContext() + self._callFUT(_cfg_ctx, component=_COMPONENT) + self.assertEqual(len(_cfg_ctx._actions), 2) + self.assertEqual(_cfg_ctx._actions[0][0], ()) + # Register the utility + action =_cfg_ctx._actions[0][1] + self.assertEqual(action['callable'], handler) + self.assertEqual(action['discriminator'], ('utility', IFoo, '')) + self.assertEqual(action['args'][0], 'registerUtility') + self.assertEqual(action['args'][1], _COMPONENT) + self.assertEqual(action['args'][2], IFoo) + self.assertEqual(action['args'][3], '') + self.assertEqual(action['args'][4], 'TESTING') + # Register the provided interface + self.assertEqual(_cfg_ctx._actions[1][0], ()) + action =_cfg_ctx._actions[1][1] + self.assertEqual(action['callable'], provideInterface) + self.assertEqual(action['discriminator'], None) + self.assertEqual(action['args'], ('', IFoo)) + + @skipIfNoSecurity + def test_w_component_w_provides_w_permission(self): + from zope.interface import Interface + from zope.proxy import removeAllProxies + from zope.component.interface import provideInterface + from zope.component.security import PermissionProxy + from zope.component.zcml import handler + class IFoo(Interface): + def bar(self): + pass + class Foo(object): + def bar(self): + pass + _COMPONENT = Foo() + _cfg_ctx = _makeConfigContext() + self._callFUT(_cfg_ctx, component=_COMPONENT, + provides=IFoo, permission='testing') + self.assertEqual(len(_cfg_ctx._actions), 2) + self.assertEqual(_cfg_ctx._actions[0][0], ()) + # Register the utility + action =_cfg_ctx._actions[0][1] + self.assertEqual(action['callable'], handler) + self.assertEqual(action['discriminator'], ('utility', IFoo, '')) + self.assertEqual(action['args'][0], 'registerUtility') + component_proxy = action['args'][1] + self.assertTrue(isinstance(component_proxy, PermissionProxy)) + self.assertTrue(removeAllProxies(component_proxy) is _COMPONENT) + self.assertEqual(component_proxy.__Security_checker__.get_permissions, + {'bar': 'testing'}) + self.assertEqual(action['args'][2], IFoo) + self.assertEqual(action['args'][3], '') + self.assertEqual(action['args'][4], 'TESTING') + # Register the provided interface + self.assertEqual(_cfg_ctx._actions[1][0], ()) + action =_cfg_ctx._actions[1][1] + self.assertEqual(action['callable'], provideInterface) + self.assertEqual(action['discriminator'], None) + self.assertEqual(action['args'], ('', IFoo)) + + +class Test_interface(unittest.TestCase): + + def _callFUT(self, *args, **kw): + from zope.component.zcml import interface + return interface(*args, **kw) + + def test_wo_name_wo_type(self): + from zope.interface import Interface + from zope.component.interface import provideInterface + class IFoo(Interface): + pass + _cfg_ctx = _makeConfigContext() + self._callFUT(_cfg_ctx, IFoo) + self.assertEqual(len(_cfg_ctx._actions), 1) + self.assertEqual(_cfg_ctx._actions[0][0], ()) + action =_cfg_ctx._actions[0][1] + self.assertEqual(action['callable'], provideInterface) + self.assertEqual(action['discriminator'], None) + self.assertEqual(action['args'], ('', IFoo, None)) + + def test_w_name_w_type(self): + from zope.interface import Interface + from zope.component.interface import provideInterface + class IFoo(Interface): + pass + class IBar(Interface): + pass + _cfg_ctx = _makeConfigContext() + self._callFUT(_cfg_ctx, IFoo, name='foo', type=IBar) + self.assertEqual(len(_cfg_ctx._actions), 1) + self.assertEqual(_cfg_ctx._actions[0][0], ()) + action =_cfg_ctx._actions[0][1] + self.assertEqual(action['callable'], provideInterface) + self.assertEqual(action['discriminator'], None) + self.assertEqual(action['args'], ('foo', IFoo, IBar)) + + +class Test_view(unittest.TestCase): + + def _callFUT(self, *args, **kw): + from zope.component.zcml import view + return view(*args, **kw) + + def test_w_allowed_interface_wo_permission(self): + from zope.interface import Interface + from zope.component.zcml import ComponentConfigurationError + class IViewType(Interface): + pass + class IView(Interface): + def foo(): + pass + def bar(): + pass + class _View(object): + def __init__(self, context): + self.context = context + def foo(): + pass + def bar(): + pass + _cfg_ctx = _makeConfigContext() + self.assertRaises(ComponentConfigurationError, + self._callFUT, _cfg_ctx, (_View,), IViewType, '', + for_=(Interface, Interface), + allowed_interface=IView) + + def test_w_allowed_attributes_wo_permission(self): + from zope.interface import Interface + from zope.component.zcml import ComponentConfigurationError + class IViewType(Interface): + pass + class _View(object): + def __init__(self, context): + self.context = context + def foo(): + pass + def bar(): + pass + _cfg_ctx = _makeConfigContext() + self.assertRaises(ComponentConfigurationError, + self._callFUT, _cfg_ctx, (_View,), IViewType, '', + for_=(Interface, Interface), + allowed_attributes=('foo', 'bar')) + + def test_w_factory_as_empty(self): + from zope.interface import Interface + from zope.component.zcml import ComponentConfigurationError + class IViewType(Interface): + pass + _cfg_ctx = _makeConfigContext() + self.assertRaises(ComponentConfigurationError, + self._callFUT, _cfg_ctx, (), IViewType, '', + for_=(Interface, Interface)) + + def test_w_multiple_factory_multiple_for_(self): + from zope.interface import Interface + from zope.component.zcml import ComponentConfigurationError + class IViewType(Interface): + pass + class Foo(object): + pass + class Bar(object): + pass + _cfg_ctx = _makeConfigContext() + self.assertRaises(ComponentConfigurationError, + self._callFUT, _cfg_ctx, (Foo, Bar), IViewType, '', + for_=(Interface, Interface)) + + def test_w_for__as_empty(self): + from zope.interface import Interface + from zope.component.zcml import ComponentConfigurationError + class IViewType(Interface): + pass + class _View(object): + def __init__(self, context): + self.context = context + _cfg_ctx = _makeConfigContext() + self.assertRaises(ComponentConfigurationError, + self._callFUT, _cfg_ctx, (_View,), IViewType, '', + for_=()) + + def test_w_single_factory_single_for__wo_permission_w_name(self): + from zope.interface import Interface + from zope.component.zcml import handler + from zope.component.interface import provideInterface + class IViewType(Interface): + pass + class _View(object): + def __init__(self, context): + self.context = context + _cfg_ctx = _makeConfigContext() + self._callFUT(_cfg_ctx, (_View,), IViewType, 'test', for_=(Interface,)) + self.assertEqual(len(_cfg_ctx._actions), 4) + self.assertEqual(_cfg_ctx._actions[0][0], ()) + # Register the adapter + action =_cfg_ctx._actions[0][1] + self.assertEqual(action['callable'], handler) + self.assertEqual(action['discriminator'], + ('view', (Interface, IViewType), 'test', Interface)) + self.assertEqual(action['args'][0], 'registerAdapter') + self.assertEqual(action['args'][1], _View) + self.assertEqual(action['args'][2], (Interface, IViewType)) + self.assertEqual(action['args'][3], Interface) + self.assertEqual(action['args'][4], 'test') + self.assertEqual(action['args'][5], 'TESTING') + # Register the provided interface + self.assertEqual(_cfg_ctx._actions[1][0], ()) + action =_cfg_ctx._actions[1][1] + self.assertEqual(action['callable'], provideInterface) + self.assertEqual(action['discriminator'], None) + self.assertEqual(action['args'], ('', Interface)) + # Register the required interface(s) + self.assertEqual(_cfg_ctx._actions[2][0], ()) + action =_cfg_ctx._actions[2][1] + self.assertEqual(action['callable'], provideInterface) + self.assertEqual(action['discriminator'], None) + self.assertEqual(action['args'], ('', Interface)) + self.assertEqual(_cfg_ctx._actions[3][0], ()) + action =_cfg_ctx._actions[3][1] + self.assertEqual(action['callable'], provideInterface) + self.assertEqual(action['discriminator'], None) + self.assertEqual(action['args'], ('', IViewType)) + + def test_w_multiple_factory_single_for__wo_permission(self): + from zope.interface import Interface + from zope.component.zcml import handler + class IViewType(Interface): + pass + class _View(object): + def __init__(self, context): + self.context = context + class _View2(object): + def __init__(self, context, request): + self.context = context + self.request = request + _cfg_ctx = _makeConfigContext() + self._callFUT(_cfg_ctx, [_View, _View2], IViewType, '', + for_=(Interface,)) + self.assertEqual(_cfg_ctx._actions[0][0], ()) + # Register the adapter + action =_cfg_ctx._actions[0][1] + self.assertEqual(action['callable'], handler) + self.assertEqual(action['discriminator'], + ('view', (Interface, IViewType), '', Interface)) + self.assertEqual(action['args'][0], 'registerAdapter') + factory = action['args'][1] + self.assertTrue(factory.factory is _View) + context = object() + request = object() + view = factory(context, request) + self.assertTrue(isinstance(view, _View2)) + self.assertTrue(view.request is request) + self.assertTrue(isinstance(view.context, _View)) + self.assertTrue(view.context.context is context) + self.assertEqual(action['args'][2], (Interface, IViewType)) + self.assertEqual(action['args'][3], Interface) + self.assertEqual(action['args'][4], '') + self.assertEqual(action['args'][5], 'TESTING') + + @skipIfNoSecurity + def test_w_single_factory_single_for__w_permission(self): + from zope.interface import Interface + from zope.component.zcml import handler + class IViewType(Interface): + pass + class _View(object): + def __init__(self, context, request): + self.context = context + self.request = request + _cfg_ctx = _makeConfigContext() + self._callFUT(_cfg_ctx, [_View], IViewType, '', for_=(Interface,), + permission='testing') + self.assertEqual(_cfg_ctx._actions[0][0], ()) + # Register the adapter + action =_cfg_ctx._actions[0][1] + self.assertEqual(action['callable'], handler) + self.assertEqual(action['discriminator'], + ('view', (Interface, IViewType), '', Interface)) + self.assertEqual(action['args'][0], 'registerAdapter') + factory = action['args'][1] + context = object() + request = object() + view = factory(context, request) + self.assertTrue(view.context is context) + self.assertTrue(view.request is request) + self.assertTrue(factory.factory is _View) + self.assertEqual(action['args'][2], (Interface, IViewType)) + self.assertEqual(action['args'][3], Interface) + self.assertEqual(action['args'][4], '') + self.assertEqual(action['args'][5], 'TESTING') + + @skipIfNoSecurity + def test_w_single_factory_single_for__w_permission_and_allowed_attrs(self): + from zope.interface import Interface + from zope.component.zcml import handler + class IViewType(Interface): + pass + class _View(object): + def __init__(self, context, request): + self.context = context + self.request = request + def bar(self): + pass + _cfg_ctx = _makeConfigContext() + self._callFUT(_cfg_ctx, [_View], IViewType, '', for_=(Interface,), + permission='testing', allowed_attributes=('bar',)) + self.assertEqual(_cfg_ctx._actions[0][0], ()) + # Register the adapter + action =_cfg_ctx._actions[0][1] + self.assertEqual(action['callable'], handler) + self.assertEqual(action['discriminator'], + ('view', (Interface, IViewType), '', Interface)) + self.assertEqual(action['args'][0], 'registerAdapter') + factory = action['args'][1] + checker = factory.checker + self.assertEqual(checker.get_permissions, {'bar': 'testing'}) + + @skipIfNoSecurity + def test_w_single_factory_single_for__w_permission_and_allowed_iface(self): + from zope.interface import Interface + from zope.component.zcml import handler + class IViewType(Interface): + def bar(self): + pass + class _View(object): + def __init__(self, context, request): + self.context = context + self.request = request + def bar(self): + pass + _cfg_ctx = _makeConfigContext() + self._callFUT(_cfg_ctx, [_View], IViewType, '', for_=(Interface,), + permission='testing', allowed_interface=(IViewType,)) + self.assertEqual(_cfg_ctx._actions[0][0], ()) + # Register the adapter + action =_cfg_ctx._actions[0][1] + self.assertEqual(action['callable'], handler) + self.assertEqual(action['discriminator'], + ('view', (Interface, IViewType), '', Interface)) + self.assertEqual(action['args'][0], 'registerAdapter') + factory = action['args'][1] + checker = factory.checker + self.assertEqual(checker.get_permissions, {'bar': 'testing'}) + + +class Test_resource(unittest.TestCase): + + def _callFUT(self, *args, **kw): + from zope.component.zcml import resource + return resource(*args, **kw) + + def test_w_allowed_interface_wo_permission(self): + from zope.interface import Interface + from zope.component.zcml import ComponentConfigurationError + class IResourceType(Interface): + pass + class IView(Interface): + def foo(): + pass + def bar(): + pass + class _Resource(object): + def __init__(self, context): + self.context = context + def foo(): + pass + def bar(): + pass + _cfg_ctx = _makeConfigContext() + self.assertRaises(ComponentConfigurationError, + self._callFUT, + _cfg_ctx, (_Resource,), IResourceType, '', + allowed_interface=IView) + + def test_w_allowed_attributes_wo_permission(self): + from zope.interface import Interface + from zope.component.zcml import ComponentConfigurationError + class IResourceType(Interface): + pass + class _Resource(object): + def __init__(self, context): + self.context = context + def foo(): + pass + def bar(): + pass + _cfg_ctx = _makeConfigContext() + self.assertRaises(ComponentConfigurationError, + self._callFUT, + _cfg_ctx, (_Resource,), IResourceType, '', + allowed_attributes=('foo', 'bar')) + + def test_wo_permission_w_name(self): + from zope.interface import Interface + from zope.component.interface import provideInterface + from zope.component.zcml import handler + class IResourceType(Interface): + pass + class _Resource(object): + def __init__(self, context): + self.context = context + def foo(): + pass + def bar(): + pass + _cfg_ctx = _makeConfigContext() + self._callFUT(_cfg_ctx, _Resource, IResourceType, 'test') + self.assertEqual(len(_cfg_ctx._actions), 3) + self.assertEqual(_cfg_ctx._actions[0][0], ()) + # Register the resource + action =_cfg_ctx._actions[0][1] + self.assertEqual(action['callable'], handler) + self.assertEqual(action['discriminator'], + ('resource', 'test', IResourceType, Interface)) + self.assertEqual(action['args'][0], 'registerAdapter') + self.assertEqual(action['args'][1], _Resource) + self.assertEqual(action['args'][2], (IResourceType,)) + self.assertEqual(action['args'][3], Interface) + self.assertEqual(action['args'][4], 'test') + self.assertEqual(action['args'][5], 'TESTING') + # Register the 'type' interface + self.assertEqual(_cfg_ctx._actions[1][0], ()) + action =_cfg_ctx._actions[1][1] + self.assertEqual(action['callable'], provideInterface) + self.assertEqual(action['discriminator'], None) + self.assertEqual(action['args'], ('', IResourceType)) + # Register the required interface(s) + self.assertEqual(_cfg_ctx._actions[2][0], ()) + action =_cfg_ctx._actions[2][1] + self.assertEqual(action['callable'], provideInterface) + self.assertEqual(action['discriminator'], None) + self.assertEqual(action['args'], ('', Interface)) + + @skipIfNoSecurity + def test_w_permission(self): + from zope.interface import Interface + from zope.component.zcml import handler + class IResourceType(Interface): + pass + class _Resource(object): + def __init__(self, context): + self.context = context + def foo(): + pass + def bar(): + pass + _cfg_ctx = _makeConfigContext() + self._callFUT(_cfg_ctx, _Resource, IResourceType, 'test', + permission='testing', allowed_attributes=('foo',)) + self.assertEqual(len(_cfg_ctx._actions), 3) + self.assertEqual(_cfg_ctx._actions[0][0], ()) + # Register the resource + action =_cfg_ctx._actions[0][1] + self.assertEqual(action['callable'], handler) + self.assertEqual(action['discriminator'], + ('resource', 'test', IResourceType, Interface)) + self.assertEqual(action['args'][0], 'registerAdapter') + factory = action['args'][1] + self.assertTrue(factory.factory is _Resource) + context = object() + resource = factory(context) + checker = resource.__Security_checker__ + self.assertEqual(checker.get_permissions, {'foo': 'testing'}) + self.assertTrue(resource.context is context) + self.assertEqual(action['args'][2], (IResourceType,)) + self.assertEqual(action['args'][3], Interface) + self.assertEqual(action['args'][4], 'test') + self.assertEqual(action['args'][5], 'TESTING') + + +def _makeConfigContext(): + class _Context(object): + info = 'TESTING' + def __init__(self): + self._actions = [] + def action(self, *args, **kw): + self._actions.append((args, kw)) + return _Context() + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(Test_handler), + unittest.makeSuite(Test__rolledUpFactory), + unittest.makeSuite(Test_adapter), + unittest.makeSuite(Test_subscriber), + unittest.makeSuite(Test_utility), + unittest.makeSuite(Test_interface), + unittest.makeSuite(Test_view), + unittest.makeSuite(Test_resource), + )) diff --git a/src/zope/component/zcml.py b/src/zope/component/zcml.py index 2b98b0f..d760ce1 100644 --- a/src/zope/component/zcml.py +++ b/src/zope/component/zcml.py @@ -13,42 +13,49 @@ ############################################################################## """Component Architecture configuration handlers """ -__docformat__ = "reStructuredText" - -import warnings -import zope.component -import zope.configuration.fields -import zope.interface -import zope.schema - -from zope.component.interface import provideInterface from zope.configuration.exceptions import ConfigurationError +from zope.configuration.fields import Bool +from zope.configuration.fields import GlobalInterface +from zope.configuration.fields import GlobalObject +from zope.configuration.fields import PythonIdentifier +from zope.configuration.fields import Tokens from zope.i18nmessageid import MessageFactory +from zope.interface import Interface +from zope.interface import implementedBy +from zope.interface import providedBy +from zope.schema import TextLine + +from zope.component._api import getSiteManager +from zope.component._declaration import adaptedBy +from zope.component.interface import provideInterface +from zope.component._compat import _BLANK try: - from zope.component.security import _checker, proxify, protectedFactory, \ - securityAdapterFactory from zope.security.zcml import Permission -except ImportError: - SECURITY_SUPPORT = False - from zope.schema import TextLine as Permission +except ImportError: #pragma NO COVER + def _no_security(*args, **kw): + raise ConfigurationError("security proxied components are not " + "supported because zope.security is not available") + _checker = proxify = protectedFactory = security =_no_security + Permission = TextLine else: - SECURITY_SUPPORT = True + from zope.component.security import _checker + from zope.component.security import proxify + from zope.component.security import protectedFactory + from zope.component.security import securityAdapterFactory _ = MessageFactory('zope') -def check_security_support(): - if not SECURITY_SUPPORT: - raise ConfigurationError("security proxied components are not " - "supported because zope.security is not available") +class ComponentConfigurationError(ValueError, ConfigurationError): + pass def handler(methodName, *args, **kwargs): - method = getattr(zope.component.getSiteManager(), methodName) + method = getattr(getSiteManager(), methodName) method(*args, **kwargs) -class IBasicComponentInformation(zope.interface.Interface): +class IBasicComponentInformation(Interface): - component = zope.configuration.fields.GlobalObject( + component = GlobalObject( title=_("Component to use"), description=_("Python name of the implementation object. This" " must identify an object in a module using the" @@ -63,7 +70,7 @@ class IBasicComponentInformation(zope.interface.Interface): required=False, ) - factory = zope.configuration.fields.GlobalObject( + factory = GlobalObject( title=_("Factory"), description=_("Python name of a factory which can create the" " implementation object. This must identify an" @@ -73,31 +80,31 @@ class IBasicComponentInformation(zope.interface.Interface): required=False, ) -class IAdapterDirective(zope.interface.Interface): +class IAdapterDirective(Interface): """ Register an adapter """ - factory = zope.configuration.fields.Tokens( + factory = Tokens( title=_("Adapter factory/factories"), description=_("A list of factories (usually just one) that create" " the adapter instance."), required=True, - value_type=zope.configuration.fields.GlobalObject() + value_type=GlobalObject() ) - provides = zope.configuration.fields.GlobalInterface( + provides = GlobalInterface( title=_("Interface the component provides"), description=_("This attribute specifies the interface the adapter" " instance must provide."), required=False, ) - for_ = zope.configuration.fields.Tokens( + for_ = Tokens( title=_("Specifications to be adapted"), description=_("This should be a list of interfaces or classes"), required=False, - value_type=zope.configuration.fields.GlobalObject( + value_type=GlobalObject( missing_value=object(), ), ) @@ -109,7 +116,7 @@ class IAdapterDirective(zope.interface.Interface): required=False, ) - name = zope.schema.TextLine( + name = TextLine( title=_("Name"), description=_("Adapters can have names.\n\n" "This attribute allows you to specify the name for" @@ -117,7 +124,7 @@ class IAdapterDirective(zope.interface.Interface): required=False, ) - trusted = zope.configuration.fields.Bool( + trusted = Bool( title=_("Trusted"), description=_("""Make the adapter a trusted adapter @@ -131,7 +138,7 @@ class IAdapterDirective(zope.interface.Interface): default=False, ) - locate = zope.configuration.fields.Bool( + locate = Bool( title=_("Locate"), description=_("""Make the adapter a locatable adapter @@ -158,7 +165,7 @@ def adapter(_context, factory, provides=None, for_=None, permission=None, if for_ is None: if len(factory) == 1: - for_ = zope.component.adaptedBy(factory[0]) + for_ = adaptedBy(factory[0]) if for_ is None: raise TypeError("No for attribute was provided and can't " @@ -168,7 +175,7 @@ def adapter(_context, factory, provides=None, for_=None, permission=None, if provides is None: if len(factory) == 1: - p = list(zope.interface.implementedBy(factory[0])) + p = list(implementedBy(factory[0])) if len(p) == 1: provides = p[0] @@ -180,19 +187,18 @@ def adapter(_context, factory, provides=None, for_=None, permission=None, if len(factories) == 1: factory = factories[0] elif len(factories) < 1: - raise ValueError("No factory specified") + raise ComponentConfigurationError("No factory specified") elif len(factories) > 1 and len(for_) != 1: - raise ValueError("Can't use multiple factories and multiple for") + raise ComponentConfigurationError( + "Can't use multiple factories and multiple for") else: factory = _rolledUpFactory(factories) if permission is not None: - check_security_support() factory = protectedFactory(factory, provides, permission) # invoke custom adapter factories if locate or permission is not None or trusted: - check_security_support() factory = securityAdapterFactory(factory, permission, locate, trusted) _context.action( @@ -215,35 +221,35 @@ def adapter(_context, factory, provides=None, for_=None, permission=None, args = ('', iface) ) -class ISubscriberDirective(zope.interface.Interface): +class ISubscriberDirective(Interface): """ Register a subscriber """ - factory = zope.configuration.fields.GlobalObject( + factory = GlobalObject( title=_("Subscriber factory"), description=_("A factory used to create the subscriber instance."), required=False, ) - handler = zope.configuration.fields.GlobalObject( + handler = GlobalObject( title=_("Handler"), description=_("A callable object that handles events."), required=False, ) - provides = zope.configuration.fields.GlobalInterface( + provides = GlobalInterface( title=_("Interface the component provides"), description=_("This attribute specifies the interface the adapter" " instance must provide."), required=False, ) - for_ = zope.configuration.fields.Tokens( + for_ = Tokens( title=_("Interfaces or classes that this subscriber depends on"), description=_("This should be a list of interfaces or classes"), required=False, - value_type=zope.configuration.fields.GlobalObject( + value_type=GlobalObject( missing_value = object(), ), ) @@ -255,7 +261,7 @@ class ISubscriberDirective(zope.interface.Interface): required=False, ) - trusted = zope.configuration.fields.Bool( + trusted = Bool( title=_("Trusted"), description=_("""Make the subscriber a trusted subscriber @@ -269,7 +275,7 @@ class ISubscriberDirective(zope.interface.Interface): default=False, ) - locate = zope.configuration.fields.Bool( + locate = Bool( title=_("Locate"), description=_("""Make the subscriber a locatable subscriber @@ -298,20 +304,18 @@ def subscriber(_context, for_=None, factory=None, handler=None, provides=None, "a factory") if for_ is None: - for_ = zope.component.adaptedBy(factory) + for_ = adaptedBy(factory) if for_ is None: raise TypeError("No for attribute was provided and can't " "determine what the factory (or handler) adapts.") if permission is not None: - check_security_support() factory = protectedFactory(factory, provides, permission) for_ = tuple(for_) # invoke custom adapter factories if locate or permission is not None or trusted: - check_security_support() factory = securityAdapterFactory(factory, permission, locate, trusted) if handler is not None: @@ -319,14 +323,14 @@ def subscriber(_context, for_=None, factory=None, handler=None, provides=None, discriminator = None, callable = _handler, args = ('registerHandler', - handler, for_, u'', _context.info), + handler, for_, _BLANK, _context.info), ) else: _context.action( discriminator = None, callable = _handler, args = ('registerSubscriptionAdapter', - factory, for_, provides, u'', _context.info), + factory, for_, provides, _BLANK, _context.info), ) if provides is not None: @@ -348,13 +352,13 @@ def subscriber(_context, for_=None, factory=None, handler=None, provides=None, class IUtilityDirective(IBasicComponentInformation): """Register a utility.""" - provides = zope.configuration.fields.GlobalInterface( + provides = GlobalInterface( title=_("Provided interface"), description=_("Interface provided by the utility."), required=False, ) - name = zope.schema.TextLine( + name = TextLine( title=_("Name"), description=_("Name of the registration. This is used by" " application code when locating a utility."), @@ -368,46 +372,45 @@ def utility(_context, provides=None, component=None, factory=None, if provides is None: if factory: - provides = list(zope.interface.implementedBy(factory)) + provides = list(implementedBy(factory)) else: - provides = list(zope.interface.providedBy(component)) + provides = list(providedBy(component)) if len(provides) == 1: provides = provides[0] else: raise TypeError("Missing 'provides' attribute") if permission is not None: - check_security_support() component = proxify(component, provides=provides, permission=permission) _context.action( discriminator = ('utility', provides, name), callable = handler, - args = ('registerUtility', component, provides, name), + args = ('registerUtility', component, provides, name, _context.info), kw = dict(factory=factory), ) _context.action( discriminator = None, callable = provideInterface, - args = (provides.__module__ + '.' + provides.getName(), provides) + args = ('', provides), ) -class IInterfaceDirective(zope.interface.Interface): +class IInterfaceDirective(Interface): """ Define an interface """ - interface = zope.configuration.fields.GlobalInterface( + interface = GlobalInterface( title=_("Interface"), required=True, ) - type = zope.configuration.fields.GlobalInterface( + type = GlobalInterface( title=_("Interface type"), required=False, ) - name = zope.schema.TextLine( + name = TextLine( title=_("Name"), required=False, ) @@ -419,15 +422,15 @@ def interface(_context, interface, type=None, name=''): args = (name, interface, type) ) -class IBasicViewInformation(zope.interface.Interface): +class IBasicViewInformation(Interface): """This is the basic information for all views.""" - for_ = zope.configuration.fields.Tokens( + for_ = Tokens( title=_("Specifications of the objects to be viewed"), description=_("""This should be a list of interfaces or classes """), required=True, - value_type=zope.configuration.fields.GlobalObject( + value_type=GlobalObject( missing_value=object(), ), ) @@ -438,22 +441,13 @@ class IBasicViewInformation(zope.interface.Interface): required=False, ) - class_ = zope.configuration.fields.GlobalObject( + class_ = GlobalObject( title=_("Class"), description=_("A class that provides attributes used by the view."), required=False, ) - layer = zope.configuration.fields.GlobalInterface( - title=_("The layer the view is in."), - description=_(""" - A skin is composed of layers. It is common to put skin - specific views in a layer named after the skin. If the 'layer' - attribute is not supplied, it defaults to 'default'."""), - required=False, - ) - - allowed_interface = zope.configuration.fields.Tokens( + allowed_interface = Tokens( title=_("Interface that is also allowed if user has permission."), description=_(""" By default, 'permission' only applies to viewing the view and @@ -464,10 +458,10 @@ class IBasicViewInformation(zope.interface.Interface): Multiple interfaces can be provided, separated by whitespace."""), required=False, - value_type=zope.configuration.fields.GlobalInterface(), + value_type=GlobalInterface(), ) - allowed_attributes = zope.configuration.fields.Tokens( + allowed_attributes = Tokens( title=_("View attributes that are also allowed if the user" " has permission."), description=_(""" @@ -476,31 +470,31 @@ class IBasicViewInformation(zope.interface.Interface): you can make the permission also apply to the extra attributes on the view object."""), required=False, - value_type=zope.configuration.fields.PythonIdentifier(), + value_type=PythonIdentifier(), ) -class IBasicResourceInformation(zope.interface.Interface): +class IBasicResourceInformation(Interface): """ Basic information for resources """ - name = zope.schema.TextLine( + name = TextLine( title=_("The name of the resource."), description=_("The name shows up in URLs/paths. For example 'foo'."), required=True, - default=u'', + default=_BLANK, ) - provides = zope.configuration.fields.GlobalInterface( + provides = GlobalInterface( title=_("The interface this component provides."), description=_(""" A view can provide an interface. This would be used for views that support other views."""), required=False, - default=zope.interface.Interface, + default=Interface, ) - type = zope.configuration.fields.GlobalInterface( + type = GlobalInterface( title=_("Request type"), required=True ) @@ -509,28 +503,26 @@ class IBasicResourceInformation(zope.interface.Interface): class IViewDirective(IBasicViewInformation, IBasicResourceInformation): """Register a view for a component""" - factory = zope.configuration.fields.Tokens( + factory = Tokens( title=_("Factory"), required=False, - value_type=zope.configuration.fields.GlobalObject(), + value_type=GlobalObject(), ) -def view(_context, factory, type, name, for_, layer=None, - permission=None, allowed_interface=None, allowed_attributes=None, - provides=zope.interface.Interface): +def view(_context, factory, type, name, for_, + permission=None, + allowed_interface=None, + allowed_attributes=None, + provides=Interface, + ): if ((allowed_attributes or allowed_interface) and (not permission)): - raise ConfigurationError( - "Must use name attribute with allowed_interface or " - "allowed_attributes" - ) - - if not factory: - raise ConfigurationError("No view factory specified.") + raise ComponentConfigurationError( + "'permission' required with 'allowed_interface' or " + "'allowed_attributes'") if permission is not None: - check_security_support() checker = _checker(_context, permission, allowed_interface, allowed_attributes) @@ -549,7 +541,7 @@ def view(_context, factory, type, name, for_, layer=None, if not for_: - raise ValueError("No for interfaces specified"); + raise ComponentConfigurationError("No for interfaces specified"); for_ = tuple(for_) # Generate a single factory from multiple factories: @@ -557,27 +549,18 @@ def view(_context, factory, type, name, for_, layer=None, if len(factories) == 1: factory = factories[0] elif len(factories) < 1: - raise ValueError("No factory specified") + raise ComponentConfigurationError("No view factory specified") elif len(factories) > 1 and len(for_) > 1: - raise ValueError("Can't use multiple factories and multiple for") + raise ComponentConfigurationError( + "Can't use multiple factories and multiple for") else: def factory(ob, request): for f in factories[:-1]: ob = f(ob) return factories[-1](ob, request) + factory.factory = factories[0] - # BBB 2006/02/18, to be removed after 12 months - if layer is not None: - for_ = for_ + (layer,) - warnings.warn_explicit( - "The 'layer' argument of the 'view' directive has been " - "deprecated. Use the 'type' argument instead. If you have " - "an existing 'type' argument IBrowserRequest, replace it with the " - "'layer' argument (the layer subclasses IBrowserRequest). " - "which subclasses BrowserRequest.", - DeprecationWarning, _context.info.file, _context.info.line) - else: - for_ = for_ + (type,) + for_ = for_ + (type,) _context.action( discriminator = ('view', for_, name, provides), @@ -585,12 +568,6 @@ def view(_context, factory, type, name, for_, layer=None, args = ('registerAdapter', factory, for_, provides, name, _context.info), ) - if type is not None: - _context.action( - discriminator = None, - callable = provideInterface, - args = ('', type) - ) _context.action( discriminator = None, @@ -612,67 +589,52 @@ class IResourceDirective(IBasicComponentInformation, IBasicResourceInformation): """Register a resource""" - layer = zope.configuration.fields.GlobalInterface( - title=_("The layer the resource is in."), - required=False, - ) - - allowed_interface = zope.configuration.fields.Tokens( + allowed_interface = Tokens( title=_("Interface that is also allowed if user has permission."), required=False, - value_type=zope.configuration.fields.GlobalInterface(), + value_type=GlobalInterface(), ) - allowed_attributes = zope.configuration.fields.Tokens( + allowed_attributes = Tokens( title=_("View attributes that are also allowed if user" " has permission."), required=False, - value_type=zope.configuration.fields.PythonIdentifier(), + value_type=PythonIdentifier(), ) -def resource(_context, factory, type, name, layer=None, +def resource(_context, factory, type, name, permission=None, allowed_interface=None, allowed_attributes=None, - provides=zope.interface.Interface): + provides=Interface): if ((allowed_attributes or allowed_interface) and (not permission)): - raise ConfigurationError( + raise ComponentConfigurationError( "Must use name attribute with allowed_interface or " "allowed_attributes" ) if permission is not None: - check_security_support() checker = _checker(_context, permission, allowed_interface, allowed_attributes) def proxyResource(request, factory=factory, checker=checker): return proxify(factory(request), checker) + proxyResource.factory = factory factory = proxyResource - if layer is not None: - warnings.warn_explicit( - "The 'layer' argument of the 'resource' directive has been " - "deprecated. Use the 'type' argument instead.", - DeprecationWarning, _context.info.file, _context.info.line) - type = layer - _context.action( discriminator = ('resource', name, type, provides), callable = handler, args = ('registerAdapter', - factory, (type,), provides, name, _context.info), - ) + factory, (type,), provides, name, _context.info)) _context.action( discriminator = None, callable = provideInterface, - args = (type.__module__ + '.' + type.__name__, type) - ) + args = ('', type)) _context.action( discriminator = None, callable = provideInterface, - args = (provides.__module__ + '.' + provides.__name__, type) - ) + args = ('', provides)) diff --git a/src/zope/component/zcml.txt b/src/zope/component/zcml.txt deleted file mode 100644 index 7a51e6d..0000000 --- a/src/zope/component/zcml.txt +++ /dev/null @@ -1,1021 +0,0 @@ -ZCML directives -=============== - -Components may be registered using the registration API exposed by -``zope.component`` (provideAdapter, provideUtility, etc.). They may -also be registered using configuration files. The common way to do -that is by using ZCML (Zope Configuration Markup Language), an XML -spelling of component registration. - -In ZCML, each XML element is a *directive*. There are different -top-level directives that let us register components. We will -introduce them one by one here. - -This helper will let us easily execute ZCML snippets: - - >>> from cStringIO import StringIO - >>> from zope.configuration.xmlconfig import xmlconfig - >>> def runSnippet(snippet): - ... template = """\ - ... <configure xmlns='http://namespaces.zope.org/zope' - ... i18n_domain="zope"> - ... %s - ... </configure>""" - ... xmlconfig(StringIO(template % snippet)) - -adapter -------- - -Adapters play a key role in the Component Architecture. In ZCML, they -are registered with the <adapter /> directive. - - >>> from zope.component.testfiles.adapter import A1, A2, A3, Handler - >>> from zope.component.testfiles.adapter import I1, I2, I3, IS - >>> from zope.component.testfiles.components import IContent, Content, Comp, comp - -Before we register the first test adapter, we can verify that adapter -lookup doesn't work yet: - - >>> from zope.component.tests import clearZCML - >>> clearZCML() - >>> from zope.component.testfiles.components import IApp - >>> IApp(Content(), None) is None - True - -Then we register the adapter and see that the lookup works: - - >>> runSnippet(''' - ... <adapter - ... factory="zope.component.testfiles.components.Comp" - ... provides="zope.component.testfiles.components.IApp" - ... for="zope.component.testfiles.components.IContent" - ... />''') - - >>> IApp(Content()).__class__ - <class 'zope.component.testfiles.components.Comp'> - -It is also possible to give adapters names. Then the combination of -required interface, provided interface and name makes the adapter -lookup unique. The name is supplied using the ``name`` argument to -the <adapter /> directive: - - >>> from zope.component.tests import clearZCML - >>> clearZCML() - >>> import zope.component - >>> zope.component.queryAdapter(Content(), IApp, 'test') is None - True - - >>> runSnippet(''' - ... <adapter - ... factory="zope.component.testfiles.components.Comp" - ... provides="zope.component.testfiles.components.IApp" - ... for="zope.component.testfiles.components.IContent" - ... name="test" - ... />''') - - >>> zope.component.getAdapter(Content(), IApp, 'test').__class__ - <class 'zope.component.testfiles.components.Comp'> - -Adapter factories -~~~~~~~~~~~~~~~~~ - -It is possible to supply more than one adapter factory. In this case, -during adapter lookup each factory will be called and the return value -will be given to the next factory. The return value of the last -factory is returned as the result of the adapter lookup. For examle: - - >>> clearZCML() - >>> runSnippet(''' - ... <adapter - ... factory="zope.component.testfiles.adapter.A1 - ... zope.component.testfiles.adapter.A2 - ... zope.component.testfiles.adapter.A3" - ... provides="zope.component.testfiles.components.IApp" - ... for="zope.component.testfiles.components.IContent" - ... />''') - -The resulting adapter is an A3, around an A2, around an A1, around the -adapted object: - - >>> content = Content() - >>> a3 = IApp(content) - >>> a3.__class__ is A3 - True - - >>> a2 = a3.context[0] - >>> a2.__class__ is A2 - True - - >>> a1 = a2.context[0] - >>> a1.__class__ is A1 - True - - >>> a1.context[0] is content - True - -Of course, if no factory is provided at all, we will get an error: - - >>> runSnippet(''' - ... <adapter - ... factory="" - ... provides="zope.component.testfiles.components.IApp" - ... for="zope.component.testfiles.components.IContent" - ... />''') - Traceback (most recent call last): - ... - ZopeXMLConfigurationError: File "<string>", line 4.2-8.8 - ValueError: No factory specified - - -Declaring ``for`` and ``provides`` in Python -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The <adapter /> directive can figure out from the in-line Python -declaration (using ``zope.component.adapts()`` or -``zope.component.adapter()`` as well as ``zope.interface.implements``) -what the adapter should be registered for and what it provides:: - - >>> clearZCML() - >>> IApp(Content(), None) is None - True - - >>> runSnippet(''' - ... <adapter factory="zope.component.testfiles.components.Comp" />''') - - >>> IApp(Content()).__class__ - <class 'zope.component.testfiles.components.Comp'> - -Of course, if the adapter has no ``implements()`` declaration, ZCML -can't figure out what it provides: - - >>> runSnippet(''' - ... <adapter - ... factory="zope.component.testfiles.adapter.A4" - ... for="zope.component.testfiles.components.IContent" - ... />''') - Traceback (most recent call last): - ... - ZopeXMLConfigurationError: File "<string>", line 4.2-7.8 - TypeError: Missing 'provides' attribute - -On the other hand, if the factory implements more than one interface, -ZCML can't figure out what it should provide either: - - >>> runSnippet(''' - ... <adapter - ... factory="zope.component.testfiles.adapter.A5" - ... for="zope.component.testfiles.components.IContent" - ... />''') - Traceback (most recent call last): - ... - ZopeXMLConfigurationError: File "<string>", line 4.2-7.8 - TypeError: Missing 'provides' attribute - -A not so common edge case is registering adapters directly for -classes, not for interfaces. For example: - - >>> clearZCML() - >>> runSnippet(''' - ... <adapter - ... for="zope.component.testfiles.components.Content" - ... provides="zope.component.testfiles.adapter.I1" - ... factory="zope.component.testfiles.adapter.A1" - ... />''') - - >>> content = Content() - >>> a1 = zope.component.getAdapter(content, I1, '') - >>> isinstance(a1, A1) - True - -This time, any object providing ``IContent`` won't work if it's not an -instance of the ``Content`` class: - - >>> import zope.interface - >>> class MyContent: - ... zope.interface.implements(IContent) - >>> zope.component.getAdapter(MyContent(), I1, '') # doctest: +ELLIPSIS - Traceback (most recent call last): - ... - ComponentLookupError: ... - -Multi-adapters -~~~~~~~~~~~~~~ - -Conventional adapters adapt one object to provide another interface. -Multi-adapters adapt several objects at once: - - >>> clearZCML() - >>> runSnippet(''' - ... <adapter - ... for="zope.component.testfiles.components.IContent - ... zope.component.testfiles.adapter.I1 - ... zope.component.testfiles.adapter.I2" - ... provides="zope.component.testfiles.adapter.I3" - ... factory="zope.component.testfiles.adapter.A3" - ... />''') - - >>> content = Content() - >>> a1 = A1() - >>> a2 = A2() - >>> a3 = zope.component.queryMultiAdapter((content, a1, a2), I3) - >>> a3.__class__ is A3 - True - >>> a3.context == (content, a1, a2) - True - -You can even adapt an empty list of objects (we call this a -null-adapter): - - >>> clearZCML() - >>> runSnippet(''' - ... <adapter - ... for="" - ... provides="zope.component.testfiles.adapter.I3" - ... factory="zope.component.testfiles.adapter.A3" - ... />''') - - >>> a3 = zope.component.queryMultiAdapter((), I3) - >>> a3.__class__ is A3 - True - >>> a3.context == () - True - -Even with multi-adapters, ZCML can figure out the ``for`` and -``provides`` parameters from the Python declarations: - - >>> clearZCML() - >>> runSnippet(''' - ... <adapter factory="zope.component.testfiles.adapter.A3" />''') - - >>> a3 = zope.component.queryMultiAdapter((content, a1, a2), I3) - >>> a3.__class__ is A3 - True - >>> a3.context == (content, a1, a2) - True - -Chained factories are not supported for multi-adapters, though: - - >>> clearZCML() - >>> runSnippet(''' - ... <adapter - ... for="zope.component.testfiles.components.IContent - ... zope.component.testfiles.adapter.I1 - ... zope.component.testfiles.adapter.I2" - ... provides="zope.component.testfiles.components.IApp" - ... factory="zope.component.testfiles.adapter.A1 - ... zope.component.testfiles.adapter.A2" - ... />''') - Traceback (most recent call last): - ... - ZopeXMLConfigurationError: File "<string>", line 4.2-11.8 - ValueError: Can't use multiple factories and multiple for - -And neither for null-adapters: - - >>> clearZCML() - >>> runSnippet(''' - ... <adapter - ... for="" - ... provides="zope.component.testfiles.components.IApp" - ... factory="zope.component.testfiles.adapter.A1 - ... zope.component.testfiles.adapter.A2" - ... />''') - Traceback (most recent call last): - ... - ZopeXMLConfigurationError: File "<string>", line 4.2-9.8 - ValueError: Can't use multiple factories and multiple for - -Protected adapters -~~~~~~~~~~~~~~~~~~ - -Adapters can be protected with a permission. First we have to define -a permission for which we'll have to register the <permission /> -directive: - - >>> clearZCML() - >>> IApp(Content(), None) is None - True - - >>> import zope.security - >>> from zope.configuration.xmlconfig import XMLConfig - >>> XMLConfig('meta.zcml', zope.security)() - >>> runSnippet(''' - ... <permission - ... id="y.x" - ... title="XY" - ... description="Allow XY." - ... /> - ... <adapter - ... factory="zope.component.testfiles.components.Comp" - ... provides="zope.component.testfiles.components.IApp" - ... for="zope.component.testfiles.components.IContent" - ... permission="y.x" - ... />''') - -We see that the adapter is a location proxy now so that the -appropriate permissions can be found from the context: - - >>> IApp(Content()).__class__ - <class 'zope.component.testfiles.components.Comp'> - >>> type(IApp(Content())) - <class 'zope.location.location.LocationProxy'> - -We can also go about it a different way. Let's make a public adapter -and wrap the adapter in a security proxy. That often happens when -an adapter is turned over to untrusted code: - - >>> clearZCML() - >>> IApp(Content(), None) is None - True - - >>> runSnippet(''' - ... <adapter - ... factory="zope.component.testfiles.components.Comp" - ... provides="zope.component.testfiles.components.IApp" - ... for="zope.component.testfiles.components.IContent" - ... permission="zope.Public" - ... />''') - - >>> from zope.security.checker import ProxyFactory - >>> adapter = ProxyFactory(IApp(Content())) - >>> from zope.security.proxy import getTestProxyItems - >>> items = [item[0] for item in getTestProxyItems(adapter)] - >>> items - ['a', 'f'] - - >>> from zope.security.proxy import removeSecurityProxy - >>> removeSecurityProxy(adapter).__class__ is Comp - True - -Of course, this still works when we let the ZCML directive handler -figure out ``for`` and ``provides`` from the Python declarations: - - >>> clearZCML() - >>> runSnippet(''' - ... <adapter - ... factory="zope.component.testfiles.components.Comp" - ... permission="zope.Public" - ... />''') - - >>> adapter = ProxyFactory(IApp(Content())) - >>> [item[0] for item in getTestProxyItems(adapter)] - ['a', 'f'] - >>> removeSecurityProxy(adapter).__class__ is Comp - True - -It also works with multi adapters: - - >>> clearZCML() - >>> runSnippet(''' - ... <adapter - ... factory="zope.component.testfiles.adapter.A3" - ... provides="zope.component.testfiles.adapter.I3" - ... for="zope.component.testfiles.components.IContent - ... zope.component.testfiles.adapter.I1 - ... zope.component.testfiles.adapter.I2" - ... permission="zope.Public" - ... />''') - - >>> content = Content() - >>> a1 = A1() - >>> a2 = A2() - >>> a3 = ProxyFactory(zope.component.queryMultiAdapter((content, a1, a2), I3)) - >>> a3.__class__ == A3 - True - >>> [item[0] for item in getTestProxyItems(a3)] - ['f1', 'f2', 'f3'] - -It's probably not worth mentioning, but when we try to protect an -adapter with a permission that doesn't exist, we'll obviously get an -error: - - >>> clearZCML() - >>> runSnippet(''' - ... <adapter - ... factory="zope.component.testfiles.components.Comp" - ... provides="zope.component.testfiles.components.IApp" - ... for="zope.component.testfiles.components.IContent" - ... permission="zope.UndefinedPermission" - ... />''') - Traceback (most recent call last): - ... - ConfigurationExecutionError: exceptions.ValueError: ('Undefined permission id', 'zope.UndefinedPermission') - in: - File "<string>", line 4.2-9.8 - Could not read source. - -Trusted adapters -~~~~~~~~~~~~~~~~ - -Trusted adapters are adapters that are trusted to do anything with the -objects they are given so that these objects are not security-proxied. -They are registered using the ``trusted`` argument to the <adapter /> -directive: - - >>> clearZCML() - >>> runSnippet(''' - ... <adapter - ... for="zope.component.testfiles.components.IContent" - ... provides="zope.component.testfiles.adapter.I1" - ... factory="zope.component.testfiles.adapter.A1" - ... trusted="yes" - ... />''') - -With an unproxied object, it's business as usual: - - >>> ob = Content() - >>> type(I1(ob)) is A1 - True - -With a security-proxied object, however, we get a security-proxied -adapter: - - >>> p = ProxyFactory(ob) - >>> a = I1(p) - >>> type(a) - <type 'zope.security._proxy._Proxy'> - -While the adapter is security-proxied, the object it adapts is now -proxy-free. The adapter has umlimited access to it: - - >>> a = removeSecurityProxy(a) - >>> type(a) is A1 - True - >>> a.context[0] is ob - True - -We can also protect the trusted adapter with a permission: - - >>> clearZCML() - >>> XMLConfig('meta.zcml', zope.security)() - >>> runSnippet(''' - ... <permission - ... id="y.x" - ... title="XY" - ... description="Allow XY." - ... /> - ... <adapter - ... for="zope.component.testfiles.components.IContent" - ... provides="zope.component.testfiles.adapter.I1" - ... factory="zope.component.testfiles.adapter.A1" - ... permission="y.x" - ... trusted="yes" - ... />''') - -Again, with an unproxied object, it's business as usual: - - >>> ob = Content() - >>> type(I1(ob)) is A1 - True - -With a security-proxied object, we again get a security-proxied -adapter: - - >>> p = ProxyFactory(ob) - >>> a = I1(p) - >>> type(a) - <type 'zope.security._proxy._Proxy'> - -Since we protected the adapter with a permission, we now encounter a -location proxy behind the security proxy: - - >>> a = removeSecurityProxy(a) - >>> type(a) - <class 'zope.location.location.LocationProxy'> - >>> a.context[0] is ob - True - -There's one exception to all of this: When you use the public -permission (``zope.Public``), there will be no location proxy: - - >>> clearZCML() - >>> runSnippet(''' - ... <adapter - ... for="zope.component.testfiles.components.IContent" - ... provides="zope.component.testfiles.adapter.I1" - ... factory="zope.component.testfiles.adapter.A1" - ... permission="zope.Public" - ... trusted="yes" - ... />''') - - >>> ob = Content() - >>> p = ProxyFactory(ob) - >>> a = I1(p) - >>> type(a) - <type 'zope.security._proxy._Proxy'> - - >>> a = removeSecurityProxy(a) - >>> type(a) is A1 - True - -We can also explicitply pass the ``locate`` argument to make sure we -get location proxies: - - >>> clearZCML() - >>> runSnippet(''' - ... <adapter - ... for="zope.component.testfiles.components.IContent" - ... provides="zope.component.testfiles.adapter.I1" - ... factory="zope.component.testfiles.adapter.A1" - ... trusted="yes" - ... locate="yes" - ... />''') - - >>> ob = Content() - >>> p = ProxyFactory(ob) - >>> a = I1(p) - >>> type(a) - <type 'zope.security._proxy._Proxy'> - - >>> a = removeSecurityProxy(a) - >>> type(a) - <class 'zope.location.location.LocationProxy'> - - -subscriber ----------- - -With the <subscriber /> directive you can register subscription -adapters or event subscribers with the adapter registry. Consider -this very typical example of a <subscriber /> directive: - - >>> clearZCML() - >>> runSnippet(''' - ... <subscriber - ... provides="zope.component.testfiles.adapter.IS" - ... factory="zope.component.testfiles.adapter.A3" - ... for="zope.component.testfiles.components.IContent - ... zope.component.testfiles.adapter.I1" - ... />''') - - >>> content = Content() - >>> a1 = A1() - - >>> subscribers = zope.component.subscribers((content, a1), IS) - >>> a3 = subscribers[0] - >>> a3.__class__ is A3 - True - >>> a3.context == (content, a1) - True - -Note how ZCML provides some additional information when registering -components, such as the ZCML filename and line numbers: - - >>> sm = zope.component.getSiteManager() - >>> doc = [reg.info for reg in sm.registeredSubscriptionAdapters() - ... if reg.provided is IS][0] - >>> print doc - File "<string>", line 4.2-9.8 - Could not read source. - -The "fun" behind subscription adapters/subscribers is that when -several ones are declared for the same for/provides, they are all -found. With regular adapters, the most specific one (and in doubt the -one registered last) wins. Consider these two subscribers: - - >>> clearZCML() - >>> runSnippet(''' - ... <subscriber - ... provides="zope.component.testfiles.adapter.IS" - ... factory="zope.component.testfiles.adapter.A3" - ... for="zope.component.testfiles.components.IContent - ... zope.component.testfiles.adapter.I1" - ... /> - ... <subscriber - ... provides="zope.component.testfiles.adapter.IS" - ... factory="zope.component.testfiles.adapter.A2" - ... for="zope.component.testfiles.components.IContent - ... zope.component.testfiles.adapter.I1" - ... />''') - - >>> subscribers = zope.component.subscribers((content, a1), IS) - >>> len(subscribers) - 2 - >>> sorted([a.__class__.__name__ for a in subscribers]) - ['A2', 'A3'] - -Declaring ``for`` and ``provides`` in Python -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Like the <adapter /> directive, the <subscriber /> directive can -figure out from the in-line Python declaration (using -``zope.component.adapts()`` or ``zope.component.adapter()``) what the -subscriber should be registered for: - - >>> clearZCML() - >>> runSnippet(''' - ... <subscriber - ... provides="zope.component.testfiles.adapter.IS" - ... factory="zope.component.testfiles.adapter.A3" - ... />''') - - >>> content = Content() - >>> a2 = A2() - >>> subscribers = zope.component.subscribers((content, a1, a2), IS) - - >>> a3 = subscribers[0] - >>> a3.__class__ is A3 - True - >>> a3.context == (content, a1, a2) - True - -In the same way the directive can figure out what a subscriber -provides: - - >>> clearZCML() - >>> runSnippet(''' - ... <subscriber handler="zope.component.testfiles.adapter.A3" />''') - - >>> sm = zope.component.getSiteManager() - >>> a3 = sm.adapters.subscriptions((IContent, I1, I2), None)[0] - >>> a3 is A3 - True - -A not so common edge case is declaring subscribers directly for -classes, not for interfaces. For example: - - >>> clearZCML() - >>> runSnippet(''' - ... <subscriber - ... for="zope.component.testfiles.components.Content" - ... provides="zope.component.testfiles.adapter.I1" - ... factory="zope.component.testfiles.adapter.A1" - ... />''') - - >>> subs = list(zope.component.subscribers((Content(),), I1)) - >>> isinstance(subs[0], A1) - True - -This time, any object providing ``IContent`` won't work if it's not an -instance of the ``Content`` class: - - >>> list(zope.component.subscribers((MyContent(),), I1)) - [] - -Protected subscribers -~~~~~~~~~~~~~~~~~~~~~ - -Subscribers can also be protected with a permission. First we have to -define a permission for which we'll have to register the <permission /> -directive: - - >>> clearZCML() - >>> XMLConfig('meta.zcml', zope.security)() - >>> runSnippet(''' - ... <permission - ... id="y.x" - ... title="XY" - ... description="Allow XY." - ... /> - ... <subscriber - ... provides="zope.component.testfiles.adapter.IS" - ... factory="zope.component.testfiles.adapter.A3" - ... for="zope.component.testfiles.components.IContent - ... zope.component.testfiles.adapter.I1" - ... permission="y.x" - ... />''') - - >>> subscribers = zope.component.subscribers((content, a1), IS) - >>> a3 = subscribers[0] - >>> a3.__class__ is A3 - True - >>> type(a3) - <class 'zope.location.location.LocationProxy'> - >>> a3.context == (content, a1) - True - -Trusted subscribers -~~~~~~~~~~~~~~~~~~~ - -Like trusted adapters, trusted subscribers are subscribers that are -trusted to do anything with the objects they are given so that these -objects are not security-proxied. In analogy to the <adapter /> -directive, they are registered using the ``trusted`` argument to the -<subscriber /> directive: - - >>> clearZCML() - >>> runSnippet(''' - ... <subscriber - ... provides="zope.component.testfiles.adapter.IS" - ... factory="zope.component.testfiles.adapter.A3" - ... for="zope.component.testfiles.components.IContent - ... zope.component.testfiles.adapter.I1" - ... trusted="yes" - ... />''') - -With an unproxied object, it's business as usual: - - >>> subscribers = zope.component.subscribers((content, a1), IS) - >>> a3 = subscribers[0] - >>> a3.__class__ is A3 - True - >>> a3.context == (content, a1) - True - >>> type(a3) is A3 - True - -Now with a proxied object. We will see that the subscriber has -unproxied access to it, but the subscriber itself is proxied: - - >>> p = ProxyFactory(content) - >>> a3 = zope.component.subscribers((p, a1), IS)[0] - >>> type(a3) - <type 'zope.security._proxy._Proxy'> - -There's no location proxy behind the security proxy: - - >>> removeSecurityProxy(a3).context[0] is content - True - >>> type(removeSecurityProxy(a3)) is A3 - True - -If you want the trusted subscriber to be located, you'll also have to -use the ``locate`` argument: - - >>> clearZCML() - >>> runSnippet(''' - ... <subscriber - ... provides="zope.component.testfiles.adapter.IS" - ... factory="zope.component.testfiles.adapter.A3" - ... for="zope.component.testfiles.components.IContent - ... zope.component.testfiles.adapter.I1" - ... trusted="yes" - ... locate="yes" - ... />''') - -Again, it's business as usual with an unproxied object: - - >>> subscribers = zope.component.subscribers((content, a1), IS) - >>> a3 = subscribers[0] - >>> a3.__class__ is A3 - True - >>> a3.context == (content, a1) - True - >>> type(a3) is A3 - True - -With a proxied object, we again get a security-proxied subscriber: - - >>> p = ProxyFactory(content) - >>> a3 = zope.component.subscribers((p, a1), IS)[0] - - >>> type(a3) - <type 'zope.security._proxy._Proxy'> - - >>> removeSecurityProxy(a3).context[0] is content - True - -However, thanks to the ``locate`` argument, we now have a location -proxy behind the security proxy: - - >>> type(removeSecurityProxy(a3)) - <class 'zope.location.location.LocationProxy'> - -Event subscriber (handlers) -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Sometimes, subscribers don't need to be adapters that actually provide -anything. It's enough that a callable is called for a certain event. - - >>> clearZCML() - >>> runSnippet(''' - ... <subscriber - ... for="zope.component.testfiles.components.IContent - ... zope.component.testfiles.adapter.I1" - ... handler="zope.component.testfiles.adapter.Handler" - ... />''') - -In this case, simply getting the subscribers is enough to invoke them: - - >>> list(zope.component.subscribers((content, a1), None)) - [] - >>> content.args == ((a1,),) - True - - -utility -------- - -Apart from adapters (and subscription adapters), the Component -Architecture knows a second kind of component: utilities. They are -registered using the <utility /> directive. - -Before we register the first test utility, we can verify that utility -lookup doesn't work yet: - - >>> clearZCML() - >>> zope.component.queryUtility(IApp) is None - True - -Then we register the utility: - - >>> runSnippet(''' - ... <utility - ... component="zope.component.testfiles.components.comp" - ... provides="zope.component.testfiles.components.IApp" - ... />''') - >>> zope.component.getUtility(IApp) is comp - True - -Like adapters, utilities can also have names. There can be more than -one utility registered for a certain interface, as long as they each -have a different name. - -First, we make sure that there's no utility yet: - - >>> clearZCML() - >>> zope.component.queryUtility(IApp, 'test') is None - True - -Then we register it: - - >>> runSnippet(''' - ... <utility - ... component="zope.component.testfiles.components.comp" - ... provides="zope.component.testfiles.components.IApp" - ... name="test" - ... />''') - >>> zope.component.getUtility(IApp, 'test') is comp - True - -Utilities can also be registered from a factory. In this case, the -ZCML handler calls the factory (without any arguments) and registers -the returned value as a utility. Typically, you'd pass a class for -the factory: - - >>> clearZCML() - >>> zope.component.queryUtility(IApp) is None - True - - >>> runSnippet(''' - ... <utility - ... factory="zope.component.testfiles.components.Comp" - ... provides="zope.component.testfiles.components.IApp" - ... />''') - >>> zope.component.getUtility(IApp).__class__ is Comp - True - -Declaring ``provides`` in Python -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Like other directives, <utility /> can also figure out which interface -a utility provides from the Python declaration: - - >>> clearZCML() - >>> zope.component.queryUtility(IApp) is None - True - - >>> runSnippet(''' - ... <utility component="zope.component.testfiles.components.comp" />''') - >>> zope.component.getUtility(IApp) is comp - True - -It won't work if the component that is to be registered doesn't -provide anything: - - >>> clearZCML() - >>> runSnippet(''' - ... <utility component="zope.component.testfiles.adapter.a4" />''') - Traceback (most recent call last): - ... - ZopeXMLConfigurationError: File "<string>", line 4.2-4.61 - TypeError: Missing 'provides' attribute - -Or if more than one interface is provided (then the ZCML directive -handler doesn't know under which the utility should be registered): - - >>> clearZCML() - >>> runSnippet(''' - ... <utility component="zope.component.testfiles.adapter.a5" />''') - Traceback (most recent call last): - ... - ZopeXMLConfigurationError: File "<string>", line 4.2-4.61 - TypeError: Missing 'provides' attribute - -We can repeat the same drill for utility factories: - - >>> clearZCML() - >>> runSnippet(''' - ... <utility factory="zope.component.testfiles.components.Comp" />''') - >>> zope.component.getUtility(IApp).__class__ is Comp - True - - >>> runSnippet(''' - ... <utility factory="zope.component.testfiles.adapter.A4" />''') - Traceback (most recent call last): - ... - ZopeXMLConfigurationError: File "<string>", line 4.2-4.59 - TypeError: Missing 'provides' attribute - - >>> clearZCML() - >>> runSnippet(''' - ... <utility factory="zope.component.testfiles.adapter.A5" />''') - Traceback (most recent call last): - ... - ZopeXMLConfigurationError: File "<string>", line 4.2-4.59 - TypeError: Missing 'provides' attribute - -Protected utilities -~~~~~~~~~~~~~~~~~~~ - -TODO:: - - def testProtectedUtility(self): - """Test that we can protect a utility. - - Also: - Check that multiple configurations for the same utility and - don't interfere. - """ - self.assertEqual(zope.component.queryUtility(IV), None) - xmlconfig(StringIO(template % ( - ''' - <permission id="tell.everyone" title="Yay" /> - <utility - component="zope.component.testfiles.components.comp" - provides="zope.component.testfiles.components.IApp" - permission="tell.everyone" - /> - <permission id="top.secret" title="shhhh" /> - <utility - component="zope.component.testfiles.components.comp" - provides="zope.component.testfiles.components.IAppb" - permission="top.secret" - /> - ''' - ))) - - utility = ProxyFactory(zope.component.getUtility(IApp)) - items = getTestProxyItems(utility) - self.assertEqual(items, [('a', 'tell.everyone'), - ('f', 'tell.everyone') - ]) - self.assertEqual(removeSecurityProxy(utility), comp) - - def testUtilityUndefinedPermission(self): - config = StringIO(template % ( - ''' - <utility - component="zope.component.testfiles.components.comp" - provides="zope.component.testfiles.components.IApp" - permission="zope.UndefinedPermission" - /> - ''' - )) - self.assertRaises(ValueError, xmlconfig, config, - testing=1) - -interface ---------- - -The <interface /> directive lets us register an interface. Interfaces -are registered as named utilities. We therefore needn't go though all -the lookup details again, it is sufficient to see whether the -directive handler emits the right actions. - -First we provide a stub configuration context: - - >>> import re, pprint - >>> atre = re.compile(' at [0-9a-fA-Fx]+') - >>> class Context(object): - ... actions = () - ... def action(self, discriminator, callable, args): - ... self.actions += ((discriminator, callable, args), ) - ... def __repr__(self): - ... stream = StringIO() - ... pprinter = pprint.PrettyPrinter(stream=stream, width=60) - ... pprinter.pprint(self.actions) - ... r = stream.getvalue() - ... return (''.join(atre.split(r))).strip() - >>> context = Context() - -Then we provide a test interface that we'd like to register: - - >>> from zope.interface import Interface - >>> class I(Interface): - ... pass - -It doesn't yet provide ``ITestType``: - - >>> from zope.component.tests import ITestType - >>> ITestType.providedBy(I) - False - -However, after calling the directive handler... - - >>> from zope.component.zcml import interface - >>> interface(context, I, ITestType) - >>> context - ((None, - <function provideInterface>, - ('', - <InterfaceClass __builtin__.I>, - <InterfaceClass zope.component.tests.ITestType>)),) - -...it does provide ``ITestType``: - - >>> from zope.interface.interfaces import IInterface - >>> ITestType.extends(IInterface) - True - >>> IInterface.providedBy(I) - True diff --git a/src/zope/component/zcml_conditional.txt b/src/zope/component/zcml_conditional.txt deleted file mode 100644 index 243f14b..0000000 --- a/src/zope/component/zcml_conditional.txt +++ /dev/null @@ -1,612 +0,0 @@ -ZCML directives without zope.security support -============================================= - -This tests run without zope.security available: - - >>> from zope.component.zcml import check_security_support - >>> check_security_support() - Traceback (most recent call last): - ... - ConfigurationError: security proxied components are not supported because zope.security is not available - -Components may be registered using the registration API exposed by -``zope.component`` (provideAdapter, provideUtility, etc.). They may -also be registered using configuration files. The common way to do -that is by using ZCML (Zope Configuration Markup Language), an XML -spelling of component registration. - -In ZCML, each XML element is a *directive*. There are different -top-level directives that let us register components. We will -introduce them one by one here. - -This helper will let us easily execute ZCML snippets: - - >>> from cStringIO import StringIO - >>> from zope.configuration.xmlconfig import xmlconfig - >>> def runSnippet(snippet): - ... template = """\ - ... <configure xmlns='http://namespaces.zope.org/zope' - ... i18n_domain="zope"> - ... %s - ... </configure>""" - ... xmlconfig(StringIO(template % snippet)) - -adapter -------- - -Adapters play a key role in the Component Architecture. In ZCML, they -are registered with the <adapter /> directive. - - >>> from zope.component.testfiles.adapter import A1, A2, A3, Handler - >>> from zope.component.testfiles.adapter import I1, I2, I3, IS - >>> from zope.component.testfiles.components import IContent, Content, Comp, comp - -Before we register the first test adapter, we can verify that adapter -lookup doesn't work yet: - - >>> from zope.component.tests import clearZCML - >>> clearZCML() - >>> from zope.component.testfiles.components import IApp - >>> IApp(Content(), None) is None - True - -Then we register the adapter and see that the lookup works: - - >>> runSnippet(''' - ... <adapter - ... factory="zope.component.testfiles.components.Comp" - ... provides="zope.component.testfiles.components.IApp" - ... for="zope.component.testfiles.components.IContent" - ... />''') - - >>> IApp(Content()).__class__ - <class 'zope.component.testfiles.components.Comp'> - -It is also possible to give adapters names. Then the combination of -required interface, provided interface and name makes the adapter -lookup unique. The name is supplied using the ``name`` argument to -the <adapter /> directive: - - >>> from zope.component.tests import clearZCML - >>> clearZCML() - >>> import zope.component - >>> zope.component.queryAdapter(Content(), IApp, 'test') is None - True - - >>> runSnippet(''' - ... <adapter - ... factory="zope.component.testfiles.components.Comp" - ... provides="zope.component.testfiles.components.IApp" - ... for="zope.component.testfiles.components.IContent" - ... name="test" - ... />''') - - >>> zope.component.getAdapter(Content(), IApp, 'test').__class__ - <class 'zope.component.testfiles.components.Comp'> - -Adapter factories -~~~~~~~~~~~~~~~~~ - -It is possible to supply more than one adapter factory. In this case, -during adapter lookup each factory will be called and the return value -will be given to the next factory. The return value of the last -factory is returned as the result of the adapter lookup. For examle: - - >>> clearZCML() - >>> runSnippet(''' - ... <adapter - ... factory="zope.component.testfiles.adapter.A1 - ... zope.component.testfiles.adapter.A2 - ... zope.component.testfiles.adapter.A3" - ... provides="zope.component.testfiles.components.IApp" - ... for="zope.component.testfiles.components.IContent" - ... />''') - -The resulting adapter is an A3, around an A2, around an A1, around the -adapted object: - - >>> content = Content() - >>> a3 = IApp(content) - >>> a3.__class__ is A3 - True - - >>> a2 = a3.context[0] - >>> a2.__class__ is A2 - True - - >>> a1 = a2.context[0] - >>> a1.__class__ is A1 - True - - >>> a1.context[0] is content - True - -Of course, if no factory is provided at all, we will get an error: - - >>> runSnippet(''' - ... <adapter - ... factory="" - ... provides="zope.component.testfiles.components.IApp" - ... for="zope.component.testfiles.components.IContent" - ... />''') - Traceback (most recent call last): - ... - ZopeXMLConfigurationError: File "<string>", line 4.2-8.8 - ValueError: No factory specified - -Declaring ``for`` and ``provides`` in Python -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The <adapter /> directive can figure out from the in-line Python -declaration (using ``zope.component.adapts()`` or -``zope.component.adapter()`` as well as ``zope.interface.implements``) -what the adapter should be registered for and what it provides:: - - >>> clearZCML() - >>> IApp(Content(), None) is None - True - - >>> runSnippet(''' - ... <adapter factory="zope.component.testfiles.components.Comp" />''') - - >>> IApp(Content()).__class__ - <class 'zope.component.testfiles.components.Comp'> - -Of course, if the adapter has no ``implements()`` declaration, ZCML -can't figure out what it provides: - - >>> runSnippet(''' - ... <adapter - ... factory="zope.component.testfiles.adapter.A4" - ... for="zope.component.testfiles.components.IContent" - ... />''') - Traceback (most recent call last): - ... - ZopeXMLConfigurationError: File "<string>", line 4.2-7.8 - TypeError: Missing 'provides' attribute - -On the other hand, if the factory implements more than one interface, -ZCML can't figure out what it should provide either: - - >>> runSnippet(''' - ... <adapter - ... factory="zope.component.testfiles.adapter.A5" - ... for="zope.component.testfiles.components.IContent" - ... />''') - Traceback (most recent call last): - ... - ZopeXMLConfigurationError: File "<string>", line 4.2-7.8 - TypeError: Missing 'provides' attribute - -A not so common edge case is registering adapters directly for -classes, not for interfaces. For example: - - >>> clearZCML() - >>> runSnippet(''' - ... <adapter - ... for="zope.component.testfiles.components.Content" - ... provides="zope.component.testfiles.adapter.I1" - ... factory="zope.component.testfiles.adapter.A1" - ... />''') - - >>> content = Content() - >>> a1 = zope.component.getAdapter(content, I1, '') - >>> isinstance(a1, A1) - True - -This time, any object providing ``IContent`` won't work if it's not an -instance of the ``Content`` class: - - >>> import zope.interface - >>> class MyContent: - ... zope.interface.implements(IContent) - >>> zope.component.getAdapter(MyContent(), I1, '') # doctest: +ELLIPSIS - Traceback (most recent call last): - ... - ComponentLookupError: ... - -Multi-adapters -~~~~~~~~~~~~~~ - -Conventional adapters adapt one object to provide another interface. -Multi-adapters adapt several objects at once: - - >>> clearZCML() - >>> runSnippet(''' - ... <adapter - ... for="zope.component.testfiles.components.IContent - ... zope.component.testfiles.adapter.I1 - ... zope.component.testfiles.adapter.I2" - ... provides="zope.component.testfiles.adapter.I3" - ... factory="zope.component.testfiles.adapter.A3" - ... />''') - - >>> content = Content() - >>> a1 = A1() - >>> a2 = A2() - >>> a3 = zope.component.queryMultiAdapter((content, a1, a2), I3) - >>> a3.__class__ is A3 - True - >>> a3.context == (content, a1, a2) - True - -You can even adapt an empty list of objects (we call this a -null-adapter): - - >>> clearZCML() - >>> runSnippet(''' - ... <adapter - ... for="" - ... provides="zope.component.testfiles.adapter.I3" - ... factory="zope.component.testfiles.adapter.A3" - ... />''') - - >>> a3 = zope.component.queryMultiAdapter((), I3) - >>> a3.__class__ is A3 - True - >>> a3.context == () - True - -Even with multi-adapters, ZCML can figure out the ``for`` and -``provides`` parameters from the Python declarations: - - >>> clearZCML() - >>> runSnippet(''' - ... <adapter factory="zope.component.testfiles.adapter.A3" />''') - - >>> a3 = zope.component.queryMultiAdapter((content, a1, a2), I3) - >>> a3.__class__ is A3 - True - >>> a3.context == (content, a1, a2) - True - -Chained factories are not supported for multi-adapters, though: - - >>> clearZCML() - >>> runSnippet(''' - ... <adapter - ... for="zope.component.testfiles.components.IContent - ... zope.component.testfiles.adapter.I1 - ... zope.component.testfiles.adapter.I2" - ... provides="zope.component.testfiles.components.IApp" - ... factory="zope.component.testfiles.adapter.A1 - ... zope.component.testfiles.adapter.A2" - ... />''') - Traceback (most recent call last): - ... - ZopeXMLConfigurationError: File "<string>", line 4.2-11.8 - ValueError: Can't use multiple factories and multiple for - -And neither for null-adapters: - - >>> clearZCML() - >>> runSnippet(''' - ... <adapter - ... for="" - ... provides="zope.component.testfiles.components.IApp" - ... factory="zope.component.testfiles.adapter.A1 - ... zope.component.testfiles.adapter.A2" - ... />''') - Traceback (most recent call last): - ... - ZopeXMLConfigurationError: File "<string>", line 4.2-9.8 - ValueError: Can't use multiple factories and multiple for - -subscriber ----------- - -With the <subscriber /> directive you can register subscription -adapters or event subscribers with the adapter registry. Consider -this very typical example of a <subscriber /> directive: - - >>> clearZCML() - >>> runSnippet(''' - ... <subscriber - ... provides="zope.component.testfiles.adapter.IS" - ... factory="zope.component.testfiles.adapter.A3" - ... for="zope.component.testfiles.components.IContent - ... zope.component.testfiles.adapter.I1" - ... />''') - - >>> content = Content() - >>> a1 = A1() - - >>> subscribers = zope.component.subscribers((content, a1), IS) - >>> a3 = subscribers[0] - >>> a3.__class__ is A3 - True - >>> a3.context == (content, a1) - True - -Note how ZCML provides some additional information when registering -components, such as the ZCML filename and line numbers: - - >>> sm = zope.component.getSiteManager() - >>> doc = [reg.info for reg in sm.registeredSubscriptionAdapters() - ... if reg.provided is IS][0] - >>> print doc - File "<string>", line 4.2-9.8 - Could not read source. - -The "fun" behind subscription adapters/subscribers is that when -several ones are declared for the same for/provides, they are all -found. With regular adapters, the most specific one (and in doubt the -one registered last) wins. Consider these two subscribers: - - >>> clearZCML() - >>> runSnippet(''' - ... <subscriber - ... provides="zope.component.testfiles.adapter.IS" - ... factory="zope.component.testfiles.adapter.A3" - ... for="zope.component.testfiles.components.IContent - ... zope.component.testfiles.adapter.I1" - ... /> - ... <subscriber - ... provides="zope.component.testfiles.adapter.IS" - ... factory="zope.component.testfiles.adapter.A2" - ... for="zope.component.testfiles.components.IContent - ... zope.component.testfiles.adapter.I1" - ... />''') - - >>> subscribers = zope.component.subscribers((content, a1), IS) - >>> len(subscribers) - 2 - >>> sorted([a.__class__.__name__ for a in subscribers]) - ['A2', 'A3'] - -Declaring ``for`` and ``provides`` in Python -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Like the <adapter /> directive, the <subscriber /> directive can -figure out from the in-line Python declaration (using -``zope.component.adapts()`` or ``zope.component.adapter()``) what the -subscriber should be registered for: - - >>> clearZCML() - >>> runSnippet(''' - ... <subscriber - ... provides="zope.component.testfiles.adapter.IS" - ... factory="zope.component.testfiles.adapter.A3" - ... />''') - - >>> content = Content() - >>> a2 = A2() - >>> subscribers = zope.component.subscribers((content, a1, a2), IS) - - >>> a3 = subscribers[0] - >>> a3.__class__ is A3 - True - >>> a3.context == (content, a1, a2) - True - -In the same way the directive can figure out what a subscriber -provides: - - >>> clearZCML() - >>> runSnippet(''' - ... <subscriber handler="zope.component.testfiles.adapter.A3" />''') - - >>> sm = zope.component.getSiteManager() - >>> a3 = sm.adapters.subscriptions((IContent, I1, I2), None)[0] - >>> a3 is A3 - True - -A not so common edge case is declaring subscribers directly for -classes, not for interfaces. For example: - - >>> clearZCML() - >>> runSnippet(''' - ... <subscriber - ... for="zope.component.testfiles.components.Content" - ... provides="zope.component.testfiles.adapter.I1" - ... factory="zope.component.testfiles.adapter.A1" - ... />''') - - >>> subs = list(zope.component.subscribers((Content(),), I1)) - >>> isinstance(subs[0], A1) - True - -This time, any object providing ``IContent`` won't work if it's not an -instance of the ``Content`` class: - - >>> list(zope.component.subscribers((MyContent(),), I1)) - [] - -Event subscriber (handlers) -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Sometimes, subscribers don't need to be adapters that actually provide -anything. It's enough that a callable is called for a certain event. - - >>> clearZCML() - >>> runSnippet(''' - ... <subscriber - ... for="zope.component.testfiles.components.IContent - ... zope.component.testfiles.adapter.I1" - ... handler="zope.component.testfiles.adapter.Handler" - ... />''') - -In this case, simply getting the subscribers is enough to invoke them: - - >>> list(zope.component.subscribers((content, a1), None)) - [] - >>> content.args == ((a1,),) - True - - -utility -------- - -Apart from adapters (and subscription adapters), the Component -Architecture knows a second kind of component: utilities. They are -registered using the <utility /> directive. - -Before we register the first test utility, we can verify that utility -lookup doesn't work yet: - - >>> clearZCML() - >>> zope.component.queryUtility(IApp) is None - True - -Then we register the utility: - - >>> runSnippet(''' - ... <utility - ... component="zope.component.testfiles.components.comp" - ... provides="zope.component.testfiles.components.IApp" - ... />''') - >>> zope.component.getUtility(IApp) is comp - True - -Like adapters, utilities can also have names. There can be more than -one utility registered for a certain interface, as long as they each -have a different name. - -First, we make sure that there's no utility yet: - - >>> clearZCML() - >>> zope.component.queryUtility(IApp, 'test') is None - True - -Then we register it: - - >>> runSnippet(''' - ... <utility - ... component="zope.component.testfiles.components.comp" - ... provides="zope.component.testfiles.components.IApp" - ... name="test" - ... />''') - >>> zope.component.getUtility(IApp, 'test') is comp - True - -Utilities can also be registered from a factory. In this case, the -ZCML handler calls the factory (without any arguments) and registers -the returned value as a utility. Typically, you'd pass a class for -the factory: - - >>> clearZCML() - >>> zope.component.queryUtility(IApp) is None - True - - >>> runSnippet(''' - ... <utility - ... factory="zope.component.testfiles.components.Comp" - ... provides="zope.component.testfiles.components.IApp" - ... />''') - >>> zope.component.getUtility(IApp).__class__ is Comp - True - -Declaring ``provides`` in Python -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Like other directives, <utility /> can also figure out which interface -a utility provides from the Python declaration: - - >>> clearZCML() - >>> zope.component.queryUtility(IApp) is None - True - - >>> runSnippet(''' - ... <utility component="zope.component.testfiles.components.comp" />''') - >>> zope.component.getUtility(IApp) is comp - True - -It won't work if the component that is to be registered doesn't -provide anything: - - >>> clearZCML() - >>> runSnippet(''' - ... <utility component="zope.component.testfiles.adapter.a4" />''') - Traceback (most recent call last): - ... - ZopeXMLConfigurationError: File "<string>", line 4.2-4.61 - TypeError: Missing 'provides' attribute - -Or if more than one interface is provided (then the ZCML directive -handler doesn't know under which the utility should be registered): - - >>> clearZCML() - >>> runSnippet(''' - ... <utility component="zope.component.testfiles.adapter.a5" />''') - Traceback (most recent call last): - ... - ZopeXMLConfigurationError: File "<string>", line 4.2-4.61 - TypeError: Missing 'provides' attribute - -We can repeat the same drill for utility factories: - - >>> clearZCML() - >>> runSnippet(''' - ... <utility factory="zope.component.testfiles.components.Comp" />''') - >>> zope.component.getUtility(IApp).__class__ is Comp - True - - >>> runSnippet(''' - ... <utility factory="zope.component.testfiles.adapter.A4" />''') - Traceback (most recent call last): - ... - ZopeXMLConfigurationError: File "<string>", line 4.2-4.59 - TypeError: Missing 'provides' attribute - - >>> clearZCML() - >>> runSnippet(''' - ... <utility factory="zope.component.testfiles.adapter.A5" />''') - Traceback (most recent call last): - ... - ZopeXMLConfigurationError: File "<string>", line 4.2-4.59 - TypeError: Missing 'provides' attribute - -interface ---------- - -The <interface /> directive lets us register an interface. Interfaces -are registered as named utilities. We therefore needn't go though all -the lookup details again, it is sufficient to see whether the -directive handler emits the right actions. - -First we provide a stub configuration context: - - >>> import re, pprint - >>> atre = re.compile(' at [0-9a-fA-Fx]+') - >>> class Context(object): - ... actions = () - ... def action(self, discriminator, callable, args): - ... self.actions += ((discriminator, callable, args), ) - ... def __repr__(self): - ... stream = StringIO() - ... pprinter = pprint.PrettyPrinter(stream=stream, width=60) - ... pprinter.pprint(self.actions) - ... r = stream.getvalue() - ... return (''.join(atre.split(r))).strip() - >>> context = Context() - -Then we provide a test interface that we'd like to register: - - >>> from zope.interface import Interface - >>> class I(Interface): - ... pass - -It doesn't yet provide ``ITestType``: - - >>> from zope.component.tests import ITestType - >>> ITestType.providedBy(I) - False - -However, after calling the directive handler... - - >>> from zope.component.zcml import interface - >>> interface(context, I, ITestType) - >>> context - ((None, - <function provideInterface>, - ('', - <InterfaceClass __builtin__.I>, - <InterfaceClass zope.component.tests.ITestType>)),) - -...it does provide ``ITestType``: - - >>> from zope.interface.interfaces import IInterface - >>> ITestType.extends(IInterface) - True - >>> IInterface.providedBy(I) - True |
