diff options
| author | Jason Madden <jamadden@gmail.com> | 2018-09-07 08:16:02 -0500 |
|---|---|---|
| committer | Jason Madden <jamadden@gmail.com> | 2018-09-07 08:16:02 -0500 |
| commit | 6d68cccd48a41bd29d1a076f6fd95b0480768bdd (patch) | |
| tree | 8892af5da16720876aeaa7fe40aac71d83f35dcc | |
| parent | 6d439c606443c1d2ea24eec598e37a02ad1145fd (diff) | |
| download | zope-schema-issue13.tar.gz | |
Move ObjectTests from test__field to test__bootstrapfield to match Object itself.issue13
| -rw-r--r-- | src/zope/schema/_bootstrapfields.py | 7 | ||||
| -rw-r--r-- | src/zope/schema/tests/test__bootstrapfields.py | 492 | ||||
| -rw-r--r-- | src/zope/schema/tests/test__field.py | 506 |
3 files changed, 501 insertions, 504 deletions
diff --git a/src/zope/schema/_bootstrapfields.py b/src/zope/schema/_bootstrapfields.py index 6c8c389..cde9df0 100644 --- a/src/zope/schema/_bootstrapfields.py +++ b/src/zope/schema/_bootstrapfields.py @@ -709,9 +709,10 @@ def get_schema_validation_errors(schema, value, # it's python represenation. A previous version was setting a volatile # attribute which didn't work with security proxy id_value = id(value) - if id_value in _validating_objects.ids_being_validated: + ids_being_validated = _validating_objects.ids_being_validated + if id_value in ids_being_validated: return errors - _validating_objects.ids_being_validated.add(id_value) + ids_being_validated.add(id_value) # (If we have gotten here, we know that `value` provides an interface # other than zope.interface.Interface; # iow, we can rely on the fact that it is an instance @@ -735,7 +736,7 @@ def get_schema_validation_errors(schema, value, # property for the given name is not implemented errors[name] = SchemaNotFullyImplemented(error).with_field_and_value(attribute, None) finally: - _validating_objects.ids_being_validated.remove(id_value) + ids_being_validated.remove(id_value) return errors diff --git a/src/zope/schema/tests/test__bootstrapfields.py b/src/zope/schema/tests/test__bootstrapfields.py index 419378a..733d5c9 100644 --- a/src/zope/schema/tests/test__bootstrapfields.py +++ b/src/zope/schema/tests/test__bootstrapfields.py @@ -14,7 +14,7 @@ import doctest import unittest -# pylint:disable=protected-access +# pylint:disable=protected-access,inherit-non-class,blacklisted-name class EqualityTestsMixin(object): @@ -991,6 +991,496 @@ class IntTests(IntegralTests): self.assertEqual(txt._type, integer_types) +class ObjectTests(EqualityTestsMixin, + unittest.TestCase): + + def setUp(self): + from zope.event import subscribers + self._before = subscribers[:] + + def tearDown(self): + from zope.event import subscribers + subscribers[:] = self._before + + def _getTargetClass(self): + from zope.schema._field import Object + return Object + + def _getTargetInterface(self): + from zope.schema.interfaces import IObject + return IObject + + def _makeOneFromClass(self, cls, schema=None, *args, **kw): + if schema is None: + schema = self._makeSchema() + return super(ObjectTests, self)._makeOneFromClass(cls, schema, *args, **kw) + + def _makeSchema(self, **kw): + from zope.interface import Interface + from zope.interface.interface import InterfaceClass + return InterfaceClass('ISchema', (Interface,), kw) + + def _getErrors(self, f, *args, **kw): + from zope.schema.interfaces import WrongContainedType + with self.assertRaises(WrongContainedType) as e: + f(*args, **kw) + return e.exception.args[0] + + def _makeCycles(self): + from zope.interface import Interface + from zope.interface import implementer + from zope.schema import Object + from zope.schema import List + from zope.schema._messageid import _ + + class IUnit(Interface): + """A schema that participate to a cycle""" + boss = Object( + schema=Interface, + title=_("Boss"), + description=_("Boss description"), + required=False, + ) + members = List( + value_type=Object(schema=Interface), + title=_("Member List"), + description=_("Member list description"), + required=False, + ) + + class IPerson(Interface): + """A schema that participate to a cycle""" + unit = Object( + schema=IUnit, + title=_("Unit"), + description=_("Unit description"), + required=False, + ) + + IUnit['boss'].schema = IPerson + IUnit['members'].value_type.schema = IPerson + + @implementer(IUnit) + class Unit(object): + def __init__(self, person, person_list): + self.boss = person + self.members = person_list + + @implementer(IPerson) + class Person(object): + def __init__(self, unit): + self.unit = unit + + return IUnit, Person, Unit + + def test_class_conforms_to_IObject(self): + from zope.interface.verify import verifyClass + from zope.schema.interfaces import IObject + verifyClass(IObject, self._getTargetClass()) + + def test_instance_conforms_to_IObject(self): + from zope.interface.verify import verifyObject + from zope.schema.interfaces import IObject + verifyObject(IObject, self._makeOne()) + + def test_ctor_w_bad_schema(self): + from zope.schema.interfaces import WrongType + self.assertRaises(WrongType, self._makeOne, object()) + + def test_validate_not_required(self): + schema = self._makeSchema() + objf = self._makeOne(schema, required=False) + objf.validate(None) # doesn't raise + + def test_validate_required(self): + from zope.schema.interfaces import RequiredMissing + field = self._makeOne(required=True) + self.assertRaises(RequiredMissing, field.validate, None) + + def test__validate_w_empty_schema(self): + from zope.interface import Interface + objf = self._makeOne(Interface) + objf.validate(object()) # doesn't raise + + def test__validate_w_value_not_providing_schema(self): + from zope.schema.interfaces import SchemaNotProvided + from zope.schema._bootstrapfields import Text + schema = self._makeSchema(foo=Text(), bar=Text()) + objf = self._makeOne(schema) + bad_value = object() + with self.assertRaises(SchemaNotProvided) as exc: + objf.validate(bad_value) + + not_provided = exc.exception + self.assertIs(not_provided.field, objf) + self.assertIs(not_provided.value, bad_value) + self.assertEqual(not_provided.args, (schema, bad_value), ) + + def test__validate_w_value_providing_schema_but_missing_fields(self): + from zope.interface import implementer + from zope.schema.interfaces import SchemaNotFullyImplemented + from zope.schema.interfaces import SchemaNotCorrectlyImplemented + from zope.schema._bootstrapfields import Text + schema = self._makeSchema(foo=Text(), bar=Text()) + + @implementer(schema) + class Broken(object): + pass + + objf = self._makeOne(schema) + broken = Broken() + with self.assertRaises(SchemaNotCorrectlyImplemented) as exc: + objf.validate(broken) + + wct = exc.exception + self.assertIs(wct.field, objf) + self.assertIs(wct.value, broken) + self.assertEqual(wct.invariant_errors, []) + self.assertEqual( + sorted(wct.schema_errors), + ['bar', 'foo'] + ) + for name in ('foo', 'bar'): + error = wct.schema_errors[name] + self.assertIsInstance(error, + SchemaNotFullyImplemented) + self.assertEqual(schema[name], error.field) + self.assertIsNone(error.value) + + # The legacy arg[0] errors list + errors = self._getErrors(objf.validate, Broken()) + self.assertEqual(len(errors), 2) + errors = sorted(errors, + key=lambda x: (type(x).__name__, str(x.args[0]))) + err = errors[0] + self.assertIsInstance(err, SchemaNotFullyImplemented) + nested = err.args[0] + self.assertIsInstance(nested, AttributeError) + self.assertIn("'bar'", str(nested)) + err = errors[1] + self.assertIsInstance(err, SchemaNotFullyImplemented) + nested = err.args[0] + self.assertIsInstance(nested, AttributeError) + self.assertIn("'foo'", str(nested)) + + def test__validate_w_value_providing_schema_but_invalid_fields(self): + from zope.interface import implementer + from zope.schema.interfaces import SchemaNotCorrectlyImplemented + from zope.schema.interfaces import RequiredMissing + from zope.schema.interfaces import WrongType + from zope.schema._bootstrapfields import Text + from zope.schema._compat import text_type + schema = self._makeSchema(foo=Text(), bar=Text()) + + @implementer(schema) + class Broken(object): + foo = None + bar = 1 + + objf = self._makeOne(schema) + broken = Broken() + with self.assertRaises(SchemaNotCorrectlyImplemented) as exc: + objf.validate(broken) + + wct = exc.exception + self.assertIs(wct.field, objf) + self.assertIs(wct.value, broken) + self.assertEqual(wct.invariant_errors, []) + self.assertEqual( + sorted(wct.schema_errors), + ['bar', 'foo'] + ) + self.assertIsInstance(wct.schema_errors['foo'], RequiredMissing) + self.assertIsInstance(wct.schema_errors['bar'], WrongType) + + # The legacy arg[0] errors list + errors = self._getErrors(objf.validate, Broken()) + self.assertEqual(len(errors), 2) + errors = sorted(errors, key=lambda x: type(x).__name__) + err = errors[0] + self.assertIsInstance(err, RequiredMissing) + self.assertEqual(err.args, ('foo',)) + err = errors[1] + self.assertIsInstance(err, WrongType) + self.assertEqual(err.args, (1, text_type, 'bar')) + + def test__validate_w_value_providing_schema(self): + from zope.interface import implementer + from zope.schema._bootstrapfields import Text + from zope.schema._field import Choice + + schema = self._makeSchema( + foo=Text(), + bar=Text(), + baz=Choice(values=[1, 2, 3]), + ) + + @implementer(schema) + class OK(object): + foo = u'Foo' + bar = u'Bar' + baz = 2 + objf = self._makeOne(schema) + objf.validate(OK()) # doesn't raise + + def test_validate_w_cycles(self): + IUnit, Person, Unit = self._makeCycles() + field = self._makeOne(schema=IUnit) + person1 = Person(None) + person2 = Person(None) + unit = Unit(person1, [person1, person2]) + person1.unit = unit + person2.unit = unit + field.validate(unit) # doesn't raise + + def test_validate_w_cycles_object_not_valid(self): + from zope.schema.interfaces import WrongContainedType + IUnit, Person, Unit = self._makeCycles() + field = self._makeOne(schema=IUnit) + person1 = Person(None) + person2 = Person(None) + person3 = Person(object()) + unit = Unit(person3, [person1, person2]) + person1.unit = unit + person2.unit = unit + self.assertRaises(WrongContainedType, field.validate, unit) + + def test_validate_w_cycles_collection_not_valid(self): + from zope.schema.interfaces import WrongContainedType + IUnit, Person, Unit = self._makeCycles() + field = self._makeOne(schema=IUnit) + person1 = Person(None) + person2 = Person(None) + person3 = Person(object()) + unit = Unit(person1, [person2, person3]) + person1.unit = unit + person2.unit = unit + self.assertRaises(WrongContainedType, field.validate, unit) + + def test_set_emits_IBOAE(self): + from zope.event import subscribers + from zope.interface import implementer + from zope.schema.interfaces import IBeforeObjectAssignedEvent + from zope.schema._bootstrapfields import Text + from zope.schema._field import Choice + + schema = self._makeSchema( + foo=Text(), + bar=Text(), + baz=Choice(values=[1, 2, 3]), + ) + + @implementer(schema) + class OK(object): + foo = u'Foo' + bar = u'Bar' + baz = 2 + log = [] + subscribers.append(log.append) + objf = self._makeOne(schema, __name__='field') + inst = DummyInst() + value = OK() + objf.set(inst, value) + self.assertIs(inst.field, value) + self.assertEqual(len(log), 5) + self.assertEqual(IBeforeObjectAssignedEvent.providedBy(log[-1]), True) + self.assertEqual(log[-1].object, value) + self.assertEqual(log[-1].name, 'field') + self.assertEqual(log[-1].context, inst) + + def test_set_allows_IBOAE_subscr_to_replace_value(self): + from zope.event import subscribers + from zope.interface import implementer + from zope.schema._bootstrapfields import Text + from zope.schema._field import Choice + + schema = self._makeSchema( + foo=Text(), + bar=Text(), + baz=Choice(values=[1, 2, 3]), + ) + + @implementer(schema) + class OK(object): + def __init__(self, foo=u'Foo', bar=u'Bar', baz=2): + self.foo = foo + self.bar = bar + self.baz = baz + ok1 = OK() + ok2 = OK(u'Foo2', u'Bar2', 3) + log = [] + subscribers.append(log.append) + + def _replace(event): + event.object = ok2 + subscribers.append(_replace) + objf = self._makeOne(schema, __name__='field') + inst = DummyInst() + self.assertEqual(len(log), 4) + objf.set(inst, ok1) + self.assertIs(inst.field, ok2) + self.assertEqual(len(log), 5) + self.assertEqual(log[-1].object, ok2) + self.assertEqual(log[-1].name, 'field') + self.assertEqual(log[-1].context, inst) + + def test_validates_invariants_by_default(self): + from zope.interface import invariant + from zope.interface import Interface + from zope.interface import implementer + from zope.interface import Invalid + from zope.schema import Text + from zope.schema import Bytes + + class ISchema(Interface): + + foo = Text() + bar = Bytes() + + @invariant + def check_foo(self): + if self.foo == u'bar': + raise Invalid("Foo is not valid") + + @invariant + def check_bar(self): + if self.bar == b'foo': + raise Invalid("Bar is not valid") + + @implementer(ISchema) + class O(object): + foo = u'' + bar = b'' + + + field = self._makeOne(ISchema) + inst = O() + + # Fine at first + field.validate(inst) + + inst.foo = u'bar' + errors = self._getErrors(field.validate, inst) + self.assertEqual(len(errors), 1) + self.assertEqual(errors[0].args[0], "Foo is not valid") + + del inst.foo + inst.bar = b'foo' + errors = self._getErrors(field.validate, inst) + self.assertEqual(len(errors), 1) + self.assertEqual(errors[0].args[0], "Bar is not valid") + + # Both invalid + inst.foo = u'bar' + errors = self._getErrors(field.validate, inst) + self.assertEqual(len(errors), 2) + errors.sort(key=lambda i: i.args) + self.assertEqual(errors[0].args[0], "Bar is not valid") + self.assertEqual(errors[1].args[0], "Foo is not valid") + + # We can specifically ask for invariants to be turned off. + field = self._makeOne(ISchema, validate_invariants=False) + field.validate(inst) + + def test_schema_defined_by_subclass(self): + from zope import interface + from zope.schema.interfaces import SchemaNotProvided + + class IValueType(interface.Interface): + "The value type schema" + + class Field(self._getTargetClass()): + schema = IValueType + + field = Field() + self.assertIs(field.schema, IValueType) + + # Non implementation is bad + self.assertRaises(SchemaNotProvided, field.validate, object()) + + # Actual implementation works + @interface.implementer(IValueType) + class ValueType(object): + "The value type" + + + field.validate(ValueType()) + + def test_bound_field_of_collection_with_choice(self): + # https://github.com/zopefoundation/zope.schema/issues/17 + from zope.interface import Interface, implementer + from zope.interface import Attribute + + from zope.schema import Choice, Object, Set + from zope.schema.fieldproperty import FieldProperty + from zope.schema.interfaces import IContextSourceBinder + from zope.schema.interfaces import WrongContainedType + from zope.schema.interfaces import SchemaNotCorrectlyImplemented + from zope.schema.vocabulary import SimpleVocabulary + + + @implementer(IContextSourceBinder) + class EnumContext(object): + def __call__(self, context): + return SimpleVocabulary.fromValues(list(context)) + + class IMultipleChoice(Interface): + choices = Set(value_type=Choice(source=EnumContext())) + # Provide a regular attribute to prove that binding doesn't + # choke. NOTE: We don't actually verify the existence of this attribute. + non_field = Attribute("An attribute") + + @implementer(IMultipleChoice) + class Choices(object): + + def __init__(self, choices): + self.choices = choices + + def __iter__(self): + # EnumContext calls this to make the vocabulary. + # Fields of the schema of the IObject are bound to the value being + # validated. + return iter(range(5)) + + class IFavorites(Interface): + fav = Object(title=u"Favorites number", schema=IMultipleChoice) + + + @implementer(IFavorites) + class Favorites(object): + fav = FieldProperty(IFavorites['fav']) + + # must not raise + good_choices = Choices({1, 3}) + IFavorites['fav'].validate(good_choices) + + # Ranges outside the context fail + bad_choices = Choices({1, 8}) + with self.assertRaises(WrongContainedType) as exc: + IFavorites['fav'].validate(bad_choices) + + e = exc.exception + self.assertEqual(IFavorites['fav'], e.field) + self.assertEqual(bad_choices, e.value) + + # Validation through field property + favorites = Favorites() + favorites.fav = good_choices + + # And validation through a field that wants IFavorites + favorites_field = Object(IFavorites) + favorites_field.validate(favorites) + + # Check the field property error + with self.assertRaises(SchemaNotCorrectlyImplemented) as exc: + favorites.fav = bad_choices + + e = exc.exception + self.assertEqual(IFavorites['fav'], e.field) + self.assertEqual(bad_choices, e.value) + self.assertEqual(['choices'], list(e.schema_errors)) + + class DummyInst(object): missing_value = object() diff --git a/src/zope/schema/tests/test__field.py b/src/zope/schema/tests/test__field.py index 7224d4a..c176326 100644 --- a/src/zope/schema/tests/test__field.py +++ b/src/zope/schema/tests/test__field.py @@ -796,9 +796,9 @@ class ChoiceTests(EqualityTestsMixin, pass source = self._makeOne(vocabulary=Vocab()) - instance = DummyInstance() + instance = object() target = source.bind(instance) - self.assertTrue(target.vocabulary is source.vocabulary) + self.assertIs(target.vocabulary, source.vocabulary) def test_bind_w_voc_is_ICSB(self): from zope.interface import implementer @@ -818,9 +818,9 @@ class ChoiceTests(EqualityTestsMixin, source = self._makeOne(vocabulary='temp') source.vocabulary = Vocab(source) source.vocabularyName = None - instance = DummyInstance() + instance = object() target = source.bind(instance) - self.assertEqual(target.vocabulary.context, instance) + self.assertIs(target.vocabulary.context, instance) def test_bind_w_voc_is_ICSB_but_not_ISource(self): from zope.interface import implementer @@ -838,7 +838,7 @@ class ChoiceTests(EqualityTestsMixin, source = self._makeOne(vocabulary='temp') source.vocabulary = Vocab(source) source.vocabularyName = None - instance = DummyInstance() + instance = object() self.assertRaises(ValueError, source.bind, instance) def test_fromUnicode_miss(self): @@ -1630,496 +1630,6 @@ class FrozenSetTests(SetTests): return IFrozenSet -class ObjectTests(EqualityTestsMixin, - unittest.TestCase): - - def setUp(self): - from zope.event import subscribers - self._before = subscribers[:] - - def tearDown(self): - from zope.event import subscribers - subscribers[:] = self._before - - def _getTargetClass(self): - from zope.schema._field import Object - return Object - - def _getTargetInterface(self): - from zope.schema.interfaces import IObject - return IObject - - def _makeOneFromClass(self, cls, schema=None, *args, **kw): - if schema is None: - schema = self._makeSchema() - return super(ObjectTests, self)._makeOneFromClass(cls, schema, *args, **kw) - - def _makeSchema(self, **kw): - from zope.interface import Interface - from zope.interface.interface import InterfaceClass - return InterfaceClass('ISchema', (Interface,), kw) - - def _getErrors(self, f, *args, **kw): - from zope.schema.interfaces import WrongContainedType - with self.assertRaises(WrongContainedType) as e: - f(*args, **kw) - return e.exception.args[0] - - def _makeCycles(self): - from zope.interface import Interface - from zope.interface import implementer - from zope.schema import Object - from zope.schema import List - from zope.schema._messageid import _ - - class IUnit(Interface): - """A schema that participate to a cycle""" - boss = Object( - schema=Interface, - title=_("Boss"), - description=_("Boss description"), - required=False, - ) - members = List( - value_type=Object(schema=Interface), - title=_("Member List"), - description=_("Member list description"), - required=False, - ) - - class IPerson(Interface): - """A schema that participate to a cycle""" - unit = Object( - schema=IUnit, - title=_("Unit"), - description=_("Unit description"), - required=False, - ) - - IUnit['boss'].schema = IPerson - IUnit['members'].value_type.schema = IPerson - - @implementer(IUnit) - class Unit(object): - def __init__(self, person, person_list): - self.boss = person - self.members = person_list - - @implementer(IPerson) - class Person(object): - def __init__(self, unit): - self.unit = unit - - return IUnit, Person, Unit - - def test_class_conforms_to_IObject(self): - from zope.interface.verify import verifyClass - from zope.schema.interfaces import IObject - verifyClass(IObject, self._getTargetClass()) - - def test_instance_conforms_to_IObject(self): - from zope.interface.verify import verifyObject - from zope.schema.interfaces import IObject - verifyObject(IObject, self._makeOne()) - - def test_ctor_w_bad_schema(self): - from zope.schema.interfaces import WrongType - self.assertRaises(WrongType, self._makeOne, object()) - - def test_validate_not_required(self): - schema = self._makeSchema() - objf = self._makeOne(schema, required=False) - objf.validate(None) # doesn't raise - - def test_validate_required(self): - from zope.schema.interfaces import RequiredMissing - field = self._makeOne(required=True) - self.assertRaises(RequiredMissing, field.validate, None) - - def test__validate_w_empty_schema(self): - from zope.interface import Interface - objf = self._makeOne(Interface) - objf.validate(object()) # doesn't raise - - def test__validate_w_value_not_providing_schema(self): - from zope.schema.interfaces import SchemaNotProvided - from zope.schema._bootstrapfields import Text - schema = self._makeSchema(foo=Text(), bar=Text()) - objf = self._makeOne(schema) - bad_value = object() - with self.assertRaises(SchemaNotProvided) as exc: - objf.validate(bad_value) - - not_provided = exc.exception - self.assertIs(not_provided.field, objf) - self.assertIs(not_provided.value, bad_value) - self.assertEqual(not_provided.args, (schema, bad_value), ) - - def test__validate_w_value_providing_schema_but_missing_fields(self): - from zope.interface import implementer - from zope.schema.interfaces import SchemaNotFullyImplemented - from zope.schema.interfaces import SchemaNotCorrectlyImplemented - from zope.schema._bootstrapfields import Text - schema = self._makeSchema(foo=Text(), bar=Text()) - - @implementer(schema) - class Broken(object): - pass - - objf = self._makeOne(schema) - broken = Broken() - with self.assertRaises(SchemaNotCorrectlyImplemented) as exc: - objf.validate(broken) - - wct = exc.exception - self.assertIs(wct.field, objf) - self.assertIs(wct.value, broken) - self.assertEqual(wct.invariant_errors, []) - self.assertEqual( - sorted(wct.schema_errors), - ['bar', 'foo'] - ) - for name in ('foo', 'bar'): - error = wct.schema_errors[name] - self.assertIsInstance(error, - SchemaNotFullyImplemented) - self.assertEqual(schema[name], error.field) - self.assertIsNone(error.value) - - # The legacy arg[0] errors list - errors = self._getErrors(objf.validate, Broken()) - self.assertEqual(len(errors), 2) - errors = sorted(errors, - key=lambda x: (type(x).__name__, str(x.args[0]))) - err = errors[0] - self.assertIsInstance(err, SchemaNotFullyImplemented) - nested = err.args[0] - self.assertIsInstance(nested, AttributeError) - self.assertIn("'bar'", str(nested)) - err = errors[1] - self.assertIsInstance(err, SchemaNotFullyImplemented) - nested = err.args[0] - self.assertIsInstance(nested, AttributeError) - self.assertIn("'foo'", str(nested)) - - def test__validate_w_value_providing_schema_but_invalid_fields(self): - from zope.interface import implementer - from zope.schema.interfaces import SchemaNotCorrectlyImplemented - from zope.schema.interfaces import RequiredMissing - from zope.schema.interfaces import WrongType - from zope.schema._bootstrapfields import Text - from zope.schema._compat import text_type - schema = self._makeSchema(foo=Text(), bar=Text()) - - @implementer(schema) - class Broken(object): - foo = None - bar = 1 - - objf = self._makeOne(schema) - broken = Broken() - with self.assertRaises(SchemaNotCorrectlyImplemented) as exc: - objf.validate(broken) - - wct = exc.exception - self.assertIs(wct.field, objf) - self.assertIs(wct.value, broken) - self.assertEqual(wct.invariant_errors, []) - self.assertEqual( - sorted(wct.schema_errors), - ['bar', 'foo'] - ) - self.assertIsInstance(wct.schema_errors['foo'], RequiredMissing) - self.assertIsInstance(wct.schema_errors['bar'], WrongType) - - # The legacy arg[0] errors list - errors = self._getErrors(objf.validate, Broken()) - self.assertEqual(len(errors), 2) - errors = sorted(errors, key=lambda x: type(x).__name__) - err = errors[0] - self.assertIsInstance(err, RequiredMissing) - self.assertEqual(err.args, ('foo',)) - err = errors[1] - self.assertIsInstance(err, WrongType) - self.assertEqual(err.args, (1, text_type, 'bar')) - - def test__validate_w_value_providing_schema(self): - from zope.interface import implementer - from zope.schema._bootstrapfields import Text - from zope.schema._field import Choice - - schema = self._makeSchema( - foo=Text(), - bar=Text(), - baz=Choice(values=[1, 2, 3]), - ) - - @implementer(schema) - class OK(object): - foo = u'Foo' - bar = u'Bar' - baz = 2 - objf = self._makeOne(schema) - objf.validate(OK()) # doesn't raise - - def test_validate_w_cycles(self): - IUnit, Person, Unit = self._makeCycles() - field = self._makeOne(schema=IUnit) - person1 = Person(None) - person2 = Person(None) - unit = Unit(person1, [person1, person2]) - person1.unit = unit - person2.unit = unit - field.validate(unit) # doesn't raise - - def test_validate_w_cycles_object_not_valid(self): - from zope.schema.interfaces import WrongContainedType - IUnit, Person, Unit = self._makeCycles() - field = self._makeOne(schema=IUnit) - person1 = Person(None) - person2 = Person(None) - person3 = Person(DummyInstance()) - unit = Unit(person3, [person1, person2]) - person1.unit = unit - person2.unit = unit - self.assertRaises(WrongContainedType, field.validate, unit) - - def test_validate_w_cycles_collection_not_valid(self): - from zope.schema.interfaces import WrongContainedType - IUnit, Person, Unit = self._makeCycles() - field = self._makeOne(schema=IUnit) - person1 = Person(None) - person2 = Person(None) - person3 = Person(DummyInstance()) - unit = Unit(person1, [person2, person3]) - person1.unit = unit - person2.unit = unit - self.assertRaises(WrongContainedType, field.validate, unit) - - def test_set_emits_IBOAE(self): - from zope.event import subscribers - from zope.interface import implementer - from zope.schema.interfaces import IBeforeObjectAssignedEvent - from zope.schema._bootstrapfields import Text - from zope.schema._field import Choice - - schema = self._makeSchema( - foo=Text(), - bar=Text(), - baz=Choice(values=[1, 2, 3]), - ) - - @implementer(schema) - class OK(object): - foo = u'Foo' - bar = u'Bar' - baz = 2 - log = [] - subscribers.append(log.append) - objf = self._makeOne(schema, __name__='field') - inst = DummyInstance() - value = OK() - objf.set(inst, value) - self.assertEqual(inst.field is value, True) - self.assertEqual(len(log), 5) - self.assertEqual(IBeforeObjectAssignedEvent.providedBy(log[-1]), True) - self.assertEqual(log[-1].object, value) - self.assertEqual(log[-1].name, 'field') - self.assertEqual(log[-1].context, inst) - - def test_set_allows_IBOAE_subscr_to_replace_value(self): - from zope.event import subscribers - from zope.interface import implementer - from zope.schema._bootstrapfields import Text - from zope.schema._field import Choice - - schema = self._makeSchema( - foo=Text(), - bar=Text(), - baz=Choice(values=[1, 2, 3]), - ) - - @implementer(schema) - class OK(object): - def __init__(self, foo=u'Foo', bar=u'Bar', baz=2): - self.foo = foo - self.bar = bar - self.baz = baz - ok1 = OK() - ok2 = OK(u'Foo2', u'Bar2', 3) - log = [] - subscribers.append(log.append) - - def _replace(event): - event.object = ok2 - subscribers.append(_replace) - objf = self._makeOne(schema, __name__='field') - inst = DummyInstance() - self.assertEqual(len(log), 4) - objf.set(inst, ok1) - self.assertEqual(inst.field is ok2, True) - self.assertEqual(len(log), 5) - self.assertEqual(log[-1].object, ok2) - self.assertEqual(log[-1].name, 'field') - self.assertEqual(log[-1].context, inst) - - def test_validates_invariants_by_default(self): - from zope.interface import invariant - from zope.interface import Interface - from zope.interface import implementer - from zope.interface import Invalid - from zope.schema import Text - from zope.schema import Bytes - - class ISchema(Interface): - - foo = Text() - bar = Bytes() - - @invariant - def check_foo(self): - if self.foo == u'bar': - raise Invalid("Foo is not valid") - - @invariant - def check_bar(self): - if self.bar == b'foo': - raise Invalid("Bar is not valid") - - @implementer(ISchema) - class O(object): - foo = u'' - bar = b'' - - - field = self._makeOne(ISchema) - inst = O() - - # Fine at first - field.validate(inst) - - inst.foo = u'bar' - errors = self._getErrors(field.validate, inst) - self.assertEqual(len(errors), 1) - self.assertEqual(errors[0].args[0], "Foo is not valid") - - del inst.foo - inst.bar = b'foo' - errors = self._getErrors(field.validate, inst) - self.assertEqual(len(errors), 1) - self.assertEqual(errors[0].args[0], "Bar is not valid") - - # Both invalid - inst.foo = u'bar' - errors = self._getErrors(field.validate, inst) - self.assertEqual(len(errors), 2) - errors.sort(key=lambda i: i.args) - self.assertEqual(errors[0].args[0], "Bar is not valid") - self.assertEqual(errors[1].args[0], "Foo is not valid") - - # We can specifically ask for invariants to be turned off. - field = self._makeOne(ISchema, validate_invariants=False) - field.validate(inst) - - def test_schema_defined_by_subclass(self): - from zope import interface - from zope.schema.interfaces import SchemaNotProvided - - class IValueType(interface.Interface): - "The value type schema" - - class Field(self._getTargetClass()): - schema = IValueType - - field = Field() - self.assertIs(field.schema, IValueType) - - # Non implementation is bad - self.assertRaises(SchemaNotProvided, field.validate, object()) - - # Actual implementation works - @interface.implementer(IValueType) - class ValueType(object): - "The value type" - - - field.validate(ValueType()) - - def test_bound_field_of_collection_with_choice(self): - # https://github.com/zopefoundation/zope.schema/issues/17 - from zope.interface import Interface, implementer - from zope.interface import Attribute - - from zope.schema import Choice, Object, Set - from zope.schema.fieldproperty import FieldProperty - from zope.schema.interfaces import IContextSourceBinder - from zope.schema.interfaces import WrongContainedType - from zope.schema.interfaces import SchemaNotCorrectlyImplemented - from zope.schema.vocabulary import SimpleVocabulary - - - @implementer(IContextSourceBinder) - class EnumContext(object): - def __call__(self, context): - return SimpleVocabulary.fromValues(list(context)) - - class IMultipleChoice(Interface): - choices = Set(value_type=Choice(source=EnumContext())) - # Provide a regular attribute to prove that binding doesn't - # choke. NOTE: We don't actually verify the existence of this attribute. - non_field = Attribute("An attribute") - - @implementer(IMultipleChoice) - class Choices(object): - - def __init__(self, choices): - self.choices = choices - - def __iter__(self): - # EnumContext calls this to make the vocabulary. - # Fields of the schema of the IObject are bound to the value being - # validated. - return iter(range(5)) - - class IFavorites(Interface): - fav = Object(title=u"Favorites number", schema=IMultipleChoice) - - - @implementer(IFavorites) - class Favorites(object): - fav = FieldProperty(IFavorites['fav']) - - # must not raise - good_choices = Choices({1, 3}) - IFavorites['fav'].validate(good_choices) - - # Ranges outside the context fail - bad_choices = Choices({1, 8}) - with self.assertRaises(WrongContainedType) as exc: - IFavorites['fav'].validate(bad_choices) - - e = exc.exception - self.assertEqual(IFavorites['fav'], e.field) - self.assertEqual(bad_choices, e.value) - - # Validation through field property - favorites = Favorites() - favorites.fav = good_choices - - # And validation through a field that wants IFavorites - favorites_field = Object(IFavorites) - favorites_field.validate(favorites) - - # Check the field property error - with self.assertRaises(SchemaNotCorrectlyImplemented) as exc: - favorites.fav = bad_choices - - e = exc.exception - self.assertEqual(IFavorites['fav'], e.field) - self.assertEqual(bad_choices, e.value) - self.assertEqual(['choices'], list(e.schema_errors)) - - class MappingTests(EqualityTestsMixin, unittest.TestCase): @@ -2221,7 +1731,7 @@ class MappingTests(EqualityTestsMixin, def test_bind_binds_key_and_value_types(self): from zope.schema import Int field = self._makeOne(key_type=Int(), value_type=Int()) - context = DummyInstance() + context = object() field2 = field.bind(context) self.assertEqual(field2.key_type.context, context) self.assertEqual(field2.value_type.context, context) @@ -2295,10 +1805,6 @@ class DictTests(MutableMappingTests): super(DictTests, self).test_mutable_mapping() -class DummyInstance(object): - pass - - def _makeSampleVocabulary(): from zope.interface import implementer from zope.schema.interfaces import IVocabulary |
