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:
 .. _issue 40:
+.. _issue 47:
 Version 3.2, 5 December 2009


 # 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)
     # 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


 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.
-            exec_function(source, filename, main_mod.__dict__)
+            exec_code_object(code, main_mod.__dict__)
             # Something went wrong while executing the user code.
             # Get the exc_info, and pack them into an exception that we can


             run_python_file('', [''])
         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("", """\
+            if 1:
+                a = 1
+                print("a is %r" % a)
+                #""")
+        abrupt = open("").read()
+        self.assertEqual(abrupt[-1], '#')
+        run_python_file("", [""])
+        self.assertEqual(self.stdout(), "a is 1\n")
     def test_no_such_file(self):
         self.assertRaises(NoSource, run_python_file, "", [])
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
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.