On Fri, Mar 30, 2012 at 06:36:13PM +0200, Alexandre Garel wrote:
> Hello,
> 
> I fill a bug report for zope.schema :
> 
> "zope.schema does not handle well nested object fields"
> https://bugs.launchpad.net/zope.schema/+bug/969350
> 
> my patch change a particular behaviour (binding of Choice field
> nested in Object field)
> 
> I explain why on the ticket.
> 
> Any comment would be appreciated.

Here's the diff attached to that bug:

> Index: src/zope/schema/tests/test_objectfield_nested.py
> ===================================================================
> --- src/zope/schema/tests/test_objectfield_nested.py  (révision 0)
> +++ src/zope/schema/tests/test_objectfield_nested.py  (révision 0)
> @@ -0,0 +1,74 @@
> +##############################################################################
> +#
> +# Copyright (c) 2001, 2002 Zope Foundation and Contributors.

Probably should be 2012 ;)

> +# All Rights Reserved.
> +#
> +# This software is subject to the provisions of the Zope Public License,
> +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
> +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
> +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
> +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
> +# FOR A PARTICULAR PURPOSE.
> +#
> +##############################################################################
> +"""This test exercises Object fields in the case of nestment inside list

s/netsment/nesting/, although the sentence could use some rewriting.

> +to verify binding correcly happens at validation time
> +"""
> +
> +from unittest import TestCase, TestSuite, main, makeSuite
> +
> +from zope.interface import Interface, implementer
> +from zope.schema import Choice, Object, Set
> +from zope.schema.interfaces import IContextSourceBinder, WrongContainedType
> +from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
> +from zope.testing.cleanup import CleanUp
> +
> +
> +@implementer(IContextSourceBinder)
> +class EnumContext(object):
> +
> +    def __call__(self, context):
> +        return SimpleVocabulary([
> +            SimpleTerm(value=v, token=v, title='Letter %s' % v)
> +            for v in context])
> +
> +
> +class IMultipleChoice(Interface):
> +    choices = Set(value_type=Choice(source=EnumContext()))
> +
> +
> +@implementer(IMultipleChoice)
> +class Choices(object):
> +
> +    def __iter__(self):
> +        return iter(range(5))  # to have EnumSource work

EnumSource?  Do you mean EnumContext?

> +
> +    def __init__(self, choices):
> +        self.choices = choices
> +
> +
> +class IFavorites(Interface):
> +    fav = Object(title=u"Favorites number", schema=IMultipleChoice)
> +
> +
> +class ObjectNestedTest(CleanUp, TestCase):
> +    """Test the Object Field in cases of nested fields"""
> +
> +    def test_validate_nested(self):
> +        context = range(5)

This is a bit confusing.  IField.bind() is usually passed a context that
is an object that has an attribute with that field.  You're passing a
list of numbers.  Would the test also work if you had context = object()?

> +        # must not raise
> +        IFavorites['fav'].bind(context).validate(Choices(set([1, 3])))
> +        # checking against dictionnary do work

I don't see any dictionaries...  Perhaps you meant 'checking of set
values against the source works'?

> +        self.assertRaises(
> +            WrongContainedType,
> +            IFavorites['fav'].bind(context).validate, Choices(set([1, 8])))
> +
> +
> +def test_suite():
> +    suite = TestSuite()
> +    suite.addTest(makeSuite(ObjectNestedTest))
> +    return suite
> +
> +
> +if __name__ == '__main__':
> +    main(defaultTest='test_suite')

> Index: src/zope/schema/_field.py
> ===================================================================
> --- src/zope/schema/_field.py (révision 124803)
> +++ src/zope/schema/_field.py (copie de travail)
> @@ -492,12 +492,7 @@
>              if not IMethod.providedBy(schema[name]):
>                  try:
>                      attribute = schema[name]
> -                    if IChoice.providedBy(attribute):
> -                        # Choice must be bound before validation otherwise
> -                        # IContextSourceBinder is not iterable in validation
> -                        bound = attribute.bind(value)
> -                        bound.validate(getattr(value, name))
> -                    elif IField.providedBy(attribute):
> +                    if IField.providedBy(attribute):
>                          # validate attributes that are fields
>                          attribute.validate(getattr(value, name))
>                  except ValidationError as error:
> @@ -510,6 +505,24 @@
>      return errors
>  
>  
> +class BindedSchema(object):

BoundSchema would be a more grammatically correct name.

> +    """This class proxies a schema to get its fields binded to a context

s/binded/bound/

> +    """
> +
> +    def __init__(self, schema, context):
> +        self.schema = schema
> +        self.context = context
> +
> +    # we redefine getitem
> +    def __getitem__(self, name):
> +        attr = self.schema[name]
> +        return attr.bind(self.context)

Shouldn't this be

           return attr.bind(getattr(self.context, name))

?

> +
> +    # but let all the rest slip to schema
> +    def __getattr__(self, name):
> +        return getattr(self.schema, name)

The comments don't really add anything to the code.

> +
> +
>  @implementer(IObject)
>  class Object(Field):
>      __doc__ = IObject.__doc__
> @@ -521,6 +534,11 @@
>          self.schema = schema
>          super(Object, self).__init__(**kw)
>  
> +    def bind(self, context=None):
> +        binded = super(Object, self).bind(context)
> +        binded.schema = BindedSchema(self.schema, context)

s/binded/bound/

> +        return binded
> +
>      def _validate(self, value):
>          super(Object, self)._validate(value)

Marius Gedminas
-- 
http://pov.lt/ -- Zope 3/BlueBream consulting and development

Attachment: signature.asc
Description: Digital signature

_______________________________________________
Zope-Dev maillist  -  Zope-Dev@zope.org
https://mail.zope.org/mailman/listinfo/zope-dev
**  No cross posts or HTML encoding!  **
(Related lists -
 https://mail.zope.org/mailman/listinfo/zope-announce
 https://mail.zope.org/mailman/listinfo/zope )

Reply via email to