Cannot initialise a Choice field with an IContextSourceBinder source and a default value

Bug #340416 reported by Martin Aspeli
2
Affects Status Importance Assigned to Milestone
Zope 3
Fix Released
Undecided
Unassigned

Bug Description

Let's say we have a context source binder:

>>> from zope.schema.interfaces import IContextSourceBinder
>>> from zope.interface import implements
>>> from zope.schema.vocabulary import SimpleVocabulary

>>> class Binder(object):
... implements(IContextSourceBinder)
... def __call__(self, context):
... return SimpleVocabulary.fromValues([u'a', u'b'])
...

If we try to initialise a Choice field with this as a source and no default value, we're OK:

>>> from zope.schema import Choice
>>> field = Choice(title=u"Test", source=Binder())

Note that the field can be validated against an item in the vocabulary only once bound:

>>> field.bind(object()).validate(u'a') is None
True

If the field is not bound, we get an error:

>>> field.validate(u'a') is None
Traceback (most recent call last):
  File "<console>", line 1, in ?
  File "/Users/optilude/.buildout/zope/Zope-2.10.6-final-py2.4/lib/python/zope/schema/_bootstrapfields.py", line 138, in validate
    self._validate(value)
  File "/Users/optilude/.buildout/zope/Zope-2.10.6-final-py2.4/lib/python/zope/schema/_field.py", line 275, in _validate
    if value not in vocabulary:
TypeError: iterable argument required

This is because at this point, self.vocabulary is an IContextSourceBinder instance, not a vocabulary.

Unfortunately, the default value is validated against an unbound field when initialised in the constructor, giving the same error:

>>> field = Choice(title=u"Test", default=u'a', source=Binder())
Traceback (most recent call last):
  File "<console>", line 1, in ?
  File "/Users/optilude/.buildout/zope/Zope-2.10.6-final-py2.4/lib/python/zope/schema/_field.py", line 229, in __init__
    super(Choice, self).__init__(**kw)
  File "/Users/optilude/.buildout/zope/Zope-2.10.6-final-py2.4/lib/python/zope/schema/_bootstrapfields.py", line 114, in __init__
    self.default = default
  File "/Users/optilude/.buildout/zope/Zope-2.10.6-final-py2.4/lib/python/zope/schema/_bootstrapfields.py", line 42, in __set__
    inst.validate(value)
  File "/Users/optilude/.buildout/zope/Zope-2.10.6-final-py2.4/lib/python/zope/schema/_bootstrapfields.py", line 138, in validate
    self._validate(value)
  File "/Users/optilude/.buildout/zope/Zope-2.10.6-final-py2.4/lib/python/zope/schema/_field.py", line 275, in _validate
    if value not in vocabulary:
TypeError: iterable argument required

This means that it is impossible to initialise a choice field with a context source binder and a default. The choice field actually has a flag that it sets in the initialiser that "turns off" validation, but it is only used for named vocabularies:

        # Before a default value is checked, it is validated. However, a
        # named vocabulary is usually not complete when these fields are
        # initialized. Therefore signal the validation method to ignore
        # default value checks during initialization of a Choice tied to a
        # registered vocabulary.
        self._init_field = bool(self.vocabularyName)
        super(Choice, self).__init__(**kw)
        self._init_field = False

I think it should also be set if IContextSourceBinder.providedBy(self.vocabulary), e.g:

        self._init_field = bool(self.vocabularyName) or IContextSourceBinder.providedBy(self.vocabulary)

Of course, this means the default value isn't validated, but that's always going to be impossible anyway with a context-bound source, since the context isn't known at field initialisation time.

Revision history for this message
Dan Korostelev (nadako) wrote :

Fix committed in 97798.

Changed in zope3:
status: New → Fix Committed
Revision history for this message
Wolfgang Schnerring (wosc) wrote :

released as zope.schema-3.5.4

Changed in zope3:
status: Fix Committed → Fix Released
To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.