================================ plone.supermodel: field handlers ================================ This file tests the various field handlers. Please note the following known limitations: * `Timedelta` fields are not supported. * When an `Object` field is serialised, the `default` and `missing_value` attributes are ignored. * `Choice` fields can only be serialised if were created from a simple list of values (the `values` constructor parameter) or use a named vocabulary. It is possible to import a Choice field with a source that is either an `ISource` or an `IContextSourceBinder`, but only if such instances can be imported from a given dotted name. Finally, `Choice` fields imported with a `values` list as a vocabulary or with the `default` or `missing_value` set, are assumed store a unicode string. First, let's wire up the package. >>> configuration = """\ ... ... ... ... ... ... ... ... """ >>> from StringIO import StringIO >>> from zope.configuration import xmlconfig >>> xmlconfig.xmlconfig(StringIO(configuration)) Then, let's test each field in turn. >>> from zope.component import getUtility >>> from zope import schema >>> from plone.supermodel.interfaces import IFieldExportImportHandler >>> from plone.supermodel.interfaces import IFieldNameExtractor >>> from plone.supermodel.utils import prettyXML >>> import datetime >>> import plone.supermodel.tests >>> from lxml import etree Bytes ----- >>> field = schema.Bytes(__name__="dummy", title=u"Test", ... description=u"Test desc", required=False, readonly=True, ... default='abc', missing_value='m', ... min_length=2, max_length=10) >>> fieldType = IFieldNameExtractor(field)() >>> handler = getUtility(IFieldExportImportHandler, name=fieldType) >>> element = handler.write(field, 'dummy', fieldType) >>> print prettyXML(element) abc Test desc 10 2 m True False Test >>> reciprocal = handler.read(element) >>> reciprocal.__class__ >>> reciprocal.__name__ 'dummy' >>> reciprocal.title u'Test' >>> reciprocal.description u'Test desc' >>> reciprocal.required False >>> reciprocal.readonly True >>> reciprocal.default 'abc' >>> reciprocal.missing_value 'm' >>> reciprocal.min_length 2 >>> reciprocal.max_length 10 BytesLine --------- >>> field = schema.BytesLine(__name__="dummy", title=u"Test", ... description=u"Test desc", required=False, readonly=True, ... default='abc', missing_value='m', ... min_length=2, max_length=10) >>> fieldType = IFieldNameExtractor(field)() >>> handler = getUtility(IFieldExportImportHandler, name=fieldType) >>> element = handler.write(field, 'dummy', fieldType) >>> print prettyXML(element) abc Test desc 10 2 m True False Test >>> reciprocal = handler.read(element) >>> reciprocal.__class__ >>> reciprocal.__name__ 'dummy' >>> reciprocal.title u'Test' >>> reciprocal.description u'Test desc' >>> reciprocal.required False >>> reciprocal.readonly True >>> reciprocal.default 'abc' >>> reciprocal.missing_value 'm' >>> reciprocal.min_length 2 >>> reciprocal.max_length 10 ASCII ----- >>> field = schema.ASCII(__name__="dummy", title=u"Test", ... description=u"Test desc", required=False, readonly=True, ... default='abc', missing_value='m', ... min_length=2, max_length=10) >>> fieldType = IFieldNameExtractor(field)() >>> handler = getUtility(IFieldExportImportHandler, name=fieldType) >>> element = handler.write(field, 'dummy', fieldType) >>> print prettyXML(element) abc Test desc 10 2 m True False Test >>> reciprocal = handler.read(element) >>> reciprocal.__class__ >>> reciprocal.__name__ 'dummy' >>> reciprocal.title u'Test' >>> reciprocal.description u'Test desc' >>> reciprocal.required False >>> reciprocal.readonly True >>> reciprocal.default 'abc' >>> reciprocal.missing_value 'm' >>> reciprocal.min_length 2 >>> reciprocal.max_length 10 ASCIILine --------- >>> field = schema.ASCIILine(__name__="dummy", title=u"Test", ... description=u"Test desc", required=False, readonly=True, ... default='abc', missing_value='m', ... min_length=2, max_length=10) >>> fieldType = IFieldNameExtractor(field)() >>> handler = getUtility(IFieldExportImportHandler, name=fieldType) >>> element = handler.write(field, 'dummy', fieldType) >>> print prettyXML(element) abc Test desc 10 2 m True False Test >>> reciprocal = handler.read(element) >>> reciprocal.__class__ >>> reciprocal.__name__ 'dummy' >>> reciprocal.title u'Test' >>> reciprocal.description u'Test desc' >>> reciprocal.required False >>> reciprocal.readonly True >>> reciprocal.default 'abc' >>> reciprocal.missing_value 'm' >>> reciprocal.min_length 2 >>> reciprocal.max_length 10 Text ---- >>> field = schema.Text(__name__="dummy", title=u"Test", ... description=u"Test desc", required=False, readonly=True, ... default=u'abc', missing_value=u'm', ... min_length=2, max_length=10) >>> fieldType = IFieldNameExtractor(field)() >>> handler = getUtility(IFieldExportImportHandler, name=fieldType) >>> element = handler.write(field, 'dummy', fieldType) >>> print prettyXML(element) abc Test desc 10 2 m True False Test >>> reciprocal = handler.read(element) >>> reciprocal.__class__ >>> reciprocal.__name__ 'dummy' >>> reciprocal.title u'Test' >>> reciprocal.description u'Test desc' >>> reciprocal.required False >>> reciprocal.readonly True >>> reciprocal.default u'abc' >>> reciprocal.missing_value u'm' >>> reciprocal.min_length 2 >>> reciprocal.max_length 10 TextLine -------- >>> field = schema.TextLine(__name__="dummy", title=u"Test", ... description=u"Test desc", required=False, readonly=True, ... default=u'abc', missing_value=u'm', ... min_length=2, max_length=10) >>> fieldType = IFieldNameExtractor(field)() >>> handler = getUtility(IFieldExportImportHandler, name=fieldType) >>> element = handler.write(field, 'dummy', fieldType) >>> print prettyXML(element) abc Test desc 10 2 m True False Test >>> reciprocal = handler.read(element) >>> reciprocal.__class__ >>> reciprocal.__name__ 'dummy' >>> reciprocal.title u'Test' >>> reciprocal.description u'Test desc' >>> reciprocal.required False >>> reciprocal.readonly True >>> reciprocal.default u'abc' >>> reciprocal.missing_value u'm' >>> reciprocal.min_length 2 >>> reciprocal.max_length 10 SourceText ---------- >>> field = schema.SourceText(__name__="dummy", title=u"Test", ... description=u"Test desc", required=False, readonly=True, ... default=u'abc', missing_value=u'm', ... min_length=2, max_length=10) >>> fieldType = IFieldNameExtractor(field)() >>> handler = getUtility(IFieldExportImportHandler, name=fieldType) >>> element = handler.write(field, 'dummy', fieldType) >>> print prettyXML(element) abc Test desc 10 2 m True False Test >>> reciprocal = handler.read(element) >>> reciprocal.__class__ >>> reciprocal.__name__ 'dummy' >>> reciprocal.title u'Test' >>> reciprocal.description u'Test desc' >>> reciprocal.required False >>> reciprocal.readonly True >>> reciprocal.default u'abc' >>> reciprocal.missing_value u'm' >>> reciprocal.min_length 2 >>> reciprocal.max_length 10 URI --- >>> field = schema.URI(__name__="dummy", title=u"Test", ... description=u"Test desc", required=False, readonly=True, ... default='http://plone.org', missing_value='m', ... min_length=2, max_length=100) >>> fieldType = IFieldNameExtractor(field)() >>> handler = getUtility(IFieldExportImportHandler, name=fieldType) >>> element = handler.write(field, 'dummy', fieldType) >>> print prettyXML(element) http://plone.org Test desc 100 2 m True False Test >>> reciprocal = handler.read(element) >>> reciprocal.__class__ >>> reciprocal.__name__ 'dummy' >>> reciprocal.title u'Test' >>> reciprocal.description u'Test desc' >>> reciprocal.required False >>> reciprocal.readonly True >>> reciprocal.default 'http://plone.org' >>> reciprocal.missing_value 'm' >>> reciprocal.min_length 2 >>> reciprocal.max_length 100 Id -- >>> field = schema.Id(__name__="dummy", title=u"Test", ... description=u"Test desc", required=False, readonly=True, ... default='a.b.c', missing_value='m', ... min_length=2, max_length=10) >>> fieldType = IFieldNameExtractor(field)() >>> handler = getUtility(IFieldExportImportHandler, name=fieldType) >>> element = handler.write(field, 'dummy', fieldType) >>> print prettyXML(element) a.b.c Test desc 10 2 m True False Test >>> reciprocal = handler.read(element) >>> reciprocal.__class__ >>> reciprocal.__name__ 'dummy' >>> reciprocal.title u'Test' >>> reciprocal.description u'Test desc' >>> reciprocal.required False >>> reciprocal.readonly True >>> reciprocal.default 'a.b.c' >>> reciprocal.missing_value 'm' >>> reciprocal.min_length 2 >>> reciprocal.max_length 10 DottedName ----------- >>> field = schema.DottedName(__name__="dummy", title=u"Test", ... description=u"Test desc", required=False, readonly=True, ... default='a.b.c', missing_value='m', ... min_length=2, max_length=10, min_dots=2, max_dots=4) >>> fieldType = IFieldNameExtractor(field)() >>> handler = getUtility(IFieldExportImportHandler, name=fieldType) >>> element = handler.write(field, 'dummy', fieldType) >>> print prettyXML(element) a.b.c Test desc 4 10 2 2 m True False Test >>> reciprocal = handler.read(element) >>> reciprocal.__class__ >>> reciprocal.__name__ 'dummy' >>> reciprocal.title u'Test' >>> reciprocal.description u'Test desc' >>> reciprocal.required False >>> reciprocal.readonly True >>> reciprocal.default 'a.b.c' >>> reciprocal.missing_value 'm' >>> reciprocal.min_length 2 >>> reciprocal.max_length 10 >>> reciprocal.min_dots 2 >>> reciprocal.max_dots 4 Password -------- >>> field = schema.Password(__name__="dummy", title=u"Test", ... description=u"Test desc", required=False, readonly=True, ... default=u'abc', missing_value=u'm', ... min_length=2, max_length=10) >>> fieldType = IFieldNameExtractor(field)() >>> handler = getUtility(IFieldExportImportHandler, name=fieldType) >>> element = handler.write(field, 'dummy', fieldType) >>> print prettyXML(element) abc Test desc 10 2 m True False Test >>> reciprocal = handler.read(element) >>> reciprocal.__class__ >>> reciprocal.__name__ 'dummy' >>> reciprocal.title u'Test' >>> reciprocal.description u'Test desc' >>> reciprocal.required False >>> reciprocal.readonly True >>> reciprocal.default u'abc' >>> reciprocal.missing_value u'm' >>> reciprocal.min_length 2 >>> reciprocal.max_length 10 Bool ---- >>> field = schema.Bool(__name__="dummy", title=u"Test", ... description=u"Test desc", required=False, readonly=True, ... default=False, missing_value=True) >>> fieldType = IFieldNameExtractor(field)() >>> handler = getUtility(IFieldExportImportHandler, name=fieldType) >>> element = handler.write(field, 'dummy', fieldType) >>> print prettyXML(element) False Test desc True True False Test >>> reciprocal = handler.read(element) >>> reciprocal.__class__ >>> reciprocal.__name__ 'dummy' >>> reciprocal.title u'Test' >>> reciprocal.description u'Test desc' >>> reciprocal.required False >>> reciprocal.readonly True >>> reciprocal.default False >>> reciprocal.missing_value True Int --- >>> field = schema.Int(__name__="dummy", title=u"Test", ... description=u"Test desc", required=False, readonly=True, ... default=12, missing_value=-1, ... min=1, max=99) >>> fieldType = IFieldNameExtractor(field)() >>> handler = getUtility(IFieldExportImportHandler, name=fieldType) >>> element = handler.write(field, 'dummy', fieldType) >>> print prettyXML(element) 12 Test desc 99 1 -1 True False Test >>> reciprocal = handler.read(element) >>> reciprocal.__class__ >>> reciprocal.__name__ 'dummy' >>> reciprocal.title u'Test' >>> reciprocal.description u'Test desc' >>> reciprocal.required False >>> reciprocal.readonly True >>> reciprocal.default 12 >>> reciprocal.missing_value -1 >>> reciprocal.min 1 >>> reciprocal.max 99 Float ----- >>> field = schema.Float(__name__="dummy", title=u"Test", ... description=u"Test desc", required=False, readonly=True, ... default=12.1, missing_value=-1.0, ... min=1.123, max=99.5) >>> fieldType = IFieldNameExtractor(field)() >>> handler = getUtility(IFieldExportImportHandler, name=fieldType) >>> element = handler.write(field, 'dummy', fieldType) >>> print prettyXML(element) 12.1 Test desc 99.5 1.123 -1.0 True False Test >>> reciprocal = handler.read(element) >>> reciprocal.__class__ >>> reciprocal.__name__ 'dummy' >>> reciprocal.title u'Test' >>> reciprocal.description u'Test desc' >>> reciprocal.required False >>> reciprocal.readonly True >>> reciprocal.default 12.1 >>> reciprocal.missing_value -1.0 >>> reciprocal.min 1.123 >>> reciprocal.max 99.5 Decimal ------- >>> import decimal >>> field = schema.Decimal(__name__="dummy", title=u"Test", ... description=u"Test desc", required=False, readonly=True, ... default=decimal.Decimal("12.1"), missing_value=decimal.Decimal("-1.0"), ... min=decimal.Decimal("1.123"), max=decimal.Decimal("99.5")) >>> fieldType = IFieldNameExtractor(field)() >>> handler = getUtility(IFieldExportImportHandler, name=fieldType) >>> element = handler.write(field, 'dummy', fieldType) >>> print prettyXML(element) 12.1 Test desc 99.5 1.123 -1.0 True False Test >>> reciprocal = handler.read(element) >>> reciprocal.__class__ >>> reciprocal.__name__ 'dummy' >>> reciprocal.title u'Test' >>> reciprocal.description u'Test desc' >>> reciprocal.required False >>> reciprocal.readonly True >>> reciprocal.default == decimal.Decimal('12.1') True >>> reciprocal.missing_value == decimal.Decimal('-1.0') True >>> reciprocal.min == decimal.Decimal('1.123') True >>> reciprocal.max == decimal.Decimal('99.5') True Date ---- >>> field = schema.Date(__name__="dummy", title=u"Test", ... description=u"Test desc", required=False, readonly=True, ... default=datetime.date(2001,1,2), missing_value=datetime.date(2000,1,1), ... min=datetime.date(2000,10,12), max=datetime.date(2099,12,31)) >>> fieldType = IFieldNameExtractor(field)() >>> handler = getUtility(IFieldExportImportHandler, name=fieldType) >>> element = handler.write(field, 'dummy', fieldType) >>> print prettyXML(element) 2001-01-02 Test desc 2099-12-31 2000-10-12 2000-01-01 True False Test >>> reciprocal = handler.read(element) >>> reciprocal.__class__ >>> reciprocal.__name__ 'dummy' >>> reciprocal.title u'Test' >>> reciprocal.description u'Test desc' >>> reciprocal.required False >>> reciprocal.readonly True >>> reciprocal.default datetime.date(2001, 1, 2) >>> reciprocal.missing_value datetime.date(2000, 1, 1) >>> reciprocal.min datetime.date(2000, 10, 12) >>> reciprocal.max datetime.date(2099, 12, 31) Datetime --------- >>> field = schema.Datetime(__name__="dummy", title=u"Test", ... description=u"Test desc", required=False, readonly=True, ... default=datetime.datetime(2001,1,2,1,2,3), missing_value=datetime.datetime(2000,1,1,2,3,4), ... min=datetime.datetime(2000,10,12,0,0,2), max=datetime.datetime(2099,12,31,1,2,2)) >>> fieldType = IFieldNameExtractor(field)() >>> handler = getUtility(IFieldExportImportHandler, name=fieldType) >>> element = handler.write(field, 'dummy', fieldType) >>> print prettyXML(element) 2001-01-02 01:02:03 Test desc 2099-12-31 01:02:02 2000-10-12 00:00:02 2000-01-01 02:03:04 True False Test >>> reciprocal = handler.read(element) >>> reciprocal.__class__ >>> reciprocal.__name__ 'dummy' >>> reciprocal.title u'Test' >>> reciprocal.description u'Test desc' >>> reciprocal.required False >>> reciprocal.readonly True >>> reciprocal.default datetime.datetime(2001, 1, 2, 1, 2, 3, 1) >>> reciprocal.missing_value datetime.datetime(2000, 1, 1, 2, 3, 4, 5) >>> reciprocal.min datetime.datetime(2000, 10, 12, 0, 0, 2, 3) >>> reciprocal.max datetime.datetime(2099, 12, 31, 1, 2, 2, 3) InterfaceField --------------- >>> field = schema.InterfaceField(__name__="dummy", title=u"Test", ... description=u"Test desc", required=False, readonly=True, ... default=plone.supermodel.tests.IDummy, ... missing_value=plone.supermodel.tests.IDummy) >>> fieldType = IFieldNameExtractor(field)() >>> handler = getUtility(IFieldExportImportHandler, name=fieldType) >>> element = handler.write(field, 'dummy', fieldType) >>> print prettyXML(element) plone.supermodel.tests.IDummy Test desc plone.supermodel.tests.IDummy True False Test >>> reciprocal = handler.read(element) >>> reciprocal.__class__ >>> reciprocal.__name__ 'dummy' >>> reciprocal.title u'Test' >>> reciprocal.description u'Test desc' >>> reciprocal.required False >>> reciprocal.readonly True >>> reciprocal.default >>> reciprocal.missing_value Tuple ----- >>> field = schema.Tuple(__name__="dummy", title=u"Test", ... description=u"Test desc", required=False, readonly=True, ... default=(1,2), missing_value=(), ... min_length=2, max_length=10, ... value_type=schema.Int(title=u"Val")) >>> fieldType = IFieldNameExtractor(field)() >>> handler = getUtility(IFieldExportImportHandler, name=fieldType) >>> element = handler.write(field, 'dummy', fieldType) >>> print prettyXML(element) 1 2 Test desc 10 2 True False Test Val >>> reciprocal = handler.read(element) >>> reciprocal.__class__ >>> reciprocal.__name__ 'dummy' >>> reciprocal.title u'Test' >>> reciprocal.description u'Test desc' >>> reciprocal.required False >>> reciprocal.readonly True >>> reciprocal.default (1, 2) >>> reciprocal.missing_value () >>> reciprocal.min_length 2 >>> reciprocal.max_length 10 >>> reciprocal.value_type.__class__ >>> reciprocal.value_type.title u'Val' List ---- >>> field = schema.List(__name__="dummy", title=u"Test", ... description=u"Test desc", required=False, readonly=True, ... default=[1,2], missing_value=[], ... min_length=2, max_length=10, ... value_type=schema.Int(title=u"Val")) >>> fieldType = IFieldNameExtractor(field)() >>> handler = getUtility(IFieldExportImportHandler, name=fieldType) >>> element = handler.write(field, 'dummy', fieldType) >>> print prettyXML(element) 1 2 Test desc 10 2 True False Test Val >>> reciprocal = handler.read(element) >>> reciprocal.__class__ >>> reciprocal.__name__ 'dummy' >>> reciprocal.title u'Test' >>> reciprocal.description u'Test desc' >>> reciprocal.required False >>> reciprocal.readonly True >>> reciprocal.default [1, 2] >>> reciprocal.missing_value [] >>> reciprocal.min_length 2 >>> reciprocal.max_length 10 >>> reciprocal.value_type.__class__ >>> reciprocal.value_type.title u'Val' Set --- >>> field = schema.Set(__name__="dummy", title=u"Test", ... description=u"Test desc", required=False, readonly=True, ... default=set((1,2)), missing_value=set(), ... min_length=2, max_length=10, ... value_type=schema.Int(title=u"Val")) >>> fieldType = IFieldNameExtractor(field)() >>> handler = getUtility(IFieldExportImportHandler, name=fieldType) >>> element = handler.write(field, 'dummy', fieldType) >>> print prettyXML(element) 1 2 Test desc 10 2 True False Test Val >>> reciprocal = handler.read(element) >>> reciprocal.__class__ >>> reciprocal.__name__ 'dummy' >>> reciprocal.title u'Test' >>> reciprocal.description u'Test desc' >>> reciprocal.required False >>> reciprocal.readonly True >>> reciprocal.default set([1, 2]) >>> reciprocal.missing_value set([]) >>> reciprocal.min_length 2 >>> reciprocal.max_length 10 >>> reciprocal.value_type.__class__ >>> reciprocal.value_type.title u'Val' FrozenSet --------- >>> field = schema.FrozenSet(__name__="dummy", title=u"Test", ... description=u"Test desc", required=False, readonly=True, ... default=frozenset((1,2)), missing_value=frozenset(), ... min_length=2, max_length=10, ... value_type=schema.Int(title=u"Val")) >>> fieldType = IFieldNameExtractor(field)() >>> handler = getUtility(IFieldExportImportHandler, name=fieldType) >>> element = handler.write(field, 'dummy', fieldType) >>> print prettyXML(element) 1 2 Test desc 10 2 True False Test Val >>> reciprocal = handler.read(element) >>> reciprocal.__class__ >>> reciprocal.__name__ 'dummy' >>> reciprocal.title u'Test' >>> reciprocal.description u'Test desc' >>> reciprocal.required False >>> reciprocal.readonly True >>> reciprocal.default frozenset([1, 2]) >>> reciprocal.missing_value frozenset([]) >>> reciprocal.min_length 2 >>> reciprocal.max_length 10 >>> reciprocal.value_type.__class__ >>> reciprocal.value_type.title u'Val' Dict ---- >>> field = schema.Dict(__name__="dummy", title=u"Test", ... description=u"Test desc", required=False, readonly=True, ... default={'a':1, 'b':2}, missing_value={}, ... min_length=2, max_length=10, ... key_type=schema.ASCIILine(title=u"Key"), ... value_type=schema.Int(title=u"Val")) >>> fieldType = IFieldNameExtractor(field)() >>> handler = getUtility(IFieldExportImportHandler, name=fieldType) >>> element = handler.write(field, 'dummy', fieldType) >>> print prettyXML(element) 1 2 Test desc Key 10 2 True False Test Val >>> reciprocal = handler.read(element) >>> reciprocal.__class__ >>> reciprocal.__name__ 'dummy' >>> reciprocal.title u'Test' >>> reciprocal.description u'Test desc' >>> reciprocal.required False >>> reciprocal.readonly True >>> reciprocal.default {'a': 1, 'b': 2} >>> reciprocal.missing_value {} >>> reciprocal.min_length 2 >>> reciprocal.max_length 10 >>> reciprocal.key_type.__class__ >>> reciprocal.key_type.title u'Key' >>> reciprocal.value_type.__class__ >>> reciprocal.value_type.title u'Val' Object ------ Note: when an object field is written, the 'default' and 'missing_value' fields will be omitted, as there is no way to write these reliably. >>> dummy1 = plone.supermodel.tests.Dummy() >>> dummy2 = plone.supermodel.tests.Dummy() >>> field = schema.Object(__name__="dummy", title=u"Test", ... description=u"Test desc", required=False, readonly=True, ... default=dummy1, missing_value=dummy2, ... schema=plone.supermodel.tests.IDummy) >>> fieldType = IFieldNameExtractor(field)() >>> handler = getUtility(IFieldExportImportHandler, name=fieldType) >>> element = handler.write(field, 'dummy', fieldType) #doctest: +ELLIPSIS >>> print prettyXML(element) Test desc True False plone.supermodel.tests.IDummy Test However, we support reading an object dotted name for an object field that references a particular dotted name. >>> element = etree.XML("""\ ... ... plone.supermodel.tests.dummy1 ... Test desc ... ... True ... False ... plone.supermodel.tests.IDummy ... Test ... ... """) >>> reciprocal = handler.read(element) >>> reciprocal.__class__ >>> reciprocal.__name__ 'dummy' >>> reciprocal.title u'Test' >>> reciprocal.description u'Test desc' >>> reciprocal.required False >>> reciprocal.readonly True >>> reciprocal.default is plone.supermodel.tests.dummy1 True >>> reciprocal.missing_value is None True Choice ------ The choice field supports several different modes: a named vocabulary, a list of values, a source object, or a source context binder object. However, plone.supermodel only supports exporting named vocabularies or lists of unicode string values. In addition, it is possible to import (but not export) a source or context source binder, provided it can be imported from a dotted name. 1. Named vocabularies These can be both exported and imported. >>> field = schema.Choice(__name__="dummy", title=u"Test", ... description=u"Test desc", required=False, readonly=True, ... default='a', missing_value='', vocabulary=u'dummy.vocab') >>> fieldType = IFieldNameExtractor(field)() >>> handler = getUtility(IFieldExportImportHandler, name=fieldType) >>> element = handler.write(field, 'dummy', fieldType) >>> print prettyXML(element) a Test desc True False Test dummy.vocab >>> reciprocal = handler.read(element) >>> reciprocal.__class__ >>> reciprocal.__name__ 'dummy' >>> reciprocal.title u'Test' >>> reciprocal.description u'Test desc' >>> reciprocal.required False >>> reciprocal.readonly True >>> reciprocal.default # note - value is always unicode u'a' >>> reciprocal.missing_value # note - value is always unicode u'' >>> reciprocal.vocabulary is None True >>> reciprocal.vocabularyName u'dummy.vocab' 2. Values vocabularies These can be both imported and exported, but note that the value is always a unicode string when importing. >>> field = schema.Choice(__name__="dummy", title=u"Test", ... description=u"Test desc", required=False, readonly=True, ... default='a', missing_value='', values=['a', 'b', 'c']) >>> fieldType = IFieldNameExtractor(field)() >>> handler = getUtility(IFieldExportImportHandler, name=fieldType) >>> element = handler.write(field, 'dummy', fieldType) >>> print prettyXML(element) a Test desc True False Test a b c >>> reciprocal = handler.read(element) >>> reciprocal.__class__ >>> reciprocal.__name__ 'dummy' >>> reciprocal.title u'Test' >>> reciprocal.description u'Test desc' >>> reciprocal.required False >>> reciprocal.readonly True >>> reciprocal.default u'a' >>> reciprocal.missing_value u'' >>> [t.value for t in reciprocal.vocabulary] [u'a', u'b', u'c'] >>> reciprocal.vocabularyName is None True There was a bug when the XML namespace was specified explicitly; let's make sure it hasn't regressed. >>> from plone.supermodel.interfaces import XML_NAMESPACE >>> element.set('xmlns', XML_NAMESPACE) >>> element = etree.parse(StringIO(prettyXML(element))).getroot() >>> reciprocal = handler.read(element) >>> [t.value for t in reciprocal.vocabulary] [u'a', u'b', u'c'] Also, make sure we can handle terms with unicode values (as long as their tokens are the utf8-encoded values). >>> from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm >>> vocab = SimpleVocabulary([ ... SimpleTerm(token='a', value=u'a', title=u'a'), ... SimpleTerm(token=r'\xe7', value=u'\xe7', title=u'\xe7'), # c with cedilla ... ]) >>> field = schema.Choice(__name__="dummy", title=u"Test", ... description=u"Test desc", required=False, readonly=True, ... default='a', missing_value='', vocabulary=vocab) >>> fieldType = IFieldNameExtractor(field)() >>> handler = getUtility(IFieldExportImportHandler, name=fieldType) >>> element = handler.write(field, 'dummy', fieldType) >>> print prettyXML(element) a Test desc True False Test a ç >>> reciprocal = handler.read(element) >>> [t.value for t in reciprocal.vocabulary] [u'a', u'\xe7'] Additionally, it is possible for Choice fields with a values vocabulary whose terms contain values distinct from term titles for each respective term. This is accomplished by using the 'key' attribute of each contained 'element' of the values element (this is consistent with how Dict fields are output, only for Choices, order is guaranteed). >>> from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm >>> vocab = SimpleVocabulary([ ... SimpleTerm(value=u'a', title=u'A'), ... SimpleTerm(value=u'b', title=u'B'), ... ]) >>> field = schema.Choice( ... __name__="dummy", ... title=u"Test", ... vocabulary=vocab, ... ) >>> handler = getUtility(IFieldExportImportHandler, name=fieldType) >>> element = handler.write(field, 'dummy', fieldType) >>> print prettyXML(element) Test A B 3. Sources and source binders We cannot export choice fields with a source or context source binder: >>> field = schema.Choice(__name__="dummy", title=u"Test", ... description=u"Test desc", required=False, readonly=True, ... vocabulary=plone.supermodel.tests.dummy_vocabulary_instance) >>> fieldType = IFieldNameExtractor(field)() >>> handler = getUtility(IFieldExportImportHandler, name=fieldType) >>> element = handler.write(field, 'dummy', fieldType) # doctest: +ELLIPSIS Traceback (most recent call last): ... NotImplementedError: Cannot export a vocabulary that is not based on a simple list of values >>> field = schema.Choice(__name__="dummy", title=u"Test", ... description=u"Test desc", required=False, readonly=True, ... source=plone.supermodel.tests.dummy_vocabulary_instance) >>> fieldType = IFieldNameExtractor(field)() >>> handler = getUtility(IFieldExportImportHandler, name=fieldType) >>> element = handler.write(field, 'dummy', fieldType) # doctest: +ELLIPSIS Traceback (most recent call last): ... NotImplementedError: Cannot export a vocabulary that is not based on a simple list of values >>> field = schema.Choice(__name__="dummy", title=u"Test", ... description=u"Test desc", required=False, readonly=True, ... source=plone.supermodel.tests.dummy_binder) >>> fieldType = IFieldNameExtractor(field)() >>> handler = getUtility(IFieldExportImportHandler, name=fieldType) >>> element = handler.write(field, 'dummy', fieldType) # doctest: +ELLIPSIS Traceback (most recent call last): ... NotImplementedError: Choice fields with vocabularies not based on a simple list of values or a named vocabulary cannot be exported However, we can import a choice field with a source, provided that source can be specified via an importable dotted name. >>> element = etree.XML("""\ ... ... a ... Test desc ... ... True ... False ... Test ... plone.supermodel.tests.dummy_binder ... ... """) >>> reciprocal = handler.read(element) >>> reciprocal.__class__ >>> reciprocal.__name__ 'dummy' >>> reciprocal.title u'Test' >>> reciprocal.description u'Test desc' >>> reciprocal.required False >>> reciprocal.readonly True >>> reciprocal.default u'a' >>> reciprocal.vocabulary is plone.supermodel.tests.dummy_binder True >>> reciprocal.vocabularyName is None True >>> element = etree.XML("""\ ... ... a ... Test desc ... ... True ... False ... Test ... plone.supermodel.tests.dummy_vocabulary_instance ... ... """) >>> reciprocal = handler.read(element) >>> reciprocal.__class__ >>> reciprocal.__name__ 'dummy' >>> reciprocal.title u'Test' >>> reciprocal.description u'Test desc' >>> reciprocal.required False >>> reciprocal.readonly True >>> reciprocal.default u'a' >>> reciprocal.vocabulary is plone.supermodel.tests.dummy_vocabulary_instance True >>> reciprocal.vocabularyName is None True defaultFactory usage -------------------- Fields may specify defaultFactory attributes as dotted interfaces. defaultFactory callables should provide either zope.schema.interfaces.IContextAwareDefaultFactory or plone.supermodel.interfaces.IDefaultFactory. Note that zope.schema allows callables without any marker interface. Our requirements are an extra validation measure. Try specifying a defaultFactory attribute:: >>> element = etree.XML("""\ ... ... plone.supermodel.tests.dummy_defaultFactory ... Test desc ... 10 ... 2 ... m ... True ... False ... Test ... ... """) Import it:: >>> handler = getUtility(IFieldExportImportHandler, name='zope.schema.TextLine') Sanity checks:: >>> reciprocal = handler.read(element) >>> reciprocal.__class__ And, look for the specified defaultFactory:: >>> reciprocal.defaultFactory == plone.supermodel.tests.dummy_defaultFactory True Let's try it with a callable that provides IContextAwareDefaultFactory:: >>> element = etree.XML("""\ ... ... plone.supermodel.tests.dummy_defaultCAFactory ... Test desc ... 10 ... 2 ... m ... True ... False ... Test ... ... """) >>> handler = getUtility(IFieldExportImportHandler, name='zope.schema.TextLine') >>> reciprocal = handler.read(element) >>> reciprocal.defaultFactory == plone.supermodel.tests.dummy_defaultCAFactory True And, check to make sure that we can't use a callable that doesn't have one of our marker interfaces:: >>> element = etree.XML("""\ ... ... plone.supermodel.tests.dummy_defaultBadFactory ... Test desc ... 10 ... 2 ... m ... True ... False ... Test ... ... """) >>> handler = getUtility(IFieldExportImportHandler, name='zope.schema.TextLine') >>> reciprocal = handler.read(element) Traceback (most recent call last): ... ImportError: defaultFactory must provide zope.schema.interfaces.IContextAwareDefaultFactory or plone.supermodel.IDefaultFactory A non-existent callable should also raise an error:: >>> element = etree.XML("""\ ... ... plone.supermodel.tests.nonExistentFactory ... Test desc ... 10 ... 2 ... m ... True ... False ... Test ... ... """) >>> handler = getUtility(IFieldExportImportHandler, name='zope.schema.TextLine') >>> reciprocal = handler.read(element) Traceback (most recent call last): ... ImportError: No module named nonExistentFactory