Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ComponentLookupError when querying the Registry at startup time

I'm building an app that queries some data in an external relational database, using collective.lead (trunk). The user can modify the database connection settings in a custom Plone control panel tool (I followed the example in Aspeli's Professional Plone Development book). The database settings are queried in this way.

My product's base configure.zcml sets up an utility for the database:

<include package="plone.app.registry" />
<include package="collective.lead" />

<i18n:registerTranslations directory="locales" />

<utility
  provides="collective.lead.interfaces.IDatabase"
  factory=".dbsettings.CalculatorDatabase"
  name="test.calc.db"
  />

dbsettings.py has:

from zope.component import getUtility
from plone.registry.interfaces import IRegistry

class CalculatorDatabase(Database):

    @property
    def _url(self):
        registry = getUtility(IRegistry)
        settings = registry.forInterface(IDatabaseSettings)
        return URL(
            drivername=settings.drivername,
            username=settings.username,
            password=settings.password,
            host=settings.hostname,
            port=settings.port,
            database=settings.database,
        )

This raises a ComponentLookupError exception at runtime:

File "/home/zope/envs/test-web/src/test.calc/test/calc/dbsettings.py", line 38, in _url
    registry = getUtility(IRegistry)
File "/home/zope/envs/test-web/eggs/zope.component-3.7.1-py2.6.egg/zope/component/_api.py", line 171, in getUtility
    raise ComponentLookupError(interface, name)
zope.configuration.config.ConfigurationExecutionError: <class 'zope.component.interfaces.ComponentLookupError'>: (<InterfaceClass plone.registry.interfaces.IRegistry>, '')
in:
File "/home/zope/envs/test-web/src/test.calc/test/calc/configure.zcml", line 26.2-30.6
<utility
  provides="collective.lead.interfaces.IDatabase"
  factory=".dbsettings.CalculatorDatabase"
  name="test.calc.db"
  />

Why isn't the Registry found at runtime? What am I doing wrong?

Thanks.

like image 403
Rigel Di Scala Avatar asked Jun 14 '11 15:06

Rigel Di Scala


3 Answers

What Steve mentions is the root of the problem, so I will only add a workaround for the cases where you cannot do exception handling. In a similar scenario I needed to register a utility which depended on settings stored in the registry. The utility could not be registered without these settings, so I absolutely wanted things to happen in order.

The workaround was to not register the utility in zcml, but instead do it inside the following subscriber:

<subscriber
    for="Products.CMFPlone.interfaces.IPloneSiteRoot
         zope.app.publication.interfaces.IBeforeTraverseEvent"
    handler=".component.setupStuff"
    />

This would guarantee that setupStuff would have access to all the local utilities. Note also that setupStuff would also return quickly after querying if the utility already exists, as the subscriber will trigger on every request.

like image 95
ggozad Avatar answered Oct 20 '22 14:10

ggozad


collective.lead has been superseded by http://pypi.python.org/pypi/z3c.saconfig which allows you to define database connections in zcml. It can be used with http://pypi.python.org/pypi/collective.saconnect if you need a control panel to configure connection.

like image 6
Laurence Rowe Avatar answered Oct 20 '22 15:10

Laurence Rowe


The registry is a local component. Each Plone site (and there may be many in a database) has its own. So, it's context dependent.

Zope figures out that context in the course of traversal (the connecting of a URL to an object). That (pretty much) means that you can only look up the registry in the context of a request. So, you can't look up the registry in startup code.

This can lead to chicken-and-egg problems writing code. One solution is to embed the lookup in a try/except that handles the lookup exception gracefully if you don't yet have traversal context.

like image 5
SteveM Avatar answered Oct 20 '22 15:10

SteveM