I'm working on zope.app.module.ZopePersistentModuleImporter. As noted in zodbcode.module.PersistentModuleImporter.__doc__, if the persistent module registry which is consulted on import queries a persistent site manager then the site manager must be activated before being queried or a circular import problem occurs when the ZODB attempts to import the globals necessary to activate the site manager.
The site manager will, however, eventually be deactivated, by a transaction.abort() for example. So I'm looking for a way that the importer can know whether or now it's being called as a part of activating the site manager. I've written (and attached diffs for) a very ugly function that inspects the frame stack to detect if the site manager is being activated just to demonstrate what I'm looking for. Is there a way to ask an object if it's being activated? Or what might be a better approach to solving this problem? Ross
Index: __init__.py =================================================================== --- __init__.py (revision 78553) +++ __init__.py (working copy) @@ -17,6 +17,7 @@ """ __docformat__ = 'restructuredtext' import sys +import ZODB.Connection import zodbcode.interfaces import zodbcode.module @@ -24,6 +25,16 @@ import zope.component from zope.app.module.interfaces import IModuleManager +def isActivating(obj): + if hasattr(obj, '_p_jar'): + frame = sys._getframe(1) + while frame is not None: + if (frame.f_code is + ZODB.Connection.Connection.setstate.func_code): + if frame.f_locals['obj'] is obj: + return True + frame = frame.f_back + return False class ZopeModuleRegistry(object): """Zope-specific registry of persistent modules. @@ -44,6 +55,9 @@ for name, modulemgr in zope.component.getUtilitiesFor(IModuleManager)] + def isActivating(self): + return isActivating(zope.component.getSiteManager().utilities) + # Make Zope Module Registry a singelton ZopeModuleRegistry = ZopeModuleRegistry() @@ -66,10 +80,11 @@ self._registry = registry def __import__(self, name, globals={}, locals={}, fromlist=[]): - mod = self._import(self._registry, name, self._get_parent(globals), - fromlist) - if mod is not None: - return mod + if not self._registry.isActivating(): + mod = self._import(self._registry, name, self._get_parent(globals), + fromlist) + if mod is not None: + return mod return self._saved_import(name, globals, locals, fromlist) Index: persistence.txt =================================================================== --- persistence.txt (revision 0) +++ persistence.txt (revision 0) @@ -0,0 +1,80 @@ +;-*-Doctest-*- +=========== +Persistencs +=========== + +If the site manager is persistent and is a ghost when an import is +executed, then it will need to be activated before the import can be +completed. Activating the site manager requires importing the +necessary globals so the importer needs to fallback to the builtin +__import__ while the site manager is being activated. + +Setup a persistent module with a name in it. + + >>> import zope.app.module.manager + >>> foo_manager = zope.app.module.manager.ModuleManager() + >>> source = """\n + ... foo = 'foo'\n + ... """ + >>> foo_manager.source = source + + >>> bar_manager = zope.app.module.manager.ModuleManager() + >>> source = """\n + ... bar = 'bar'\n + ... """ + >>> bar_manager.source = source + +Register the module with a persistent site manager that has been added +to a ZODB. + + >>> import zope.app.testing + >>> from zope.app.module import interfaces + >>> from ZODB.DB import DB + >>> from ZODB.DemoStorage import DemoStorage + >>> root = zope.app.testing.setup.buildSampleFolderTree() + >>> db = DB(DemoStorage()) + >>> connection = db.open() + >>> connection.root()['root'] = root + >>> root_sm = zope.app.testing.setup.createSiteManager( + ... root, setsite=True) + >>> foo_manager = zope.app.testing.setup.addUtility( + ... root_sm, u'foo', interfaces.IModuleManager, foo_manager) + >>> bar_manager = zope.app.testing.setup.addUtility( + ... root_sm, u'bar', interfaces.IModuleManager, bar_manager) + +Install the importer. + + >>> import zope.app.module + >>> zope.app.module.importer.install() + +Now we can import the module. + + >>> import foo + >>> foo.foo + 'foo' + +Commit everything. + + >>> import transaction + >>> transaction.commit() + +Deactivate the site manager and the utilities registry to turn them +into ghosts by aborting a transaction. + + >>> from zope import interface, component + >>> sm = component.getSiteManager() + >>> ignored = zope.app.testing.setup.addUtility( + ... root_sm, u'baz', interface.Interface, lambda: 'baz') + >>> transaction.abort() + +We can still import persistent modules. + + >>> import bar + >>> bar.bar + 'bar' + +Cleanup. + + >>> transaction.abort() + >>> connection.close() + >>> db.close() Index: tests.py =================================================================== --- tests.py (revision 78553) +++ tests.py (working copy) @@ -35,6 +35,7 @@ def test_suite(): return unittest.TestSuite(( doctest.DocFileSuite('README.txt', 'interfaces.txt', + 'persistence.txt', setUp=setUp, tearDown=tearDown), ))
_______________________________________________ Zope-Dev maillist - Zope-Dev@zope.org http://mail.zope.org/mailman/listinfo/zope-dev ** No cross posts or HTML encoding! ** (Related lists - http://mail.zope.org/mailman/listinfo/zope-announce http://mail.zope.org/mailman/listinfo/zope )