[Checkins] SVN: zope.schema/trunk/ - fix validation of schema with Object Field that specify Interface schema.
Godefroid Chapelle
gotcha at bubblenet.be
Tue May 18 08:12:26 EDT 2010
Log message for revision 112445:
- fix validation of schema with Object Field that specify Interface schema.
- improve tests for validation of cycles
Changed:
U zope.schema/trunk/CHANGES.txt
U zope.schema/trunk/src/zope/schema/_field.py
U zope.schema/trunk/src/zope/schema/tests/test_objectfield.py
-=-
Modified: zope.schema/trunk/CHANGES.txt
===================================================================
--- zope.schema/trunk/CHANGES.txt 2010-05-18 12:08:13 UTC (rev 112444)
+++ zope.schema/trunk/CHANGES.txt 2010-05-18 12:12:25 UTC (rev 112445)
@@ -5,6 +5,7 @@
3.6.4 (unreleased)
------------------
+- fix validation of schema with Object Field that specify Interface schema.
3.6.3 (2010-04-30)
------------------
Modified: zope.schema/trunk/src/zope/schema/_field.py
===================================================================
--- zope.schema/trunk/src/zope/schema/_field.py 2010-05-18 12:08:13 UTC (rev 112444)
+++ zope.schema/trunk/src/zope/schema/_field.py 2010-05-18 12:12:25 UTC (rev 112445)
@@ -23,7 +23,7 @@
from datetime import datetime, date, timedelta, time
from zope.event import notify
-from zope.interface import classImplements, implements
+from zope.interface import classImplements, implements, Interface
from zope.interface.interfaces import IInterface, IMethod
from zope.schema.interfaces import IField
@@ -457,22 +457,37 @@
def _validate_fields(schema, value, errors=None):
if errors is None:
errors = []
- if hasattr(value, '__validating_schema'):
+ # Interface can be used as schema property for Object fields that plan to
+ # hold values of any type.
+ # Because Interface does not include any Attribute, it is obviously not
+ # worth looping on its methods and filter them all out.
+ if schema is Interface:
return errors
- value.__validating_schema = True
- for name in schema.names(all=True):
- if not IMethod.providedBy(schema[name]):
- try:
- attribute = schema[name]
- if IField.providedBy(attribute):
- # validate attributes that are fields
- attribute.validate(getattr(value, name))
- except ValidationError, error:
- errors.append(error)
- except AttributeError, error:
- # property for the given name is not implemented
- errors.append(SchemaNotFullyImplemented(error))
- delattr(value, '__validating_schema')
+ # if `value` is part of a cyclic graph, we need to break the cycle to avoid
+ # infinite recursion.
+ if hasattr(value, '__schema_being_validated'):
+ return errors
+ # Mark the value as being validated.
+ value.__schema_being_validated = True
+ # (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
+ # that supports attribute assignment.)
+ try:
+ for name in schema.names(all=True):
+ if not IMethod.providedBy(schema[name]):
+ try:
+ attribute = schema[name]
+ if IField.providedBy(attribute):
+ # validate attributes that are fields
+ attribute.validate(getattr(value, name))
+ except ValidationError, error:
+ errors.append(error)
+ except AttributeError, error:
+ # property for the given name is not implemented
+ errors.append(SchemaNotFullyImplemented(error))
+ finally:
+ delattr(value, '__schema_being_validated')
return errors
Modified: zope.schema/trunk/src/zope/schema/tests/test_objectfield.py
===================================================================
--- zope.schema/trunk/src/zope/schema/tests/test_objectfield.py 2010-05-18 12:08:13 UTC (rev 112444)
+++ zope.schema/trunk/src/zope/schema/tests/test_objectfield.py 2010-05-18 12:12:25 UTC (rev 112445)
@@ -50,55 +50,6 @@
attribute = Attribute("Test attribute, an attribute can't be validated.")
-class ICyclic(Interface):
- """A test schema"""
-
- baz = Object(
- schema=Interface,
- title=_(u"Baz"),
- description=_(u"Baz description"),
- required=False,
- )
-
- baz_list = List(
- value_type=Object(schema=Interface),
- title=_(u"Baz List"),
- description=_(u"Baz description"),
- required=False,
- )
-
-
-class IBaz(Interface):
- """A test schema"""
-
- cyclic = Object(
- schema=ICyclic,
- title=_(u"Cyclic"),
- description=_(u"Cyclic description"),
- required=False,
- )
-
-ICyclic['baz'].schema = IBaz
-ICyclic['baz_list'].value_type.schema = IBaz
-
-
-class Cyclic(object):
-
- implements(ICyclic)
-
- def __init__(self, baz, baz_list):
- self.baz = baz
- self.baz_list = baz_list
-
-
-class Baz(object):
-
- implements(IBaz)
-
- def __init__(self, cyclic):
- self.cyclic = cyclic
-
-
class TestClass(object):
implements(ITestSchema)
@@ -151,6 +102,79 @@
# attribute
+class ISchemaWithObjectFieldAsInterface(Interface):
+
+ obj = Object(
+ schema=Interface,
+ title=_(u"Object"),
+ description=_(u"object description"),
+ required=False)
+
+
+class ClassWithObjectFieldAsInterface(object):
+
+ implements(ISchemaWithObjectFieldAsInterface)
+
+ _obj = None
+
+ def getobj(self):
+ return self._obj
+
+ def setobj(self, value):
+ self._obj = value
+
+ obj = property(getobj, setobj, None, u'obj')
+
+
+class IUnit(Interface):
+ """A schema that participate to a cycle"""
+
+ boss = Object(
+ schema=Interface,
+ title=_(u"Boss"),
+ description=_(u"Boss description"),
+ required=False,
+ )
+
+ members = List(
+ value_type=Object(schema=Interface),
+ title=_(u"Member List"),
+ description=_(u"Member list description"),
+ required=False,
+ )
+
+
+class IPerson(Interface):
+ """A schema that participate to a cycle"""
+
+ unit = Object(
+ schema=IUnit,
+ title=_(u"Unit"),
+ description=_(u"Unit description"),
+ required=False,
+ )
+
+IUnit['boss'].schema = IPerson
+IUnit['members'].value_type.schema = IPerson
+
+
+class Unit(object):
+
+ implements(IUnit)
+
+ def __init__(self, person, person_list):
+ self.boss = person
+ self.members = person_list
+
+
+class Person(object):
+
+ implements(IPerson)
+
+ def __init__(self, unit):
+ self.unit = unit
+
+
class ObjectTest(CleanUp, FieldTestBase):
"""Test the Object Field."""
@@ -235,6 +259,14 @@
errors = self.getErrors(field.validate, data)
self.assert_(isinstance(errors[0], SchemaNotFullyImplemented))
+ def test_validate_with_non_object_value(self):
+ field = self.makeTestObject(
+ schema=ISchemaWithObjectFieldAsInterface,
+ required=False)
+ instance = ClassWithObjectFieldAsInterface()
+ instance.obj = (1, 1)
+ field.validate(instance)
+
def test_beforeAssignEvent(self):
field = self.makeTestObject(schema=ITestSchema, required=False,
__name__='object_field')
@@ -259,35 +291,35 @@
# cycles
def test_with_cycles_validate(self):
- field = self.makeTestObject(schema=ICyclic)
- baz1 = Baz(None)
- baz2 = Baz(None)
- cyclic = Cyclic(baz1, [baz1, baz2])
- baz1.cyclic = cyclic
- baz2.cyclic = cyclic
- field.validate(cyclic)
+ field = self.makeTestObject(schema=IUnit)
+ person1 = Person(None)
+ person2 = Person(None)
+ unit = Unit(person1, [person1, person2])
+ person1.unit = unit
+ person2.unit = unit
+ field.validate(unit)
def test_with_cycles_object_not_valid(self):
- field = self.makeTestObject(schema=ICyclic)
+ field = self.makeTestObject(schema=IUnit)
data = self.makeTestData()
- baz1 = Baz(None)
- baz2 = Baz(None)
- baz3 = Baz(data)
- cyclic = Cyclic(baz3, [baz1, baz2])
- baz1.cyclic = cyclic
- baz2.cyclic = cyclic
- self.assertRaises(WrongContainedType, field.validate, cyclic)
+ person1 = Person(None)
+ person2 = Person(None)
+ person3 = Person(data)
+ unit = Unit(person3, [person1, person2])
+ person1.unit = unit
+ person2.unit = unit
+ self.assertRaises(WrongContainedType, field.validate, unit)
def test_with_cycles_collection_not_valid(self):
- field = self.makeTestObject(schema=ICyclic)
+ field = self.makeTestObject(schema=IUnit)
data = self.makeTestData()
- baz1 = Baz(None)
- baz2 = Baz(None)
- baz3 = Baz(data)
- cyclic = Cyclic(baz1, [baz2, baz3])
- baz1.cyclic = cyclic
- baz2.cyclic = cyclic
- self.assertRaises(WrongContainedType, field.validate, cyclic)
+ person1 = Person(None)
+ person2 = Person(None)
+ person3 = Person(data)
+ unit = Unit(person1, [person2, person3])
+ person1.unit = unit
+ person2.unit = unit
+ self.assertRaises(WrongContainedType, field.validate, unit)
def test_suite():
More information about the checkins
mailing list