Skip to content

Commit

Permalink
Add 'errors' to WrongContainedType and 'schema' to SchemaNotProvided.
Browse files Browse the repository at this point in the history
  • Loading branch information
jamadden committed Sep 10, 2018
1 parent fb398c2 commit febb909
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 15 deletions.
5 changes: 5 additions & 0 deletions CHANGES.rst
Expand Up @@ -11,6 +11,11 @@
``SchemaNotProvided`` and raised by the constructor of ``Object``
and when validation fails for ``InterfaceField``.

- Give ``SchemaNotProvided`` a ``schema`` field.

- Give ``WrongContainedType`` an ``errors`` list.


4.6.2 (2018-09-10)
==================

Expand Down
7 changes: 4 additions & 3 deletions src/zope/schema/_bootstrapfields.py
Expand Up @@ -951,10 +951,11 @@ def _validate(self, value):
errors = list(schema_error_dict.values()) + invariant_errors
exception = SchemaNotCorrectlyImplemented(
errors,
self.__name__
self.__name__,
schema_error_dict,
invariant_errors
).with_field_and_value(self, value)
exception.schema_errors = schema_error_dict
exception.invariant_errors = invariant_errors

try:
raise exception
finally:
Expand Down
49 changes: 49 additions & 0 deletions src/zope/schema/_bootstrapinterfaces.py
Expand Up @@ -78,10 +78,18 @@ class WrongType(ValidationError):
__doc__ = _("""Object is of wrong type.""")

#: The type or tuple of types that was expected.
#:
#: .. versionadded:: 4.7.0
expected_type = None

def __init__(self, value=None, expected_type=None, name=None, *args):
"""
WrongType(value, expected_type, name)
.. versionchanged:: 4.7.0
Added named arguments tot he constructor and the `expected_type`
field.
"""
ValidationError.__init__(self, value, expected_type, name, *args)
self.expected_type = expected_type
self.value = value
Expand Down Expand Up @@ -121,6 +129,22 @@ class NotAnIterator(ValidationError):
class WrongContainedType(ValidationError):
__doc__ = _("""Wrong contained type""")

#: A collection of exceptions raised when validating
#: the *value*.
#:
#: .. versionadded:: 4.7.0
errors = ()

def __init__(self, errors=None, name=None, *args):
"""
WrongContainedType(errors, name)
.. versionchanged:: 4.7.0
Added named arguments to the constructor, and the `errors` property.
"""
super(WrongContainedType, self).__init__(errors, name, *args)
self.errors = errors


class SchemaNotCorrectlyImplemented(WrongContainedType):
__doc__ = _("""An object failed schema or invariant validation.""")
Expand All @@ -133,6 +157,17 @@ class SchemaNotCorrectlyImplemented(WrongContainedType):
#: of the schema.
invariant_errors = ()

def __init__(self, errors=None, name=None, schema_errors=None, invariant_errors=(), *args):
"""
SchemaNotCorrectlyImplemented(errors, name, schema_errors, invariant_errors)
.. versionchanged:: 4.7.0
Added named arguments to the constructor.
"""
super(SchemaNotCorrectlyImplemented, self).__init__(errors, name, *args)
self.schema_errors = schema_errors
self.invariant_errors = invariant_errors


class SchemaNotFullyImplemented(ValidationError):
__doc__ = _("""Schema not fully implemented""")
Expand All @@ -141,6 +176,20 @@ class SchemaNotFullyImplemented(ValidationError):
class SchemaNotProvided(ValidationError):
__doc__ = _("""Schema not provided""")

#: The interface that the *value* was supposed to provide,
#: but does not.
schema = None

def __init__(self, schema=None, value=None, *args):
"""
SchemaNotProvided(schema, value)
.. versionchanged:: 4.7.0
Added named arguments to the constructor and the `schema` property.
"""
super(SchemaNotProvided, self).__init__(schema, value, *args)
self.schema = schema
self.value = value

class NotAnInterface(WrongType, SchemaNotProvided):
"""
Expand Down
62 changes: 51 additions & 11 deletions src/zope/schema/tests/test__bootstrapfields.py
Expand Up @@ -1172,10 +1172,10 @@ def _makeSchema(self, **kw):
return InterfaceClass('ISchema', (Interface,), kw)

def _getErrors(self, f, *args, **kw):
from zope.schema.interfaces import WrongContainedType
with self.assertRaises(WrongContainedType) as e:
from zope.schema.interfaces import SchemaNotCorrectlyImplemented
with self.assertRaises(SchemaNotCorrectlyImplemented) as e:
f(*args, **kw)
return e.exception.args[0]
return e.exception.errors

def _makeCycles(self):
from zope.interface import Interface
Expand Down Expand Up @@ -1385,19 +1385,39 @@ def test_validate_w_cycles(self):
field.validate(unit) # doesn't raise

def test_validate_w_cycles_object_not_valid(self):
from zope.schema.interfaces import WrongContainedType
from zope.schema.interfaces import SchemaNotCorrectlyImplemented
from zope.schema.interfaces import SchemaNotProvided
IUnit, Person, Unit = self._makeCycles()
field = self._makeOne(schema=IUnit)
person1 = Person(None)
person2 = Person(None)
person3 = Person(object())
unit = Unit(person3, [person1, person2])
boss_unit = object()
boss = Person(boss_unit)
unit = Unit(boss, [person1, person2])
person1.unit = unit
person2.unit = unit
self.assertRaises(WrongContainedType, field.validate, unit)
with self.assertRaises(SchemaNotCorrectlyImplemented) as exc:
field.validate(unit)

ex = exc.exception
self.assertEqual(1, len(ex.schema_errors))
self.assertEqual(1, len(ex.errors))
self.assertEqual(0, len(ex.invariant_errors))

boss_error = ex.schema_errors['boss']
self.assertIsInstance(boss_error, SchemaNotCorrectlyImplemented)

self.assertEqual(1, len(boss_error.schema_errors))
self.assertEqual(1, len(boss_error.errors))
self.assertEqual(0, len(boss_error.invariant_errors))

unit_error = boss_error.schema_errors['unit']
self.assertIsInstance(unit_error, SchemaNotProvided)
self.assertIs(IUnit, unit_error.schema)
self.assertIs(boss_unit, unit_error.value)

def test_validate_w_cycles_collection_not_valid(self):
from zope.schema.interfaces import WrongContainedType
from zope.schema.interfaces import SchemaNotCorrectlyImplemented
IUnit, Person, Unit = self._makeCycles()
field = self._makeOne(schema=IUnit)
person1 = Person(None)
Expand All @@ -1406,7 +1426,7 @@ def test_validate_w_cycles_collection_not_valid(self):
unit = Unit(person1, [person2, person3])
person1.unit = unit
person2.unit = unit
self.assertRaises(WrongContainedType, field.validate, unit)
self.assertRaises(SchemaNotCorrectlyImplemented, field.validate, unit)

def test_set_emits_IBOAE(self):
from zope.event import subscribers
Expand Down Expand Up @@ -1547,7 +1567,10 @@ class Field(self._getTargetClass()):
self.assertIs(field.schema, IValueType)

# Non implementation is bad
self.assertRaises(SchemaNotProvided, field.validate, object())
with self.assertRaises(SchemaNotProvided) as exc:
field.validate(object())

self.assertIs(IValueType, exc.exception.schema)

# Actual implementation works
@interface.implementer(IValueType)
Expand All @@ -1566,6 +1589,7 @@ def test_bound_field_of_collection_with_choice(self):
from zope.schema.fieldproperty import FieldProperty
from zope.schema.interfaces import IContextSourceBinder
from zope.schema.interfaces import WrongContainedType
from zope.schema.interfaces import ConstraintNotSatisfied
from zope.schema.interfaces import SchemaNotCorrectlyImplemented
from zope.schema.vocabulary import SimpleVocabulary

Expand Down Expand Up @@ -1607,12 +1631,28 @@ class Favorites(object):

# Ranges outside the context fail
bad_choices = Choices({1, 8})
with self.assertRaises(WrongContainedType) as exc:
with self.assertRaises(SchemaNotCorrectlyImplemented) as exc:
IFavorites['fav'].validate(bad_choices)

e = exc.exception
self.assertEqual(IFavorites['fav'], e.field)
self.assertEqual(bad_choices, e.value)
self.assertEqual(1, len(e.schema_errors))
self.assertEqual(0, len(e.invariant_errors))
self.assertEqual(1, len(e.errors))

fav_error = e.schema_errors['choices']
self.assertIs(fav_error, e.errors[0])
self.assertIsInstance(fav_error, WrongContainedType)
self.assertNotIsInstance(fav_error, SchemaNotCorrectlyImplemented)
# The field is not actually equal to the one in the interface
# anymore because its bound.
self.assertEqual('choices', fav_error.field.__name__)
self.assertEqual(bad_choices, fav_error.field.context)
self.assertEqual({1, 8}, fav_error.value)
self.assertEqual(1, len(fav_error.errors))

self.assertIsInstance(fav_error.errors[0], ConstraintNotSatisfied)

# Validation through field property
favorites = Favorites()
Expand Down
26 changes: 25 additions & 1 deletion src/zope/schema/tests/test__field.py
Expand Up @@ -1331,6 +1331,7 @@ def _getTargetInterface(self):
def test_schema_defined_by_subclass(self):
from zope import interface
from zope.schema import Object
from zope.schema.interfaces import SchemaNotProvided
from zope.schema.interfaces import WrongContainedType

class IValueType(interface.Interface):
Expand All @@ -1348,7 +1349,14 @@ class Field(self._getTargetClass()):
field.validate(self._makeCollection([]))

# Collection with a non-implemented object is bad
self.assertRaises(WrongContainedType, field.validate, self._makeCollection([object()]))
with self.assertRaises(WrongContainedType) as exc:
field.validate(self._makeCollection([object()]))

ex = exc.exception
self.assertIs(ex.__class__, WrongContainedType)
self.assertEqual(1, len(ex.errors))
self.assertIsInstance(ex.errors[0], SchemaNotProvided)
self.assertIs(ex.errors[0].schema, IValueType)

# Actual implementation works
@interface.implementer(IValueType)
Expand Down Expand Up @@ -1396,6 +1404,7 @@ def test_bind_w_value_Type(self):

def test__validate_wrong_contained_type(self):
from zope.schema.interfaces import WrongContainedType
from zope.schema.interfaces import WrongType
from zope.schema._bootstrapfields import Text
text = Text()
absc = self._makeOne(text)
Expand All @@ -1405,6 +1414,10 @@ def test__validate_wrong_contained_type(self):
wct = exc.exception
self.assertIs(wct.field, absc)
self.assertEqual(wct.value, self._makeCollection([1]))
self.assertIs(wct.__class__, WrongContainedType)
self.assertEqual(1, len(wct.errors))
self.assertIsInstance(wct.errors[0], WrongType)
self.assertIs(wct.errors[0].expected_type, text._type)

def test__validate_miss_uniqueness(self):
from zope.schema.interfaces import NotUnique
Expand Down Expand Up @@ -1703,6 +1716,7 @@ def test_validate_required(self):

def test_validate_invalid_key_type(self):
from zope.schema.interfaces import WrongContainedType
from zope.schema.interfaces import WrongType
from zope.schema._bootstrapfields import Int
field = self._makeOne(key_type=Int())
field.validate({})
Expand All @@ -1714,9 +1728,14 @@ def test_validate_invalid_key_type(self):
wct = exc.exception
self.assertIs(wct.field, field)
self.assertEqual(wct.value, {'a': 1})
self.assertIs(wct.__class__, WrongContainedType)
self.assertEqual(1, len(wct.errors))
self.assertIsInstance(wct.errors[0], WrongType)
self.assertIs(field.key_type._type, wct.errors[0].expected_type)

def test_validate_invalid_value_type(self):
from zope.schema.interfaces import WrongContainedType
from zope.schema.interfaces import WrongType
from zope.schema._bootstrapfields import Int
field = self._makeOne(value_type=Int())
field.validate({})
Expand All @@ -1728,6 +1747,11 @@ def test_validate_invalid_value_type(self):
wct = exc.exception
self.assertIs(wct.field, field)
self.assertEqual(wct.value, {1: 'a'})
self.assertIs(wct.__class__, WrongContainedType)
self.assertEqual(1, len(wct.errors))
self.assertIsInstance(wct.errors[0], WrongType)
self.assertIs(field.value_type._type, wct.errors[0].expected_type)


def test_validate_min_length(self):
from zope.schema.interfaces import TooShort
Expand Down

0 comments on commit febb909

Please sign in to comment.