Metadata-Version: 1.0
Name: zope.app.component
Version: 3.4.0
Summary: Local Zope Component Support
Home-page: http://pypi.python.org/pypi/zope.app.component
Author: Zope Corporation and Contributors
Author-email: zope3-dev@zope.org
License: ZPL 2.1
Description: This package allows for local component registries and local component
        management.
        
        
        Detailed Documentation
        **********************
        
        
        =====================================
        Zope 3's Local Component Architecture
        =====================================
        
        The local component architecture provides several packages that can be used
        independent of the entire component architecture framework. Thus, I decided to
        document these frameworks in different files.
        
        o Registration Framework (`registration.txt`)
        
        Provides an API for creating custom registries. The entries of the
        registries are managed via registration components. A specific
        implementation for component-based registrations is also
        provided. Finally, there are some generic container classes that allow the
        developer to manage the components and their registrations.
        
        o Local Adapter Registry (`adapterregistry.txt`)
        
        Provides a persistent adapter registry that uses the registration
        framework. Local registries can be assembled to a registry tree where
        nodes further down in the tree overrride registrations of higher-up nodes.
        
        o Sites and Local Site Managers (`site.txt`)
        
        Provides a local and persistent site manager implementation, so that one
        can register local utilities and adapters. It uses local adapter
        registries for its adapter and utility registry. The module also provides
        some facilities to organize the local software and ensures the correct
        behavior inside the ZODB.
        
        
        Local Component Architecture API
        --------------------------------
        
        While the component architecture API provided by ``zope.component`` is
        sufficient most of the time, there are a couple of functions that are useful
        in the context of multiple sites and base component registries.
        
        --- BBB: Deprecated on 9/26/2006 --
        
        >>> import zope.deprecation
        >>> zope.deprecation.__show__.off()
        
        A very common use case is to get the nearest site manager in a given
        context. Sometimes, however, one wants the next higher-up site manager. First,
        let's create a folder tree and create some sites from it:
        
        >>> from zope.app.testing import setup
        >>> root = setup.buildSampleFolderTree()
        >>> root_sm = setup.createSiteManager(root)
        >>> folder1_sm = setup.createSiteManager(root['folder1'])
        
        If we ask `folder1` for its nearest site manager, we get
        
        >>> from zope.app import zapi
        >>> zapi.getSiteManager(root['folder1']) is folder1_sm
        True
        
        but its next site manager is
        
        >>> from zope.app import component
        >>> component.getNextSiteManager(root['folder1']) is root_sm
        True
        
        The next site manager of the local root is the global site manager:
        
        >>> gsm = zapi.getGlobalSiteManager()
        >>> component.getNextSiteManager(root) is gsm
        True
        
        If a non-location is passed into the function, a component lookup error is
        raised, since there is no site manager beyond the global site manager:
        
        >>> component.getNextSiteManager(object())
        Traceback (most recent call last):
        ...
        ComponentLookupError: No more site managers have been found.
        
        If you use the `queryNextSiteManager()` function, you can specify a `default`
        return value:
        
        >>> component.queryNextSiteManager(object(), 'default')
        'default'
        
        >>> zope.deprecation.__show__.on()
        
        --- BBB: End of block --
        
        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 start by
        creating a utility and inserting it in our folder hiearchy:
        
        >>> import zope.interface
        >>> class IMyUtility(zope.interface.Interface):
        ...     pass
        
        >>> class MyUtility(object):
        ...     zope.interface.implements(IMyUtility)
        ...     def __init__(self, id):
        ...         self.id = id
        ...     def __repr__(self):
        ...         return "%s('%s')" %(self.__class__.__name__, self.id)
        
        >>> gutil = MyUtility('global')
        >>> gsm.registerUtility(gutil, IMyUtility, 'myutil')
        
        >>> util1 = setup.addUtility(folder1_sm, 'myutil', IMyUtility,
        ...                          MyUtility('one'))
        
        >>> folder1_1_sm = setup.createSiteManager(root['folder1']['folder1_1'])
        >>> util1_1 = setup.addUtility(folder1_1_sm, 'myutil', IMyUtility,
        ...                            MyUtility('one-one'))
        
        Now, if we ask `util1_1` for its next available utility and we get
        
        >>> component.getNextUtility(util1_1, IMyUtility, 'myutil')
        MyUtility('one')
        
        Next we ask `util1` for its next utility and we should get the global version:
        
        >>> component.getNextUtility(util1, IMyUtility, 'myutil')
        MyUtility('global')
        
        However, if we ask the global utility for the next one, an error is raised
        
        >>> component.getNextUtility(gutil, IMyUtility,
        ...                          'myutil') #doctest: +NORMALIZE_WHITESPACE
        Traceback (most recent call last):
        ...
        ComponentLookupError:
        No more utilities for <InterfaceClass __builtin__.IMyUtility>,
        'myutil' have been found.
        
        or you can simply use the `queryNextUtility` and specify a default:
        
        >>> component.queryNextUtility(gutil, IMyUtility, 'myutil', 'default')
        'default'
        
        Let's now ensure that the function also works with multiple registries. First
        we create another base registry:
        
        >>> from zope.component import registry
        >>> myregistry = registry.Components()
        
        >>> custom_util = MyUtility('my_custom_util')
        >>> myregistry.registerUtility(custom_util, IMyUtility, 'my_custom_util')
        
        Now we add it as a base to the local site manager:
        
        >>> folder1_sm.__bases__ = (myregistry,) + folder1_sm.__bases__
        
        Both, the ``myregistry`` and global utilities should be available:
        
        >>> component.queryNextUtility(folder1_sm, IMyUtility, 'my_custom_util')
        MyUtility('my_custom_util')
        >>> component.queryNextUtility(folder1_sm, IMyUtility, 'myutil')
        MyUtility('global')
        
        
        =============================
        Sites and Local Site Managers
        =============================
        
        This chapter is an introduction of the location-based component
        architecture. This code uses the registration framework introduced in
        `registration.txt` and local adapter registries described in
        `adapterregistry.txt`.
        
        
        Creating and Accessing Sites
        ----------------------------
        
        *Sites* are used to provide custom component setups for parts of your
        application or Web site. Every folder,
        
        >>> from zope.app.folder import folder
        >>> myfolder = folder.rootFolder()
        
        has the potential to become a site
        
        >>> from zope.app.component import interfaces
        >>> interfaces.IPossibleSite.providedBy(myfolder)
        True
        
        but is not yet one:
        
        >>> interfaces.ISite.providedBy(myfolder)
        False
        
        If you would like your custom content component to be able to become a site,
        you can use the `SiteManagerContainer` mix-in class:
        
        >>> from zope.app.component import site
        >>> class MyContentComponent(site.SiteManagerContainer):
        ...     pass
        
        >>> myContent = MyContentComponent()
        >>> interfaces.IPossibleSite.providedBy(myContent)
        True
        >>> interfaces.ISite.providedBy(myContent)
        False
        
        To convert a possible site to a real site, we have to provide a site manager:
        
        >>> sm = site.LocalSiteManager(myfolder)
        >>> myfolder.setSiteManager(sm)
        >>> interfaces.ISite.providedBy(myfolder)
        True
        >>> myfolder.getSiteManager() is sm
        True
        
        Note that an event is generated when a local site manager is created:
        
        >>> from zope.component.eventtesting import getEvents
        >>> from zope.app.component.interfaces import INewLocalSite
        >>> [event] = getEvents(INewLocalSite)
        >>> event.manager is sm
        True
        
        If one tries to set a bogus site manager, a `ValueError` will be raised:
        
        >>> myfolder2 = folder.Folder()
        >>> myfolder2.setSiteManager(object)
        Traceback (most recent call last):
        ...
        ValueError: setSiteManager requires an IComponentLookup
        
        Also, if the possible site has been changed to a site already, a `TypeError`
        is raised, when one attempts to add a new site manager:
        
        >>> myfolder.setSiteManager(site.LocalSiteManager(myfolder))
        Traceback (most recent call last):
        ...
        TypeError: Already a site
        
        There is also an adapter you can use to get the next site manager from any
        location:
        
        >>> myfolder['mysubfolder'] = folder.Folder()
        >>> import zope.component
        >>> zope.component.interfaces.IComponentLookup(myfolder['mysubfolder']) is sm
        True
        
        If the location passed is a site, the site manager of that site is returned:
        
        >>> zope.component.interfaces.IComponentLookup(myfolder) is sm
        True
        
        During traversal,
        
        >>> from zope.app import publication
        >>> request = object()
        >>> ev = publication.interfaces.BeforeTraverseEvent(myfolder, request)
        >>> site.threadSiteSubscriber(myfolder, ev)
        
        the nearest site is also recorded in a thread-global variable:
        
        >>> from zope.app.component import hooks
        >>> hooks.getSite() is myfolder
        True
        
        After a request is completed, the site setting is cleared again:
        
        >>> ev = publication.interfaces.EndRequestEvent(myfolder, request)
        >>> site.clearThreadSiteSubscriber(ev)
        >>> hooks.getSite() is None
        True
        
        
        Using the Site Manager
        ----------------------
        
        A site manager contains several *site management folders*, which are used to
        logically organize the software. When a site manager is initialized, a default
        site management folder is created:
        
        >>> sm = myfolder.getSiteManager()
        >>> default = sm['default']
        >>> default.__class__
        <class 'zope.app.component.site.SiteManagementFolder'>
        
        You can easily create a new site management folder:
        
        >>> sm['mySMF'] = site.SiteManagementFolder()
        >>> sm['mySMF'].__class__
        <class 'zope.app.component.site.SiteManagementFolder'>
        
        Once you have your site management folder -- let's use the default one -- we
        can register some components. Let's start with a utility
        
        >>> import zope.interface
        >>> class IMyUtility(zope.interface.Interface):
        ...     pass
        
        >>> import persistent
        >>> from zope.app.container.contained import Contained
        >>> class MyUtility(persistent.Persistent, Contained):
        ...     zope.interface.implements(IMyUtility)
        ...     def __init__(self, title):
        ...         self.title = title
        ...     def __repr__(self):
        ...         return "%s('%s')" %(self.__class__.__name__, self.title)
        
        Now we can create an instance of our utility and put it in the site
        management folder and register it:
        
        >>> myutil = MyUtility('My custom utility')
        >>> default['myutil'] = myutil
        >>> sm.registerUtility(myutil, IMyUtility, 'u1')
        
        Now we can ask the site manager for the utility:
        
        >>> sm.queryUtility(IMyUtility, 'u1')
        MyUtility('My custom utility')
        
        Of course, the local site manager has also access to the global component
        registrations:
        
        >>> gutil = MyUtility('Global Utility')
        >>> from zope.app import zapi
        >>> gsm = zapi.getGlobalSiteManager()
        >>> gsm.registerUtility(gutil, IMyUtility, 'gutil')
        
        >>> sm.queryUtility(IMyUtility, 'gutil')
        MyUtility('Global Utility')
        
        Next let's see whether we can also successfully register an adapter as
        well. Here the adapter will provide the size of a file:
        
        >>> class IFile(zope.interface.Interface):
        ...     pass
        
        >>> class ISized(zope.interface.Interface):
        ...     pass
        
        >>> class File(object):
        ...     zope.interface.implements(IFile)
        
        >>> class FileSize(object):
        ...     zope.interface.implements(ISized)
        ...     def __init__(self, context):
        ...         self.context = context
        
        Now that we have the adapter we need to register it:
        
        >>> sm.registerAdapter(FileSize, [IFile])
        
        Finally, we can get the adapter for a file:
        
        >>> file = File()
        >>> size = sm.queryAdapter(file, ISized, name='')
        >>> size.__class__
        <class 'FileSize'>
        >>> size.context is file
        True
        
        By the way, once you set a site
        
        >>> hooks.setSite(myfolder)
        
        you can simply use the ZAPI's `getSiteManager()` method to get the nearest
        site manager:
        
        >>> zapi.getSiteManager() is sm
        True
        
        This also means that you can simply use ZAPI to look up your utility
        
        >>> zapi.getUtility(IMyUtility, 'gutil')
        MyUtility('Global Utility')
        
        or the adapter via the interface's `__call__` method:
        
        >>> size = ISized(file)
        >>> size.__class__
        <class 'FileSize'>
        >>> size.context is file
        True
        
        
        Multiple Sites
        --------------
        
        Until now we have only dealt with one local and the global site. But things
        really become interesting, once we have multiple sites. We can override other
        local configuration.
        
        Let's now create a new folder called `folder11`, add it to `myfolder` and make
        it a site:
        
        >>> myfolder11 = folder.Folder()
        >>> myfolder['myfolder11'] = myfolder11
        >>> myfolder11.setSiteManager(site.LocalSiteManager(myfolder11))
        >>> sm11 = myfolder11.getSiteManager()
        
        If we ask the second site manager for its next, we get
        
        >>> sm11.__bases__ == (sm, )
        True
        
        and the first site manager should have the folling sub manager:
        
        >>> sm.subs == (sm11,)
        True
        
        If we now register a second utility with the same name and interface with the
        new site manager folder,
        
        >>> default11 = sm11['default']
        >>> myutil11 = MyUtility('Utility, uno & uno')
        >>> default11['myutil'] = myutil11
        
        >>> sm11.registerUtility(myutil11, IMyUtility, 'u1')
        
        then it will will be available in the second site manager
        
        >>> sm11.queryUtility(IMyUtility, 'u1')
        MyUtility('Utility, uno & uno')
        
        but not in the first one:
        
        >>> sm.queryUtility(IMyUtility, 'u1')
        MyUtility('My custom utility')
        
        It is also interesting to look at the use cases of moving and copying a
        site. To do that we create a second root folder and make it a site, so that
        site hierarchy is as follows:
        
        ::
        
        _____ global site _____
        /                       \
        myfolder1                myfolder2
        |
        myfolder11
        
        
        >>> myfolder2 = folder.rootFolder()
        >>> myfolder2.setSiteManager(site.LocalSiteManager(myfolder2))
        
        Before we can move or copy sites, we need to register two event subscribers
        that manage the wiring of site managers after moving or copying:
        
        >>> from zope.app import container
        >>> gsm.registerHandler(
        ...    site.changeSiteConfigurationAfterMove,
        ...    (interfaces.ISite, container.interfaces.IObjectMovedEvent),
        ...    )
        
        We only have to register one event listener, since the copy action causes an
        `IObjectAddedEvent` to be created, which is just a special type of
        `IObjectMovedEvent`.
        
        First, make sure that everything is setup correctly in the first place:
        
        >>> myfolder11.getSiteManager().__bases__ == (myfolder.getSiteManager(), )
        True
        >>> myfolder.getSiteManager().subs[0] is myfolder11.getSiteManager()
        True
        >>> myfolder2.getSiteManager().subs
        ()
        
        Let's now move `myfolder11` from `myfolder` to `myfolder2`:
        
        >>> myfolder2['myfolder21'] = myfolder11
        >>> del myfolder['myfolder11']
        
        Now the next site manager for `myfolder11`'s site manager should have changed:
        
        >>> myfolder21 = myfolder11
        >>> myfolder21.getSiteManager().__bases__ == (myfolder2.getSiteManager(), )
        True
        >>> myfolder2.getSiteManager().subs[0] is myfolder21.getSiteManager()
        True
        >>> myfolder.getSiteManager().subs
        ()
        
        Finally, we make a copy of `myfolder21` and add it as `myfolder11` to
        `myfolder`:
        
        # Make sure that our interfaces and classes are picklable.
        
        >>> import sys
        >>> sys.modules['zope.app.component.tests'].IMyUtility = IMyUtility
        >>> IMyUtility.__module__ = 'zope.app.component.tests'
        >>> sys.modules['zope.app.component.tests'].MyUtility = MyUtility
        >>> MyUtility.__module__ = 'zope.app.component.tests'
        
        >>> from zope.location.pickling import locationCopy
        >>> myfolder['myfolder11'] = locationCopy(myfolder2['myfolder21'])
        
        >>> myfolder11 = myfolder['myfolder11']
        >>> myfolder11.getSiteManager().__bases__ == (myfolder.getSiteManager(), )
        True
        >>> myfolder.getSiteManager().subs[0] is myfolder11.getSiteManager()
        True
        >>> myfolder2.getSiteManager().subs[0] is myfolder21.getSiteManager()
        True
        
        
        =======
        CHANGES
        =======
        
        3.4.0 (2007-10-11)
        ------------------
        
        - Initial release independent of the main Zope tree.
        
Keywords: zope component architecture local
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
