Commits

Ned Batchelder committed 5e127e5

Even better execution of main files.

Comments (0)

Files changed (4)

coverage/cmdline.py

             if not args:
                 help_fn("Nothing to do.")
                 return ERR
-            # Create the runtime environment the script on the cmdline expects.
-            sys.argv = args
+            
+            # Run the script.
             self.coverage.start()
-            run_python_file(sys.argv[0])
+            run_python_file(args[0], args)
             self.coverage.stop()
         
         if settings.get('combine'):

coverage/execfile.py

 """Execute files of Python code."""
 
-import os, sys
+import imp, os, sys
 
-def run_python_file(filename):
-    mod_globals = {
-        '__name__': '__main__',
-        '__file__': filename,
-    }
+def run_python_file(filename, args):
+    """Run a python source file as if it were the main program on the python
+    command line.
+    
+    `filename` is the path to the file to execute, must be a .py file.
+    `args` is the argument array to present as sys.argv.
+    
+    """
+    # Most code that does this does it in a way that leaves __main__ or __file__
+    # with the wrong values.  Importing the code as __main__ gets all of this
+    # right automatically.
+    #
+    # One difference from python.exe: if I run foo.py from the command line, it
+    # always uses foo.py.  With this code, it might find foo.pyc instead.
+    
+    sys.argv = args
     sys.path[0] = os.path.dirname(filename)
-    execfile(filename, mod_globals)
+
+    try:
+        src = open(filename)
+        imp.load_module('__main__', src, filename, (".py", "r", imp.PY_SOURCE))
+    finally:
+        src.close()

test/test_execfile.py

 
 here = os.path.dirname(__file__)
 
+class Tee(object):
+    def __init__(self, *files):
+        self.files = files
+        
+    def write(self, data):
+        for f in self.files:
+            f.write(data)
+
 class RunTests(unittest.TestCase):
     def setUp(self):
         self.oldstdout = sys.stdout
-        self.stdout = sys.stdout = cStringIO.StringIO()
+        self.stdout = cStringIO.StringIO()
+        sys.stdout = Tee(sys.stdout, self.stdout)
         
     def tearDown(self):
         self.stdout = self.oldstdout
 
     def test_run_python_file(self):
         tryfile = os.path.join(here, "try_execfile.py")
-        run_python_file(tryfile)
+        run_python_file(tryfile, [tryfile, "arg1", "arg2"])
         mod_globs = eval(self.stdout.getvalue())
+
+        # The file should think it is __main__
         self.assertEqual(mod_globs['__name__'], "__main__")
-        self.assertEqual(os.path.basename(mod_globs['__file__']), "try_execfile.py")
+
+        # It should seem to come from a file named try_execfile
+        dunder_file = os.path.splitext(os.path.basename(mod_globs['__file__']))[0]
+        self.assertEqual(dunder_file, "try_execfile")
+
+        # It should have its correct module data.
+        self.assertEqual(mod_globs['__doc__'], "Test file for run_python_file.")
+        self.assertEqual(mod_globs['DATA'], "xyzzy")
+        self.assertEqual(mod_globs['FN_VAL'], "my_fn('fooey')")
+        
+        # It must be self-importable as __main__.
+        self.assertEqual(mod_globs['__main__.DATA'], "xyzzy")
+        
+        # Argv should have the proper values.
+        self.assertEqual(mod_globs['argv'], [tryfile, "arg1", "arg2"])

test/try_execfile.py

+"""Test file for run_python_file."""
+
+import pprint, sys
+
+DATA = "xyzzy"
+
+import __main__
+
+def my_function(a):
+    return "my_fn(%r)" % a
+
+FN_VAL = my_function("fooey")
+
 globals_to_check = {
     '__name__': __name__,
     '__file__': __file__,
+    '__doc__': __doc__,
+    'DATA': DATA,
+    'FN_VAL': FN_VAL,
+    '__main__.DATA': getattr(__main__, "DATA", "nothing"),
+    'argv': sys.argv,
+    'path0': sys.path[0],
 }
-print repr(globals_to_check)
+
+pprint.pprint(globals_to_check)