execfile.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
  2. # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
  3. """Execute files of Python code."""
  4. import marshal
  5. import os
  6. import sys
  7. import types
  8. from coverage.backward import BUILTINS
  9. from coverage.backward import PYC_MAGIC_NUMBER, imp, importlib_util_find_spec
  10. from coverage.misc import ExceptionDuringRun, NoCode, NoSource, isolate_module
  11. from coverage.phystokens import compile_unicode
  12. from coverage.python import get_python_source
  13. os = isolate_module(os)
  14. class DummyLoader(object):
  15. """A shim for the pep302 __loader__, emulating pkgutil.ImpLoader.
  16. Currently only implements the .fullname attribute
  17. """
  18. def __init__(self, fullname, *_args):
  19. self.fullname = fullname
  20. if importlib_util_find_spec:
  21. def find_module(modulename):
  22. """Find the module named `modulename`.
  23. Returns the file path of the module, and the name of the enclosing
  24. package.
  25. """
  26. try:
  27. spec = importlib_util_find_spec(modulename)
  28. except ImportError as err:
  29. raise NoSource(str(err))
  30. if not spec:
  31. raise NoSource("No module named %r" % (modulename,))
  32. pathname = spec.origin
  33. packagename = spec.name
  34. if pathname.endswith("__init__.py") and not modulename.endswith("__init__"):
  35. mod_main = modulename + ".__main__"
  36. spec = importlib_util_find_spec(mod_main)
  37. if not spec:
  38. raise NoSource(
  39. "No module named %s; "
  40. "%r is a package and cannot be directly executed"
  41. % (mod_main, modulename)
  42. )
  43. pathname = spec.origin
  44. packagename = spec.name
  45. packagename = packagename.rpartition(".")[0]
  46. return pathname, packagename
  47. else:
  48. def find_module(modulename):
  49. """Find the module named `modulename`.
  50. Returns the file path of the module, and the name of the enclosing
  51. package.
  52. """
  53. openfile = None
  54. glo, loc = globals(), locals()
  55. try:
  56. # Search for the module - inside its parent package, if any - using
  57. # standard import mechanics.
  58. if '.' in modulename:
  59. packagename, name = modulename.rsplit('.', 1)
  60. package = __import__(packagename, glo, loc, ['__path__'])
  61. searchpath = package.__path__
  62. else:
  63. packagename, name = None, modulename
  64. searchpath = None # "top-level search" in imp.find_module()
  65. openfile, pathname, _ = imp.find_module(name, searchpath)
  66. # Complain if this is a magic non-file module.
  67. if openfile is None and pathname is None:
  68. raise NoSource(
  69. "module does not live in a file: %r" % modulename
  70. )
  71. # If `modulename` is actually a package, not a mere module, then we
  72. # pretend to be Python 2.7 and try running its __main__.py script.
  73. if openfile is None:
  74. packagename = modulename
  75. name = '__main__'
  76. package = __import__(packagename, glo, loc, ['__path__'])
  77. searchpath = package.__path__
  78. openfile, pathname, _ = imp.find_module(name, searchpath)
  79. except ImportError as err:
  80. raise NoSource(str(err))
  81. finally:
  82. if openfile:
  83. openfile.close()
  84. return pathname, packagename
  85. def run_python_module(modulename, args):
  86. """Run a Python module, as though with ``python -m name args...``.
  87. `modulename` is the name of the module, possibly a dot-separated name.
  88. `args` is the argument array to present as sys.argv, including the first
  89. element naming the module being executed.
  90. """
  91. pathname, packagename = find_module(modulename)
  92. pathname = os.path.abspath(pathname)
  93. args[0] = pathname
  94. run_python_file(pathname, args, package=packagename, modulename=modulename, path0="")
  95. def run_python_file(filename, args, package=None, modulename=None, path0=None):
  96. """Run a Python file as if it were the main program on the command line.
  97. `filename` is the path to the file to execute, it need not be a .py file.
  98. `args` is the argument array to present as sys.argv, including the first
  99. element naming the file being executed. `package` is the name of the
  100. enclosing package, if any.
  101. `modulename` is the name of the module the file was run as.
  102. `path0` is the value to put into sys.path[0]. If it's None, then this
  103. function will decide on a value.
  104. """
  105. if modulename is None and sys.version_info >= (3, 3):
  106. modulename = '__main__'
  107. # Create a module to serve as __main__
  108. old_main_mod = sys.modules['__main__']
  109. main_mod = types.ModuleType('__main__')
  110. sys.modules['__main__'] = main_mod
  111. main_mod.__file__ = filename
  112. if package:
  113. main_mod.__package__ = package
  114. if modulename:
  115. main_mod.__loader__ = DummyLoader(modulename)
  116. main_mod.__builtins__ = BUILTINS
  117. # Set sys.argv properly.
  118. old_argv = sys.argv
  119. sys.argv = args
  120. if os.path.isdir(filename):
  121. # Running a directory means running the __main__.py file in that
  122. # directory.
  123. my_path0 = filename
  124. for ext in [".py", ".pyc", ".pyo"]:
  125. try_filename = os.path.join(filename, "__main__" + ext)
  126. if os.path.exists(try_filename):
  127. filename = try_filename
  128. break
  129. else:
  130. raise NoSource("Can't find '__main__' module in '%s'" % filename)
  131. else:
  132. my_path0 = os.path.abspath(os.path.dirname(filename))
  133. # Set sys.path correctly.
  134. old_path0 = sys.path[0]
  135. sys.path[0] = path0 if path0 is not None else my_path0
  136. try:
  137. # Make a code object somehow.
  138. if filename.endswith((".pyc", ".pyo")):
  139. code = make_code_from_pyc(filename)
  140. else:
  141. code = make_code_from_py(filename)
  142. # Execute the code object.
  143. try:
  144. exec(code, main_mod.__dict__)
  145. except SystemExit:
  146. # The user called sys.exit(). Just pass it along to the upper
  147. # layers, where it will be handled.
  148. raise
  149. except:
  150. # Something went wrong while executing the user code.
  151. # Get the exc_info, and pack them into an exception that we can
  152. # throw up to the outer loop. We peel one layer off the traceback
  153. # so that the coverage.py code doesn't appear in the final printed
  154. # traceback.
  155. typ, err, tb = sys.exc_info()
  156. # PyPy3 weirdness. If I don't access __context__, then somehow it
  157. # is non-None when the exception is reported at the upper layer,
  158. # and a nested exception is shown to the user. This getattr fixes
  159. # it somehow? https://bitbucket.org/pypy/pypy/issue/1903
  160. getattr(err, '__context__', None)
  161. raise ExceptionDuringRun(typ, err, tb.tb_next)
  162. finally:
  163. # Restore the old __main__, argv, and path.
  164. sys.modules['__main__'] = old_main_mod
  165. sys.argv = old_argv
  166. sys.path[0] = old_path0
  167. def make_code_from_py(filename):
  168. """Get source from `filename` and make a code object of it."""
  169. # Open the source file.
  170. try:
  171. source = get_python_source(filename)
  172. except (IOError, NoSource):
  173. raise NoSource("No file to run: '%s'" % filename)
  174. code = compile_unicode(source, filename, "exec")
  175. return code
  176. def make_code_from_pyc(filename):
  177. """Get a code object from a .pyc file."""
  178. try:
  179. fpyc = open(filename, "rb")
  180. except IOError:
  181. raise NoCode("No file to run: '%s'" % filename)
  182. with fpyc:
  183. # First four bytes are a version-specific magic number. It has to
  184. # match or we won't run the file.
  185. magic = fpyc.read(4)
  186. if magic != PYC_MAGIC_NUMBER:
  187. raise NoCode("Bad magic number in .pyc file")
  188. # Skip the junk in the header that we don't need.
  189. fpyc.read(4) # Skip the moddate.
  190. if sys.version_info >= (3, 3):
  191. # 3.3 added another long to the header (size), skip it.
  192. fpyc.read(4)
  193. # The rest of the file is the code object we want.
  194. code = marshal.load(fpyc)
  195. return code