[Zope3-checkins] SVN: Zope3/trunk/src/zope/importtool/ Add a cyclic import detector.

Fred L. Drake, Jr. fred at zope.com
Fri May 28 23:54:50 EDT 2004


Log message for revision 25118:
Add a cyclic import detector.

Run "utilities/importtool --cyclic-imports somescript [scriptargs]" to run
somescript with the cycle detector enabled.  As soon as a cyclic import is
detected (which will often be well after the import is complete!) an exception
will be raised which reports the problem and the "import stack" that caused
import cycle to be detected.



-=-
Modified: Zope3/trunk/src/zope/importtool/app.py
===================================================================
--- Zope3/trunk/src/zope/importtool/app.py	2004-05-29 03:07:47 UTC (rev 25117)
+++ Zope3/trunk/src/zope/importtool/app.py	2004-05-29 03:54:49 UTC (rev 25118)
@@ -33,8 +33,7 @@
 
 
 def run(options):
-    reporter = FirstImportReporter()
-    h = hook.ReportingHook(reporter)
+    h = hook.ReportingHook(options.reporter)
     h.install()
     globals = {"__name__": "__main__",
                "__file__": options.argv[0]}
@@ -45,19 +44,39 @@
     finally:
         h.uninstall()
         sys.argv[:] = old_argv
-    reporter.display_report()
+    options.reporter.display_report()
 
 
 class Options:
 
+    known_options = ["--first-import", "--cyclic-imports"]
+
     def __init__(self, argv):
         self.program = os.path.basename(argv[0])
+        self.argv = argv[1:]
+        self.reporter = None
+        while self.argv and self.argv[0] in self.known_options:
+            opt = self.argv.pop(0)
+            m = getattr(self, opt[2:].replace("-", "_"))
+            m()
         if len(argv) < 2:
             raise SystemExit(2)
-        self.argv = argv[1:]
         self.script = self.argv[0]
+        if self.reporter is None:
+            self.reporter = FirstImportReporter()
 
+    def first_import(self):
+        if self.reporter is not None:
+            raise SystemExit(2)
+        self.reporter = FirstImportReporter()
 
+    def cyclic_imports(self):
+        if self.reporter is not None:
+            raise SystemExit(2)
+        from zope.importtool import cycle
+        self.reporter = cycle.CycleReporter()
+
+
 class FirstImportReporter(reporter.Reporter):
 
     def __init__(self):

Added: Zope3/trunk/src/zope/importtool/cycle.py
===================================================================
--- Zope3/trunk/src/zope/importtool/cycle.py	2004-05-29 03:07:47 UTC (rev 25117)
+++ Zope3/trunk/src/zope/importtool/cycle.py	2004-05-29 03:54:49 UTC (rev 25118)
@@ -0,0 +1,80 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Import cycle detector.
+
+This 
+
+$Id$
+"""
+from zope.importtool import reporter
+
+
+START = "<start>"
+FINISH = "<finish>"
+ERROR = "<error>"
+
+
+class CyclicImportError(ImportError):
+
+    def __init__(self, name, stack):
+        ImportError.__init__(self, name)
+        self.name = name
+        self.stack = stack[:]
+
+    def __str__(self):
+        return ("disallowed cyclic import of %r; active imports are %r"
+                % (self.name, self.stack[:-1]))
+
+
+class CycleReporter(reporter.Reporter):
+
+    def __init__(self):
+        self.log = []
+        self.stack = []
+
+    def request(self, importer, name, fromlist):
+        entry = [importer, None, START]
+        self.log.append(entry)
+        self.stack.append(entry)
+
+    def found(self, importer, imported, fromlist):
+        entry = self.stack.pop()
+        entry[1] = imported
+        self.log.append([importer, imported, FINISH])
+        if not self.stack:
+            self.check()
+            self.log = []
+
+    def check(self):
+        stack = []
+        for entry in self.log:
+            importer, imported, event = entry
+            if event == START:
+                # If imported is None, an exception prevented the
+                # import; we don't care about that case, but None can
+                # be stack more than once, so we need to be careful.
+                if imported is not None and imported in stack:
+                    # report cycle
+                    self.report_cycle(stack + [imported])
+                stack.append(imported)
+            else:
+                assert event in FINISH, ERROR
+                stack.pop()
+        assert not stack
+
+    def report_cycle(self, stack):
+        raise CyclicImportError(stack[-1], stack)
+
+    def display_report(self):
+        pass


Property changes on: Zope3/trunk/src/zope/importtool/cycle.py
___________________________________________________________________
Name: svn:mime-type
   + text/x-python
Name: svn:eol-style
   + native




More information about the Zope3-Checkins mailing list