Metadata-Version: 1.0
Name: z3c.baseregistry
Version: 1.3.0
Summary: Manage IComponents instances using Python code and ZCML.
Home-page: http://pypi.python.org/pypi/z3c.baseregistry
Author: Stephan Richter, Roger Ineichen and the Zope Community
Author-email: zope-dev@zope.org
License: ZPL 2.1
Description: The purpose of this package is to define, populate and use multiple
        ``IComponents`` instances using filesystem-based development -- in other
        words, Python code and ZCML.
        
        
        Detailed Documentation
        **********************
        
        ===============
        Base Components
        ===============
        
        The purpose of this package is to define, populate and use multiple
        ``IComponents`` instances using filesystem-based development -- in other
        words, Python code and ZCML.
        
        
        Motivation
        ----------
        
        The current state of the component architecture allows us to
        
        (1) create a global components registry, populate it using ZCML, and use it
        via the ``zope.component`` API functions.
        
        (2) define local sites (local components registries), populate them with local
        (persistent) components, and use them selectively based on location --
        commonly defined by the path of the URL.
        
        Unfortunately, it is impossible to populate local sites with ZCML. The main
        reason is the lack of addressibility of local sites during the initial startup
        process.
        
        However, on the other hand we have a very advanced UI configuration system
        that involves views, resources, layers ans skins. So let's compare the two.
        
        (1) Views/Resources in the UI are like registered components in the component
        architecture.
        
        (2) Skin Layers in the UI behave very much like registries. The default skin
        is like the global base registry. Skins, like local sites, are activated
        during traversal, but can be populated using ZCML.
        
        (3) Layers are really base layers to the skin layer. The equivalent in the
        component architecture is to specify bases for a components registry,
        which is possible since the Great Component Architecture refactoring for
        Zope 3.3 in 2006.
        
        But layers can be defined and configured via ZCML. The purpose of this package
        is to be able to create base components registries and then populate them
        using ZCML. (As a side note: As skin layers and layers are practically the
        same components, there is no difference between the concept of global, local
        and base components registries.)
        
        The second feature is specific to the Zope application server. It provides an
        UI to set the bases on a local site manager. The user can select among all
        registries that have been registered as ``IComponents`` utilities.
        
        There are also a few options that could be considered in the future. For
        example, it would be simple to integrate the ``zope:registerIn`` directive
        (see below for documentation) into the ``zope:configure`` directive.
        
        If the above text is too dry and theoretical for you, here is the
        summary. This package
        
        (1) implements Steve Alexander's long dream (at least 3 years) of defining
        local sites via ZCML.
        
        (2) solves all of my (Stephan Richter) problems I am having with a complex
        Application Service Provider (ASP) setup.
        
        (3) implements a missing feature that you and everyone else really wanted,
        even if you did not know it yet.
        
        Thanks goes to Jim Fulton, whose outstanding design of the
        ``zope.configuration`` and ``zope.component`` packages made the implementation
        of the feature such a breeze. I also want to thank Fred Drake for helping with
        the initial design ideas.
        
        
        "Base Components" Registries
        ----------------------------
        
        Base registries are global component registries implementing the
        ``IComponents`` interface. In comparison to the base global registry (also
        known as ``globalSiteManager``), these registries are not necessarily
        available via module globals and *must* be registered with a parent registry,
        most commonly the base global registry:
        
        >>> from z3c.baseregistry import baseregistry
        >>> import zope.component
        >>> myRegistry = baseregistry.BaseComponents(
        ...     zope.component.globalSiteManager, 'myRegistry')
        
        >>> myRegistry
        <BaseComponents myRegistry>
        
        Another *VERY IMPORTANT* requirement is that ``zope.component`` hooks are in
        place. Install the hooks now:
        
        >>> import zope.component.hooks
        >>> zope.component.hooks.setHooks()
        
        
        Since this registry does not implement any of the ``IComponents`` API itself,
        it is not necessary to demonstrate those features here. Please see the
        corresponding documentation in the ``zope.component`` package.
        
        One feature of global registries must be that they pickle efficiently, since
        they can be referenced in persisted objects. As you can see, the base registry
        pickles quiet well:
        
        >>> import cPickle
        >>> jar = cPickle.dumps(myRegistry)
        >>> len(jar)
        100
        
        However, when reading the jar, we get an error:
        
        >>> cPickle.loads(jar)
        Traceback (most recent call last):
        ...
        ComponentLookupError: (ComponentLookupError(<InterfaceClass zope.component.interfaces.IComponents>, 'myRegistry'),
        <function BC at 0x...>,
        (<BaseGlobalComponents base>, 'myRegistry'))
        
        This is because we have not registered the registry in its parent as an
        ``IComponents`` utility, yet:
        
        >>> from zope.component.interfaces import IComponents
        >>> zope.component.provideUtility(myRegistry, IComponents, 'myRegistry')
        
        >>> cPickle.loads(jar)
        <BaseComponents myRegistry>
        
        Thus it is very important that you *always* register your base registry with
        its parent!
        
        Like any other components registry, a base registry can also have bases:
        
        >>> myOtherRegistry = baseregistry.BaseComponents(
        ...     zope.component.globalSiteManager, 'myRegistry', (myRegistry,))
        >>> myOtherRegistry.__bases__
        (<BaseComponents myRegistry>,)
        
        Let's now have a look at how base registries can be defined and used
        via ZCML, which is the usual mode of operation.
        
        
        Defining Base Registries
        ------------------------
        
        The above tasks are more commonly done in ZCML. Base components registries --
        or any ``IComponents`` implementation for that matter -- can be seen as
        utilities providing the aformentioned interface and are distinguishable by
        name. So let's define a "custom" registry:
        
        >>> custom = baseregistry.BaseComponents(
        ...     zope.component.globalSiteManager, 'custom')
        
        Let's make sure that the parent of the custom registry is the base registry:
        
        >>> custom.__parent__
        <BaseGlobalComponents base>
        
        The registry is then registered using the standard utility directive. After
        loading the meta directives for this package,
        
        >>> from zope.configuration import xmlconfig
        >>> context = xmlconfig.string('''
        ... <configure i18n_domain="zope">
        ...   <include package="z3c.baseregistry" file="meta.zcml" />
        ...   <include package="zope.component" file="meta.zcml" />
        ... </configure>
        ... ''')
        
        we can register the registry:
        
        >>> context = xmlconfig.string('''
        ... <configure xmlns="http://namespaces.zope.org/zope" i18n_domain="zope">
        ...
        ...   <utility
        ...       component="README.custom"
        ...       provides="zope.component.interfaces.IComponents"
        ...       name="custom" />
        ...
        ... </configure>
        ... ''', context=context)
        
        The new registry can now be accessed as follows:
        
        >>> custom = zope.component.getUtility(IComponents, name='custom')
        >>> custom
        <BaseComponents custom>
        
        
        Populating Different Registries
        -------------------------------
        
        Now to the interesting part. Let's register components for both the global
        base and the "custom" registry. Let's first create some utilities we can
        register:
        
        >>> import zope.interface
        
        >>> class IExample(zope.interface.Interface):
        ...     name = zope.interface.Attribute('Name of Example')
        
        >>> class Example(object):
        ...     zope.interface.implements(IExample)
        ...     def __init__(self, name):
        ...         self.name = name
        ...     def __repr__(self):
        ...         return '<%s %r>' %(self.__class__.__name__, self.name)
        
        >>> example1 = Example('example1')
        >>> example2 = Example('example2')
        
        Create some adapters we can register:
        
        >>> class IToAdapt1(zope.interface.Interface):
        ...     pass
        
        >>> class IToAdapt2(zope.interface.Interface):
        ...     pass
        
        >>> class IAdapted(zope.interface.Interface):
        ...     pass
        
        >>> @zope.component.adapter(IToAdapt1)
        ... @zope.interface.implementer(IAdapted)
        ... def adapter1(context):
        ...     return "adapted1"
        
        >>> @zope.component.adapter(IToAdapt2)
        ... @zope.interface.implementer(IAdapted)
        ... def adapter2(context):
        ...     return "adapted2"
        
        >>> class ToAdapt1(object):
        ...     zope.interface.implements(IToAdapt1)
        ...     def __init__(self, name):
        ...         self.name = name
        ...     def __repr__(self):
        ...         return '<%s %r>' %(self.__class__.__name__, self.name)
        >>> toAdapt1 = ToAdapt1('toAdapt1')
        
        >>> class ToAdapt2(object):
        ...     zope.interface.implements(IToAdapt2)
        ...     def __init__(self, name):
        ...         self.name = name
        ...     def __repr__(self):
        ...         return '<%s %r>' %(self.__class__.__name__, self.name)
        >>> toAdapt2 = ToAdapt2('toAdapt2')
        
        Let' now register "example1", adapter1 in the global registry
        and "example2", "adapter2" in our custom registry:
        
        >>> context = xmlconfig.string('''
        ... <configure xmlns="http://namespaces.zope.org/zope" i18n_domain="zope">
        ...
        ...   <utility component="README.example1"
        ...            name="example1" />
        ...   <adapter
        ...         factory="README.adapter1"
        ...         name="adapter1"/>
        ...
        ...   <registerIn registry="README.custom">
        ...     <utility component="README.example2"
        ...              name="example2" />
        ...     <adapter
        ...         factory="README.adapter2"
        ...         name="adapter2"/>
        ...   </registerIn>
        ...
        ... </configure>
        ... ''', context=context)
        
        Let's now make sure that the utilities have been registered in the right
        registry:
        
        >>> zope.component.getUtility(IExample, name="example1")
        <Example 'example1'>
        
        >>> zope.component.getUtility(IExample, name="example2")
        Traceback (most recent call last):
        ...
        ComponentLookupError: (<InterfaceClass README.IExample>, 'example2')
        
        Let's now make sure that the adapters have been registered in the right
        registry:
        
        >>> zope.component.getAdapter(toAdapt1, IAdapted, name="adapter1")
        'adapted1'
        
        >>> zope.component.getAdapter(toAdapt2, IAdapted, name="adapter2")
        Traceback (most recent call last):
        ...
        ComponentLookupError: (<ToAdapt2 'toAdapt2'>, <InterfaceClass README.IAdapted>, 'adapter2')
        
        
        >>> custom = zope.component.getUtility(IComponents, name='custom')
        
        >>> custom.getUtility(IExample, name="example1")
        Traceback (most recent call last):
        ...
        ComponentLookupError: (<InterfaceClass README.IExample>, 'example1')
        
        >>> custom.getUtility(IExample, name="example2")
        <Example 'example2'>
        
        
        >>> custom.getAdapter(toAdapt1, IAdapted, name="adapter1")
        Traceback (most recent call last):
        ...
        ComponentLookupError: (<ToAdapt1 'toAdapt1'>, <InterfaceClass README.IAdapted>, 'adapter1')
        
        >>> custom.getAdapter(toAdapt2, IAdapted, name="adapter2")
        'adapted2'
        
        
        Let's now register other instances of the ``Example`` class without a
        name. This should *not* cause a conflict error:
        
        >>> example3 = Example('example3')
        >>> example4 = Example('example4')
        
        >>> context = xmlconfig.string('''
        ... <configure xmlns="http://namespaces.zope.org/zope" i18n_domain="zope">
        ...
        ...   <utility component="README.example3" />
        ...
        ...   <registerIn registry="README.custom">
        ...     <utility component="README.example4" />
        ...   </registerIn>
        ...
        ... </configure>
        ... ''', context=context)
        
        >>> zope.component.getUtility(IExample)
        <Example 'example3'>
        
        >>> custom.getUtility(IExample)
        <Example 'example4'>
        
        
        Using Base Registries
        ---------------------
        
        Most commonly base registries will be used in local site managers. So let's
        create a local site:
        
        >>> from zope.site.folder import Folder
        >>> site = Folder()
        
        >>> from zope.site.site import LocalSiteManager
        >>> site.setSiteManager(LocalSiteManager(site))
        >>> sm = site.getSiteManager()
        
        Initially only the base global registry is a base of the local site manager:
        
        >>> sm.__bases__
        (<BaseGlobalComponents base>,)
        
        Now only registrations from the base site are available:
        
        >>> sm.getUtility(IExample)
        <Example 'example3'>
        
        >>> sm.getUtility(IExample, name="example1")
        <Example 'example1'>
        
        >>> sm.getUtility(IExample, name="example2")
        Traceback (most recent call last):
        ...
        ComponentLookupError: (<InterfaceClass README.IExample>, 'example2')
        
        >>> sm.getAdapter(toAdapt1, IAdapted, name="adapter1")
        'adapted1'
        
        >>> sm.getAdapter(toAdapt2, IAdapted, name="adapter2")
        Traceback (most recent call last):
        ...
        ComponentLookupError: (<ToAdapt2 'toAdapt2'>, <InterfaceClass README.IAdapted>, 'adapter2')
        
        But if we add the "custom" registry, then things look more interesting:
        
        >>> sm.__bases__ += (custom,)
        >>> sm.__bases__
        (<BaseGlobalComponents base>, <BaseComponents custom>)
        
        >>> sm.getUtility(IExample)
        <Example 'example3'>
        
        >>> sm.getUtility(IExample, name="example1")
        <Example 'example1'>
        
        >>> sm.getUtility(IExample, name="example2")
        <Example 'example2'>
        
        >>> sm.getAdapter(toAdapt1, IAdapted, name="adapter1")
        'adapted1'
        
        >>> sm.getAdapter(toAdapt2, IAdapted, name="adapter2")
        'adapted2'
        
        But where is the registration for example 4? Well, the order of the bases
        matters, like the order of base classes in Python matters. The bases run from
        must specific to most generic. Thus, if we reverse the order,
        
        >>> bases = list(sm.__bases__)
        >>> bases.reverse()
        >>> sm.__bases__ = bases
        >>> sm.__bases__
        (<BaseComponents custom>, <BaseGlobalComponents base>)
        
        then our "custom" registry effectively overrides the global one:
        
        >>> sm.getUtility(IExample)
        <Example 'example4'>
        
        >>> sm.getUtility(IExample, name="example1")
        <Example 'example1'>
        
        >>> sm.getUtility(IExample, name="example2")
        <Example 'example2'>
        
        
        Edge Cases and Food for Thought
        -------------------------------
        
        Duplicate Registrations
        ~~~~~~~~~~~~~~~~~~~~~~~
        
        Like before, duplicate registrations are detected and reported:
        
        >>> context = xmlconfig.string('''
        ... <configure xmlns="http://namespaces.zope.org/zope" i18n_domain="zope">
        ...
        ...   <registerIn registry="README.custom">
        ...     <utility component="README.example3" name="default" />
        ...     <utility component="README.example4" name="default" />
        ...   </registerIn>
        ...
        ... </configure>
        ... ''', context=context)
        Traceback (most recent call last):
        ...
        ConfigurationConflictError: Conflicting configuration actions
        For: (<BaseComponents custom>,
        ('utility', <InterfaceClass README.IExample>, u'default'))
        ...
        
        But as we have seen before, no duplication error is raied, if the same
        registration is made for different sites:
        
        >>> context = xmlconfig.string('''
        ... <configure xmlns="http://namespaces.zope.org/zope" i18n_domain="zope">
        ...
        ...   <utility component="README.example3" name="default" />
        ...
        ...   <registerIn registry="README.custom">
        ...     <utility component="README.example4" name="default" />
        ...   </registerIn>
        ...
        ... </configure>
        ... ''', context=context)
        
        
        Overriding ZCML
        ~~~~~~~~~~~~~~~
        
        Overriding should behave as usual. If I define something within a particular
        site, then it should be only overridable in that site.
        
        In the following example, ``base-overrides.zcml`` overrides only the global
        registration of the following snippet to "example3":
        
        >>> context.includepath = ('base.zcml', 'original.zcml')
        >>> context = xmlconfig.string('''
        ... <configure xmlns="http://namespaces.zope.org/zope" i18n_domain="zope">
        ...
        ...   <utility component="README.example1" />
        ...
        ...   <registerIn registry="README.custom">
        ...     <utility component="README.example2" />
        ...   </registerIn>
        ...
        ... </configure>
        ... ''', context=context, execute=False)
        
        >>> context.includepath = ('base.zcml',)
        >>> context = xmlconfig.string('''
        ...   <includeOverrides package="z3c.baseregistry.tests"
        ...                     file="base-overrides.zcml" />
        ... ''', context=context)
        
        >>> zope.component.getUtility(IExample)
        <Example 'example3'>
        
        >>> custom.getUtility(IExample)
        <Example 'example2'>
        
        In the next example, ``custom-overrides.zcml`` overrides only the custom
        registration of the following snippet to "example3":
        
        >>> context.includepath = ('base.zcml', 'original.zcml')
        >>> context = xmlconfig.string('''
        ... <configure xmlns="http://namespaces.zope.org/zope" i18n_domain="zope">
        ...
        ...   <utility component="README.example1" />
        ...
        ...   <registerIn registry="README.custom">
        ...     <utility component="README.example4" />
        ...   </registerIn>
        ...
        ... </configure>
        ... ''', context=context, execute=False)
        
        >>> context.includepath = ('base.zcml',)
        >>> context = xmlconfig.string('''
        ... <configure xmlns="http://namespaces.zope.org/zope" i18n_domain="zope">
        ...
        ...   <includeOverrides package="z3c.baseregistry.tests"
        ...                     file="custom-overrides.zcml" />
        ...
        ... </configure>
        ... ''', context=context)
        
        >>> zope.component.getUtility(IExample)
        <Example 'example1'>
        
        >>> custom.getUtility(IExample)
        <Example 'example3'>
        
        Note: Sorry for the convoluted test sequence; this is just how it works. :-(
        
        
        Nested Registry Usage
        ~~~~~~~~~~~~~~~~~~~~~
        
        I thought about this one for a long time, but I think it is better not
        allowing to nest ``zope:registerIn`` directives, because the logic of
        manipulating the discriminator would be very complex for very little added
        benefit.
        
        >>> context = xmlconfig.string('''
        ... <configure xmlns="http://namespaces.zope.org/zope" i18n_domain="zope">
        ...
        ...   <registerIn registry="README.custom">
        ...     <registerIn registry="zope.component.globalregistry.base">
        ...       <utility component="README.example4" />
        ...     </registerIn>
        ...   </registerIn>
        ...
        ... </configure>
        ... ''', context=context)
        Traceback (most recent call last):
        ...
        ZopeXMLConfigurationError: File "<string>", line 5...
        ConfigurationError: Nested ``registerIn`` directives are not permitted.
        
        Cleanup
        ~~~~~~~
        
        Just unregister the ``zope.component`` hooks:
        
        >>> zope.component.hooks.resetHooks()
        
        
        Global Non-Component-Registration Actions
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        
        ZCML is not only responsible for populating the comonents registries, but also
        to do other global configuration, such as defining security and assigning
        interfaces to classes. On the other hand, the ``registerIn`` directive works
        by manipulating the discriminator by prefixing it with the current
        registry. While I assert that this is the right approach for component
        registrations, it does not work for those other global configurations.
        
        In order to address the issue, I need somehow more information. A balance must
        be struck between the need to change existing directives and making the
        solution non-monolithic. Here are some design ideas:
        
        1. A Special Discriminator Prefix
        
        All directives that globally manipulate the state of the system and do not
        register a component have as their first discriminator entry a special
        string, like "StateChange". The directive can then look for those entries and
        not change the discriminator at this point.
        
        Advantages include the ability to use those directives inside the
        ``registerIn`` directive and allow gradual upgrading. In the other hand, util
        directives are adjusted, conflict resolution will not be available for those
        scenarios.
        
        2. A Registry of Global Action Callables
        
        Here this package provides a registry of callables that change the state of
        the system. Directive authors can then subscribe their callables to this
        registry.
        
        The big advantage of this approach is that you can make it work now for all
        built-in directives without changing any implementation. The disadvantage is
        that the solution hides the problem to directive authors, so that detailed
        documentation must be provided to ensure integrity and avoid
        surprises. Another disadvantage is the complexity of yet another registry.
        
        3. Autodetection with False-Positives
        
        As far as I can tell, all actions that manipulate the components registries
        use the ``zope.component.zcml.handler`` function. Okay, so that allows me to
        detect those. Unfortunately, there might be directives that do *not*
        manipulate the state, for example ensuring the existance of something. There
        are a bunch of those directives in the core.
        
        The advantage here is that for the core it should just work. However, 3rd
        party directive developers might be tripped by this feature. Also, we could
        only issue warnings with this solution and probably need to be able to turn
        them off.
        
        I have not implemented any of those suggestions, waiting for input from the
        community.
        
        
        =======
        CHANGES
        =======
        
        1.3.0 (2010-10-28)
        ------------------
        
        - *Fundamental change* in the way how baseregistry hooks into ZCA.
        Now it uses ``hooks.setSite``, which requires that ``zope.component`` hooks
        are in place. Usually they are installed by ``zope.app.appsetup``.
        Unless you use ``zope.app.appsetup``, install the hooks with
        ``zope.component.hooks.setHooks()``.
        This applies to ``zope.component`` versions >= 3.9.4.
        
        
        1.2.0 (2009-12-27)
        ------------------
        
        - Moved browser dependencies to 'zmi' extras
        
        
        1.1.0 (2009-03-19)
        ------------------
        
        - Fix base registry management form failure in case, when a site has its
        parent's local site manager (that isn't registered as utility) in its
        __bases__.
        
        - Use zope.site instead of zope.app.component.
        
        - Drop unused dependencies on zope.app.i18n and zope.app.pagetemplate.
        
        
        1.0.0 (2008-01-24)
        ------------------
        
        - Initial Release
        
Keywords: zope3 z3c component global registry baseregistry
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Zope Public License
Classifier: Programming Language :: Python
Classifier: Natural Language :: English
Classifier: Operating System :: OS Independent
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Framework :: Zope3
