diff options
| author | Jason Madden <jamadden@gmail.com> | 2018-08-13 09:52:07 -0500 |
|---|---|---|
| committer | Jason Madden <jamadden@gmail.com> | 2018-08-13 09:52:07 -0500 |
| commit | 4a8b0269eaf2b39724aa19ac497fa4da9db6df36 (patch) | |
| tree | 223f69a2fc17cd55f94fb59cced12908736c7487 /src | |
| parent | ca3e6f89371bca80b589d758e97e45fddfb729c6 (diff) | |
| download | zope-schema-4a8b0269eaf2b39724aa19ac497fa4da9db6df36.tar.gz | |
Allow `missing_value` without setting `min` and `max`
Fixes #9
Note that you must still specify a `default` in that case. I'm not
sure that makes sense or not.
This impacts Int, Float, Decimal, DateTime, Timedelta, Date, and
Time---all the subclasses of Orderable. They all have a test case.
Also fix ValidatedProperty and DefaultProperty to do the right thing
when accessesd on the class and not the instance (which is to return
themself; previously they would raise AttributeError).
Diffstat (limited to 'src')
| -rw-r--r-- | src/zope/schema/_bootstrapfields.py | 34 | ||||
| -rw-r--r-- | src/zope/schema/tests/test__bootstrapfields.py | 24 | ||||
| -rw-r--r-- | src/zope/schema/tests/test__field.py | 44 |
3 files changed, 75 insertions, 27 deletions
diff --git a/src/zope/schema/_bootstrapfields.py b/src/zope/schema/_bootstrapfields.py index e16d138..5c318e7 100644 --- a/src/zope/schema/_bootstrapfields.py +++ b/src/zope/schema/_bootstrapfields.py @@ -40,31 +40,35 @@ from zope.schema._schema import getFields class ValidatedProperty(object): - def __init__(self, name, check=None): - self._info = name, check + def __init__(self, name, check=None, allow_none=False): + self._name = name + self._check = check + self._allow_none = allow_none def __set__(self, inst, value): - name, check = self._info - if value != inst.missing_value: - if check is not None: - check(inst, value) + bypass_validation = (value is None and self._allow_none) or value == inst.missing_value + if not bypass_validation: + if self._check is not None: + self._check(inst, value) else: inst.validate(value) - inst.__dict__[name] = value + inst.__dict__[self._name] = value def __get__(self, inst, owner): - name, check = self._info - return inst.__dict__[name] + if inst is None: + return self + return inst.__dict__[self._name] class DefaultProperty(ValidatedProperty): def __get__(self, inst, owner): - name, check = self._info + if inst is None: + return self defaultFactory = inst.__dict__.get('defaultFactory') # If there is no default factory, simply return the default. if defaultFactory is None: - return inst.__dict__[name] + return inst.__dict__[self._name] # Get the default value by calling the factory. Some factories might # require a context to produce a value. if IContextAwareDefaultFactory.providedBy(defaultFactory): @@ -72,8 +76,8 @@ class DefaultProperty(ValidatedProperty): else: value = defaultFactory() # Check that the created value is valid. - if check is not None: - check(inst, value) + if self._check is not None: + self._check(inst, value) elif value != inst.missing_value: inst.validate(value) return value @@ -279,8 +283,8 @@ class Orderable(object): Orderable is a mixin used in combination with Field. """ - min = ValidatedProperty('min') - max = ValidatedProperty('max') + min = ValidatedProperty('min', allow_none=True) + max = ValidatedProperty('max', allow_none=True) def __init__(self, min=None, max=None, default=None, **kw): diff --git a/src/zope/schema/tests/test__bootstrapfields.py b/src/zope/schema/tests/test__bootstrapfields.py index 75a5166..30b9126 100644 --- a/src/zope/schema/tests/test__bootstrapfields.py +++ b/src/zope/schema/tests/test__bootstrapfields.py @@ -741,7 +741,29 @@ class BoolTests(unittest.TestCase): self.assertEqual(txt.fromUnicode(u'true'), True) -class IntTests(unittest.TestCase): +class OrderableMissingValueMixin(object): + + mvm_missing_value = -1 + mvm_default = 0 + + def test_missing_value_no_min_or_max(self): + # We should be able to provide a missing_value without + # also providing a min or max. But note that we must still + # provide a default. + # See https://github.com/zopefoundation/zope.schema/issues/9 + Kind = self._getTargetClass() + self.assertTrue(Kind.min._allow_none) + self.assertTrue(Kind.max._allow_none) + + field = self._makeOne(missing_value=self.mvm_missing_value, + default=self.mvm_default) + self.assertIsNone(field.min) + self.assertIsNone(field.max) + self.assertEqual(self.mvm_missing_value, field.missing_value) + + +class IntTests(OrderableMissingValueMixin, + unittest.TestCase): def _getTargetClass(self): from zope.schema._bootstrapfields import Int diff --git a/src/zope/schema/tests/test__field.py b/src/zope/schema/tests/test__field.py index 6b8384e..0758c42 100644 --- a/src/zope/schema/tests/test__field.py +++ b/src/zope/schema/tests/test__field.py @@ -11,8 +11,11 @@ # FOR A PARTICULAR PURPOSE. # ############################################################################## +import datetime +import decimal import unittest +from zope.schema.tests.test__bootstrapfields import OrderableMissingValueMixin class BytesTests(unittest.TestCase): @@ -249,7 +252,11 @@ class ASCIILineTests(unittest.TestCase): self.assertEqual(field.constraint('abc\ndef'), False) -class FloatTests(unittest.TestCase): +class FloatTests(OrderableMissingValueMixin, + unittest.TestCase): + + mvm_missing_value = -1.0 + mvm_default = 0.0 def _getTargetClass(self): from zope.schema._field import Float @@ -326,7 +333,11 @@ class FloatTests(unittest.TestCase): self.assertEqual(flt.fromUnicode(u'1.23e6'), 1230000.0) -class DecimalTests(unittest.TestCase): +class DecimalTests(OrderableMissingValueMixin, + unittest.TestCase): + + mvm_missing_value = decimal.Decimal("-1") + mvm_default = decimal.Decimal("0") def _getTargetClass(self): from zope.schema._field import Decimal @@ -346,7 +357,6 @@ class DecimalTests(unittest.TestCase): verifyObject(IDecimal, self._makeOne()) def test_validate_not_required(self): - import decimal field = self._makeOne(required=False) field.validate(decimal.Decimal("10.0")) field.validate(decimal.Decimal("0.93")) @@ -354,7 +364,6 @@ class DecimalTests(unittest.TestCase): field.validate(None) def test_validate_required(self): - import decimal from zope.schema.interfaces import RequiredMissing field = self._makeOne() field.validate(decimal.Decimal("10.0")) @@ -363,7 +372,6 @@ class DecimalTests(unittest.TestCase): self.assertRaises(RequiredMissing, field.validate, None) def test_validate_min(self): - import decimal from zope.schema.interfaces import TooSmall field = self._makeOne(min=decimal.Decimal("10.5")) field.validate(decimal.Decimal("10.6")) @@ -372,7 +380,6 @@ class DecimalTests(unittest.TestCase): self.assertRaises(TooSmall, field.validate, decimal.Decimal("10.4")) def test_validate_max(self): - import decimal from zope.schema.interfaces import TooBig field = self._makeOne(max=decimal.Decimal("10.5")) field.validate(decimal.Decimal("5.3")) @@ -381,7 +388,6 @@ class DecimalTests(unittest.TestCase): self.assertRaises(TooBig, field.validate, decimal.Decimal("20.7")) def test_validate_min_and_max(self): - import decimal from zope.schema.interfaces import TooBig from zope.schema.interfaces import TooSmall field = self._makeOne(min=decimal.Decimal("-0.6"), @@ -410,7 +416,11 @@ class DecimalTests(unittest.TestCase): self.assertEqual(flt.fromUnicode(u'12345.6'), Decimal('12345.6')) -class DatetimeTests(unittest.TestCase): +class DatetimeTests(OrderableMissingValueMixin, + unittest.TestCase): + + mvm_missing_value = datetime.datetime.now() + mvm_default = datetime.datetime.now() def _getTargetClass(self): from zope.schema._field import Datetime @@ -496,7 +506,11 @@ class DatetimeTests(unittest.TestCase): self.assertRaises(TooBig, field.validate, d5) -class DateTests(unittest.TestCase): +class DateTests(OrderableMissingValueMixin, + unittest.TestCase): + + mvm_missing_value = datetime.date.today() + mvm_default = datetime.date.today() def _getTargetClass(self): from zope.schema._field import Date @@ -586,7 +600,11 @@ class DateTests(unittest.TestCase): self.assertRaises(TooBig, field.validate, d5) -class TimedeltaTests(unittest.TestCase): +class TimedeltaTests(OrderableMissingValueMixin, + unittest.TestCase): + + mvm_missing_value = datetime.timedelta(minutes=15) + mvm_default = datetime.timedelta(minutes=12) def _getTargetClass(self): from zope.schema._field import Timedelta @@ -656,7 +674,11 @@ class TimedeltaTests(unittest.TestCase): self.assertRaises(TooBig, field.validate, t5) -class TimeTests(unittest.TestCase): +class TimeTests(OrderableMissingValueMixin, + unittest.TestCase): + + mvm_missing_value = datetime.time(12, 15, 37) + mvm_default = datetime.time(12, 25, 42) def _getTargetClass(self): from zope.schema._field import Time |
