[z3-checkins] r28821 - in z3/jsonserver/branch/merge/configfeature: . tests

reebalazs at codespeak.net reebalazs at codespeak.net
Thu Jun 15 16:02:58 CEST 2006


Author: reebalazs
Date: Thu Jun 15 16:02:55 2006
New Revision: 28821

Added:
   z3/jsonserver/branch/merge/configfeature/
   z3/jsonserver/branch/merge/configfeature/README
   z3/jsonserver/branch/merge/configfeature/__init__.py
   z3/jsonserver/branch/merge/configfeature/configure.zcml
   z3/jsonserver/branch/merge/configfeature/directives.py
   z3/jsonserver/branch/merge/configfeature/fiveconfig.py
   z3/jsonserver/branch/merge/configfeature/fiveconfig.zcml
   z3/jsonserver/branch/merge/configfeature/meta.py
   z3/jsonserver/branch/merge/configfeature/meta.zcml
   z3/jsonserver/branch/merge/configfeature/tests/
   z3/jsonserver/branch/merge/configfeature/tests/__init__.py
   z3/jsonserver/branch/merge/configfeature/tests/__parent__.py
   z3/jsonserver/branch/merge/configfeature/tests/configtest.py
   z3/jsonserver/branch/merge/configfeature/tests/test_directive.py
   z3/jsonserver/branch/merge/configfeature/tests/test_fiveconfig.py
Log:
Adding configfeature directive to tree

Added: z3/jsonserver/branch/merge/configfeature/README
==============================================================================
--- (empty file)
+++ z3/jsonserver/branch/merge/configfeature/README	Thu Jun 15 16:02:55 2006
@@ -0,0 +1,35 @@
+
+Objectives
+----------
+
+The goal of the program is to enable defining a feature based on
+configuration values set from python code. The directive imports
+a python object and uses an attribute of it as a boolean value
+to declare a feature or not.
+
+
+    <five:configfeature
+        feature="myFeature"
+        instance=".config"
+        attribute="Five2_3"
+        negate="True"
+    />
+
+
+Instance can be used as a module itself (in which case a module level
+attribute can be looked up) or a class instance (in this case an attribute
+of the instance will be used). If there is no such attribute on the instance,
+a dictionary key access will be attempted, allowing dictionaries to be used.
+
+The given attribute is used as a boolean value. It must exist, or else
+an error is raised.
+
+The featured fiveconfig.zcml serves both as an example and also can be
+used to identify which version of five is in effect. The configuration
+can be used both by zope configuration (by using the features), or
+from python code, by directly importing the module that forms the base
+for the feature setup.
+
+The negate attribute is only necessary to overcome the lack of negation
+feature in zcml:condition.
+

Added: z3/jsonserver/branch/merge/configfeature/__init__.py
==============================================================================
--- (empty file)
+++ z3/jsonserver/branch/merge/configfeature/__init__.py	Thu Jun 15 16:02:55 2006
@@ -0,0 +1,3 @@
+'''\
+Product init
+'''

Added: z3/jsonserver/branch/merge/configfeature/configure.zcml
==============================================================================
--- (empty file)
+++ z3/jsonserver/branch/merge/configfeature/configure.zcml	Thu Jun 15 16:02:55 2006
@@ -0,0 +1,7 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+           i18n_domain="zope">
+
+    <!-- Define Five compatibility features -->
+    <include file="fiveconfig.zcml" />
+
+</configure>

Added: z3/jsonserver/branch/merge/configfeature/directives.py
==============================================================================
--- (empty file)
+++ z3/jsonserver/branch/merge/configfeature/directives.py	Thu Jun 15 16:02:55 2006
@@ -0,0 +1,34 @@
+from zope.interface import Interface
+from zope.configuration.fields import GlobalObject, Bool
+from zope.schema import ASCII
+from zope.app.publisher.browser.metadirectives import IBasicResourceInformation
+
+class IConfigFeature(IBasicResourceInformation):
+    """
+    Defines a feature based on a python configuration
+    """
+
+    feature = ASCII(
+        title = u"Feature",
+        description = u"Name of the feature to define",
+        required = True,
+        )
+
+    instance = GlobalObject(
+        title = u'Object instance',
+        description = u'Dotted name of the object that holds the attribute.',
+        required = True,
+        )
+
+    attribute = ASCII(
+        title = u"Attribute",
+        description = u"Attribute name within the object.",
+        required = True,
+        )
+
+    negate = Bool(
+        title = u'Negate',
+        description = u'If to negate the boolean value, default False',
+        default = False,
+        required = False,
+        )

Added: z3/jsonserver/branch/merge/configfeature/fiveconfig.py
==============================================================================
--- (empty file)
+++ z3/jsonserver/branch/merge/configfeature/fiveconfig.py	Thu Jun 15 16:02:55 2006
@@ -0,0 +1,73 @@
+'''\
+Compatibility configuration switches
+
+This is a transitional solution to attack the problem
+of quickly changing Zope3 and Five APIs.
+
+Import checks are done centrally from here.
+The point is that compatibility imports should fail here,
+if anything goes wrong. Components should only check
+the switches set from here.
+
+Corresponding configuration features are also set up
+from the compat.zcml file. The idea is that all switches
+are accessible both from python and zcml.
+
+
+Supported versions:
+-------------------
+
+Zope  2.9, 2.10
+Zope  3.2, 3.3
+
+Compatibility matrix
+--------------------
+
+The following table shows which Five version can and should be used 
+with which Zope 2 and Zope 3 versions.
+
+.           Zope 2.8         Zope 2.9     Zope 2.10
+.           Zope X3 3.0      Zope 3.2     Zope 3.3  
+Five 1.0    included                               
+Five 1.2    X                                      
+Five 1.3                     included              
+Five 1.4                     X                      
+Five trunk                                included 
+
+'''
+
+__all__ = ('__compat__', )
+
+class DictLike(object):
+    pass
+    
+__compat__ = DictLike()
+
+try:
+    import zope.component.interface
+    import zope.component.location
+    __compat__.zope_pre_3_3 = False
+except ImportError:
+    # The only supported pre_3_3 version is 3.2
+    import zope.app.component.interface
+    import zope.app.location
+    __compat__.zope_pre_3_3 = True
+    
+try:
+    import Products.Five
+except ImportError:
+    __compat__.five = False
+else:
+    __compat__.five = True
+    try:
+        # Zope 2.8 / Five 1.0.2
+        from Products.Five.resource import Resource
+        __compat__.five_pre_1_3 = True
+    except ImportError:
+        # Zope 2.9 / Five 1.3
+        from Products.Five.browser.resource import Resource
+        __compat__.five_pre_1_3 = False
+
+# Unsupported versions.
+if __compat__.five and __compat__.five_pre_1_3:
+    raise Exception, 'Zope 2.8 or prior versions (Five 1.2 or prior versions) are unsupported, please upgrade!'

Added: z3/jsonserver/branch/merge/configfeature/fiveconfig.zcml
==============================================================================
--- (empty file)
+++ z3/jsonserver/branch/merge/configfeature/fiveconfig.zcml	Thu Jun 15 16:02:55 2006
@@ -0,0 +1,48 @@
+<configure xmlns:meta="http://namespaces.zope.org/meta"
+           xmlns:five="http://namespaces.zope.org/five"
+           xmlns:zcml="http://namespaces.zope.org/zcml"
+           i18n_domain="zope">
+
+    <!-- Define Five compatibility features -->
+
+    <five:configfeature
+        feature="compat_zope_pre_3_3"
+        instance=".fiveconfig.__compat__"
+        attribute="zope_pre_3_3"
+    />
+    <five:configfeature
+        feature="compat_not_zope_pre_3_3"
+        instance=".fiveconfig.__compat__"
+        attribute="zope_pre_3_3"
+        negate="True"
+    />
+
+    <five:configfeature
+        feature="compat_five"
+        instance=".fiveconfig.__compat__"
+        attribute="five"
+    />
+    <five:configfeature
+        feature="compat_not_five"
+        instance=".fiveconfig.__compat__"
+        attribute="five"
+        negate="True"
+    />
+
+    <configure zcml:condition="have compat_five">
+
+        <five:configfeature
+            feature="compat_five_pre_1_3"
+            instance=".fiveconfig.__compat__"
+            attribute="five_pre_1_3"
+        />
+        <five:configfeature
+            feature="compat_not_five_pre_1_3"
+            instance=".fiveconfig.__compat__"
+            attribute="five_pre_1_3"
+            negate="True"
+        />
+
+    </configure>
+
+</configure>

Added: z3/jsonserver/branch/merge/configfeature/meta.py
==============================================================================
--- (empty file)
+++ z3/jsonserver/branch/merge/configfeature/meta.py	Thu Jun 15 16:02:55 2006
@@ -0,0 +1,99 @@
+
+from zope.configuration.exceptions import ConfigurationError
+
+def configfeature(_context, feature, instance, attribute, negate=False):
+    """Imports a python object and uses an attribute of it as a boolean value
+    to declare a feature or not.
+
+    Usage
+    -----
+
+    We can have an object with attributes,
+
+    >>> class dictlike(object):
+    ...     pass
+    >>> conf1 = dictlike()
+    >>> conf1.this = True
+    >>> conf1.that = False
+
+    and its attributes can defined a feature if they evaluate to True.
+    The feature will not be defined if the value evaluates to False.
+
+    >>> from zope.configuration.config import ConfigurationContext
+    >>> c = ConfigurationContext()
+    >>> configfeature(c, 'f1', conf1, 'this')
+    >>> c.hasFeature('f1')
+    True
+    >>> configfeature(c, 'f2', conf1, 'that')
+    >>> c.hasFeature('f2')
+    False
+
+    The boolean value can be negated, this allows it
+    to overcome the shortage of negating possibilit of zcml:condition.
+
+    >>> configfeature(c, 'f3', conf1, 'this', True)
+    >>> c.hasFeature('f3')
+    False
+    >>> configfeature(c, 'f4', conf1, 'that', True)
+    >>> c.hasFeature('f4')
+    True
+
+    Instead of an object with attributes, a dictionary can also be used:
+
+    >>> dconf = {}
+    >>> dconf['this'] = True
+    >>> dconf['that'] = False
+
+    >>> configfeature(c, 'f5', dconf, 'this')
+    >>> c.hasFeature('f5')
+    True
+    >>> configfeature(c, 'f6', dconf, 'that')
+    >>> c.hasFeature('f6')
+    False
+
+
+    Error handling
+    --------------
+
+    If there is an unexistent attribute, an error is reported.
+
+    >>> configfeature(c, 'f7', conf1, 'nosuch')         #doctest: +ELLIPSIS
+    Traceback (most recent call last):
+    ...
+    ConfigurationError: Object ... does not have attribute or key "nosuch"
+
+    >>> configfeature(c, 'f8', dconf, 'nosuch')         #doctest: +ELLIPSIS
+    Traceback (most recent call last):
+    ...
+    ConfigurationError: Object ... does not have attribute or key "nosuch"
+
+
+    Finally, like with the "provides" directive normally:
+    Spaces are not allowed in feature names (this is reserved for providing
+    many features with a single directive in the futute).
+
+    >>> configfeature(c, 'two words', conf1, 'this')
+    Traceback (most recent call last):
+    ...
+    ValueError: Only one feature name allowed
+
+    """
+
+    try:
+        value = getattr(instance, attribute)
+    except AttributeError:
+        # also try as dictionary value
+        try:
+            value = instance[attribute]
+        except (TypeError, ValueError, KeyError):
+            raise ConfigurationError, 'Object %s does not have attribute or key "%s"' % (instance, attribute)
+
+    value = bool(value)
+    if negate:
+        value = not value
+
+    if len(feature.split()) > 1:
+        raise ValueError("Only one feature name allowed")
+
+    if value:
+        _context.provideFeature(feature)

Added: z3/jsonserver/branch/merge/configfeature/meta.zcml
==============================================================================
--- (empty file)
+++ z3/jsonserver/branch/merge/configfeature/meta.zcml	Thu Jun 15 16:02:55 2006
@@ -0,0 +1,15 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+           xmlns:meta="http://namespaces.zope.org/meta"
+           i18n_domain="zope">
+
+    <meta:directives namespace="http://namespaces.zope.org/five">
+
+    <meta:directive
+        name="configfeature"
+        schema=".directives.IConfigFeature"
+        handler=".meta.configfeature"
+        />
+
+  </meta:directives>
+
+</configure>

Added: z3/jsonserver/branch/merge/configfeature/tests/__init__.py
==============================================================================
--- (empty file)
+++ z3/jsonserver/branch/merge/configfeature/tests/__init__.py	Thu Jun 15 16:02:55 2006
@@ -0,0 +1,4 @@
+'''\
+Module init
+'''
+

Added: z3/jsonserver/branch/merge/configfeature/tests/__parent__.py
==============================================================================
--- (empty file)
+++ z3/jsonserver/branch/merge/configfeature/tests/__parent__.py	Thu Jun 15 16:02:55 2006
@@ -0,0 +1,9 @@
+import sys
+
+def __parent__(modulename, level=1):
+    'Figure out parent module'
+    for i in range(level):
+        if not isinstance(modulename, basestring):
+            modulename = modulename.__name__
+        modulename = sys.modules['.'.join(modulename.split('.')[:-1])]
+    return modulename

Added: z3/jsonserver/branch/merge/configfeature/tests/configtest.py
==============================================================================
--- (empty file)
+++ z3/jsonserver/branch/merge/configfeature/tests/configtest.py	Thu Jun 15 16:02:55 2006
@@ -0,0 +1,18 @@
+
+# module level
+conf1 = True
+conf2 = False
+
+class dictlike(object):
+    pass
+
+# an object
+cobj = dictlike()
+cobj.conf3 = True
+cobj.conf4 = False
+
+# a dict
+dobj = {}
+dobj['conf5'] = True
+dobj['conf6'] = False
+

Added: z3/jsonserver/branch/merge/configfeature/tests/test_directive.py
==============================================================================
--- (empty file)
+++ z3/jsonserver/branch/merge/configfeature/tests/test_directive.py	Thu Jun 15 16:02:55 2006
@@ -0,0 +1,228 @@
+
+import sys
+import unittest
+from zope.testing.doctestunit import DocTestSuite
+from __parent__ import __parent__
+configfeature = __parent__(__name__, 2)
+
+def test_module_level():
+    """
+    Module level access.
+
+    >>> import zope.configuration.tests
+    >>> import zope.configuration.xmlconfig
+    >>> context = zope.configuration.xmlconfig.file('meta.zcml', configfeature)
+
+    >>> import tempfile
+    >>> fn = tempfile.mktemp('.zcml')
+    >>> zcml = open(fn, 'w')
+    >>> zcml.write('''
+    ... <configure xmlns:meta="http://namespaces.zope.org/meta"
+    ...            xmlns:five="http://namespaces.zope.org/five">
+    ...     <five:configfeature
+    ...         feature="conf1"
+    ...         instance=".configtest"
+    ...         attribute="conf1"
+    ...     />
+    ...     <five:configfeature
+    ...         feature="conf2"
+    ...         instance=".configtest"
+    ...         attribute="conf2"
+    ...     />
+    ...     <five:configfeature
+    ...         feature="conf2n"
+    ...         instance=".configtest"
+    ...         attribute="conf2"
+    ...         negate="True"
+    ...     />
+    ... </configure>
+    ... ''')
+    >>> zcml.close()
+
+    >>> context = zope.configuration.xmlconfig.file(fn, configfeature.tests, context)
+
+    >>> context.hasFeature('conf1')
+    True
+    >>> context.hasFeature('conf2')
+    False
+    >>> context.hasFeature('conf2n')
+    True
+
+    """
+
+
+def test_objectlevel():
+    """
+    Object level access.
+
+    >>> import zope.configuration.tests
+    >>> import zope.configuration.xmlconfig
+    >>> context = zope.configuration.xmlconfig.file('meta.zcml', configfeature)
+
+    >>> import tempfile
+    >>> fn = tempfile.mktemp('.zcml')
+    >>> zcml = open(fn, 'w')
+    >>> zcml.write('''
+    ... <configure xmlns:meta="http://namespaces.zope.org/meta"
+    ...            xmlns:five="http://namespaces.zope.org/five">
+    ...     <five:configfeature
+    ...         feature="conf3"
+    ...         instance=".configtest.cobj"
+    ...         attribute="conf3"
+    ...     />
+    ...     <five:configfeature
+    ...         feature="conf4"
+    ...         instance=".configtest.cobj"
+    ...         attribute="conf4"
+    ...     />
+    ...     <five:configfeature
+    ...         feature="conf4n"
+    ...         instance=".configtest.cobj"
+    ...         attribute="conf4"
+    ...         negate="True"
+    ...     />
+    ... </configure>
+    ... ''')
+    >>> zcml.close()
+
+    >>> context = zope.configuration.xmlconfig.file(fn, configfeature.tests, context)
+
+    >>> context.hasFeature('conf3')
+    True
+    >>> context.hasFeature('conf4')
+    False
+    >>> context.hasFeature('conf4n')
+    True
+
+    """
+
+
+def test_dict_level():
+    """
+    Dictionary level access.
+
+    >>> import zope.configuration.tests
+    >>> import zope.configuration.xmlconfig
+    >>> context = zope.configuration.xmlconfig.file('meta.zcml', configfeature)
+
+    >>> import tempfile
+    >>> fn = tempfile.mktemp('.zcml')
+    >>> zcml = open(fn, 'w')
+    >>> zcml.write('''
+    ... <configure xmlns:meta="http://namespaces.zope.org/meta"
+    ...            xmlns:five="http://namespaces.zope.org/five">
+    ...     <five:configfeature
+    ...         feature="conf5"
+    ...         instance=".configtest.dobj"
+    ...         attribute="conf5"
+    ...     />
+    ...     <five:configfeature
+    ...         feature="conf6"
+    ...         instance=".configtest.dobj"
+    ...         attribute="conf6"
+    ...     />
+    ...     <five:configfeature
+    ...         feature="conf6n"
+    ...         instance=".configtest.dobj"
+    ...         attribute="conf6"
+    ...         negate="True"
+    ...     />
+    ... </configure>
+    ... ''')
+    >>> zcml.close()
+
+    >>> context = zope.configuration.xmlconfig.file(fn, configfeature.tests, context)
+
+    >>> context.hasFeature('conf5')
+    True
+    >>> context.hasFeature('conf6')
+    False
+    >>> context.hasFeature('conf6n')
+    True
+
+    """
+
+
+def test_errors():
+    """
+    Testing errors.
+
+    >>> import zope.configuration.tests
+    >>> import zope.configuration.xmlconfig
+    >>> context = zope.configuration.xmlconfig.file('meta.zcml', configfeature)
+
+    No such instance: raises an error.
+
+    >>> import tempfile
+    >>> fn = tempfile.mktemp('.zcml')
+    >>> zcml = open(fn, 'w')
+    >>> zcml.write('''
+    ... <configure xmlns:meta="http://namespaces.zope.org/meta"
+    ...            xmlns:five="http://namespaces.zope.org/five">
+    ...     <five:configfeature
+    ...         feature="conf1"
+    ...         instance=".configtest.nosuch"
+    ...         attribute="conf1"
+    ...     />
+    ... </configure>
+    ... ''')
+    >>> zcml.close()
+
+    >>> context = zope.configuration.xmlconfig.file(fn, configfeature.tests, context) #doctest: +ELLIPSIS
+    Traceback (most recent call last):
+    ...
+    ZopeXMLConfigurationError: ...
+        ConfigurationError: ...
+
+    No such attribute: an error too
+
+    >>> fn = tempfile.mktemp('.zcml')
+    >>> zcml = open(fn, 'w')
+    >>> zcml.write('''
+    ... <configure xmlns:meta="http://namespaces.zope.org/meta"
+    ...            xmlns:five="http://namespaces.zope.org/five">
+    ...     <five:configfeature
+    ...         feature="conf1"
+    ...         instance=".configtest.cobj"
+    ...         attribute="nosuch"
+    ...     />
+    ... </configure>
+    ... ''')
+    >>> zcml.close()
+
+    >>> context = zope.configuration.xmlconfig.file(fn, configfeature.tests, context) #doctest: +ELLIPSIS
+    Traceback (most recent call last):
+    ...
+    ZopeXMLConfigurationError: ...
+        ConfigurationError: Object ... does not have attribute or key "nosuch"
+
+    No such key: an error too 
+
+    >>> fn = tempfile.mktemp('.zcml')
+    >>> zcml = open(fn, 'w')
+    >>> zcml.write('''
+    ... <configure xmlns:meta="http://namespaces.zope.org/meta"
+    ...            xmlns:five="http://namespaces.zope.org/five">
+    ...     <five:configfeature
+    ...         feature="conf1"
+    ...         instance=".configtest.dobj"
+    ...         attribute="nosuch"
+    ...     />
+    ... </configure>
+    ... ''')
+    >>> zcml.close()
+
+    >>> context = zope.configuration.xmlconfig.file(fn, configfeature.tests, context) #doctest: +ELLIPSIS
+    Traceback (most recent call last):
+    ...
+    ZopeXMLConfigurationError: ...
+        ConfigurationError: Object ... does not have attribute or key "nosuch"
+    
+    """
+
+
+def test_suite():
+    return unittest.TestSuite((
+        DocTestSuite(configfeature.__name__ + '.meta'),
+        DocTestSuite(),
+        ))

Added: z3/jsonserver/branch/merge/configfeature/tests/test_fiveconfig.py
==============================================================================
--- (empty file)
+++ z3/jsonserver/branch/merge/configfeature/tests/test_fiveconfig.py	Thu Jun 15 16:02:55 2006
@@ -0,0 +1,23 @@
+
+import sys
+import unittest
+from zope.testing.doctestunit import DocTestSuite
+from __parent__ import __parent__
+configfeature = __parent__(__name__, 2)
+
+def test_fiveconfig():
+    """
+    The fiveconfig.zcml file declares various Five compatibility features.
+    We only test that the configuration file can be run.
+
+    >>> import zope.configuration.tests
+    >>> import zope.configuration.xmlconfig
+    >>> context = zope.configuration.xmlconfig.file('meta.zcml', configfeature)
+    >>> context = zope.configuration.xmlconfig.file('fiveconfig.zcml', configfeature, context)
+    """
+
+
+def test_suite():
+    return unittest.TestSuite((
+        DocTestSuite(),
+        ))


More information about the z3-checkins mailing list