[Checkins] SVN: zope.schema/branches/faassen-transformer/src/zope/schema/ Some code to help with transforming interfaces to other ones.

Martijn Faassen faassen at startifact.com
Thu Feb 4 13:09:18 EST 2010


Log message for revision 108775:
  Some code to help with transforming interfaces to other ones.
  

Changed:
  U   zope.schema/branches/faassen-transformer/src/zope/schema/__init__.py
  A   zope.schema/branches/faassen-transformer/src/zope/schema/_transformer.py
  U   zope.schema/branches/faassen-transformer/src/zope/schema/tests/test_docs.py
  A   zope.schema/branches/faassen-transformer/src/zope/schema/transformer.txt

-=-
Modified: zope.schema/branches/faassen-transformer/src/zope/schema/__init__.py
===================================================================
--- zope.schema/branches/faassen-transformer/src/zope/schema/__init__.py	2010-02-04 18:06:39 UTC (rev 108774)
+++ zope.schema/branches/faassen-transformer/src/zope/schema/__init__.py	2010-02-04 18:09:18 UTC (rev 108775)
@@ -29,5 +29,6 @@
     getValidationErrors, getSchemaValidationErrors)
 from zope.schema.accessors import accessors
 from zope.schema.interfaces import ValidationError
+from zope.schema._transformer import transformer
 
 __all__ = tuple(name for name in globals() if not name.startswith('_'))

Added: zope.schema/branches/faassen-transformer/src/zope/schema/_transformer.py
===================================================================
--- zope.schema/branches/faassen-transformer/src/zope/schema/_transformer.py	                        (rev 0)
+++ zope.schema/branches/faassen-transformer/src/zope/schema/_transformer.py	2010-02-04 18:09:18 UTC (rev 108775)
@@ -0,0 +1,57 @@
+from zope.interface.interface import InterfaceClass
+from zope.schema import getFieldNamesInOrder
+
+class transformer(object):
+    def __init__(self, schema):
+        self.schema = schema
+
+    def select(self, names):
+        attrs = {}
+        order = 0
+        for name in names:
+            attrs[name] = self._copy_field(self.schema[name],
+                                           order=order)
+            order += 1
+            
+        return transformer(InterfaceClass(name=self.schema.__name__,
+                                          bases=self.schema.__bases__,
+                                          __module__=self.schema.__module__,
+                                          attrs=attrs))
+
+    def omit(self, names):
+        attrs = {}
+        order = 0
+        for name in getFieldNamesInOrder(self.schema):
+            if name not in names:
+                attrs[name] = self._copy_field(self.schema[name],
+                                               order=order)
+            order += 1
+            
+        return transformer(InterfaceClass(name=self.schema.__name__,
+                                          bases=self.schema.__bases__,
+                                          __module__=self.schema.__module__,
+                                          attrs=attrs))
+
+    def override(self, name, **kw):
+        attrs = {}
+        order = 0
+        for name in getFieldNamesInOrder(self.schema):
+            attrs[name] = self._copy_field(self.schema[name],
+                                           order=order,
+                                           **kw)
+            order += 1
+    
+        return transformer(InterfaceClass(name=self.schema.__name__,
+                                          bases=self.schema.__bases__,
+                                          __module__=self.schema.__module__,
+                                          attrs=attrs))
+
+        
+    def _copy_field(self, field, **kw):
+        # by passing None as context, we can trick the binding
+        # machinery into making a straight copy
+        clone = field.bind(None)
+        for key, value in kw.items():
+            setattr(clone, key, value)
+        return clone
+

Modified: zope.schema/branches/faassen-transformer/src/zope/schema/tests/test_docs.py
===================================================================
--- zope.schema/branches/faassen-transformer/src/zope/schema/tests/test_docs.py	2010-02-04 18:06:39 UTC (rev 108774)
+++ zope.schema/branches/faassen-transformer/src/zope/schema/tests/test_docs.py	2010-02-04 18:09:18 UTC (rev 108775)
@@ -31,6 +31,9 @@
         doctest.DocFileSuite(
             '../validation.txt', checker=checker,
             optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS),
+        doctest.DocFileSuite(
+            '../transformer.txt',
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS),     
         ))
 
 if __name__ == '__main__':

Added: zope.schema/branches/faassen-transformer/src/zope/schema/transformer.txt
===================================================================
--- zope.schema/branches/faassen-transformer/src/zope/schema/transformer.txt	                        (rev 0)
+++ zope.schema/branches/faassen-transformer/src/zope/schema/transformer.txt	2010-02-04 18:09:18 UTC (rev 108775)
@@ -0,0 +1,94 @@
+Transforming schemas
+====================
+
+It is possible to transform schemas into other ones. The transformer 
+facility follows the rule that schemas are immutable, but that other
+schemas can be constructed out of old ones.
+
+Let's take a simple schema::
+
+  >>> from zope.interface import Interface
+  >>> from zope import schema
+  >>> class IFoo(Interface):
+  ...     a = schema.Int(title=u'A')
+  ...     b = schema.TextLine(title=u'B')
+  ...     c = schema.TextLine(title=u'C')
+  ...     d = schema.Int(title=u'D')
+
+To transform the schema, we first need to wrap it into a transformer::
+
+  >>> from zope.schema import transformer
+  >>> ifoo_transformer = transformer(IFoo)
+
+A transformer has a ``schema`` attribute indicating its schema::
+
+  >>> ifoo_transformer.schema is IFoo
+  True
+
+Transformers define operations, and each returns a transformer. This
+allows these operations to be chained.
+
+Select
+------
+
+We can use the transformer to construct a new schema, selecting only
+``a``, and ``b``::
+
+  >>> selected_transformer = ifoo_transformer.select(['a', 'b'])
+
+Out comes another transformer, with the transformed schema::
+
+  >>> IFooSelect = selected_transformer.schema
+  >>> from zope.schema import getFieldNamesInOrder
+  >>> getFieldNamesInOrder(IFooSelect)
+  ['a', 'b']
+
+We can also reorder the fields in the schema this way::
+
+  >>> reordered_transformer = ifoo_transformer.select(['d', 'c', 'b', 'a'])
+  >>> getFieldNamesInOrder(reordered_transformer.schema)
+  ['d', 'c', 'b', 'a']
+
+We cannot select fields that do not exist::
+ 
+  >>> ifoo_transformer.select(['z'])
+  Traceback (most recent call last):
+    ...
+  KeyError: 'z'
+
+Omit
+----
+
+We can also omit fields. This results in a new schema without the omitted
+field. The order of the remaining fields is unaffected::
+
+  >>> omitted_transformer = ifoo_transformer.omit(['a'])
+  >>> getFieldNamesInOrder(omitted_transformer.schema)
+  ['b', 'c', 'd']
+
+We are free to omit fields that do not exist, as they aren't there anyway::
+
+  >>> getFieldNamesInOrder(ifoo_transformer.omit(['z']).schema)
+  ['a', 'b', 'c', 'd']
+
+Override
+--------
+
+We can also override field properties. Let's say we want to modify the
+title of the ``a`` field.
+
+The title starts out as ``'A'``:
+
+  >>> IFoo['a'].title
+  u'A'
+
+Now we override the title, producing a new schema::
+
+  >>> schema = ifoo_transformer.override('a', title=u'A overridden').schema
+  >>> schema['a'].title
+  u'A overridden'
+
+You can override any property, and also more than one, by using
+keyword arguments.
+  
+  



More information about the checkins mailing list