Ned Batchelder avatar Ned Batchelder committed c58e0c8

Python source files that don't end with a newline can now be executed, fixing #47.

Comments (0)

Files changed (4)

   an added dot rather than simply appended, so that .coveragerc files will not
   be confused for data files.
 
+- Python source files that don't end with a newline can now be executed, fixing
+  `issue 47`.
+
 - Added an AUTHORS.txt file.
 
 .. _issue 39: http://bitbucket.org/ned/coveragepy/issue/39
 .. _issue 40: http://bitbucket.org/ned/coveragepy/issue/40
+.. _issue 47: http://bitbucket.org/ned/coveragepy/issue/47
 
 
 Version 3.2, 5 December 2009

coverage/backward.py

 
 # Exec is a statement in Py2, a function in Py3
 if sys.version_info >= (3, 0):
-    def exec_function(source, filename, global_map):
+    def exec_code_object(code, global_map):
         """A wrapper around exec()."""
-        exec(compile(source, filename, "exec"), global_map)
+        exec(code, global_map)
 else:
     # OK, this is pretty gross.  In Py2, exec was a statement, but that will
     # be a syntax error if we try to put it in a Py3 file, even if it is never
     # executed.  So hide it inside an evaluated string literal instead.
-    eval(compile("""\
-def exec_function(source, filename, global_map):
-    exec compile(source, filename, "exec") in global_map
-""",
-    "<exec_function>", "exec"
-    ))
+    eval(
+        compile(
+            "def exec_code_object(code, global_map):\n"
+            "    exec code in global_map\n",
+            "<exec_function>", "exec"
+            )
+        )
 
 # ConfigParser was renamed to the more-standard configparser
 try:

coverage/execfile.py

 
 import imp, os, sys
 
-from coverage.backward import exec_function
+from coverage.backward import exec_code_object
 from coverage.misc import NoSource, ExceptionDuringRun
 
 
         except IOError:
             raise NoSource("No file to run: %r" % filename)
 
+        # We have the source.  `compile` still needs the last line to be clean,
+        # so make sure it is, then compile a code object from it.
+        if source[-1] != '\n':
+            source += '\n'
+        code = compile(source, filename, "exec")
+
         # Execute the source file.
         try:
-            exec_function(source, filename, main_mod.__dict__)
+            exec_code_object(code, main_mod.__dict__)
         except:
             # Something went wrong while executing the user code.
             # Get the exc_info, and pack them into an exception that we can

test/test_execfile.py

             run_python_file('nl.py', ['nl.py'])
         self.assertEqual(self.stdout(), "Hello, world!\n"*3)
 
+    def test_missing_final_newline(self):
+        # Make sure we can deal with a Python file with no final newline.
+        self.make_file("abrupt.py", """\
+            if 1:
+                a = 1
+                print("a is %r" % a)
+                #""")
+        abrupt = open("abrupt.py").read()
+        self.assertEqual(abrupt[-1], '#')
+        run_python_file("abrupt.py", ["abrupt.py"])
+        self.assertEqual(self.stdout(), "a is 1\n")
+
     def test_no_such_file(self):
         self.assertRaises(NoSource, run_python_file, "xyzzy.py", [])
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.