Skip to content

Commit

Permalink
Fix an edge case when field missing_value is not None but a custo…
Browse files Browse the repository at this point in the history
…m value that works as `None`.
  • Loading branch information
agroszer committed Mar 4, 2019
1 parent a1b2024 commit 0593a36
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 4 deletions.
5 changes: 4 additions & 1 deletion CHANGES.rst
Expand Up @@ -5,7 +5,10 @@ Changelog
4.1.2 (unreleased)
------------------

- Nothing changed yet.
- Fix an edge case when field `missing_value` is not `None` but a custom
value that works as `None`.
That ended up calling `zope.i18n` `NumberFormat.format` with `None` what
then failed.


4.1.1 (2018-11-26)
Expand Down
4 changes: 2 additions & 2 deletions src/z3c/form/converter.py
Expand Up @@ -56,7 +56,7 @@ def _getConverter(self, field):

def toWidgetValue(self, value):
"""See interfaces.IDataConverter"""
if value is self.field.missing_value:
if value == self.field.missing_value:
return u''
return util.toUnicode(value)

Expand Down Expand Up @@ -128,7 +128,7 @@ def __init__(self, field, widget):

def toWidgetValue(self, value):
"""See interfaces.IDataConverter"""
if value is self.field.missing_value:
if value == self.field.missing_value:
return u''
return self.formatter.format(value)

Expand Down
72 changes: 71 additions & 1 deletion src/z3c/form/tests/test_bugfix.py
Expand Up @@ -62,5 +62,75 @@ class TestForm(z3c.form.form.BaseForm):
self.assertEqual({'text': 'a'}, content)


class Mock(object):
pass


class MockNumberFormatter(object):
def format(self, value):
if value is None:
# execution should never get here
raise ValueError('Cannot format None')
return str(value)


class MockLocale(object):
def getFormatter(self, category):
return MockNumberFormatter()


class OurNone(object):
def __eq__(self, other):
return isinstance(other, (type(None), OurNone))


OUR_NONE = OurNone()
# OUR_NONE == None
# but
# OUR_NONE is not None


class ConverterFixTests(unittest.TestCase):
# most of the time `==` and `is` works the same
# unless you have some custom or mutable values

def test_BaseDataConverter_toWidgetValue(self):
from z3c.form.converter import BaseDataConverter

field = Mock()
field.missing_value = None
bdc = BaseDataConverter(field, None)
self.assertEqual(bdc.toWidgetValue(u''), u'')
self.assertEqual(bdc.toWidgetValue(None), u'')
self.assertEqual(bdc.toWidgetValue([]), u'[]')

field.missing_value = []
self.assertEqual(bdc.toWidgetValue(u''), u'')
self.assertEqual(bdc.toWidgetValue(None), u'None')
self.assertEqual(bdc.toWidgetValue([]), u'')

def test_NumberDataConverter_toWidgetValue(self):
from z3c.form.converter import NumberDataConverter

field = Mock()
field.missing_value = None
widget = Mock()
widget.request = Mock()
widget.request.locale = Mock()
widget.request.locale.numbers = MockLocale()
ndc = NumberDataConverter(field, widget)
self.assertEqual(ndc.toWidgetValue(u''), u'')
self.assertEqual(ndc.toWidgetValue(None), u'')
# here is the real deal, OUR_NONE should be considered as None
self.assertEqual(ndc.toWidgetValue(OUR_NONE), u'')
self.assertEqual(ndc.toWidgetValue([]), u'[]')

field.missing_value = OUR_NONE
self.assertEqual(ndc.toWidgetValue(u''), u'')
self.assertEqual(ndc.toWidgetValue(None), u'')
self.assertEqual(ndc.toWidgetValue(OUR_NONE), u'')
self.assertEqual(ndc.toWidgetValue([]), u'[]')


def test_suite():
return unittest.makeSuite(TestApplyChangesDictDatamanager)
return unittest.defaultTestLoader.loadTestsFromName(__name__)

0 comments on commit 0593a36

Please sign in to comment.