[Zope-Checkins] CVS: Zope/lib/python/RestrictedPython/tests - testRestrictions.py:1.13.6.1

Shane Hathaway shane@cvs.zope.org
Thu, 8 Aug 2002 14:49:26 -0400


Update of /cvs-repository/Zope/lib/python/RestrictedPython/tests
In directory cvs.zope.org:/tmp/cvs-serv20389/tests

Modified Files:
      Tag: shane-leaks-repair-branch
	testRestrictions.py 
Log Message:
With much help from Cyclops, removed all the cyclic references in the compiler
package that can be discovered by the unit tests.  Used weak references in most
places, _break_cr() methods in other places, and even a "tuple lookalike" in
one place.

There may be other cycles, but they aren't likely to be so frequent.

Note that this corrects only the Python 2.1 version of the compiler.
Python 2.2's compiler will have to be fixed independently.


=== Zope/lib/python/RestrictedPython/tests/testRestrictions.py 1.13 => 1.13.6.1 ===
--- Zope/lib/python/RestrictedPython/tests/testRestrictions.py:1.13	Wed Jun 12 16:39:19 2002
+++ Zope/lib/python/RestrictedPython/tests/testRestrictions.py	Thu Aug  8 14:49:25 2002
@@ -1,6 +1,7 @@
 
 from string import rfind
 import sys, os
+import gc
 
 import unittest
 from RestrictedPython import compile_restricted, PrintCollector
@@ -52,14 +53,22 @@
     # Sanity check
     compile(source, fn, 'exec')
     # Now compile it for real
-    code = compile_restricted(source, fn, 'exec')
+    if 0:
+        import cyclops
+        z = cyclops.CycleFinder()
+        c = z.run(compile_restricted, (source, fn, 'exec'))
+        z.find_cycles()
+        z.show_stats()
+        z.show_cycles()
+    else:
+        c = compile_restricted(source, fn, 'exec')
     rmodule = {'__builtins__':{'__import__':__import__, 'None':None}}
     builtins = getattr(__builtins__, '__dict__', __builtins__)
     for name in ('map', 'reduce', 'int', 'pow', 'range', 'filter',
                  'len', 'chr', 'ord',
                  ):
         rmodule[name] = builtins[name]
-    exec code in rmodule
+    exec c in rmodule
 
 create_rmodule()
 
@@ -156,6 +165,45 @@
 ##    attribute_of_anything = 98.6
 
 class RestrictionTests(unittest.TestCase):
+
+    def setUp(self):
+        self.gc_was_enabled = gc.isenabled()
+        gc.disable()
+        gc.collect()
+        self.refcount_baseline = self.getCompilerRefcounts()
+
+    def tearDown(self):
+        if self.gc_was_enabled:
+            gc.enable()
+
+    def getCompilerRefcounts(self):
+        from types import ClassType
+        res = {}
+        for key, module in sys.modules.items():
+            if module is not None and (
+                key.startswith('compiler.') or
+                key.startswith('RestrictedPython.')):
+                dict = module.__dict__
+                for name, v in dict.items():
+                    if isinstance(v, ClassType):
+                        class_name = '%s.%s' % (v.__module__, v.__name__)
+                        res[class_name] = sys.getrefcount(v)
+        return res
+
+    def assertNoLeftoverReferences(self):
+        counts = self.getCompilerRefcounts()
+        res = {}
+        for key, value in counts.items():
+            diff = value - self.refcount_baseline.get(key, 0)
+            if diff != 0:
+                res[key] = diff
+        if res:
+            for key in res.keys():
+                if key.find('.ast.') > 0:
+                    del res[key]
+            raise AssertionError, (
+                'The compiler did not clean up cyclic references.\n%s' % res)
+
     def execFunc(self, name, *args, **kw):
         func = rmodule[name]
         func.func_globals.update({'_getattr_': guarded_getattr,
@@ -164,6 +212,14 @@
                                   '_print_': PrintCollector})
         return func(*args, **kw)
 
+    def checkSimpleModuleLeaks(self):
+        compile_restricted('pass', '<string>', 'exec')
+        self.assertNoLeftoverReferences()
+
+    def checkComplexModuleLeaks(self):
+        create_rmodule()
+        self.assertNoLeftoverReferences()
+
     def checkPrint(self):
         for i in range(2):
             res = self.execFunc('print%s' % i)
@@ -236,6 +292,7 @@
                     pass
                 else:
                     raise AssertionError, '%s should not have compiled' % k
+        self.assertNoLeftoverReferences()
 
 ##    def checkStrangeAttribute(self):
 ##        res = self.execFunc('strange_attribute')
@@ -263,6 +320,8 @@
         v = [12, 34]
         res = expr(m=v)
         assert res == expect
+        expr = None
+        self.assertNoLeftoverReferences()
 
     def checkStackSize(self):
         for k, rfunc in rmodule.items():
@@ -277,20 +336,5 @@
 def test_suite():
     return unittest.makeSuite(RestrictionTests, 'check')
 
-def main():
-    alltests=test_suite()
-    runner = unittest.TextTestRunner()
-    runner.run(alltests)
-
-def debug():
-   test_suite().debug()
-
-def pdebug():
-    import pdb
-    pdb.run('debug()')
-
 if __name__=='__main__':
-    if len(sys.argv) > 1:
-        globals()[sys.argv[1]]()
-    else:
-        main()
+    unittest.main(defaultTest='test_suite')