[Checkins] SVN: zope.schema/trunk/ Avoid maximum recursion when validating Object field that points to cycles

Godefroid Chapelle gotcha at bubblenet.be
Fri Apr 30 09:36:09 EDT 2010


Log message for revision 111614:
  Avoid maximum recursion when validating Object field that points to 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-04-30 13:27:49 UTC (rev 111613)
+++ zope.schema/trunk/CHANGES.txt	2010-04-30 13:36:09 UTC (rev 111614)
@@ -5,6 +5,8 @@
 3.6.2 (unreleased)
 ------------------
 
+- Avoid maximum recursion when validating Object field that points to cycles
+
 - Made the dependency on ``zope.i18nmessageid`` optional.
 
 3.6.1 (2010-01-05)

Modified: zope.schema/trunk/src/zope/schema/_field.py
===================================================================
--- zope.schema/trunk/src/zope/schema/_field.py	2010-04-30 13:27:49 UTC (rev 111613)
+++ zope.schema/trunk/src/zope/schema/_field.py	2010-04-30 13:36:09 UTC (rev 111614)
@@ -168,7 +168,7 @@
         >>> f.fromUnicode("1.25")
         1.25
         >>> f.fromUnicode("1.25.6") #doctest: +IGNORE_EXCEPTION_DETAIL
-        Traceback (most recent call last): 
+        Traceback (most recent call last):
         ...
         ValueError: invalid literal for float(): 1.25.6
         """
@@ -457,6 +457,9 @@
 def _validate_fields(schema, value, errors=None):
     if errors is None:
         errors = []
+    if hasattr(value, '__validating_schema'):
+        return errors
+    value.__validating_schema = True
     for name in schema.names(all=True):
         if not IMethod.providedBy(schema[name]):
             try:
@@ -469,6 +472,7 @@
             except AttributeError, error:
                 # property for the given name is not implemented
                 errors.append(SchemaNotFullyImplemented(error))
+    delattr(value, '__validating_schema')
     return errors
 
 

Modified: zope.schema/trunk/src/zope/schema/tests/test_objectfield.py
===================================================================
--- zope.schema/trunk/src/zope/schema/tests/test_objectfield.py	2010-04-30 13:27:49 UTC (rev 111613)
+++ zope.schema/trunk/src/zope/schema/tests/test_objectfield.py	2010-04-30 13:36:09 UTC (rev 111614)
@@ -1,3 +1,4 @@
+from zope.schema import List
 ##############################################################################
 #
 # Copyright (c) 2001, 2002 Zope Foundation and Contributors.
@@ -49,6 +50,55 @@
     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)
@@ -103,6 +153,7 @@
 
 class ObjectTest(CleanUp, FieldTestBase):
     """Test the Object Field."""
+
     def getErrors(self, f, *args, **kw):
         try:
             f(*args, **kw)
@@ -136,7 +187,7 @@
 
     def test_init(self):
         for schema in self.validSchemas():
-            field = Object(schema=schema)
+            Object(schema=schema)
         for schema in self.invalidSchemas():
             self.assertRaises(ValidationError, Object, schema=schema)
             self.assertRaises(WrongType, Object, schema=schema)
@@ -189,9 +240,11 @@
                                     __name__='object_field')
         data = self.makeTestData()
         events = []
+
         def register_event(event):
             events.append(event)
         zope.event.subscribers.append(register_event)
+
         class Dummy(object):
             pass
         context = Dummy()
@@ -203,7 +256,40 @@
         self.assertEquals('object_field', event.name)
         self.assertEquals(context, event.context)
 
+    # 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)
+
+    def test_with_cycles_object_not_valid(self):
+        field = self.makeTestObject(schema=ICyclic)
+        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)
+
+    def test_with_cycles_collection_not_valid(self):
+        field = self.makeTestObject(schema=ICyclic)
+        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)
+
+
 def test_suite():
     suite = TestSuite()
     suite.addTest(makeSuite(ObjectTest))



More information about the checkins mailing list