eric6/DebugClients/Python/coverage/execfile.py

changeset 6942
2602857055c5
parent 6649
f1b3a73831c9
child 7427
362cd1b6f81a
equal deleted inserted replaced
6941:f99d60d6b59b 6942:2602857055c5
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
4 """Execute files of Python code."""
5
6 import marshal
7 import os
8 import struct
9 import sys
10 import types
11
12 from coverage.backward import BUILTINS
13 from coverage.backward import PYC_MAGIC_NUMBER, imp, importlib_util_find_spec
14 from coverage.misc import CoverageException, ExceptionDuringRun, NoCode, NoSource, isolate_module
15 from coverage.phystokens import compile_unicode
16 from coverage.python import get_python_source
17
18 os = isolate_module(os)
19
20
21 class DummyLoader(object):
22 """A shim for the pep302 __loader__, emulating pkgutil.ImpLoader.
23
24 Currently only implements the .fullname attribute
25 """
26 def __init__(self, fullname, *_args):
27 self.fullname = fullname
28
29
30 if importlib_util_find_spec:
31 def find_module(modulename):
32 """Find the module named `modulename`.
33
34 Returns the file path of the module, and the name of the enclosing
35 package.
36 """
37 try:
38 spec = importlib_util_find_spec(modulename)
39 except ImportError as err:
40 raise NoSource(str(err))
41 if not spec:
42 raise NoSource("No module named %r" % (modulename,))
43 pathname = spec.origin
44 packagename = spec.name
45 if pathname.endswith("__init__.py") and not modulename.endswith("__init__"):
46 mod_main = modulename + ".__main__"
47 spec = importlib_util_find_spec(mod_main)
48 if not spec:
49 raise NoSource(
50 "No module named %s; "
51 "%r is a package and cannot be directly executed"
52 % (mod_main, modulename)
53 )
54 pathname = spec.origin
55 packagename = spec.name
56 packagename = packagename.rpartition(".")[0]
57 return pathname, packagename
58 else:
59 def find_module(modulename):
60 """Find the module named `modulename`.
61
62 Returns the file path of the module, and the name of the enclosing
63 package.
64 """
65 openfile = None
66 glo, loc = globals(), locals()
67 try:
68 # Search for the module - inside its parent package, if any - using
69 # standard import mechanics.
70 if '.' in modulename:
71 packagename, name = modulename.rsplit('.', 1)
72 package = __import__(packagename, glo, loc, ['__path__'])
73 searchpath = package.__path__
74 else:
75 packagename, name = None, modulename
76 searchpath = None # "top-level search" in imp.find_module()
77 openfile, pathname, _ = imp.find_module(name, searchpath)
78
79 # Complain if this is a magic non-file module.
80 if openfile is None and pathname is None:
81 raise NoSource(
82 "module does not live in a file: %r" % modulename
83 )
84
85 # If `modulename` is actually a package, not a mere module, then we
86 # pretend to be Python 2.7 and try running its __main__.py script.
87 if openfile is None:
88 packagename = modulename
89 name = '__main__'
90 package = __import__(packagename, glo, loc, ['__path__'])
91 searchpath = package.__path__
92 openfile, pathname, _ = imp.find_module(name, searchpath)
93 except ImportError as err:
94 raise NoSource(str(err))
95 finally:
96 if openfile:
97 openfile.close()
98
99 return pathname, packagename
100
101
102 def run_python_module(modulename, args):
103 """Run a Python module, as though with ``python -m name args...``.
104
105 `modulename` is the name of the module, possibly a dot-separated name.
106 `args` is the argument array to present as sys.argv, including the first
107 element naming the module being executed.
108
109 """
110 pathname, packagename = find_module(modulename)
111
112 pathname = os.path.abspath(pathname)
113 args[0] = pathname
114 # Python 3.7.0b3 changed the behavior of the sys.path[0] entry for -m. It
115 # used to be an empty string (meaning the current directory). It changed
116 # to be the actual path to the current directory, so that os.chdir wouldn't
117 # affect the outcome.
118 if sys.version_info >= (3, 7, 0, 'beta', 3):
119 path0 = os.getcwd()
120 else:
121 path0 = ""
122 run_python_file(pathname, args, package=packagename, modulename=modulename, path0=path0)
123
124
125 def run_python_file(filename, args, package=None, modulename=None, path0=None):
126 """Run a Python file as if it were the main program on the command line.
127
128 `filename` is the path to the file to execute, it need not be a .py file.
129 `args` is the argument array to present as sys.argv, including the first
130 element naming the file being executed. `package` is the name of the
131 enclosing package, if any.
132
133 `modulename` is the name of the module the file was run as.
134
135 `path0` is the value to put into sys.path[0]. If it's None, then this
136 function will decide on a value.
137
138 """
139 if modulename is None and sys.version_info >= (3, 3):
140 modulename = '__main__'
141
142 # Create a module to serve as __main__
143 old_main_mod = sys.modules['__main__']
144 main_mod = types.ModuleType('__main__')
145 sys.modules['__main__'] = main_mod
146 main_mod.__file__ = filename
147 if package:
148 main_mod.__package__ = package
149 if modulename:
150 main_mod.__loader__ = DummyLoader(modulename)
151
152 main_mod.__builtins__ = BUILTINS
153
154 # Set sys.argv properly.
155 old_argv = sys.argv
156 sys.argv = args
157
158 if os.path.isdir(filename):
159 # Running a directory means running the __main__.py file in that
160 # directory.
161 my_path0 = filename
162
163 for ext in [".py", ".pyc", ".pyo"]:
164 try_filename = os.path.join(filename, "__main__" + ext)
165 if os.path.exists(try_filename):
166 filename = try_filename
167 break
168 else:
169 raise NoSource("Can't find '__main__' module in '%s'" % filename)
170 else:
171 my_path0 = os.path.abspath(os.path.dirname(filename))
172
173 # Set sys.path correctly.
174 old_path0 = sys.path[0]
175 sys.path[0] = path0 if path0 is not None else my_path0
176
177 try:
178 try:
179 # Make a code object somehow.
180 if filename.endswith((".pyc", ".pyo")):
181 code = make_code_from_pyc(filename)
182 else:
183 code = make_code_from_py(filename)
184 except CoverageException:
185 raise
186 except Exception as exc:
187 msg = "Couldn't run {filename!r} as Python code: {exc.__class__.__name__}: {exc}"
188 raise CoverageException(msg.format(filename=filename, exc=exc))
189
190 # Execute the code object.
191 try:
192 exec(code, main_mod.__dict__)
193 except SystemExit:
194 # The user called sys.exit(). Just pass it along to the upper
195 # layers, where it will be handled.
196 raise
197 except Exception:
198 # Something went wrong while executing the user code.
199 # Get the exc_info, and pack them into an exception that we can
200 # throw up to the outer loop. We peel one layer off the traceback
201 # so that the coverage.py code doesn't appear in the final printed
202 # traceback.
203 typ, err, tb = sys.exc_info()
204
205 # PyPy3 weirdness. If I don't access __context__, then somehow it
206 # is non-None when the exception is reported at the upper layer,
207 # and a nested exception is shown to the user. This getattr fixes
208 # it somehow? https://bitbucket.org/pypy/pypy/issue/1903
209 getattr(err, '__context__', None)
210
211 # Call the excepthook.
212 try:
213 if hasattr(err, "__traceback__"):
214 err.__traceback__ = err.__traceback__.tb_next
215 sys.excepthook(typ, err, tb.tb_next)
216 except SystemExit:
217 raise
218 except Exception:
219 # Getting the output right in the case of excepthook
220 # shenanigans is kind of involved.
221 sys.stderr.write("Error in sys.excepthook:\n")
222 typ2, err2, tb2 = sys.exc_info()
223 err2.__suppress_context__ = True
224 if hasattr(err2, "__traceback__"):
225 err2.__traceback__ = err2.__traceback__.tb_next
226 sys.__excepthook__(typ2, err2, tb2.tb_next)
227 sys.stderr.write("\nOriginal exception was:\n")
228 raise ExceptionDuringRun(typ, err, tb.tb_next)
229 else:
230 sys.exit(1)
231
232 finally:
233 # Restore the old __main__, argv, and path.
234 sys.modules['__main__'] = old_main_mod
235 sys.argv = old_argv
236 sys.path[0] = old_path0
237
238
239 def make_code_from_py(filename):
240 """Get source from `filename` and make a code object of it."""
241 # Open the source file.
242 try:
243 source = get_python_source(filename)
244 except (IOError, NoSource):
245 raise NoSource("No file to run: '%s'" % filename)
246
247 code = compile_unicode(source, filename, "exec")
248 return code
249
250
251 def make_code_from_pyc(filename):
252 """Get a code object from a .pyc file."""
253 try:
254 fpyc = open(filename, "rb")
255 except IOError:
256 raise NoCode("No file to run: '%s'" % filename)
257
258 with fpyc:
259 # First four bytes are a version-specific magic number. It has to
260 # match or we won't run the file.
261 magic = fpyc.read(4)
262 if magic != PYC_MAGIC_NUMBER:
263 raise NoCode("Bad magic number in .pyc file")
264
265 date_based = True
266 if sys.version_info >= (3, 7, 0, 'alpha', 4):
267 flags = struct.unpack('<L', fpyc.read(4))[0]
268 hash_based = flags & 0x01
269 if hash_based:
270 fpyc.read(8) # Skip the hash.
271 date_based = False
272 if date_based:
273 # Skip the junk in the header that we don't need.
274 fpyc.read(4) # Skip the moddate.
275 if sys.version_info >= (3, 3):
276 # 3.3 added another long to the header (size), skip it.
277 fpyc.read(4)
278
279 # The rest of the file is the code object we want.
280 code = marshal.load(fpyc)
281
282 return code

eric ide

mercurial