[Testbot] Plone 4.3 - Python 2.6 - Build # 2109 - Regression! - 2 failure(s)
jenkins at plone.org
jenkins at plone.org
Fri May 30 18:49:04 UTC 2014
-------------------------------------------------------------------------------
Plone 4.3 - Python 2.6 - Build # 2109 - Still Failing!
-------------------------------------------------------------------------------
http://jenkins.plone.org/job/plone-4.3-python-2.6/2109/
-------------------------------------------------------------------------------
CHANGES
-------------------------------------------------------------------------------
Repository: plone.supermodel
Branch: refs/heads/master
Date: 2014-04-16T12:08:33-06:00
Author: Sean Upton (seanupton) <sdupton at gmail.com>
Commit: https://github.com/plone/plone.supermodel/commit/f16f480b6062a44bda6f729f03de4555c65c82b0
Fix parsing of empty Choice term to u'', not None, which addresses a cause of https://github.com/plone/plone.app.dexterity/issues/49 -- also explicitly construct SimpleTerm instances for each Choice field element, instead of relying on zope.schema constructors to do so. This ensures that all terms have non-None title attributes. Added tests for ChoiceHandler serialization and parsing.
Files changed:
M CHANGES.rst
M plone/supermodel/exportimport.py
M plone/supermodel/tests.py
diff --git a/CHANGES.rst b/CHANGES.rst
index 4f07a1d..2f7a55d 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -4,7 +4,17 @@ Changelog
1.2.5 (unreleased)
------------------
-- Nothing changed yet.
+- Fix parsing of empty Choice term to u'', not None, which addresses a
+ cause of https://github.com/plone/plone.app.dexterity/issues/49
+ [seanupton]
+
+- Explicitly construct SimpleTerm instances for each Choice field
+ element, instead of relying on zope.schema constructors to do so.
+ This ensures that all terms have non-None title attributes.
+ [seanupton]
+
+- Tests for ChoiceHandler serialization and parsing.
+ [seanupton]
1.2.4 (2014-01-27)
diff --git a/plone/supermodel/exportimport.py b/plone/supermodel/exportimport.py
index 8fa7972..b502ee8 100644
--- a/plone/supermodel/exportimport.py
+++ b/plone/supermodel/exportimport.py
@@ -274,16 +274,16 @@ def __init__(self, klass):
def _constructField(self, attributes):
if 'values' in attributes:
terms = []
- unicode_found = False
for value in attributes['values']:
- encoded = value.encode('unicode_escape')
+ encoded = (value or '').encode('unicode_escape')
if value != encoded:
- unicode_found = True
- term = SimpleTerm(token=encoded, value=value, title=value)
+ value = value or u''
+ term = SimpleTerm(token=encoded, value=value, title=value)
+ else:
+ term = SimpleTerm(value=value, title=value)
terms.append(term)
- if unicode_found:
- attributes['vocabulary'] = SimpleVocabulary(terms)
- del attributes['values']
+ attributes['vocabulary'] = SimpleVocabulary(terms)
+ del attributes['values']
return super(ChoiceHandler, self)._constructField(attributes)
def write(self, field, name, type, elementName='field'):
diff --git a/plone/supermodel/tests.py b/plone/supermodel/tests.py
index 8ab9e58..fa762c6 100644
--- a/plone/supermodel/tests.py
+++ b/plone/supermodel/tests.py
@@ -10,13 +10,31 @@
from zope.schema import getFieldNamesInOrder
from zope.schema.interfaces import IContextAwareDefaultFactory
from zope.schema.interfaces import IContextSourceBinder
-from zope.schema.vocabulary import SimpleVocabulary
+from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
from zope import schema
from plone.supermodel import utils
+from plone.supermodel.exportimport import ChoiceHandler
from plone.supermodel.interfaces import IDefaultFactory
+def configure():
+ zope.component.testing.setUp()
+ configuration = """\
+ <configure
+ xmlns="http://namespaces.zope.org/zope"
+ i18n_domain="plone.supermodel.tests">
+
+ <include package="zope.component" file="meta.zcml" />
+
+ <include package="plone.supermodel" />
+
+ </configure>
+ """
+ from zope.configuration import xmlconfig
+ xmlconfig.xmlconfig(StringIO(configuration))
+
+
class IBase(Interface):
title = schema.TextLine(title=u"Title")
description = schema.TextLine(title=u"Description")
@@ -304,21 +322,7 @@ class ISchema(IBase1, IBase2, IBase3):
class TestValueToElement(unittest.TestCase):
def setUp(self):
- zope.component.testing.setUp()
- configuration = """\
- <configure
- xmlns="http://namespaces.zope.org/zope"
- i18n_domain="plone.supermodel.tests">
-
- <include package="zope.component" file="meta.zcml" />
-
- <include package="plone.supermodel" />
-
- </configure>
- """
- from zope.configuration import xmlconfig
- xmlconfig.xmlconfig(StringIO(configuration))
-
+ configure()
tearDown = zope.component.testing.tearDown
@@ -386,10 +390,57 @@ def test_nested_dicts(self):
)
+class TestChoiceHandling(unittest.TestCase):
+
+ def setUp(self):
+ configure()
+ self.handler = ChoiceHandler(schema.Choice)
+
+ def _choice(self):
+ vocab = SimpleVocabulary([SimpleTerm(t, title=t) for t in (u'a', u'b', u'c')])
+ expected = '<field name="myfield" type="zope.schema.Choice">'\
+ '<values>'\
+ '<element>a</element><element>b</element><element>c</element>'\
+ '</values>'\
+ '</field>'
+ return (schema.Choice(vocabulary=vocab), expected)
+
+ def _choice_with_empty(self):
+ # add an empty string term to vocabulary
+ vocab = SimpleVocabulary([SimpleTerm(t, title=t) for t in (u'a', u'')])
+ expected = '<field name="myfield" type="zope.schema.Choice">'\
+ '<values>'\
+ '<element>a</element>'\
+ '<element></element>'\
+ '</values>'\
+ '</field>'
+ return (schema.Choice(vocabulary=vocab), expected)
+
+ def test_choice_serialized(self):
+ field, expected = self._choice()
+ el = self.handler.write(field, 'myfield', 'zope.schema.Choice')
+ self.assertEquals(etree.tostring(el), expected)
+ # now with an empty string term in vocab:
+ field, expected = self._choice_with_empty()
+ el = self.handler.write(field, 'myfield', 'zope.schema.Choice')
+ self.assertEquals(etree.tostring(el), expected)
+
+ def test_choice_parsing(self):
+ _termvalues = lambda vocab: tuple((t.value, t.title) for t in vocab)
+ for field, expected in (self._choice(), self._choice_with_empty()):
+ el = etree.fromstring(expected)
+ imported_field = self.handler.read(el)
+ self.assertEquals(
+ _termvalues(imported_field.vocabulary),
+ _termvalues(field.vocabulary),
+ )
+
+
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(TestUtils),
unittest.makeSuite(TestValueToElement),
+ unittest.makeSuite(TestChoiceHandling),
doctest.DocFileSuite('schema.txt',
setUp=zope.component.testing.setUp,
tearDown=zope.component.testing.tearDown,
Repository: plone.supermodel
Branch: refs/heads/master
Date: 2014-04-17T00:10:11-06:00
Author: Sean Upton (seanupton) <sdupton at gmail.com>
Commit: https://github.com/plone/plone.supermodel/commit/1e84a6b34394ddbe4c23a124a781d6156d36f270
Support Choice fields with terms containing distinct title from value as option, while preserving backward-compatible round-trip for all Choice fields where title is not distinct from value. Includes tests for serialize, parse/read. Uses OrderedDict as a mechanism to re-use IDict field rendering semantics for output of Choice field values where terms have distinct titles (py2.6 compatibility should be possible via zope.schema>=4.1.0).
Files changed:
M CHANGES.rst
M plone/supermodel/exportimport.py
M plone/supermodel/fields.txt
M plone/supermodel/tests.py
M plone/supermodel/utils.py
M setup.py
diff --git a/CHANGES.rst b/CHANGES.rst
index 2f7a55d..64b0e33 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -4,6 +4,11 @@ Changelog
1.2.5 (unreleased)
------------------
+- Support Choice fields with terms containing distinct title from value
+ as option, while preserving backward-compatible round-trip for all
+ Choice fields where title is not distinct from value.
+ [seanupton]
+
- Fix parsing of empty Choice term to u'', not None, which addresses a
cause of https://github.com/plone/plone.app.dexterity/issues/49
[seanupton]
diff --git a/plone/supermodel/exportimport.py b/plone/supermodel/exportimport.py
index b502ee8..e18273e 100644
--- a/plone/supermodel/exportimport.py
+++ b/plone/supermodel/exportimport.py
@@ -17,6 +17,15 @@
from plone.supermodel.utils import noNS, valueToElement, elementToValue
from plone.supermodel.debug import parseinfo
+try:
+ from collections import OrderedDict
+except:
+ from zope.schema import OrderedDict # <py27
+
+
+class OrderedDictField(zope.schema.Dict):
+ _type = OrderedDict
+
class BaseHandler(object):
"""Base class for import/export handlers.
@@ -184,7 +193,6 @@ def readAttribute(self, element, attributeField):
"""Read a single attribute from the given element. The attribute is of
a type described by the given Field object.
"""
-
return elementToValue(attributeField, element)
def writeAttribute(self, attributeField, field, ignoreDefault=True):
@@ -271,16 +279,30 @@ def __init__(self, klass):
self.fieldAttributes['source'] = \
zope.schema.Object(__name__='source', title=u"Source", schema=Interface)
+ def readAttribute(self, element, attributeField):
+ if element.tag == 'values':
+ if any([child.get('key') for child in element]):
+ attributeField = OrderedDictField(
+ key_type=zope.schema.TextLine(),
+ value_type=zope.schema.TextLine(),
+ )
+ return elementToValue(attributeField, element)
+
def _constructField(self, attributes):
if 'values' in attributes:
+ if isinstance(attributes['values'], OrderedDict):
+ attributes['values'] = attributes['values'].items()
terms = []
for value in attributes['values']:
+ title = (value or u'')
+ if isinstance(value, tuple):
+ value, title = value
encoded = (value or '').encode('unicode_escape')
if value != encoded:
value = value or u''
- term = SimpleTerm(token=encoded, value=value, title=value)
+ term = SimpleTerm(token=encoded, value=value, title=title)
else:
- term = SimpleTerm(value=value, title=value)
+ term = SimpleTerm(value=value, title=title)
terms.append(term)
attributes['vocabulary'] = SimpleVocabulary(terms)
del attributes['values']
@@ -306,9 +328,19 @@ def write(self, field, name, type, elementName='field'):
or term.token != term.value.encode('unicode_escape')):
raise NotImplementedError(u"Cannot export a vocabulary that is not "
"based on a simple list of values")
- value.append(term.value)
+ if term.title and term.title != term.value:
+ value.append((term.value, term.title))
+ else:
+ value.append(term.value)
attributeField = self.fieldAttributes['values']
+ if any(map(lambda v: isinstance(v, tuple), value)):
+ _pair = lambda v: v if len(v) == 2 else (v[0],) * 2
+ value = OrderedDict(map(_pair, value))
+ attributeField = OrderedDictField(
+ key_type=zope.schema.TextLine(),
+ value_type=zope.schema.TextLine(),
+ )
child = valueToElement(attributeField, value, name='values', force=True)
element.append(child)
diff --git a/plone/supermodel/fields.txt b/plone/supermodel/fields.txt
index 3e82204..c4ba642 100644
--- a/plone/supermodel/fields.txt
+++ b/plone/supermodel/fields.txt
@@ -1321,6 +1321,34 @@ tokens are the utf8-encoded values).
>>> [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)
+ <field name="dummy" type="zope.schema.Choice">
+ <title>Test</title>
+ <values>
+ <element key="a">A</element>
+ <element key="b">B</element>
+ </values>
+ </field>
+
3. Sources and source binders
We cannot export choice fields with a source or context source binder:
diff --git a/plone/supermodel/tests.py b/plone/supermodel/tests.py
index fa762c6..f0ccaa3 100644
--- a/plone/supermodel/tests.py
+++ b/plone/supermodel/tests.py
@@ -397,7 +397,9 @@ def setUp(self):
self.handler = ChoiceHandler(schema.Choice)
def _choice(self):
- vocab = SimpleVocabulary([SimpleTerm(t, title=t) for t in (u'a', u'b', u'c')])
+ vocab = SimpleVocabulary(
+ [SimpleTerm(t, title=t) for t in (u'a', u'b', u'c')]
+ )
expected = '<field name="myfield" type="zope.schema.Choice">'\
'<values>'\
'<element>a</element><element>b</element><element>c</element>'\
@@ -415,6 +417,21 @@ def _choice_with_empty(self):
'</values>'\
'</field>'
return (schema.Choice(vocabulary=vocab), expected)
+
+ def _choice_with_term_titles(self):
+ # two terms with distinct titles, one with same as value:
+ vocab = SimpleVocabulary(
+ [SimpleTerm(t, title=t.upper()) for t in (u'a', u'b')] +
+ [SimpleTerm(u'c', title=u'c')],
+ )
+ expected = '<field name="myfield" type="zope.schema.Choice">'\
+ '<values>'\
+ '<element key="a">A</element>'\
+ '<element key="b">B</element>'\
+ '<element key="c">c</element>'\
+ '</values>'\
+ '</field>'
+ return (schema.Choice(vocabulary=vocab), expected)
def test_choice_serialized(self):
field, expected = self._choice()
@@ -424,10 +441,19 @@ def test_choice_serialized(self):
field, expected = self._choice_with_empty()
el = self.handler.write(field, 'myfield', 'zope.schema.Choice')
self.assertEquals(etree.tostring(el), expected)
+ # now with terms that have titles:
+ field, expected = self._choice_with_term_titles()
+ el = self.handler.write(field, 'myfield', 'zope.schema.Choice')
+ self.assertEquals(etree.tostring(el), expected)
def test_choice_parsing(self):
_termvalues = lambda vocab: tuple((t.value, t.title) for t in vocab)
- for field, expected in (self._choice(), self._choice_with_empty()):
+ cases = (
+ self._choice(),
+ self._choice_with_empty(),
+ self._choice_with_term_titles(),
+ )
+ for field, expected in cases:
el = etree.fromstring(expected)
imported_field = self.handler.read(el)
self.assertEquals(
diff --git a/plone/supermodel/utils.py b/plone/supermodel/utils.py
index 24cf9d8..d66515f 100644
--- a/plone/supermodel/utils.py
+++ b/plone/supermodel/utils.py
@@ -11,6 +11,12 @@
from plone.supermodel.interfaces import XML_NAMESPACE, I18N_NAMESPACE, IToUnicode
from plone.supermodel.debug import parseinfo
+try:
+ from collections import OrderedDict
+except:
+ from zope.schema import OrderedDict # <py27
+
+
_marker = object()
noNS_re = re.compile('^{\S+}')
@@ -87,7 +93,7 @@ def elementToValue(field, element, default=_marker):
if IDict.providedBy(field):
key_converter = IFromUnicode(field.key_type)
- value = {}
+ value = OrderedDict()
for child in element.iterchildren(tag=etree.Element):
if noNS(child.tag.lower()) != 'element':
continue
diff --git a/setup.py b/setup.py
index 3c2359e..756ef54 100644
--- a/setup.py
+++ b/setup.py
@@ -1,7 +1,14 @@
import os
+import sys
from setuptools import setup, find_packages
+# if <= Python 2.6 or less, specify minimum zope.schema compatible:
+ZOPESCHEMA = 'zope.schema'
+if sys.version_info < (2, 7):
+ ZOPESCHEMA += '>=4.1.0'
+
+
def read(*rnames):
return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
@@ -39,7 +46,7 @@ def read(*rnames):
'lxml',
'zope.component',
'zope.interface',
- 'zope.schema',
+ ZOPESCHEMA,
'zope.deferredimport',
'zope.dottedname',
'z3c.zcmlhook',
Repository: plone.supermodel
Branch: refs/heads/master
Date: 2014-05-30T12:11:23-06:00
Author: Sean Upton (seanupton) <sdupton at gmail.com>
Commit: https://github.com/plone/plone.supermodel/commit/0bb35825de9b5180f4d6a9be232e775c9dcecd96
Merge pull request #7 from seanupton/master
Fix parsing of empty Choice term, explicit term construction, support terms with title!=value, new tests
Files changed:
M CHANGES.rst
M plone/supermodel/exportimport.py
M plone/supermodel/fields.txt
M plone/supermodel/tests.py
M plone/supermodel/utils.py
M setup.py
diff --git a/CHANGES.rst b/CHANGES.rst
index d90c4c6..53f9152 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -4,7 +4,22 @@ Changelog
1.2.5 (unreleased)
------------------
-- Nothing changed yet.
+- Support Choice fields with terms containing distinct title from value
+ as option, while preserving backward-compatible round-trip for all
+ Choice fields where title is not distinct from value.
+ [seanupton]
+
+- Fix parsing of empty Choice term to u'', not None, which addresses a
+ cause of https://github.com/plone/plone.app.dexterity/issues/49
+ [seanupton]
+
+- Explicitly construct SimpleTerm instances for each Choice field
+ element, instead of relying on zope.schema constructors to do so.
+ This ensures that all terms have non-None title attributes.
+ [seanupton]
+
+- Tests for ChoiceHandler serialization and parsing.
+ [seanupton]
1.2.4 (2014-01-27)
diff --git a/plone/supermodel/exportimport.py b/plone/supermodel/exportimport.py
index 8fa7972..e18273e 100644
--- a/plone/supermodel/exportimport.py
+++ b/plone/supermodel/exportimport.py
@@ -17,6 +17,15 @@
from plone.supermodel.utils import noNS, valueToElement, elementToValue
from plone.supermodel.debug import parseinfo
+try:
+ from collections import OrderedDict
+except:
+ from zope.schema import OrderedDict # <py27
+
+
+class OrderedDictField(zope.schema.Dict):
+ _type = OrderedDict
+
class BaseHandler(object):
"""Base class for import/export handlers.
@@ -184,7 +193,6 @@ def readAttribute(self, element, attributeField):
"""Read a single attribute from the given element. The attribute is of
a type described by the given Field object.
"""
-
return elementToValue(attributeField, element)
def writeAttribute(self, attributeField, field, ignoreDefault=True):
@@ -271,19 +279,33 @@ def __init__(self, klass):
self.fieldAttributes['source'] = \
zope.schema.Object(__name__='source', title=u"Source", schema=Interface)
+ def readAttribute(self, element, attributeField):
+ if element.tag == 'values':
+ if any([child.get('key') for child in element]):
+ attributeField = OrderedDictField(
+ key_type=zope.schema.TextLine(),
+ value_type=zope.schema.TextLine(),
+ )
+ return elementToValue(attributeField, element)
+
def _constructField(self, attributes):
if 'values' in attributes:
+ if isinstance(attributes['values'], OrderedDict):
+ attributes['values'] = attributes['values'].items()
terms = []
- unicode_found = False
for value in attributes['values']:
- encoded = value.encode('unicode_escape')
+ title = (value or u'')
+ if isinstance(value, tuple):
+ value, title = value
+ encoded = (value or '').encode('unicode_escape')
if value != encoded:
- unicode_found = True
- term = SimpleTerm(token=encoded, value=value, title=value)
+ value = value or u''
+ term = SimpleTerm(token=encoded, value=value, title=title)
+ else:
+ term = SimpleTerm(value=value, title=title)
terms.append(term)
- if unicode_found:
- attributes['vocabulary'] = SimpleVocabulary(terms)
- del attributes['values']
+ attributes['vocabulary'] = SimpleVocabulary(terms)
+ del attributes['values']
return super(ChoiceHandler, self)._constructField(attributes)
def write(self, field, name, type, elementName='field'):
@@ -306,9 +328,19 @@ def write(self, field, name, type, elementName='field'):
or term.token != term.value.encode('unicode_escape')):
raise NotImplementedError(u"Cannot export a vocabulary that is not "
"based on a simple list of values")
- value.append(term.value)
+ if term.title and term.title != term.value:
+ value.append((term.value, term.title))
+ else:
+ value.append(term.value)
attributeField = self.fieldAttributes['values']
+ if any(map(lambda v: isinstance(v, tuple), value)):
+ _pair = lambda v: v if len(v) == 2 else (v[0],) * 2
+ value = OrderedDict(map(_pair, value))
+ attributeField = OrderedDictField(
+ key_type=zope.schema.TextLine(),
+ value_type=zope.schema.TextLine(),
+ )
child = valueToElement(attributeField, value, name='values', force=True)
element.append(child)
diff --git a/plone/supermodel/fields.txt b/plone/supermodel/fields.txt
index 3e82204..c4ba642 100644
--- a/plone/supermodel/fields.txt
+++ b/plone/supermodel/fields.txt
@@ -1321,6 +1321,34 @@ tokens are the utf8-encoded values).
>>> [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)
+ <field name="dummy" type="zope.schema.Choice">
+ <title>Test</title>
+ <values>
+ <element key="a">A</element>
+ <element key="b">B</element>
+ </values>
+ </field>
+
3. Sources and source binders
We cannot export choice fields with a source or context source binder:
diff --git a/plone/supermodel/tests.py b/plone/supermodel/tests.py
index aeb41a9..ebf4d89 100644
--- a/plone/supermodel/tests.py
+++ b/plone/supermodel/tests.py
@@ -11,14 +11,32 @@
from zope.schema import getFieldNamesInOrder
from zope.schema.interfaces import IContextAwareDefaultFactory
from zope.schema.interfaces import IContextSourceBinder
-from zope.schema.vocabulary import SimpleVocabulary
+from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
from zope import schema
from plone.supermodel import utils
+from plone.supermodel.exportimport import ChoiceHandler
from plone.supermodel.interfaces import IDefaultFactory
from plone.supermodel.interfaces import IInvariant
+def configure():
+ zope.component.testing.setUp()
+ configuration = """\
+ <configure
+ xmlns="http://namespaces.zope.org/zope"
+ i18n_domain="plone.supermodel.tests">
+
+ <include package="zope.component" file="meta.zcml" />
+
+ <include package="plone.supermodel" />
+
+ </configure>
+ """
+ from zope.configuration import xmlconfig
+ xmlconfig.xmlconfig(StringIO(configuration))
+
+
class IBase(Interface):
title = schema.TextLine(title=u"Title")
description = schema.TextLine(title=u"Description")
@@ -321,21 +339,7 @@ class ISchema(IBase1, IBase2, IBase3):
class TestValueToElement(unittest.TestCase):
def setUp(self):
- zope.component.testing.setUp()
- configuration = """\
- <configure
- xmlns="http://namespaces.zope.org/zope"
- i18n_domain="plone.supermodel.tests">
-
- <include package="zope.component" file="meta.zcml" />
-
- <include package="plone.supermodel" />
-
- </configure>
- """
- from zope.configuration import xmlconfig
- xmlconfig.xmlconfig(StringIO(configuration))
-
+ configure()
tearDown = zope.component.testing.tearDown
@@ -403,10 +407,83 @@ def test_nested_dicts(self):
)
+class TestChoiceHandling(unittest.TestCase):
+
+ def setUp(self):
+ configure()
+ self.handler = ChoiceHandler(schema.Choice)
+
+ def _choice(self):
+ vocab = SimpleVocabulary(
+ [SimpleTerm(t, title=t) for t in (u'a', u'b', u'c')]
+ )
+ expected = '<field name="myfield" type="zope.schema.Choice">'\
+ '<values>'\
+ '<element>a</element><element>b</element><element>c</element>'\
+ '</values>'\
+ '</field>'
+ return (schema.Choice(vocabulary=vocab), expected)
+
+ def _choice_with_empty(self):
+ # add an empty string term to vocabulary
+ vocab = SimpleVocabulary([SimpleTerm(t, title=t) for t in (u'a', u'')])
+ expected = '<field name="myfield" type="zope.schema.Choice">'\
+ '<values>'\
+ '<element>a</element>'\
+ '<element></element>'\
+ '</values>'\
+ '</field>'
+ return (schema.Choice(vocabulary=vocab), expected)
+
+ def _choice_with_term_titles(self):
+ # two terms with distinct titles, one with same as value:
+ vocab = SimpleVocabulary(
+ [SimpleTerm(t, title=t.upper()) for t in (u'a', u'b')] +
+ [SimpleTerm(u'c', title=u'c')],
+ )
+ expected = '<field name="myfield" type="zope.schema.Choice">'\
+ '<values>'\
+ '<element key="a">A</element>'\
+ '<element key="b">B</element>'\
+ '<element key="c">c</element>'\
+ '</values>'\
+ '</field>'
+ return (schema.Choice(vocabulary=vocab), expected)
+
+ def test_choice_serialized(self):
+ field, expected = self._choice()
+ el = self.handler.write(field, 'myfield', 'zope.schema.Choice')
+ self.assertEquals(etree.tostring(el), expected)
+ # now with an empty string term in vocab:
+ field, expected = self._choice_with_empty()
+ el = self.handler.write(field, 'myfield', 'zope.schema.Choice')
+ self.assertEquals(etree.tostring(el), expected)
+ # now with terms that have titles:
+ field, expected = self._choice_with_term_titles()
+ el = self.handler.write(field, 'myfield', 'zope.schema.Choice')
+ self.assertEquals(etree.tostring(el), expected)
+
+ def test_choice_parsing(self):
+ _termvalues = lambda vocab: tuple((t.value, t.title) for t in vocab)
+ cases = (
+ self._choice(),
+ self._choice_with_empty(),
+ self._choice_with_term_titles(),
+ )
+ for field, expected in cases:
+ el = etree.fromstring(expected)
+ imported_field = self.handler.read(el)
+ self.assertEquals(
+ _termvalues(imported_field.vocabulary),
+ _termvalues(field.vocabulary),
+ )
+
+
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(TestUtils),
unittest.makeSuite(TestValueToElement),
+ unittest.makeSuite(TestChoiceHandling),
doctest.DocFileSuite('schema.txt',
setUp=zope.component.testing.setUp,
tearDown=zope.component.testing.tearDown,
diff --git a/plone/supermodel/utils.py b/plone/supermodel/utils.py
index 24cf9d8..d66515f 100644
--- a/plone/supermodel/utils.py
+++ b/plone/supermodel/utils.py
@@ -11,6 +11,12 @@
from plone.supermodel.interfaces import XML_NAMESPACE, I18N_NAMESPACE, IToUnicode
from plone.supermodel.debug import parseinfo
+try:
+ from collections import OrderedDict
+except:
+ from zope.schema import OrderedDict # <py27
+
+
_marker = object()
noNS_re = re.compile('^{\S+}')
@@ -87,7 +93,7 @@ def elementToValue(field, element, default=_marker):
if IDict.providedBy(field):
key_converter = IFromUnicode(field.key_type)
- value = {}
+ value = OrderedDict()
for child in element.iterchildren(tag=etree.Element):
if noNS(child.tag.lower()) != 'element':
continue
diff --git a/setup.py b/setup.py
index 3c2359e..756ef54 100644
--- a/setup.py
+++ b/setup.py
@@ -1,7 +1,14 @@
import os
+import sys
from setuptools import setup, find_packages
+# if <= Python 2.6 or less, specify minimum zope.schema compatible:
+ZOPESCHEMA = 'zope.schema'
+if sys.version_info < (2, 7):
+ ZOPESCHEMA += '>=4.1.0'
+
+
def read(*rnames):
return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
@@ -39,7 +46,7 @@ def read(*rnames):
'lxml',
'zope.component',
'zope.interface',
- 'zope.schema',
+ ZOPESCHEMA,
'zope.deferredimport',
'zope.dottedname',
'z3c.zcmlhook',
-------------------------------------------------------------------------------
-------------- next part --------------
A non-text attachment was scrubbed...
Name: CHANGES.log
Type: application/octet-stream
Size: 31088 bytes
Desc: not available
URL: <http://lists.plone.org/pipermail/plone-testbot/attachments/20140530/434e9348/attachment-0002.obj>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: build.log
Type: application/octet-stream
Size: 162798 bytes
Desc: not available
URL: <http://lists.plone.org/pipermail/plone-testbot/attachments/20140530/434e9348/attachment-0003.obj>
More information about the Testbot
mailing list