summaryrefslogtreecommitdiff
path: root/coverage/execfile.py
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2013-09-28 11:07:41 -0400
committerNed Batchelder <ned@nedbatchelder.com>2013-09-28 11:07:41 -0400
commit1d179a1077819aec18b960a3fa24ea3490c884a5 (patch)
tree82cbfa11032e782578faa1eeaaca3dd6ae9c9b80 /coverage/execfile.py
parenta9739749d841818a31ae956fe02e0b3f03a82a31 (diff)
downloadpython-coveragepy-1d179a1077819aec18b960a3fa24ea3490c884a5.tar.gz
Now we can run .pyc files directly. Closes #264.
Diffstat (limited to 'coverage/execfile.py')
-rw-r--r--coverage/execfile.py72
1 files changed, 53 insertions, 19 deletions
diff --git a/coverage/execfile.py b/coverage/execfile.py
index fbb49b2..2e89290 100644
--- a/coverage/execfile.py
+++ b/coverage/execfile.py
@@ -1,9 +1,9 @@
"""Execute files of Python code."""
-import imp, os, sys
+import imp, marshal, os, sys
from coverage.backward import exec_code_object, open_source
-from coverage.misc import NoSource, ExceptionDuringRun
+from coverage.misc import ExceptionDuringRun, NoCode, NoSource
try:
@@ -93,24 +93,13 @@ def run_python_file(filename, args, package=None):
sys.argv = args
try:
- # Open the source file.
- try:
- source_file = open_source(filename)
- except IOError:
- raise NoSource("No file to run: %r" % filename)
-
- try:
- source = source_file.read()
- finally:
- source_file.close()
-
- # 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 not source or source[-1] != '\n':
- source += '\n'
- code = compile(source, filename, "exec")
+ # Make a code object somehow.
+ if filename.endswith(".pyc") or filename.endswith(".pyo"):
+ code = make_code_from_pyc(filename)
+ else:
+ code = make_code_from_py(filename)
- # Execute the source file.
+ # Execute the code object.
try:
exec_code_object(code, main_mod.__dict__)
except SystemExit:
@@ -131,3 +120,48 @@ def run_python_file(filename, args, package=None):
# Restore the old argv and path
sys.argv = old_argv
+
+def make_code_from_py(filename):
+ """Get source from `filename` and make a code object of it."""
+ # Open the source file.
+ try:
+ source_file = open_source(filename)
+ except IOError:
+ raise NoSource("No file to run: %r" % filename)
+
+ try:
+ source = source_file.read()
+ finally:
+ source_file.close()
+
+ # 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 not source or source[-1] != '\n':
+ source += '\n'
+ code = compile(source, filename, "exec")
+
+ return code
+
+
+def make_code_from_pyc(filename):
+ """Get a code object from a .pyc file."""
+ try:
+ fpyc = open(filename, "rb")
+ except IOError:
+ raise NoCode("No file to run: %r" % filename)
+
+ # First four bytes are a version-specific magic number. It has to match
+ # or we won't run the file.
+ magic = fpyc.read(4)
+ if magic != imp.get_magic():
+ raise NoCode("Bad magic number in .pyc file")
+
+ # Skip the junk in the header that we don't need.
+ fpyc.read(4) # Skip the moddate.
+ if sys.version_info >= (3, 3):
+ # 3.3 added another long to the header (size), skip it.
+ fpyc.read(4)
+
+ # The rest of the file is the code object we want.
+ code = marshal.load(fpyc)
+ return code