Two zope.schema and z3c.form vocabulary/source bugs

Bug #343083 reported by nick
4
Affects Status Importance Assigned to Milestone
Zope 3
Won't Fix
Undecided
Unassigned
zope.schema
Won't Fix
Undecided
Unassigned

Bug Description

I've been trying to get a Choice schema field populated by a sql database load, and have run into 2 bugs/inconsistencies both within zope.schema and z3c.forms

#1. in zope.schema.fields.txt it states the following for use with the vocabulary field parameter:

"""
...
Because registered vocabularies are simply callables passed a context, many
registered vocabularies can simply be functions that rely on SimpleVocabulary:

  >>> from zope.schema.vocabulary import SimpleVocabulary
  >>> def myDynamicVocabulary(context):
  ... v = dynamic_context_calculation_that_returns_an_iterable(context)
  ... return SimpleVocabulary.fromValues(v)
"""

This suggested "shortcut" is wrong because at line 201 of /export/opt/Plone-3.1/portal/parts/zope2/lib/python/zope/schema/_field.py we have:

    def __init__(self, values=None, vocabulary=None, source=None, **kw):
        """Initialize object."""
        if vocabulary is not None:
            assert (isinstance(vocabulary, basestring)
                    or IBaseVocabulary.providedBy(vocabulary))

The documentation is suggesting a feature that is no longer supported due to the hard assertion of IBaseVocabulary, which the function is not capable of doing. I suggest making it clear how vocaulary="..." on a field is meant to support a contextual result.

#2. Giving up on vocabularies, I looked at the situation with source="..." field parameter, specifying an IContextSourceBinder implementation. So doing this

    sensorId = schema.Choice(title = u'Sensor',
                             required = True,
                             source = Sensors2.FieldSourceBinder())

and providing the Sensors2.FieldSourceBinder:

class FieldSourceBinder(object):
    interface.implements(IContextSourceBinder)
    def __call__(self, context):
        """Return the list or {sensor_id: sensor_name} from t_sensor"""
        # Database read into sensors variable omitted
        return SimpleVocabulary.fromItems(sensors)

things break. It is because the z3c.form.term.Terms terms attribute is now a IContextSourceBinder instead of an ITerms implementation;

  Module z3c.form.form, line 189, in __call__
  Module z3c.form.form, line 184, in update
  Module z3c.form.form, line 134, in update
  Module z3c.form.form, line 126, in updateWidgets
  Module z3c.form.field, line 259, in update
  Module z3c.form.browser.select, line 64, in update
  Module z3c.form.term, line 40, in __iter__
TypeError: iteration over non-sequence

> /export/opt/Plone-3.1/buildout-cache/eggs/z3c.form-1.9.0-py2.4.egg/z3c/form/term.py(42)__iter__()
-> return iter(self.terms)
(Pdb) print self.terms
<wastac.wastacskin.Sensors2.FieldSourceBinder object at 0xb3feb8c>

Why is this just so hard and what is the right way to get a dynamically populated Choice? This is my first foray into zope3 and it's been trial by fire to be honest.

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

As a quick fix solution, you can register your vocabulary factory as a named utility and pass the name as the "vocabulary" argument.

However, this things should certainly be looked at and fixed, thanks for the report.

Revision history for this message
Christian Zagrodnick (zagy) wrote : Re: [Bug 343083] [NEW] Two zope.schema and z3c.form vocabulary/source bugs

On 15.03.2009, at 09:06, nick wrote:

> Public bug reported:
>
> I've been trying to get a Choice schema field populated by a sql
> database load, and have run into 2 bugs/inconsistencies both within
> zope.schema and z3c.forms

[...snip vocabularies as I don't know nothing about vocabulary
(besides being too complicated for me)...]

> #2. Giving up on vocabularies, I looked at the situation with
> source="..." field parameter, specifying an IContextSourceBinder
> implementation. So doing this
>
> sensorId = schema.Choice(title = u'Sensor',
> required = True,
> source = Sensors2.FieldSourceBinder())
>
> and providing the Sensors2.FieldSourceBinder:
>
> class FieldSourceBinder(object):
> interface.implements(IContextSourceBinder)
> def __call__(self, context):
> """Return the list or {sensor_id: sensor_name} from t_sensor"""
> # Database read into sensors variable omitted
> return SimpleVocabulary.fromItems(sensors)

The ISource is not an IVocabulary. ISource's __iter__ returns the
*values* not terms or anything knowing tokens/titles.

>
> things break. It is because the z3c.form.term.Terms terms attribute
> is
> now a IContextSourceBinder instead of an ITerms implementation;

The easiest is using zc.sourcefactory (example from mind, might be
slightly wrong):

class Contextual(zc.sourcefactory.contextual.ContextualSourceFactory):

     def getValues(self, context):
         ... do what you need to produce *values* and return/yield
them...

>
> Module z3c.form.form, line 189, in __call__
> Module z3c.form.form, line 184, in update
> Module z3c.form.form, line 134, in update
> Module z3c.form.form, line 126, in updateWidgets
> Module z3c.form.field, line 259, in update
> Module z3c.form.browser.select, line 64, in update
> Module z3c.form.term, line 40, in __iter__
> TypeError: iteration over non-sequence
>
>
>> /export/opt/Plone-3.1/buildout-cache/eggs/z3c.form-1.9.0-py2.4.egg/
>> z3c/form/term.py(42)__iter__()
> -> return iter(self.terms)
> (Pdb) print self.terms
> <wastac.wastacskin.Sensors2.FieldSourceBinder object at 0xb3feb8c>
>
>
> Why is this just so hard and what is the right way to get a
> dynamically populated Choice? This is my first foray into zope3 and
> it's been trial by fire to be honest.

Yeah, that's a rough edge I guess :(

--
Christian Zagrodnick · <email address hidden>
gocept gmbh & co. kg · forsterstraße 29 · 06112 halle (saale) · germany
http://gocept.com · tel +49 345 1229889 0 · fax +49 345 1229889 1
Zope and Plone consulting and development

Revision history for this message
nick (nick-bower) wrote :

> The ISource is not an IVocabulary. ISource's __iter__ returns
> the *values* not terms or anything knowing tokens/titles.

Thanks for pointing this out. But on this, ISource does not actually define support for anything but __contains__. There is no __iter__ below:

class ISource(Interface):
    """A set of values from which to choose

    Sources represent sets of values. They are used to specify the
    source for choice fields.

    Sources can be large (even infinite), in which case, they need to
    be queried to find out what their values are.

    """

    def __contains__(value):
        """Return whether the value is available in this source
        """

zc.sourcefactory just confuses so I won't go there. Apart from not being mentioned anywhere in zope.schema docs, we've got this way of hooking up either vocabs (which are/were/maybe on the way out depending on who you read) and sources (dumb - just a __contains__ interface), so zc.sourcefactory goes and defines a whole new interface that may or may not be supported by various form libraries. Presumably this is just showing the limit of my understanding about zope3 concepts right now as I expected zope.schema(.fields) to document the complete (and only) way in which fields could be built through a schema defn.

Tres Seaver (tseaver)
Changed in zope3:
status: New → Won't Fix
Revision history for this message
Tres Seaver (tseaver) wrote :
Changed in zope.schema:
status: New → Won't Fix
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.