eric7/DebugClients/Python/coverage/execfile.py

branch
eric7
changeset 8312
800c432b34c8
parent 7427
362cd1b6f81a
child 8775
0802ae193343
equal deleted inserted replaced
8311:4e8b98454baa 8312:800c432b34c8
1 # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
2 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
3
4 """Execute files of Python code."""
5
6 import inspect
7 import marshal
8 import os
9 import struct
10 import sys
11 import types
12
13 from coverage import env
14 from coverage.backward import BUILTINS
15 from coverage.backward import PYC_MAGIC_NUMBER, imp, importlib_util_find_spec
16 from coverage.files import canonical_filename, python_reported_file
17 from coverage.misc import CoverageException, ExceptionDuringRun, NoCode, NoSource, isolate_module
18 from coverage.phystokens import compile_unicode
19 from coverage.python import get_python_source
20
21 os = isolate_module(os)
22
23
24 class DummyLoader(object):
25 """A shim for the pep302 __loader__, emulating pkgutil.ImpLoader.
26
27 Currently only implements the .fullname attribute
28 """
29 def __init__(self, fullname, *_args):
30 self.fullname = fullname
31
32
33 if importlib_util_find_spec:
34 def find_module(modulename):
35 """Find the module named `modulename`.
36
37 Returns the file path of the module, the name of the enclosing
38 package, and the spec.
39 """
40 try:
41 spec = importlib_util_find_spec(modulename)
42 except ImportError as err:
43 raise NoSource(str(err))
44 if not spec:
45 raise NoSource("No module named %r" % (modulename,))
46 pathname = spec.origin
47 packagename = spec.name
48 if spec.submodule_search_locations:
49 mod_main = modulename + ".__main__"
50 spec = importlib_util_find_spec(mod_main)
51 if not spec:
52 raise NoSource(
53 "No module named %s; "
54 "%r is a package and cannot be directly executed"
55 % (mod_main, modulename)
56 )
57 pathname = spec.origin
58 packagename = spec.name
59 packagename = packagename.rpartition(".")[0]
60 return pathname, packagename, spec
61 else:
62 def find_module(modulename):
63 """Find the module named `modulename`.
64
65 Returns the file path of the module, the name of the enclosing
66 package, and None (where a spec would have been).
67 """
68 openfile = None
69 glo, loc = globals(), locals()
70 try:
71 # Search for the module - inside its parent package, if any - using
72 # standard import mechanics.
73 if '.' in modulename:
74 packagename, name = modulename.rsplit('.', 1)
75 package = __import__(packagename, glo, loc, ['__path__'])
76 searchpath = package.__path__
77 else:
78 packagename, name = None, modulename
79 searchpath = None # "top-level search" in imp.find_module()
80 openfile, pathname, _ = imp.find_module(name, searchpath)
81
82 # Complain if this is a magic non-file module.
83 if openfile is None and pathname is None:
84 raise NoSource(
85 "module does not live in a file: %r" % modulename
86 )
87
88 # If `modulename` is actually a package, not a mere module, then we
89 # pretend to be Python 2.7 and try running its __main__.py script.
90 if openfile is None:
91 packagename = modulename
92 name = '__main__'
93 package = __import__(packagename, glo, loc, ['__path__'])
94 searchpath = package.__path__
95 openfile, pathname, _ = imp.find_module(name, searchpath)
96 except ImportError as err:
97 raise NoSource(str(err))
98 finally:
99 if openfile:
100 openfile.close()
101
102 return pathname, packagename, None
103
104
105 class PyRunner(object):
106 """Multi-stage execution of Python code.
107
108 This is meant to emulate real Python execution as closely as possible.
109
110 """
111 def __init__(self, args, as_module=False):
112 self.args = args
113 self.as_module = as_module
114
115 self.arg0 = args[0]
116 self.package = self.modulename = self.pathname = self.loader = self.spec = None
117
118 def prepare(self):
119 """Set sys.path properly.
120
121 This needs to happen before any importing, and without importing anything.
122 """
123 if self.as_module:
124 if env.PYBEHAVIOR.actual_syspath0_dash_m:
125 path0 = os.getcwd()
126 else:
127 path0 = ""
128 elif os.path.isdir(self.arg0):
129 # Running a directory means running the __main__.py file in that
130 # directory.
131 path0 = self.arg0
132 else:
133 path0 = os.path.abspath(os.path.dirname(self.arg0))
134
135 if os.path.isdir(sys.path[0]):
136 # sys.path fakery. If we are being run as a command, then sys.path[0]
137 # is the directory of the "coverage" script. If this is so, replace
138 # sys.path[0] with the directory of the file we're running, or the
139 # current directory when running modules. If it isn't so, then we
140 # don't know what's going on, and just leave it alone.
141 top_file = inspect.stack()[-1][0].f_code.co_filename
142 sys_path_0_abs = os.path.abspath(sys.path[0])
143 top_file_dir_abs = os.path.abspath(os.path.dirname(top_file))
144 sys_path_0_abs = canonical_filename(sys_path_0_abs)
145 top_file_dir_abs = canonical_filename(top_file_dir_abs)
146 if sys_path_0_abs != top_file_dir_abs:
147 path0 = None
148
149 else:
150 # sys.path[0] is a file. Is the next entry the directory containing
151 # that file?
152 if sys.path[1] == os.path.dirname(sys.path[0]):
153 # Can it be right to always remove that?
154 del sys.path[1]
155
156 if path0 is not None:
157 sys.path[0] = python_reported_file(path0)
158
159 def _prepare2(self):
160 """Do more preparation to run Python code.
161
162 Includes finding the module to run and adjusting sys.argv[0].
163 This method is allowed to import code.
164
165 """
166 if self.as_module:
167 self.modulename = self.arg0
168 pathname, self.package, self.spec = find_module(self.modulename)
169 if self.spec is not None:
170 self.modulename = self.spec.name
171 self.loader = DummyLoader(self.modulename)
172 self.pathname = os.path.abspath(pathname)
173 self.args[0] = self.arg0 = self.pathname
174 elif os.path.isdir(self.arg0):
175 # Running a directory means running the __main__.py file in that
176 # directory.
177 for ext in [".py", ".pyc", ".pyo"]:
178 try_filename = os.path.join(self.arg0, "__main__" + ext)
179 if os.path.exists(try_filename):
180 self.arg0 = try_filename
181 break
182 else:
183 raise NoSource("Can't find '__main__' module in '%s'" % self.arg0)
184
185 if env.PY2:
186 self.arg0 = os.path.abspath(self.arg0)
187
188 # Make a spec. I don't know if this is the right way to do it.
189 try:
190 import importlib.machinery
191 except ImportError:
192 pass
193 else:
194 try_filename = python_reported_file(try_filename)
195 self.spec = importlib.machinery.ModuleSpec("__main__", None, origin=try_filename)
196 self.spec.has_location = True
197 self.package = ""
198 self.loader = DummyLoader("__main__")
199 else:
200 if env.PY3:
201 self.loader = DummyLoader("__main__")
202
203 self.arg0 = python_reported_file(self.arg0)
204
205 def run(self):
206 """Run the Python code!"""
207
208 self._prepare2()
209
210 # Create a module to serve as __main__
211 main_mod = types.ModuleType('__main__')
212
213 from_pyc = self.arg0.endswith((".pyc", ".pyo"))
214 main_mod.__file__ = self.arg0
215 if from_pyc:
216 main_mod.__file__ = main_mod.__file__[:-1]
217 if self.package is not None:
218 main_mod.__package__ = self.package
219 main_mod.__loader__ = self.loader
220 if self.spec is not None:
221 main_mod.__spec__ = self.spec
222
223 main_mod.__builtins__ = BUILTINS
224
225 sys.modules['__main__'] = main_mod
226
227 # Set sys.argv properly.
228 sys.argv = self.args
229
230 try:
231 # Make a code object somehow.
232 if from_pyc:
233 code = make_code_from_pyc(self.arg0)
234 else:
235 code = make_code_from_py(self.arg0)
236 except CoverageException:
237 raise
238 except Exception as exc:
239 msg = "Couldn't run '{filename}' as Python code: {exc.__class__.__name__}: {exc}"
240 raise CoverageException(msg.format(filename=self.arg0, exc=exc))
241
242 # Execute the code object.
243 # Return to the original directory in case the test code exits in
244 # a non-existent directory.
245 cwd = os.getcwd()
246 try:
247 exec(code, main_mod.__dict__)
248 except SystemExit: # pylint: disable=try-except-raise
249 # The user called sys.exit(). Just pass it along to the upper
250 # layers, where it will be handled.
251 raise
252 except Exception:
253 # Something went wrong while executing the user code.
254 # Get the exc_info, and pack them into an exception that we can
255 # throw up to the outer loop. We peel one layer off the traceback
256 # so that the coverage.py code doesn't appear in the final printed
257 # traceback.
258 typ, err, tb = sys.exc_info()
259
260 # PyPy3 weirdness. If I don't access __context__, then somehow it
261 # is non-None when the exception is reported at the upper layer,
262 # and a nested exception is shown to the user. This getattr fixes
263 # it somehow? https://bitbucket.org/pypy/pypy/issue/1903
264 getattr(err, '__context__', None)
265
266 # Call the excepthook.
267 try:
268 if hasattr(err, "__traceback__"):
269 err.__traceback__ = err.__traceback__.tb_next
270 sys.excepthook(typ, err, tb.tb_next)
271 except SystemExit: # pylint: disable=try-except-raise
272 raise
273 except Exception:
274 # Getting the output right in the case of excepthook
275 # shenanigans is kind of involved.
276 sys.stderr.write("Error in sys.excepthook:\n")
277 typ2, err2, tb2 = sys.exc_info()
278 err2.__suppress_context__ = True
279 if hasattr(err2, "__traceback__"):
280 err2.__traceback__ = err2.__traceback__.tb_next
281 sys.__excepthook__(typ2, err2, tb2.tb_next)
282 sys.stderr.write("\nOriginal exception was:\n")
283 raise ExceptionDuringRun(typ, err, tb.tb_next)
284 else:
285 sys.exit(1)
286 finally:
287 os.chdir(cwd)
288
289
290 def run_python_module(args):
291 """Run a Python module, as though with ``python -m name args...``.
292
293 `args` is the argument array to present as sys.argv, including the first
294 element naming the module being executed.
295
296 This is a helper for tests, to encapsulate how to use PyRunner.
297
298 """
299 runner = PyRunner(args, as_module=True)
300 runner.prepare()
301 runner.run()
302
303
304 def run_python_file(args):
305 """Run a Python file as if it were the main program on the command line.
306
307 `args` is the argument array to present as sys.argv, including the first
308 element naming the file being executed. `package` is the name of the
309 enclosing package, if any.
310
311 This is a helper for tests, to encapsulate how to use PyRunner.
312
313 """
314 runner = PyRunner(args, as_module=False)
315 runner.prepare()
316 runner.run()
317
318
319 def make_code_from_py(filename):
320 """Get source from `filename` and make a code object of it."""
321 # Open the source file.
322 try:
323 source = get_python_source(filename)
324 except (IOError, NoSource):
325 raise NoSource("No file to run: '%s'" % filename)
326
327 code = compile_unicode(source, filename, "exec")
328 return code
329
330
331 def make_code_from_pyc(filename):
332 """Get a code object from a .pyc file."""
333 try:
334 fpyc = open(filename, "rb")
335 except IOError:
336 raise NoCode("No file to run: '%s'" % filename)
337
338 with fpyc:
339 # First four bytes are a version-specific magic number. It has to
340 # match or we won't run the file.
341 magic = fpyc.read(4)
342 if magic != PYC_MAGIC_NUMBER:
343 raise NoCode("Bad magic number in .pyc file: {} != {}".format(magic, PYC_MAGIC_NUMBER))
344
345 date_based = True
346 if env.PYBEHAVIOR.hashed_pyc_pep552:
347 flags = struct.unpack('<L', fpyc.read(4))[0]
348 hash_based = flags & 0x01
349 if hash_based:
350 fpyc.read(8) # Skip the hash.
351 date_based = False
352 if date_based:
353 # Skip the junk in the header that we don't need.
354 fpyc.read(4) # Skip the moddate.
355 if env.PYBEHAVIOR.size_in_pyc:
356 # 3.3 added another long to the header (size), skip it.
357 fpyc.read(4)
358
359 # The rest of the file is the code object we want.
360 code = marshal.load(fpyc)
361
362 return code

eric ide

mercurial