Using choice within DX Dictionary (AttributeError: portal_catalog)

Using Plone 5 and doing add-on development.
I'm able to use the vocabulary in a schema.Choice field outside the schema.Dict. I'm unsure how to correct this issue, any thoughts on the matter are very welcome.

class IThreeFrameRun(IVeracisRunBase):
aliquot_to_well = schema.Dict(
key_type=schema.TextLine(title=u"Aliquot ID"),
value_type=schema.Choice(source=IChipsAllVocabulary)

returns the following error
Traceback (innermost last):
Module ZPublisher.Publish, line 138, in publish
Module ZPublisher.mapply, line 77, in mapply
Module ZPublisher.Publish, line 48, in call_object
Module plone.z3cform.layout, line 66, in call
Module plone.z3cform.layout, line 50, in update
Module plone.dexterity.browser.add, line 130, in update
Module plone.z3cform.fieldsets.extensible, line 59, in update
Module plone.z3cform.patch, line 30, in GroupForm_update
Module z3c.form.group, line 132, in update
Module z3c.form.form, line 136, in updateWidgets
Module z3c.form.field, line 277, in update
Module z3c.form.browser.multi, line 65, in update
Module plone.app.z3cform.csrf, line 21, in execute
Module z3c.form.action, line 98, in execute
Module z3c.form.button, line 315, in call
Module z3c.form.button, line 170, in call
Module z3c.form.browser.multi, line 71, in handleAdd
Module z3c.form.widget, line 355, in appendAddingWidget
Module z3c.form.widget, line 343, in getWidget
Module z3c.form.browser.select, line 51, in update
Module z3c.form.browser.widget, line 171, in update
Module z3c.form.widget, line 233, in update
Module z3c.form.widget, line 227, in updateTerms
Module zope.component._api, line 107, in getMultiAdapter
Module zope.component._api, line 120, in queryMultiAdapter
Module zope.component.registry, line 238, in queryMultiAdapter
Module zope.interface.adapter, line 532, in queryMultiAdapter
Module z3c.form.term, line 104, in ChoiceTerms
Module zope.schema._field, line 309, in bind
Module immunarray.lims.vocabularies.ichip, line 49, in call
Module Products.CMFCore.utils, line 13, in check_getToolByName
Module Products.CMFCore.utils, line 120, in getToolByName
AttributeError: portal_catalog

Code for vocab creation
@implementer(IVocabularyFactory, IContextSourceBinder)
class IChipsAll(object):

#want to not have v.status = 'Retired (No Longer Offered)'
def __call__(self, context):
    catalog = getToolByName(context, 'portal_catalog')
    values = catalog(portal_type='iChip')
    names = [([v.id]) for v in values]
    normalizer = queryUtility(IIDNormalizer)
    items = [(n, normalizer.normalize(n).upper()) for n in names]
    return SimpleVocabulary.fromItems(items)

IChipsAllVocabulary = IChipsAll()
alsoProvides(IChipsAll, IFormFieldProvider)

the problem is the way you're declaring the vocabulary: your context don't have a portal_catalog object.

check how to create a name vocabulary here:

https://docs.plone.org/external/plone.app.dexterity/docs/advanced/vocabularies.html#named-vocabularies

Why would the vocab work in the same interface in a schema.Choice, but not in the Dict? I resolved the issue by using

values = api.content.find(context=api.portal.get(), portal_type='iChip')

I'm not able to follow your post (after reading the article you suggested)

I'm guessing context is not a content object/different in Dict somehow.

FYI plone.api.find doesn't require context. It will assume the portal root it context is None.

A first step toward learning why this happened might be to check the value passed as context in the failing code. The code you've added fetches the portal object, which is the direct home of the portal_catalog. Other objects which might be passed as context would need to use acquisition to find portal_catalog, but perhaps the object that was passed is not one with acquisition available.

Can you try adding a breakpoint in your code above the call to getToolByName and check what the symbol context is bound to? What type of object? What is its name?

(Pdb) v
Products.ZCatalog.Catalog.mybrains object at 0x7fb8f283f460"

So, I think the return from

values = api.content.find(context=api.portal.get(), portal_type='iChip')
ichips = [v.Title for v in values]

is giving me the catalog brians (Metadata) of the "iChip" and not getting the attributes of the object. Is that the way to read that?

again, the problem is the way you're declaring the vocabulary: you're using @implementer(IVocabularyFactory, IContextSourceBinder) but you seem not to have an __init__() method initializing the context.

anyway that will not work, because you have to use a named vocabulary as I stated in my first answer.

Going back to the beginning, thanks for your advice.

Here is what worked for me.

@immunarray: you are absolutely correct that you are looking at a catalog brain rather than at an object. You can think of a brain as being like a set of indexed values about an object that helps you to get the object back from the ZODB with searches. When you ask the plone.api to find things with the portal_type iChip, you are saying "find me references to all the objects in my database which are iChips." The api returns you catalog brains. Much of what you need to work with is in those brains, so you can often just use them to get things like the title and description of an object.

But sometimes you need to have the object itself, and this is one such case. For this use-case, catalog brains have a getObject method which can be used to "wake up" the object from the database and bring it into memory. This method is relatively expensive, so it should not be used unless you really need the full object so that you can use it as a context or for some other reason.