[Checkins] SVN: z3c.baseregistry/trunk/src/z3c/ Initial import of
base registry code. Here is the introduction sentence
Stephan Richter
srichter at cosmos.phy.tufts.edu
Sun Sep 3 14:51:48 EDT 2006
Log message for revision 69952:
Initial import of base registry code. Here is the introduction sentence
of the README:
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.
** This is something you always wanted -- even if you did not know! **
Changed:
A z3c.baseregistry/trunk/src/z3c/
A z3c.baseregistry/trunk/src/z3c/__init__.py
A z3c.baseregistry/trunk/src/z3c/baseregistry/
A z3c.baseregistry/trunk/src/z3c/baseregistry/README.txt
A z3c.baseregistry/trunk/src/z3c/baseregistry/SETUP.cfg
A z3c.baseregistry/trunk/src/z3c/baseregistry/__init__.py
A z3c.baseregistry/trunk/src/z3c/baseregistry/baseregistry.py
A z3c.baseregistry/trunk/src/z3c/baseregistry/browser/
A z3c.baseregistry/trunk/src/z3c/baseregistry/browser/README.txt
A z3c.baseregistry/trunk/src/z3c/baseregistry/browser/__init__.py
A z3c.baseregistry/trunk/src/z3c/baseregistry/browser/base.py
A z3c.baseregistry/trunk/src/z3c/baseregistry/browser/configure.zcml
A z3c.baseregistry/trunk/src/z3c/baseregistry/browser/ftesting.zcml
A z3c.baseregistry/trunk/src/z3c/baseregistry/browser/ftests.py
A z3c.baseregistry/trunk/src/z3c/baseregistry/meta.zcml
A z3c.baseregistry/trunk/src/z3c/baseregistry/tests/
A z3c.baseregistry/trunk/src/z3c/baseregistry/tests/__init__.py
A z3c.baseregistry/trunk/src/z3c/baseregistry/tests/base-overrides.zcml
A z3c.baseregistry/trunk/src/z3c/baseregistry/tests/custom-overrides.zcml
A z3c.baseregistry/trunk/src/z3c/baseregistry/tests/testdoc.py
A z3c.baseregistry/trunk/src/z3c/baseregistry/z3c.baseregistry-meta.zcml
A z3c.baseregistry/trunk/src/z3c/baseregistry/z3c.baseregistry.browser-configure.zcml
A z3c.baseregistry/trunk/src/z3c/baseregistry/z3c.baseregistry.browser-ftesting.zcml
A z3c.baseregistry/trunk/src/z3c/baseregistry/zcml.py
-=-
Added: z3c.baseregistry/trunk/src/z3c/__init__.py
===================================================================
--- z3c.baseregistry/trunk/src/z3c/__init__.py 2006-09-03 18:49:32 UTC (rev 69951)
+++ z3c.baseregistry/trunk/src/z3c/__init__.py 2006-09-03 18:51:47 UTC (rev 69952)
@@ -0,0 +1,2 @@
+# Make a package.
+
Property changes on: z3c.baseregistry/trunk/src/z3c/__init__.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.baseregistry/trunk/src/z3c/baseregistry/README.txt
===================================================================
--- z3c.baseregistry/trunk/src/z3c/baseregistry/README.txt 2006-09-03 18:49:32 UTC (rev 69951)
+++ z3c.baseregistry/trunk/src/z3c/baseregistry/README.txt 2006-09-03 18:51:47 UTC (rev 69952)
@@ -0,0 +1,522 @@
+===============
+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>
+
+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: (<zope.component.interfaces.ComponentLookupError ...>,
+ <function BC at ...>,
+ (<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 ther 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')
+
+Let' now register "example1" in the global registry and "example2" in our
+custom registry:
+
+ >>> context = xmlconfig.string('''
+ ... <configure xmlns="http://namespaces.zope.org/zope" i18n_domain="zope">
+ ...
+ ... <utility component="README.example1"
+ ... name="example1" />
+ ...
+ ... <registerIn registry="README.custom">
+ ... <utility component="README.example2"
+ ... name="example2" />
+ ... </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')
+
+ >>> 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'>
+
+Let's now register other instances of the ``Example`` class without a
+name. This should *not* cause a conflect 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.app.folder import Folder
+ >>> site = Folder()
+
+ >>> from zope.app.component.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')
+
+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'>
+
+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 Regstry 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.4
+ ConfigurationError: Nested ``registerIn`` directives are not permitted.
+
+
+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.
Property changes on: z3c.baseregistry/trunk/src/z3c/baseregistry/README.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.baseregistry/trunk/src/z3c/baseregistry/SETUP.cfg
===================================================================
--- z3c.baseregistry/trunk/src/z3c/baseregistry/SETUP.cfg 2006-09-03 18:49:32 UTC (rev 69951)
+++ z3c.baseregistry/trunk/src/z3c/baseregistry/SETUP.cfg 2006-09-03 18:51:47 UTC (rev 69952)
@@ -0,0 +1,4 @@
+<data-files zopeskel/etc/package-includes>
+ z3c.baseregistry-*.zcml
+ z3c.baseregistry.browser-*.zcml
+</data-files>
Added: z3c.baseregistry/trunk/src/z3c/baseregistry/__init__.py
===================================================================
--- z3c.baseregistry/trunk/src/z3c/baseregistry/__init__.py 2006-09-03 18:49:32 UTC (rev 69951)
+++ z3c.baseregistry/trunk/src/z3c/baseregistry/__init__.py 2006-09-03 18:51:47 UTC (rev 69952)
@@ -0,0 +1 @@
+# Make a package.
Property changes on: z3c.baseregistry/trunk/src/z3c/baseregistry/__init__.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.baseregistry/trunk/src/z3c/baseregistry/baseregistry.py
===================================================================
--- z3c.baseregistry/trunk/src/z3c/baseregistry/baseregistry.py 2006-09-03 18:49:32 UTC (rev 69951)
+++ z3c.baseregistry/trunk/src/z3c/baseregistry/baseregistry.py 2006-09-03 18:51:47 UTC (rev 69952)
@@ -0,0 +1,34 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Base Components support
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+from zope.component import globalregistry, interfaces
+
+def BC(components, name):
+ return components.getUtility(interfaces.IComponents, name)
+
+class BaseComponents(globalregistry.BaseGlobalComponents):
+ """An ``IComponents`` implementation that serves as base for other
+ components."""
+
+ def __init__(self, parent, *args, **kw):
+ self.__parent__ = parent
+ super(BaseComponents, self).__init__(*args, **kw)
+
+ def __reduce__(self):
+ # Global site managers are pickled as global objects
+ return BC, (self.__parent__, self.__name__)
Property changes on: z3c.baseregistry/trunk/src/z3c/baseregistry/baseregistry.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.baseregistry/trunk/src/z3c/baseregistry/browser/README.txt
===================================================================
--- z3c.baseregistry/trunk/src/z3c/baseregistry/browser/README.txt 2006-09-03 18:49:32 UTC (rev 69951)
+++ z3c.baseregistry/trunk/src/z3c/baseregistry/browser/README.txt 2006-09-03 18:51:47 UTC (rev 69952)
@@ -0,0 +1,91 @@
+=================================
+Setting Named Registries as Bases
+=================================
+
+While setting up named registries and filling them in ZCML is interesting in
+itself, those features alone will not bring much, unless you can hook up those
+named registries to local sites.
+
+This is accomplished by setting the bases. Let's take our root folder, for
+example. By default, only the global base registry is registered as a site. If
+you have a quick look at ``ftesting.zcml``, you will notice that only
+"example2" is available as named utility and "example1" as the unnamed one.
+
+By setting the root folder site as the current site, we can simulate the
+behavior of calling from within the root folder:
+
+ >>> import zope.component
+ >>> from zope.app.component import hooks
+
+ >>> site = getRootFolder()
+ >>> hooks.setSite(site)
+ >>> site.getSiteManager().__bases__
+ (<BaseGlobalComponents base>,)
+
+ >>> zope.component.getUtility(IExample)
+ <Example 'example1'>
+
+ >>> zope.component.getUtility(IExample, name="example2")
+ <Example 'example2'>
+
+ >>> zope.component.getUtility(IExample, name="example4")
+ Traceback (most recent call last):
+ ...
+ ComponentLookupError:
+ (<InterfaceClass z3c.baseregistry.browser.ftests.IExample>, 'example4')
+
+Let's now add the "custom" registry to the site as a base. After logging in ..
+
+ >>> from zope.testbrowser.testing import Browser
+ >>> manager = Browser()
+ >>> manager.addHeader('Authorization', 'Basic mgr:mgrpw')
+
+ >>> manager.open('http://localhost/manage')
+
+you enter the site management area and then click on the "Bases" tab:
+
+ >>> manager.getLink('Manage Site').click()
+ >>> manager.getLink('Bases').click()
+
+Let' now add the "custom" registry:
+
+ >>> addBasesSelection(manager, ['-- Global Base Registry --', 'custom'])
+ >>> manager.getControl('Apply').click()
+
+Now, "example4" should be available, but "example3" ist overridden by
+"example1".
+
+ >>> site = getRootFolder()
+ >>> hooks.setSite(site)
+ >>> site.getSiteManager().__bases__
+ (<BaseGlobalComponents base>, <BaseComponents custom>)
+
+ >>> zope.component.getUtility(IExample)
+ <Example 'example1'>
+
+ >>> zope.component.getUtility(IExample, name="example2")
+ <Example 'example2'>
+
+ >>> zope.component.getUtility(IExample, name="example4")
+ <Example 'example4'>
+
+However, if we change the order of the bases,
+
+ >>> addBasesSelection(manager, ['custom', '-- Global Base Registry --'])
+ >>> manager.getControl('Apply').click()
+
+then "custom" registry overrides entries from the global base registry:
+
+ >>> site = getRootFolder()
+ >>> hooks.setSite(site)
+ >>> site.getSiteManager().__bases__
+ (<BaseComponents custom>, <BaseGlobalComponents base>)
+
+ >>> zope.component.getUtility(IExample)
+ <Example 'example3'>
+
+ >>> zope.component.getUtility(IExample, name="example2")
+ <Example 'example2'>
+
+ >>> zope.component.getUtility(IExample, name="example4")
+ <Example 'example4'>
Property changes on: z3c.baseregistry/trunk/src/z3c/baseregistry/browser/README.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.baseregistry/trunk/src/z3c/baseregistry/browser/__init__.py
===================================================================
--- z3c.baseregistry/trunk/src/z3c/baseregistry/browser/__init__.py 2006-09-03 18:49:32 UTC (rev 69951)
+++ z3c.baseregistry/trunk/src/z3c/baseregistry/browser/__init__.py 2006-09-03 18:51:47 UTC (rev 69952)
@@ -0,0 +1 @@
+# Make a package.
Property changes on: z3c.baseregistry/trunk/src/z3c/baseregistry/browser/__init__.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.baseregistry/trunk/src/z3c/baseregistry/browser/base.py
===================================================================
--- z3c.baseregistry/trunk/src/z3c/baseregistry/browser/base.py 2006-09-03 18:49:32 UTC (rev 69951)
+++ z3c.baseregistry/trunk/src/z3c/baseregistry/browser/base.py 2006-09-03 18:51:47 UTC (rev 69952)
@@ -0,0 +1,52 @@
+##############################################################################
+#
+# Copyright (c) 2006 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.
+#
+##############################################################################
+"""Setting bases for local sites.
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import zope.interface
+import zope.component
+import zope.schema
+from zope.app.component import vocabulary
+from zope.app.i18n import ZopeMessageFactory as _
+from zope.app.pagetemplate import ViewPageTemplateFile
+from zope.formlib import form
+
+BASENAME = _('-- Global Base Registry --')
+
+class BaseComponentsVocabulary(vocabulary.UtilityVocabulary):
+ """A vocabulary for ``IComponents`` utilities."""
+
+ interface = zope.component.interfaces.IComponents
+
+ def __init__(self, context, **kw):
+ super(BaseComponentsVocabulary, self).__init__(context, **kw)
+ self._terms[BASENAME] = vocabulary.UtilityTerm(
+ zope.component.globalregistry.base, BASENAME)
+
+class IComponentsBases(zope.interface.Interface):
+ """An interface describing the bases API of the IComponents object."""
+
+ __bases__ = zope.schema.List(
+ title=_('Bases'),
+ description=_('The base components registires of this registry.'),
+ value_type=zope.schema.Choice(vocabulary='Base Components'),
+ required=True)
+
+
+class SetBasesPage(form.EditForm):
+ """A page to set the bases of a local site manager"""
+ form_fields = form.FormFields(IComponentsBases)
+
Property changes on: z3c.baseregistry/trunk/src/z3c/baseregistry/browser/base.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.baseregistry/trunk/src/z3c/baseregistry/browser/configure.zcml
===================================================================
--- z3c.baseregistry/trunk/src/z3c/baseregistry/browser/configure.zcml 2006-09-03 18:49:32 UTC (rev 69951)
+++ z3c.baseregistry/trunk/src/z3c/baseregistry/browser/configure.zcml 2006-09-03 18:51:47 UTC (rev 69952)
@@ -0,0 +1,30 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ xmlns:browser="http://namespaces.zope.org/browser"
+ i18n_domain="zope">
+
+ <utility
+ component=".base.BaseComponentsVocabulary"
+ provides="zope.schema.interfaces.IVocabularyFactory"
+ name="Base Components"
+ />
+
+ <class class="zope.app.component.site.LocalSiteManager">
+ <implements interface=".base.IComponentsBases" />
+ <require
+ permission="zope.Public"
+ attributes="__bases__" />
+ <require
+ permission="zope.ManageSite"
+ set_attributes="__bases__" />
+ </class>
+
+ <browser:page
+ name="setBases.html"
+ for="zope.app.component.interfaces.ILocalSiteManager"
+ class=".base.SetBasesPage"
+ permission="zope.ManageSite"
+ menu="zmi_views" title="Bases"
+ />
+
+</configure>
Property changes on: z3c.baseregistry/trunk/src/z3c/baseregistry/browser/configure.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.baseregistry/trunk/src/z3c/baseregistry/browser/ftesting.zcml
===================================================================
--- z3c.baseregistry/trunk/src/z3c/baseregistry/browser/ftesting.zcml 2006-09-03 18:49:32 UTC (rev 69951)
+++ z3c.baseregistry/trunk/src/z3c/baseregistry/browser/ftesting.zcml 2006-09-03 18:51:47 UTC (rev 69952)
@@ -0,0 +1,18 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ i18n_domain="zope">
+
+ <utility
+ component=".ftests.custom"
+ provides="zope.component.interfaces.IComponents"
+ name="custom" />
+
+ <utility component=".ftests.example1" />
+ <utility component=".ftests.example2" name="example2" />
+
+ <registerIn registry=".ftests.custom">
+ <utility component=".ftests.example3" />
+ <utility component=".ftests.example4" name="example4" />
+ </registerIn>
+
+</configure>
Property changes on: z3c.baseregistry/trunk/src/z3c/baseregistry/browser/ftesting.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.baseregistry/trunk/src/z3c/baseregistry/browser/ftests.py
===================================================================
--- z3c.baseregistry/trunk/src/z3c/baseregistry/browser/ftests.py 2006-09-03 18:49:32 UTC (rev 69951)
+++ z3c.baseregistry/trunk/src/z3c/baseregistry/browser/ftests.py 2006-09-03 18:51:47 UTC (rev 69952)
@@ -0,0 +1,78 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Test setup for Base setting UI tests.
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import unittest
+import zope.component
+import zope.interface
+from zope.app.testing.functional import FunctionalDocFileSuite
+
+from z3c.baseregistry import baseregistry
+
+
+custom = baseregistry.BaseComponents(
+ zope.component.globalSiteManager, 'custom')
+
+
+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')
+example3 = Example('example3')
+example4 = Example('example4')
+
+
+def addBasesSelection(browser, bases):
+ # Get the form
+ form = browser.mech_browser.forms().next()
+ select_attrs = {'name': 'form.__bases__', 'size': '5',
+ 'multiple': 'multiple'}
+ # Create the select tag
+ form.new_control(
+ 'select', 'form.__bases__',
+ attrs={'__select': select_attrs})
+ # Add options
+ for idx, base in enumerate(bases):
+ form.new_control(
+ 'select', 'form.__bases__',
+ attrs={'__select': select_attrs,
+ 'selected': 'selected',
+ 'value': base},
+ index=idx
+ )
+
+
+def test_suite():
+ suite = unittest.TestSuite((
+ FunctionalDocFileSuite(
+ 'README.txt',
+ globs={'IExample': IExample,
+ 'addBasesSelection': addBasesSelection}),
+ ))
+
+ return suite
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
Property changes on: z3c.baseregistry/trunk/src/z3c/baseregistry/browser/ftests.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.baseregistry/trunk/src/z3c/baseregistry/meta.zcml
===================================================================
--- z3c.baseregistry/trunk/src/z3c/baseregistry/meta.zcml 2006-09-03 18:49:32 UTC (rev 69951)
+++ z3c.baseregistry/trunk/src/z3c/baseregistry/meta.zcml 2006-09-03 18:51:47 UTC (rev 69952)
@@ -0,0 +1,15 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ xmlns:meta="http://namespaces.zope.org/meta">
+
+ <meta:directives namespace="http://namespaces.zope.org/zope">
+
+ <meta:groupingDirective
+ name="registerIn"
+ schema=".zcml.IRegisterInDirective"
+ handler=".zcml.RegisterIn"
+ />
+
+ </meta:directives>
+
+</configure>
Property changes on: z3c.baseregistry/trunk/src/z3c/baseregistry/meta.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.baseregistry/trunk/src/z3c/baseregistry/tests/__init__.py
===================================================================
--- z3c.baseregistry/trunk/src/z3c/baseregistry/tests/__init__.py 2006-09-03 18:49:32 UTC (rev 69951)
+++ z3c.baseregistry/trunk/src/z3c/baseregistry/tests/__init__.py 2006-09-03 18:51:47 UTC (rev 69952)
@@ -0,0 +1 @@
+# Make a package.
Property changes on: z3c.baseregistry/trunk/src/z3c/baseregistry/tests/__init__.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.baseregistry/trunk/src/z3c/baseregistry/tests/base-overrides.zcml
===================================================================
--- z3c.baseregistry/trunk/src/z3c/baseregistry/tests/base-overrides.zcml 2006-09-03 18:49:32 UTC (rev 69951)
+++ z3c.baseregistry/trunk/src/z3c/baseregistry/tests/base-overrides.zcml 2006-09-03 18:51:47 UTC (rev 69952)
@@ -0,0 +1,5 @@
+<configure xmlns="http://namespaces.zope.org/zope">
+
+ <utility component="README.example3" />
+
+</configure>
Property changes on: z3c.baseregistry/trunk/src/z3c/baseregistry/tests/base-overrides.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.baseregistry/trunk/src/z3c/baseregistry/tests/custom-overrides.zcml
===================================================================
--- z3c.baseregistry/trunk/src/z3c/baseregistry/tests/custom-overrides.zcml 2006-09-03 18:49:32 UTC (rev 69951)
+++ z3c.baseregistry/trunk/src/z3c/baseregistry/tests/custom-overrides.zcml 2006-09-03 18:51:47 UTC (rev 69952)
@@ -0,0 +1,7 @@
+<configure xmlns="http://namespaces.zope.org/zope">
+
+ <registerIn registry="README.custom">
+ <utility component="README.example3" />
+ </registerIn>
+
+</configure>
Property changes on: z3c.baseregistry/trunk/src/z3c/baseregistry/tests/custom-overrides.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.baseregistry/trunk/src/z3c/baseregistry/tests/testdoc.py
===================================================================
--- z3c.baseregistry/trunk/src/z3c/baseregistry/tests/testdoc.py 2006-09-03 18:49:32 UTC (rev 69951)
+++ z3c.baseregistry/trunk/src/z3c/baseregistry/tests/testdoc.py 2006-09-03 18:51:47 UTC (rev 69952)
@@ -0,0 +1,40 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Base Components test setup
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import doctest
+import unittest
+from zope.app.testing import placelesssetup, setup
+from zope.testing.doctestunit import DocFileSuite
+
+def setUp(test):
+ placelesssetup.setUp(test)
+ setup.setUpTestAsModule(test, name='README')
+
+def tearDown(test):
+ placelesssetup.tearDown(test)
+
+def test_suite():
+ return unittest.TestSuite((
+ DocFileSuite('../README.txt',
+ setUp=setUp, tearDown=tearDown,
+ optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+ ),
+ ))
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
Property changes on: z3c.baseregistry/trunk/src/z3c/baseregistry/tests/testdoc.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.baseregistry/trunk/src/z3c/baseregistry/z3c.baseregistry-meta.zcml
===================================================================
--- z3c.baseregistry/trunk/src/z3c/baseregistry/z3c.baseregistry-meta.zcml 2006-09-03 18:49:32 UTC (rev 69951)
+++ z3c.baseregistry/trunk/src/z3c/baseregistry/z3c.baseregistry-meta.zcml 2006-09-03 18:51:47 UTC (rev 69952)
@@ -0,0 +1 @@
+<include package="z3c.baseregistry" file="meta.zcml" />
Property changes on: z3c.baseregistry/trunk/src/z3c/baseregistry/z3c.baseregistry-meta.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.baseregistry/trunk/src/z3c/baseregistry/z3c.baseregistry.browser-configure.zcml
===================================================================
--- z3c.baseregistry/trunk/src/z3c/baseregistry/z3c.baseregistry.browser-configure.zcml 2006-09-03 18:49:32 UTC (rev 69951)
+++ z3c.baseregistry/trunk/src/z3c/baseregistry/z3c.baseregistry.browser-configure.zcml 2006-09-03 18:51:47 UTC (rev 69952)
@@ -0,0 +1 @@
+<include package="z3c.baseregistry.browser" />
Property changes on: z3c.baseregistry/trunk/src/z3c/baseregistry/z3c.baseregistry.browser-configure.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.baseregistry/trunk/src/z3c/baseregistry/z3c.baseregistry.browser-ftesting.zcml
===================================================================
--- z3c.baseregistry/trunk/src/z3c/baseregistry/z3c.baseregistry.browser-ftesting.zcml 2006-09-03 18:49:32 UTC (rev 69951)
+++ z3c.baseregistry/trunk/src/z3c/baseregistry/z3c.baseregistry.browser-ftesting.zcml 2006-09-03 18:51:47 UTC (rev 69952)
@@ -0,0 +1 @@
+<include package="z3c.baseregistry.browser" file="ftesting.zcml" />
Property changes on: z3c.baseregistry/trunk/src/z3c/baseregistry/z3c.baseregistry.browser-ftesting.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.baseregistry/trunk/src/z3c/baseregistry/zcml.py
===================================================================
--- z3c.baseregistry/trunk/src/z3c/baseregistry/zcml.py 2006-09-03 18:49:32 UTC (rev 69951)
+++ z3c.baseregistry/trunk/src/z3c/baseregistry/zcml.py 2006-09-03 18:51:47 UTC (rev 69952)
@@ -0,0 +1,117 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""ZCML Implementation to populate base registries.
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import zope.interface
+import zope.component.globalregistry
+import zope.configuration.config
+import zope.configuration.fields
+from zope.configuration.exceptions import ConfigurationError
+
+from zope.i18nmessageid import MessageFactory
+_ = MessageFactory('zope')
+
+class IRegisterInDirective(zope.interface.Interface):
+ """Use the specified registry for registering the contained components."""
+
+ registry = zope.configuration.fields.GlobalObject(
+ title=_("Registry"),
+ description=_("Python path to the registry to use."),
+ required=True)
+
+
+class ActionsProxy(object):
+ """A proxy object for the actions list to decorate the incoming actions."""
+
+ original = None
+ registry = None
+
+ def __init__(self, original, registry):
+ self.original = original
+ self.registry = registry
+
+ def __decorate(self, item):
+ discriminator = None
+ if item[0] is not None:
+ discriminator = (self.registry, item[0])
+ return (discriminator,) + item[1:]
+
+ def __setitem__(self, i, item):
+ self.original.__setitem__(i, self.__decorate(item))
+
+ def __setslice__(self, i, j, other):
+ other = [self.__decorate(item) for item in other]
+ self.original.__setslice__(i, j, other)
+
+ def __iadd__(self, other):
+ other = [self.__decorate(item) for item in other]
+ self.original.__iadd__(other)
+
+ def append(self, item):
+ self.original.append(self.__decorate(item))
+
+ def insert(self, i, item):
+ self.original.insert(i, self.__decorate(item))
+
+ def extend(self, other):
+ other = [self.__decorate(item) for item in other]
+ self.original.extend(other)
+
+ def __getattr__(self, name):
+ return getattr(self.original, name)
+
+
+def setActiveRegistry(context, registry):
+ context.original = zope.component.globalregistry.globalSiteManager
+ # Set the temporary, base registry
+ zope.component.globalregistry.globalSiteManager = registry
+
+def resetOriginalRegistry(context):
+ zope.component.globalregistry.globalSiteManager = context.original
+
+
+class RegisterIn(zope.configuration.config.GroupingContextDecorator):
+
+ # Marker that this directive has been used in the path
+ registryChanged=True
+
+ # Storage for the original site
+ original = None
+
+ def __init__(self, context, registry, **kw):
+ if hasattr(context, 'registryChanged') and context.registryChanged:
+ raise ConfigurationError(
+ 'Nested ``registerIn`` directives are not permitted.')
+
+ super(RegisterIn, self).__init__(context, **kw)
+ self.registry = registry
+ self.actions = ActionsProxy(context.actions, registry)
+
+ def before(self):
+ self.context.action(
+ discriminator=None,
+ callable=setActiveRegistry,
+ args=(self, self.registry)
+ )
+
+ def after(self):
+ self.context.action(
+ discriminator=None,
+ callable=resetOriginalRegistry,
+ args=(self,)
+ )
+
Property changes on: z3c.baseregistry/trunk/src/z3c/baseregistry/zcml.py
___________________________________________________________________
Name: svn:keywords
+ Id
More information about the Checkins
mailing list