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 )

Reply via email to