[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