[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