[Checkins] SVN: z3c.schema2xml/ Initial import of this package.

Martijn Faassen faassen at infrae.com
Fri Dec 21 00:07:59 EST 2007


Log message for revision 82377:
  Initial import of this package.
  

Changed:
  A   z3c.schema2xml/
  A   z3c.schema2xml/trunk/
  A   z3c.schema2xml/trunk/CHANGES.txt
  A   z3c.schema2xml/trunk/COPYRIGHT.txt
  A   z3c.schema2xml/trunk/CREDITS.txt
  A   z3c.schema2xml/trunk/LICENSE.txt
  A   z3c.schema2xml/trunk/TODO.txt
  A   z3c.schema2xml/trunk/bootstrap.py
  A   z3c.schema2xml/trunk/buildout.cfg
  A   z3c.schema2xml/trunk/setup.py
  A   z3c.schema2xml/trunk/src/
  A   z3c.schema2xml/trunk/src/z3c/
  A   z3c.schema2xml/trunk/src/z3c/__init__.py
  A   z3c.schema2xml/trunk/src/z3c/schema2xml/
  A   z3c.schema2xml/trunk/src/z3c/schema2xml/README.txt
  A   z3c.schema2xml/trunk/src/z3c/schema2xml/__init__.py
  A   z3c.schema2xml/trunk/src/z3c/schema2xml/_schema2xml.py
  A   z3c.schema2xml/trunk/src/z3c/schema2xml/configure.zcml
  A   z3c.schema2xml/trunk/src/z3c/schema2xml/tests.py
  A   z3c.schema2xml/trunk/svn-commit.tmp

-=-
Added: z3c.schema2xml/trunk/CHANGES.txt
===================================================================
--- z3c.schema2xml/trunk/CHANGES.txt	                        (rev 0)
+++ z3c.schema2xml/trunk/CHANGES.txt	2007-12-21 05:07:57 UTC (rev 82377)
@@ -0,0 +1,8 @@
+======================
+z3c.schema2xml changes
+======================
+
+0.10 (unreleased)
+=================
+
+* First checkin in svn.zope.org.

Added: z3c.schema2xml/trunk/COPYRIGHT.txt
===================================================================
--- z3c.schema2xml/trunk/COPYRIGHT.txt	                        (rev 0)
+++ z3c.schema2xml/trunk/COPYRIGHT.txt	2007-12-21 05:07:57 UTC (rev 82377)
@@ -0,0 +1,9 @@
+Copyright (c) 2007 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. 

Added: z3c.schema2xml/trunk/CREDITS.txt
===================================================================
--- z3c.schema2xml/trunk/CREDITS.txt	                        (rev 0)
+++ z3c.schema2xml/trunk/CREDITS.txt	2007-12-21 05:07:57 UTC (rev 82377)
@@ -0,0 +1,16 @@
+======================
+z3c.schema2xml credits
+======================
+
+Developers
+----------
+
+* Jan-Wijbrand Kolman
+
+* Martijn Faassen 
+
+Thank you
+---------
+
+* This software was developed for the Institute of Netherlands
+  History. Thanks for their support.

Added: z3c.schema2xml/trunk/LICENSE.txt
===================================================================
--- z3c.schema2xml/trunk/LICENSE.txt	                        (rev 0)
+++ z3c.schema2xml/trunk/LICENSE.txt	2007-12-21 05:07:57 UTC (rev 82377)
@@ -0,0 +1,54 @@
+Zope Public License (ZPL) Version 2.1
+-------------------------------------
+
+A copyright notice accompanies this license document that
+identifies the copyright holders.
+
+This license has been certified as open source. It has also
+been designated as GPL compatible by the Free Software
+Foundation (FSF).
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the
+following conditions are met:
+
+1. Redistributions in source code must retain the
+   accompanying copyright notice, this list of conditions,
+   and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the accompanying
+   copyright notice, this list of conditions, and the
+   following disclaimer in the documentation and/or other
+   materials provided with the distribution.
+
+3. Names of the copyright holders must not be used to
+   endorse or promote products derived from this software
+   without prior written permission from the copyright
+   holders.
+
+4. The right to distribute this software or to use it for
+   any purpose does not give you the right to use
+   Servicemarks (sm) or Trademarks (tm) of the copyright
+   holders. Use of them is covered by separate agreement
+   with the copyright holders.
+
+5. If any files are modified, you must cause the modified
+   files to carry prominent notices stating that you changed
+   the files and the date of any change.
+
+Disclaimer
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS''
+  AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+  NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+  AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
+  NO EVENT SHALL THE COPYRIGHT HOLDERS BE
+  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+  DAMAGE.

Added: z3c.schema2xml/trunk/TODO.txt
===================================================================
--- z3c.schema2xml/trunk/TODO.txt	                        (rev 0)
+++ z3c.schema2xml/trunk/TODO.txt	2007-12-21 05:07:57 UTC (rev 82377)
@@ -0,0 +1,22 @@
+Todo
+====
+
+* test adapter lookup failure
+
+* test whether GeneratedObject implements right schema interface.
+
+* test without explicit __name__ for Lists. What should happen? Fallback
+  on interface name?
+
+* test deserialization on an empty object.
+
+* what to do in case of a missing element in an XML document?
+
+* form data file instances need to provide schema
+
+Punt
+----
+
+* custom factories instead of GeneratedObject.
+
+* What to do with XML namespaces?

Added: z3c.schema2xml/trunk/bootstrap.py
===================================================================
--- z3c.schema2xml/trunk/bootstrap.py	                        (rev 0)
+++ z3c.schema2xml/trunk/bootstrap.py	2007-12-21 05:07:57 UTC (rev 82377)
@@ -0,0 +1,52 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+
+$Id: bootstrap.py 73916 2007-03-29 15:28:25Z dobe $
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+ez = {}
+exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+                     ).read() in ez
+ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+import pkg_resources
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+if sys.platform == 'win32':
+    cmd = '"%s"' % cmd # work around spawn lamosity on windows
+
+ws = pkg_resources.working_set
+assert os.spawnle(
+    os.P_WAIT, sys.executable, sys.executable,
+    '-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout',
+    dict(os.environ,
+         PYTHONPATH=
+         ws.find(pkg_resources.Requirement.parse('setuptools')).location
+         ),
+    ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout')
+import zc.buildout.buildout
+zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap'])
+shutil.rmtree(tmpeggs)

Added: z3c.schema2xml/trunk/buildout.cfg
===================================================================
--- z3c.schema2xml/trunk/buildout.cfg	                        (rev 0)
+++ z3c.schema2xml/trunk/buildout.cfg	2007-12-21 05:07:57 UTC (rev 82377)
@@ -0,0 +1,20 @@
+[buildout]
+develop = .
+parts = test devpython
+extends = http://grok.zope.org/releaseinfo/grok-0.11.cfg
+versions = versions
+
+[versions]
+martian = 0.9.2
+lxml = 1.3.6
+
+[test]
+recipe = zc.recipe.testrunner
+eggs = z3c.schema2xml
+
+# installs bin/devpython to do simple interpreter tests
+[devpython]
+recipe = zc.recipe.egg
+interpreter = devpython
+eggs = z3c.schema2xml
+

Added: z3c.schema2xml/trunk/setup.py
===================================================================
--- z3c.schema2xml/trunk/setup.py	                        (rev 0)
+++ z3c.schema2xml/trunk/setup.py	2007-12-21 05:07:57 UTC (rev 82377)
@@ -0,0 +1,28 @@
+from setuptools import setup, find_packages
+import sys, os
+
+setup(name='z3c.schema2xml',
+      version='0.10dev',
+      description="Convert schema-described Zope 3 objects to XML and back",
+      long_description="""\
+""",
+      classifiers=[],
+      keywords="",
+      author="Martijn Faassen, Jan-Wijbrand Kolman",
+      author_email="faassen at startifact.com",
+      url="",
+      license="ZPL",
+      package_dir={'': 'src'},
+      packages=find_packages('src'),
+      include_package_data=True,
+      zip_safe=False,
+      install_requires=[
+        'setuptools',
+        'lxml',
+        'grok',
+        'zc.sourcefactory',
+      ],
+      entry_points="""
+      # -*- Entry points: -*-
+      """,
+      )

Added: z3c.schema2xml/trunk/src/z3c/__init__.py
===================================================================
--- z3c.schema2xml/trunk/src/z3c/__init__.py	                        (rev 0)
+++ z3c.schema2xml/trunk/src/z3c/__init__.py	2007-12-21 05:07:57 UTC (rev 82377)
@@ -0,0 +1,8 @@
+# this is a namespace package
+try:
+    import pkg_resources
+    pkg_resources.declare_namespace(__name__)
+except ImportError:
+    import pkgutil
+    __path__ = pkgutil.extend_path(__path__, __name__)
+

Added: z3c.schema2xml/trunk/src/z3c/schema2xml/README.txt
===================================================================
--- z3c.schema2xml/trunk/src/z3c/schema2xml/README.txt	                        (rev 0)
+++ z3c.schema2xml/trunk/src/z3c/schema2xml/README.txt	2007-12-21 05:07:57 UTC (rev 82377)
@@ -0,0 +1,518 @@
+Schema To XML
+=============
+
+This package can convert objects described by Zope 3 schema to simple
+XML structures. It's also able to convert this XML back into objects.
+The export and import processes are completely schema-driven; any
+attribute not described in the schema is not seen by this system at
+all.
+
+This system can be used to create export and import systems for Zope 3
+applications. It could also be used to provide XML representations of
+objects for other purposes, such as XSLT transformations, or even just
+to get a full-text representation for index purposes.
+
+The package lies on ``lxml``. It also relies on ``grok``, which will
+make it somewhat harder to integrate with a normal Zope 3 application
+at present. It is expected this will change as the core grokking
+procedure used by this package (to Grok adapters) makes it into a more
+limited package that does not rely on other aspects of Grok.
+
+Serialization
+-------------
+
+Let's first define a simple Zope 3 schema::
+
+    >>> from zope import interface, schema
+    >>> class IName(interface.Interface):
+    ...     first_name = schema.TextLine(title=u'First name')
+    ...     last_name = schema.TextLine(title=u'Last name')
+
+Let's now make a class that implements this schema::
+
+    >>> from zope.interface import implements
+    >>> class Name(object):
+    ...     implements(IName)
+    ...     def __init__(self, first_name, last_name):
+    ...         self.first_name = first_name
+    ...         self.last_name = last_name
+
+Let's make an instance of the class::
+
+    >>> name = Name('Karel', 'Titulaer')
+
+Now let's serialize it to XML:
+
+    >>> from z3c.schema2xml import serialize
+    >>> print serialize('container', IName, name)
+    <container>
+      <first_name>Karel</first_name>
+      <last_name>Titulaer</last_name>
+    </container>
+
+This also works for other kinds of fields::
+
+    >>> from zope import interface, schema
+    >>> class IAddress(interface.Interface):
+    ...     street_name = schema.TextLine(title=u'Street name')
+    ...     number = schema.Int(title=u'House number')
+    >>> class Address(object):
+    ...     implements(IAddress)
+    ...     def __init__(self, street_name, number):
+    ...         self.street_name = street_name
+    ...         self.number = number
+    >>> address = Address('Hofplein', 42)
+    >>> print serialize('container', IAddress, address)
+    <container>
+      <street_name>Hofplein</street_name>
+      <number>42</number>
+    </container>
+
+If a field is not filled in, the serialization will result in an empty
+element::
+
+    >>> address2 = Address(None, None)
+    >>> print serialize('container', IAddress, address2)
+    <container>
+      <street_name/>
+      <number/>
+    </container>
+
+If a schema defines an Object field with its own schema, the serialization
+can also handle this::
+
+    >>> class IPerson(interface.Interface):
+    ...     name = schema.Object(title=u"Name", schema=IName)
+    ...     address = schema.Object(title=u"Address", schema=IAddress)
+
+    >>> class Person(object):
+    ...     implements(IPerson)
+    ...     def __init__(self, name, address):
+    ...         self.name = name
+    ...         self.address = address
+
+    >>> person = Person(name, address)
+    >>> print serialize('person', IPerson, person)
+    <person>
+      <name>
+        <first_name>Karel</first_name>
+        <last_name>Titulaer</last_name>
+      </name>
+      <address>
+        <street_name>Hofplein</street_name>
+        <number>42</number>
+      </address>
+    </person>
+
+A schema can also define a List field with elements with their own
+schema. Let's make an object and serialize it::
+
+    >>> class ICommission(interface.Interface):
+    ...     members = schema.List(
+    ...         title=u"Commission",
+    ...         value_type=schema.Object(__name__='person',
+    ...         schema=IPerson))
+
+Note that we have to explicitly specify __name__ for the field that's
+used for value_type here, otherwise we have no name to serialize to
+XML with.
+
+    >>> class Commission(object):
+    ...     implements(ICommission)
+    ...     def __init__(self, members):
+    ...         self.members = members
+
+    >>> commission = Commission(
+    ...     [person, Person(Name('Chriet', 'Titulaer'), Address('Ruimteweg', 3))])
+    >>> print serialize('commission', ICommission, commission)
+    <commission>
+      <members>
+        <person>
+          <name>
+            <first_name>Karel</first_name>
+            <last_name>Titulaer</last_name>
+          </name>
+          <address>
+            <street_name>Hofplein</street_name>
+            <number>42</number>
+          </address>
+        </person>
+        <person>
+          <name>
+            <first_name>Chriet</first_name>
+            <last_name>Titulaer</last_name>
+          </name>
+          <address>
+            <street_name>Ruimteweg</street_name>
+            <number>3</number>
+          </address>
+        </person>
+      </members>
+    </commission>
+
+We get an adapter lookop failure whenever we try to serialize a field type for
+which there's no an serializer::
+
+    >>> class IWithNonSerializableField(interface.Interface):
+    ...     field = schema.Field(title=u"Commission")
+    >>> class NotSerializable(object):
+    ...     implements(IWithNonSerializableField)
+    ...     def __init__(self, value):
+    ...         self.field = value
+    >>> not_serializable = NotSerializable(None)
+    >>> serialize('noway', IWithNonSerializableField, not_serializable)
+    Traceback (most recent call last):
+     ...
+    TypeError: ('Could not adapt', <zope.schema._bootstrapfields.Field object at ...>, <InterfaceClass z3c.schema2xml._schema2xml.IXMLGenerator>)
+
+Deserialization
+---------------
+
+Now we want to deserialize XML according to a schema to an object that
+provides this schema.
+
+    >>> from z3c.schema2xml import deserialize
+    >>> xml = '''
+    ...  <container>
+    ...    <first_name>Karel</first_name>
+    ...    <last_name>Titulaer</last_name>
+    ...  </container>
+    ...  '''
+    >>> name = Name('', '')
+    >>> deserialize(xml, IName, name)
+    >>> name.first_name
+    u'Karel'
+    >>> name.last_name
+    u'Titulaer'
+
+The order of the fields in XML does not matter::
+
+    >>> xml = '''
+    ...  <container>
+    ...    <last_name>Titulaer</last_name>
+    ...    <first_name>Karel</first_name>
+    ...  </container>
+    ...  '''
+    >>> name = Name('', '')
+    >>> deserialize(xml, IName, name)
+    >>> name.first_name
+    u'Karel'
+    >>> name.last_name
+    u'Titulaer'
+
+After deserialization, the object alsoProvides the schema interface::
+
+    >>> IName.providedBy(name)
+    True
+
+This also works for other kinds of fields::
+
+    >>> xml = '''
+    ...  <container>
+    ...    <street_name>Hofplein</street_name>
+    ...    <number>42</number>
+    ...  </container>
+    ...  '''
+    >>> address = Address('', 0)
+    >>> deserialize(xml, IAddress, address)
+    >>> address.street_name
+    u'Hofplein'
+    >>> address.number
+    42
+
+If a schema defines an Object field with its own schema, the serialization
+can also handle this::
+
+    >>> xml = '''
+    ...  <person>
+    ...    <name>
+    ...      <first_name>Karel</first_name>
+    ...      <last_name>Titulaer</last_name>
+    ...    </name>
+    ...    <address>
+    ...      <street_name>Hofplein</street_name>
+    ...      <number>42</number>
+    ...    </address>
+    ...  </person>
+    ...  '''
+    >>> person = Person(Name('', ''), Address('', 0))
+    >>> deserialize(xml, IPerson, person)
+    >>> person.name.first_name
+    u'Karel'
+    >>> person.name.last_name
+    u'Titulaer'
+    >>> person.address.street_name
+    u'Hofplein'
+    >>> person.address.number
+    42
+    >>> IPerson.providedBy(person)
+    True
+    >>> IName.providedBy(person.name)
+    True
+    >>> IAddress.providedBy(person.address)
+    True
+
+Again the order in which the fields come in XML shouldn't matter::
+
+    >>> xml = '''
+    ...  <person>
+    ...    <address>
+    ...      <number>42</number>
+    ...      <street_name>Hofplein</street_name>
+    ...    </address>
+    ...    <name>
+    ...      <last_name>Titulaer</last_name>
+    ...      <first_name>Karel</first_name>
+    ...    </name>
+    ...  </person>
+    ...  '''
+    >>> person = Person(Name('', ''), Address('', 0))
+    >>> deserialize(xml, IPerson, person)
+    >>> person.name.first_name
+    u'Karel'
+    >>> person.name.last_name
+    u'Titulaer'
+    >>> person.address.street_name
+    u'Hofplein'
+    >>> person.address.number
+    42
+    >>> IPerson.providedBy(person)
+    True
+    >>> IName.providedBy(person.name)
+    True
+    >>> IAddress.providedBy(person.address)
+    True
+
+    >>> xml = '''
+    ... <commission>
+    ...  <members>
+    ...    <person>
+    ...      <name>
+    ...        <first_name>Karel</first_name>
+    ...        <last_name>Titulaer</last_name>
+    ...      </name>
+    ...      <address>
+    ...        <street_name>Hofplein</street_name>
+    ...        <number>42</number>
+    ...      </address>
+    ...    </person>
+    ...    <person>
+    ...      <name>
+    ...        <first_name>Chriet</first_name>
+    ...        <last_name>Titulaer</last_name>
+    ...     </name>
+    ...      <address>
+    ...        <street_name>Ruimteweg</street_name>
+    ...        <number>3</number>
+    ...      </address>
+    ...    </person>
+    ...  </members>
+    ... </commission>
+    ... '''
+
+    >>> commission = Commission([])
+    >>> deserialize(xml, ICommission, commission)
+    >>> len(commission.members)
+    2
+    >>> member = commission.members[0]
+    >>> member.name.first_name
+    u'Karel'
+    >>> member.address.street_name
+    u'Hofplein'
+    >>> member = commission.members[1]
+    >>> member.name.first_name
+    u'Chriet'
+    >>> member.address.street_name
+    u'Ruimteweg'
+
+Whenever the XML element is empty, the resulting value should be None:
+
+    >>> from z3c.schema2xml import deserialize
+    >>> xml = '''
+    ...  <container>
+    ...    <first_name></first_name>
+    ...    <last_name/>
+    ...  </container>
+    ...  '''
+    >>> name = Name('', '')
+    >>> deserialize(xml, IName, name)
+    >>> name.first_name is None
+    True
+    >>> name.last_name is None
+    True
+
+For all kinds of fields, like strings and ints...::
+
+    >>> xml = '''
+    ...  <container>
+    ...    <street_name/>
+    ...    <number/>
+    ...  </container>
+    ...  '''
+    >>> address = Address('', 0)
+    >>> deserialize(xml, IAddress, address)
+    >>> address.street_name is None
+    True
+    >>> address.number is None
+    True
+
+...and the fields of subobjects (but not the subobject themselves!)::
+
+    >>> xml = '''
+    ...  <person>
+    ...    <name>
+    ...      <first_name/>
+    ...      <last_name/>
+    ...    </name>
+    ...    <address>
+    ...      <street_name/>
+    ...      <number/>
+    ...    </address>
+    ...  </person>
+    ...  '''
+    >>> person = Person(Name('', ''), Address('', 0))
+    >>> deserialize(xml, IPerson, person)
+    >>> person.name.first_name is None
+    True
+    >>> person.name.last_name is None
+    True
+    >>> IPerson.providedBy(person)
+    True
+    >>> IName.providedBy(person.name)
+    True
+    >>> person.address is None
+    False
+    >>> person.address.street_name is None
+    True
+    >>> person.address.number is None
+    True
+    >>> IAddress.providedBy(person.address)
+    True
+
+Similarly, where a sequence is expected the value should be an empty sequence:
+
+    >>> xml = '''
+    ... <commission>
+    ...   <members/>
+    ... </commission>
+    ... '''
+    >>> commission = Commission([])
+    >>> deserialize(xml, ICommission, commission)
+    >>> len(commission.members)
+    0
+
+TextLine, Int, Object and List have just been tested. Now follow tests
+for the other field types that have a serializer.
+
+Datetime
+--------
+
+Datetime objects::
+
+    >>> from datetime import datetime
+    >>> class IWithDatetime(interface.Interface):
+    ...     datetime = schema.Datetime(title=u'Date and time')
+    >>> class WithDatetime(object):
+    ...     implements(IWithDatetime)
+    ...     def __init__(self, datetime):
+    ...         self.datetime = datetime
+    >>> with_datetime = WithDatetime(datetime(2006, 12, 31))
+    >>> xml = serialize('container', IWithDatetime, with_datetime)
+    >>> print xml
+    <container>
+      <datetime>2006-12-31T00:00:00</datetime>
+    </container>
+    >>> new_datetime = WithDatetime(None)
+    >>> deserialize(xml, IWithDatetime, new_datetime)
+    >>> new_datetime.datetime.year
+    2006
+    >>> new_datetime.datetime.month
+    12
+    >>> new_datetime.datetime.day
+    31
+
+Let's try it with the field not filled in::
+
+    >>> with_datetime = WithDatetime(None)
+    >>> xml = serialize('container', IWithDatetime, with_datetime)
+    >>> print xml
+    <container>
+      <datetime/>
+    </container>
+    >>> new_datetime= WithDatetime(None)
+    >>> deserialize(xml, IWithDatetime, new_datetime)
+    >>> new_datetime.datetime is None
+    True
+
+Choice
+------
+
+Choice fields. For now, we only work with Choice fields that have 
+text values::
+
+
+    >>> from zc.sourcefactory.basic import BasicSourceFactory
+    >>> class ChoiceSource(BasicSourceFactory):
+    ...     def getValues(self):
+    ...         return [u'alpha', u'beta']
+    >>> class IWithChoice(interface.Interface):
+    ...     choice = schema.Choice(title=u'Choice', required=False,
+    ...                            source=ChoiceSource())
+    >>> class WithChoice(object):
+    ...     implements(IWithChoice)
+    ...     def __init__(self, choice):
+    ...         self.choice = choice
+    >>> with_choice = WithChoice('alpha')
+    >>> xml = serialize('container', IWithChoice, with_choice)
+    >>> print xml
+    <container>
+      <choice>alpha</choice>
+    </container>
+    >>> new_choice = WithChoice(None)
+    >>> deserialize(xml, IWithChoice, new_choice)
+    >>> new_choice.choice
+    'alpha'
+    >>> with_choice = WithChoice(None)
+    >>> xml = serialize('container', IWithChoice, with_choice)
+    >>> print xml
+    <container>
+      <choice/>
+    </container>
+    >>> deserialize(xml, IWithChoice, new_choice)
+    >>> new_choice.choice is None
+    True
+
+Set
+---
+
+Set fields are very similar to List fields::
+
+    >>> class IWithSet(interface.Interface):
+    ...     set = schema.Set(title=u'Set', required=False,
+    ...                      value_type=schema.Choice(__name__='choice',
+    ...                                               source=ChoiceSource()))
+    >>> class WithSet(object):
+    ...     implements(IWithSet)
+    ...     def __init__(self, set):
+    ...         self.set = set
+    >>> with_set = WithSet(set(['alpha']))
+    >>> xml = serialize('container', IWithSet, with_set)
+    >>> print xml
+    <container>
+      <set>
+        <choice>alpha</choice>
+      </set>
+    </container>
+    >>> with_set = WithSet(set(['alpha', 'beta']))
+    >>> xml = serialize('container', IWithSet, with_set)
+    >>> print xml
+    <container>
+      <set>
+        <choice>alpha</choice>
+        <choice>beta</choice>
+      </set>
+    </container>
+    >>> new_set = WithSet(None)
+    >>> deserialize(xml, IWithSet, new_set)
+    >>> new_set.set
+    set(['alpha', 'beta'])

Added: z3c.schema2xml/trunk/src/z3c/schema2xml/__init__.py
===================================================================
--- z3c.schema2xml/trunk/src/z3c/schema2xml/__init__.py	                        (rev 0)
+++ z3c.schema2xml/trunk/src/z3c/schema2xml/__init__.py	2007-12-21 05:07:57 UTC (rev 82377)
@@ -0,0 +1,4 @@
+from _schema2xml import IXMLGenerator
+from _schema2xml import serialize, serialize_to_tree
+from _schema2xml import deserialize, deserialize_from_tree
+from _schema2xml import GeneratedObject

Added: z3c.schema2xml/trunk/src/z3c/schema2xml/_schema2xml.py
===================================================================
--- z3c.schema2xml/trunk/src/z3c/schema2xml/_schema2xml.py	                        (rev 0)
+++ z3c.schema2xml/trunk/src/z3c/schema2xml/_schema2xml.py	2007-12-21 05:07:57 UTC (rev 82377)
@@ -0,0 +1,152 @@
+from lxml import etree
+
+import grok
+
+from persistent import Persistent
+
+from zope.interface import Interface, alsoProvides
+import zope.datetime
+from zope.location import Location
+from zope.schema import getFieldsInOrder
+from zope.schema.interfaces import IText, IInt, IObject, IList, IChoice, ISet
+from zope.schema.interfaces import IDatetime
+
+def serialize_to_tree(container, schema, instance):
+    for name, field in getFieldsInOrder(schema):
+        value = field.get(instance)
+        IXMLGenerator(field).output(container, value)
+    return container
+
+def serialize(
+        container_name, schema, instance, encoding='UTF-8', pretty_print=True):
+    container = etree.Element(container_name)
+    container = serialize_to_tree(container, schema, instance)
+    return etree.tostring(
+        container, encoding=encoding, pretty_print=pretty_print)
+
+def deserialize_from_tree(container, schema, instance):
+    for element in container:
+        field = schema[element.tag]
+        value = IXMLGenerator(field).input(element)
+        field.set(instance, value)
+
+    alsoProvides(instance, schema)
+
+def deserialize(xml, schema, instance):
+    container = etree.XML(xml)
+    deserialize_from_tree(container, schema, instance)
+
+class GeneratedObject(Location, Persistent):
+    def __init__(self):
+        pass
+
+class IXMLGenerator(Interface):
+
+    def output(container, value):
+        """Output value as XML element according to field.
+        """
+
+    def input(element):
+        """Input XML element according to field and return value.
+        """
+
+class Text(grok.Adapter):
+    grok.context(IText)
+    grok.implements(IXMLGenerator)
+
+    def output(self, container, value):
+        element = etree.SubElement(container, self.context.__name__)
+        element.text = value
+
+    def input(self, element):
+        if element.text is not None:
+            return unicode(element.text)
+        return None
+
+class Int(grok.Adapter):
+    grok.context(IInt)
+    grok.implements(IXMLGenerator)
+
+    def output(self, container, value):
+        element = etree.SubElement(container, self.context.__name__)
+        if value is not None:
+            element.text = str(value)
+
+    def input(self, element):
+        if element.text is not None and element.text != '':
+            return int(element.text)
+        return None
+
+class Object(grok.Adapter):
+    grok.context(IObject)
+    grok.implements(IXMLGenerator)
+
+    def output(self, container, value):
+        container = etree.SubElement(container, self.context.__name__)
+
+        for name, field in getFieldsInOrder(self.context.schema):
+            IXMLGenerator(field).output(container, field.get(value))
+
+    def input(self, element):
+        instance = GeneratedObject()
+        deserialize_from_tree(element, self.context.schema, instance)
+        return instance
+
+class List(grok.Adapter):
+    grok.context(IList)
+    grok.implements(IXMLGenerator)
+
+    def output(self, container, value):
+        container = etree.SubElement(container, self.context.__name__)
+        field = self.context.value_type
+        for v in value:
+            IXMLGenerator(field).output(container, v)
+
+    def input(self, element):
+        field = self.context.value_type
+        return [
+            IXMLGenerator(field).input(sub_element)
+            for sub_element in element]
+
+class Datetime(grok.Adapter):
+    grok.context(IDatetime)
+    grok.implements(IXMLGenerator)
+
+    def output(self, container, value):
+        element = etree.SubElement(container, self.context.__name__)
+        if value is not None:
+            element.text = value.isoformat()
+
+    def input(self, element):
+        if element.text is not None:
+            return zope.datetime.parseDatetimetz(element.text)
+        return None
+
+class Choice(grok.Adapter):
+    grok.context(IChoice)
+    grok.implements(IXMLGenerator)
+    
+    def output(self, container, value):
+        element = etree.SubElement(container, self.context.__name__)
+        element.text = value
+
+    def input(self, element):
+        if element.text is not None:
+            return element.text
+        return None
+
+class Set(grok.Adapter):
+    grok.context(ISet)
+    grok.implements(IXMLGenerator)
+
+    def output(self, container, value):
+        container = etree.SubElement(container, self.context.__name__)
+        field = self.context.value_type
+        for v in value:
+            IXMLGenerator(field).output(container, v)
+
+    def input(self, element):
+        field = self.context.value_type
+        return set([
+            IXMLGenerator(field).input(sub_element)
+            for sub_element in element])

Added: z3c.schema2xml/trunk/src/z3c/schema2xml/configure.zcml
===================================================================
--- z3c.schema2xml/trunk/src/z3c/schema2xml/configure.zcml	                        (rev 0)
+++ z3c.schema2xml/trunk/src/z3c/schema2xml/configure.zcml	2007-12-21 05:07:57 UTC (rev 82377)
@@ -0,0 +1 @@
+<grok package="." xmlns="http://namespaces.zope.org/grok" />

Added: z3c.schema2xml/trunk/src/z3c/schema2xml/tests.py
===================================================================
--- z3c.schema2xml/trunk/src/z3c/schema2xml/tests.py	                        (rev 0)
+++ z3c.schema2xml/trunk/src/z3c/schema2xml/tests.py	2007-12-21 05:07:57 UTC (rev 82377)
@@ -0,0 +1,23 @@
+import unittest
+
+from zope.testing import doctest
+
+def grok_setup(iets):
+    import grok
+    grok.grok('z3c.schema2xml')
+
+def test_suite():
+    optionflags = (
+        doctest.ELLIPSIS
+        | doctest.REPORT_NDIFF
+        | doctest.NORMALIZE_WHITESPACE
+        )
+
+    return unittest.TestSuite([
+        doctest.DocFileSuite(
+            'README.txt', setUp=grok_setup, optionflags=optionflags)
+        ])
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
+

Added: z3c.schema2xml/trunk/svn-commit.tmp
===================================================================
--- z3c.schema2xml/trunk/svn-commit.tmp	                        (rev 0)
+++ z3c.schema2xml/trunk/svn-commit.tmp	2007-12-21 05:07:57 UTC (rev 82377)
@@ -0,0 +1,4 @@
+Initial import of this package.
+--This line, and those below, will be ignored--
+
+A    .



More information about the Checkins mailing list