[Checkins] SVN: zope.generic/trunk/src/zope/generic/ generic
configuration adapters:
Dominik Huber
dominik.huber at perse.ch
Tue Apr 18 12:48:57 EDT 2006
Log message for revision 67080:
generic configuration adapters:
Sub directive configurationAdapter
Transient ConfigurationAdapterClass factory
Fix:
ConfigurationData -> allow to store IPersistent attributes
Changed:
U zope.generic/trunk/src/zope/generic/component/base.py
U zope.generic/trunk/src/zope/generic/type/README.txt
U zope.generic/trunk/src/zope/generic/type/adapter.py
U zope.generic/trunk/src/zope/generic/type/helper.py
U zope.generic/trunk/src/zope/generic/type/meta.zcml
U zope.generic/trunk/src/zope/generic/type/metaconfigure.py
U zope.generic/trunk/src/zope/generic/type/metadirectives.py
U zope.generic/trunk/src/zope/generic/type/testing.py
U zope.generic/trunk/src/zope/generic/type/tests.py
-=-
Modified: zope.generic/trunk/src/zope/generic/component/base.py
===================================================================
--- zope.generic/trunk/src/zope/generic/component/base.py 2006-04-18 16:22:46 UTC (rev 67079)
+++ zope.generic/trunk/src/zope/generic/component/base.py 2006-04-18 16:48:55 UTC (rev 67080)
@@ -19,6 +19,7 @@
__docformat__ = 'restructuredtext'
from persistent import Persistent
+from persistent import IPersistent
from persistent.dict import PersistentDict
from zope.app.annotation.interfaces import IAttributeAnnotatable
@@ -220,7 +221,7 @@
schema = self.__dict__['__keyface__']
data = self.__dict__['_ConfigurationData__data']
- if name != '__provides__':
+ if not(name == '__provides__' or name in IPersistent):
try:
field = schema[name]
except KeyError:
@@ -234,7 +235,7 @@
-class InformationProvider(KeyfaceDescription, dict):
+class InformationProvider(KeyfaceDescription):
"""Generic information provider.
Information do relate a dedicated type of information marked as an interface
Modified: zope.generic/trunk/src/zope/generic/type/README.txt
===================================================================
--- zope.generic/trunk/src/zope/generic/type/README.txt 2006-04-18 16:22:46 UTC (rev 67079)
+++ zope.generic/trunk/src/zope/generic/type/README.txt 2006-04-18 16:48:55 UTC (rev 67080)
@@ -165,6 +165,9 @@
... keyface='example.IAnyConfiguration'
... data='example.typedata'
... />
+ ... <configurationAdapter
+ ... keyface='example.IAnyConfiguration'
+ ... />
... </generic:type>
... ''')
@@ -221,3 +224,26 @@
>>> api.acquireObjectConfiguration(bar, IAnyConfiguration).any
u'Guguseli from Object!'
+
+The configurationAdapter subdirective provides an adapter too:
+
+ >>> IOtherConfiguration(bar)
+ Traceback (most recent call last):
+ ...
+ TypeError: ('Could not adapt', ... example.IOtherConfiguration>)
+
+ >>> IAnyConfiguration(bar).any
+ u'Guguseli from Object!'
+
+If we remove the object's configuration the adapter will invoke
+the type configuration, but only the object's configuration can be set:
+
+ >>> from zope.generic.component.api import deleteInformation
+ >>> deleteInformation(bar, IAnyConfiguration)
+
+ >>> IAnyConfiguration(bar).any
+ u'Guguseli from Type!'
+
+ >>> IAnyConfiguration(bar).any = u'Guguseli from Object another time!'
+ >>> IAnyConfiguration(bar).any
+ u'Guguseli from Object another time!'
Modified: zope.generic/trunk/src/zope/generic/type/adapter.py
===================================================================
--- zope.generic/trunk/src/zope/generic/type/adapter.py 2006-04-18 16:22:46 UTC (rev 67079)
+++ zope.generic/trunk/src/zope/generic/type/adapter.py 2006-04-18 16:48:55 UTC (rev 67080)
@@ -18,17 +18,16 @@
__docformat__ = 'restructuredtext'
-from BTrees.OOBTree import OOBTree
-import transaction
-from UserDict import DictMixin
-
from zope.app.location import Location
-from zope.app.location.interfaces import ILocation
from zope.component import adapts
-from zope.event import notify
+from zope.interface import classImplements
from zope.interface import implements
+from zope.generic.component import IAttributeKeyface
+from zope.generic.component import IConfigurations
+from zope.generic.component import IConfigurationType
from zope.generic.component.api import provideInformation
+from zope.generic.component.api import ConfigurationData
from zope.generic.type import IInitializer
from zope.generic.type import IInitializerConfiguration
@@ -58,3 +57,190 @@
if config.handler:
config.handler(self.context, *pos, **kws)
+
+
+_marker = object()
+
+class ConfigurationAdapterProperty(object):
+ """Compute configuration adapter attributes based on schema fields
+
+ Field properties provide default values, data validation and error messages
+ based on data found in field meta-data.
+
+ Note that ConfigurationAdapterProperty cannot be used with slots.
+ They can only be used for attributes stored in instance configurations
+ dictionaries.
+ """
+
+ def __init__(self, field, name=None):
+ if name is None:
+ name = field.__name__
+
+ self.__field = field
+ self.__name = name
+
+ def __get__(self, inst, klass):
+ if inst is None:
+ return self
+
+ configurations = inst.__configurations__
+ keyface = inst.__keyface__
+ context = inst.__context__
+
+ configuration = inst.__keyface__(configurations, None)
+ if configuration is None:
+ # Try to evaluate a type configuration
+ configuration = queryTypeConfiguration(context, keyface)
+
+ value = getattr(configuration, self.__name, _marker)
+ if value is _marker:
+ field = self.__field.bind(inst)
+ value = getattr(field, 'default', _marker)
+ if value is _marker:
+ raise AttributeError(self.__name)
+
+ return value
+
+ def __set__(self, inst, value):
+ field = self.__field.bind(inst)
+ field.validate(value)
+ if field.readonly:
+ raise ValueError(self.__name, 'field is readonly')
+
+ configurations = inst.__configurations__
+ keyface = inst.__keyface__
+ # update existing configuration
+ if keyface in configurations:
+ configurations.update(keyface, {self.__name: value})
+
+ # create a new configuration
+ else:
+ configurations[keyface] = ConfigurationData(keyface, {self.__name: value})
+
+ inst.__dict__[self.__name] = value
+
+ def __getattr__(self, name):
+ return getattr(self.__field, name)
+
+
+
+class ConfigurationAdapterBase(Location):
+ """Base mixin for simple configuration adapter."""
+
+ __keyface__ = None
+
+ def __init__(self, context):
+ self.__context__ = context
+ self.__configurations__ = IConfigurations(context)
+
+
+
+def ConfigurationAdapterClass(keyface, bases=()):
+ """Generic configuration adapter class factory.
+
+ The generic configuration adapter is a generic adapter class for
+ configurations based instances. First we declare a configuration
+ schema:
+
+ >>> from zope.interface import Interface
+ >>> from zope.schema import TextLine
+
+ >>> class IFooConfiguration(Interface):
+ ... foo = TextLine(title=u'Foo')
+
+ We register the configuration schema using generic:configuration directive:
+
+ >>> registerDirective('''
+ ... <generic:configuration
+ ... keyface="example.IFooConfiguration"
+ ... />
+ ... ''')
+
+ >>> from zope.generic.component import IConfigurationType
+ >>> IConfigurationType.providedBy(IFooConfiguration)
+ True
+
+ We use a generic type including a default configuration for our example, too:
+
+ >>> class IFoo(Interface):
+ ... pass
+
+ >>>
+
+ >>> from zope.generic.component.api import ConfigurationData
+
+ >>> foo_configuration = ConfigurationData(IFooConfiguration, {'foo': u'Type Foo'})
+
+ >>> registerDirective('''
+ ... <generic:type
+ ... keyface="example.IFoo"
+ ... class='zope.generic.type.api.Object'
+ ... >
+ ... <configuration
+ ... keyface='example.IFooConfiguration'
+ ... data='example.foo_configuration'
+ ... />
+ ... </generic:type>
+ ... ''')
+
+ >>> foo = api.createObject(IFoo)
+ >>> IFoo.providedBy(foo)
+ True
+ >>> api.queryTypeConfiguration(foo, IFooConfiguration).foo
+ u'Type Foo'
+
+ At the moment the foo instance does not provide a foo configuration:
+
+ >>> api.queryObjectConfiguration(foo, IFooConfiguration) is None
+ True
+
+ The simple configuration adapter function will construct an adapter class
+ implementing the IFooConfiguration interface:
+
+ >>> from zope.generic.type.adapter import ConfigurationAdapterClass
+
+ >>> FooConfigurationAdapter = ConfigurationAdapterClass(IFooConfiguration)
+ >>> IFooConfiguration.implementedBy(FooConfigurationAdapter)
+ True
+
+ We can adapt our foo to IFooConfiguration:
+
+ >>> adapted = FooConfigurationAdapter(foo)
+ >>> IFooConfiguration.providedBy(adapted)
+ True
+ >>> adapted.foo
+ u'Type Foo'
+
+ >>> adapted.foo = u'Object Foo.'
+ >>> adapted.foo
+ u'Object Foo.'
+
+ >>> api.queryObjectConfiguration(foo, IFooConfiguration).foo
+ u'Object Foo.'
+
+ """
+
+ # preconditions
+ if not IConfigurationType.providedBy(keyface):
+ raise ValueError('Interface must provide %s.' % IConfigurationType.__name__)
+
+ # essentails
+ if not bases:
+ bases = (ConfigurationAdapterBase, )
+
+ class_ = type('ConfigurationAdapterClass from %s' % keyface, bases,
+ {'__keyface__': keyface})
+
+ classImplements(class_, keyface)
+
+ # add field properties for each object field
+ for name in keyface:
+ field = keyface[name]
+ setattr(class_, name, ConfigurationAdapterProperty(keyface[name]))
+
+ # security assertions
+ # protectName(class_, name, 'zope.ManageSite')
+ # protectSetAttribute(class_, name, 'zope.ManageSite')
+
+ return class_
+
Modified: zope.generic/trunk/src/zope/generic/type/helper.py
===================================================================
--- zope.generic/trunk/src/zope/generic/type/helper.py 2006-04-18 16:22:46 UTC (rev 67079)
+++ zope.generic/trunk/src/zope/generic/type/helper.py 2006-04-18 16:48:55 UTC (rev 67080)
@@ -31,9 +31,9 @@
-def createObject(interface, *pos, **kws):
+def createObject(keyface, *pos, **kws):
"""Create an instance of a logical type using the type marker."""
- return component.createObject(toDottedName(interface), *pos, **kws)
+ return component.createObject(toDottedName(keyface), *pos, **kws)
def createParameter(keyface):
Modified: zope.generic/trunk/src/zope/generic/type/meta.zcml
===================================================================
--- zope.generic/trunk/src/zope/generic/type/meta.zcml 2006-04-18 16:22:46 UTC (rev 67079)
+++ zope.generic/trunk/src/zope/generic/type/meta.zcml 2006-04-18 16:48:55 UTC (rev 67080)
@@ -20,6 +20,12 @@
schema="zope.generic.type.metadirectives.IInitializerSubdirective"
/>
+
+ <meta:subdirective
+ name="configurationAdapter"
+ schema=".metadirectives.IConfigurationAdapterSubdirective"
+ />
+
</meta:complexDirective>
</meta:directives>
Modified: zope.generic/trunk/src/zope/generic/type/metaconfigure.py
===================================================================
--- zope.generic/trunk/src/zope/generic/type/metaconfigure.py 2006-04-18 16:22:46 UTC (rev 67079)
+++ zope.generic/trunk/src/zope/generic/type/metaconfigure.py 2006-04-18 16:48:55 UTC (rev 67080)
@@ -20,6 +20,7 @@
from types import ModuleType
+from zope.app.component.contentdirective import ClassDirective
from zope.app.component.metaconfigure import proxify
from zope.app.component.metaconfigure import adapter
from zope.component import provideAdapter
@@ -40,6 +41,7 @@
from zope.generic.type import ITypeInformation
from zope.generic.type import ITypeType
from zope.generic.type.adapter import Initializer
+from zope.generic.type.adapter import ConfigurationAdapterClass
from zope.generic.type.factory import TypeFactory
from zope.generic.type.helper import queryTypeInformation
@@ -120,7 +122,9 @@
if keyface is not None:
data['keyface'] = keyface
- adapter(self._context, [Initializer], None, [self._keyface], None, '', True, False)
+ adapter(self._context, factory=[Initializer], provides=None,
+ for_=[self._keyface], permission=None, name='', trusted=True,
+ locate=False)
_context.action(
discriminator = (
@@ -128,3 +132,23 @@
callable = provideTypeConfiguration,
args = (self._keyface, IInitializerConfiguration, data),
)
+
+ def configurationAdapter(self, _context, keyface, class_=None, writePermission=None, readPermission=None):
+ """Provide a generic configuration adatper."""
+
+ # we will provide a generic adapter class
+ if class_ is None:
+ class_ = ConfigurationAdapterClass(keyface)
+
+ # register class
+ class_directive = ClassDirective(_context, class_)
+ if writePermission:
+ class_directive.require(_context, permission=writePermission, set_schema=[keyface])
+
+ if readPermission:
+ class_directive.require(_context, permission=readPermission, interface=[keyface])
+
+ # register adapter
+ adapter(self._context, factory=[class_], provides=keyface,
+ for_=[self._keyface], permission=None, name='', trusted=True,
+ locate=False)
Modified: zope.generic/trunk/src/zope/generic/type/metadirectives.py
===================================================================
--- zope.generic/trunk/src/zope/generic/type/metadirectives.py 2006-04-18 16:22:46 UTC (rev 67079)
+++ zope.generic/trunk/src/zope/generic/type/metadirectives.py 2006-04-18 16:48:55 UTC (rev 67080)
@@ -19,6 +19,7 @@
__docformat__ = 'restructuredtext'
from zope.app.i18n import ZopeMessageFactory as _
+from zope.app.security.fields import Permission
from zope.configuration.fields import Bool
from zope.configuration.fields import GlobalInterface
from zope.configuration.fields import GlobalObject
@@ -56,3 +57,35 @@
description=_('Callable (context, *pos, **kws).'),
required=False
)
+
+
+
+class IConfigurationAdapterSubdirective(Interface):
+ """Provide an adapter to a certain configuration."""
+
+ keyface = GlobalInterface(
+ title=_('Configuration Key Interface3'),
+ description=_('Configuration interface defining adapter interface.'),
+ required=True
+ )
+
+ class_ = GlobalObject(
+ title=_('Adapter class'),
+ description=_('If not declared a generic implementation will be used.'),
+ required=False
+ )
+
+ writePermission = Permission(
+ title=_('Write Permission'),
+ description=_('Specifies the permission by id that will be required ' +
+ ' to mutate the attributes and methods specified.'),
+ required=False,
+ )
+
+ readPermission = Permission(
+ title=_('Read Permission'),
+ description=_('Specifies the permission by id that will be required ' +
+ ' to accessthe attributes and methods specified.'),
+ required=False,
+ )
+
Modified: zope.generic/trunk/src/zope/generic/type/testing.py
===================================================================
--- zope.generic/trunk/src/zope/generic/type/testing.py 2006-04-18 16:22:46 UTC (rev 67079)
+++ zope.generic/trunk/src/zope/generic/type/testing.py 2006-04-18 16:48:55 UTC (rev 67080)
@@ -21,8 +21,6 @@
from zope.configuration.xmlconfig import XMLConfig
import zope.app.testing.placelesssetup
-import zope.generic.component.testing
-import zope.generic.component.testing
import zope.generic.directlyprovides.testing
import zope.generic.component.testing
import zope.generic.testing.testing
@@ -63,8 +61,6 @@
zope.generic.testing.testing.setUp(doctest)
zope.generic.directlyprovides.testing.setUp(doctest)
zope.generic.component.testing.setUp(doctest)
- zope.generic.component.testing.setUp(doctest)
- zope.generic.component.testing.setUp(doctest)
# internal setup
setUp(doctest)
@@ -74,8 +70,6 @@
zope.generic.testing.testing.tearDown(doctest)
zope.generic.directlyprovides.testing.tearDown(doctest)
zope.generic.component.testing.tearDown(doctest)
- zope.generic.component.testing.tearDown(doctest)
- zope.generic.component.testing.tearDown(doctest)
# internal teardown
tearDown(doctest)
Modified: zope.generic/trunk/src/zope/generic/type/tests.py
===================================================================
--- zope.generic/trunk/src/zope/generic/type/tests.py 2006-04-18 16:22:46 UTC (rev 67079)
+++ zope.generic/trunk/src/zope/generic/type/tests.py 2006-04-18 16:48:55 UTC (rev 67080)
@@ -24,6 +24,7 @@
from zope.testing import doctest
+from zope.generic.type import api
from zope.generic.type import testing
from zope.generic.testing.testing import registerDirective
@@ -31,6 +32,14 @@
def test_suite():
return unittest.TestSuite((
+ doctest.DocTestSuite('zope.generic.type.adapter',
+ setUp=testing.placelesssetup.setUp,
+ tearDown=testing.placelesssetup.tearDown,
+ globs={'component': component, 'interface': interface,
+ 'registerDirective': registerDirective,
+ 'testing': testing, 'api': api},
+ optionflags=doctest.NORMALIZE_WHITESPACE+
+ doctest.ELLIPSIS),
doctest.DocTestSuite('zope.generic.type.factory'),
doctest.DocTestSuite('zope.generic.type.metaconfigure'),
doctest.DocFileSuite('README.txt',
More information about the Checkins
mailing list