summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJason Madden <jamadden@gmail.com>2018-08-13 09:52:07 -0500
committerJason Madden <jamadden@gmail.com>2018-08-13 09:52:07 -0500
commit4a8b0269eaf2b39724aa19ac497fa4da9db6df36 (patch)
tree223f69a2fc17cd55f94fb59cced12908736c7487 /src
parentca3e6f89371bca80b589d758e97e45fddfb729c6 (diff)
downloadzope-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.py34
-rw-r--r--src/zope/schema/tests/test__bootstrapfields.py24
-rw-r--r--src/zope/schema/tests/test__field.py44
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