eric7/DebugClients/Python/coverage/execfile.py

branch
eric7
changeset 8775
0802ae193343
parent 8312
800c432b34c8
child 8929
fcca2fa618bf
equal deleted inserted replaced
8774:d728227e8ebb 8775:0802ae193343
1 # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 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 2 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
3 3
4 """Execute files of Python code.""" 4 """Execute files of Python code."""
5 5
6 import importlib.machinery
7 import importlib.util
6 import inspect 8 import inspect
7 import marshal 9 import marshal
8 import os 10 import os
9 import struct 11 import struct
10 import sys 12 import sys
11 import types 13 import types
12 14
13 from coverage import env 15 from coverage import env
14 from coverage.backward import BUILTINS 16 from coverage.exceptions import CoverageException, ExceptionDuringRun, NoCode, NoSource
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.files import canonical_filename, python_reported_file
17 from coverage.misc import CoverageException, ExceptionDuringRun, NoCode, NoSource, isolate_module 18 from coverage.misc import isolate_module
18 from coverage.phystokens import compile_unicode 19 from coverage.phystokens import compile_unicode
19 from coverage.python import get_python_source 20 from coverage.python import get_python_source
20 21
21 os = isolate_module(os) 22 os = isolate_module(os)
22 23
23 24
24 class DummyLoader(object): 25 PYC_MAGIC_NUMBER = importlib.util.MAGIC_NUMBER
26
27 class DummyLoader:
25 """A shim for the pep302 __loader__, emulating pkgutil.ImpLoader. 28 """A shim for the pep302 __loader__, emulating pkgutil.ImpLoader.
26 29
27 Currently only implements the .fullname attribute 30 Currently only implements the .fullname attribute
28 """ 31 """
29 def __init__(self, fullname, *_args): 32 def __init__(self, fullname, *_args):
30 self.fullname = fullname 33 self.fullname = fullname
31 34
32 35
33 if importlib_util_find_spec: 36 def find_module(modulename):
34 def find_module(modulename): 37 """Find the module named `modulename`.
35 """Find the module named `modulename`. 38
36 39 Returns the file path of the module, the name of the enclosing
37 Returns the file path of the module, the name of the enclosing 40 package, and the spec.
38 package, and the spec. 41 """
39 """ 42 try:
40 try: 43 spec = importlib.util.find_spec(modulename)
41 spec = importlib_util_find_spec(modulename) 44 except ImportError as err:
42 except ImportError as err: 45 raise NoSource(str(err)) from err
43 raise NoSource(str(err)) 46 if not spec:
47 raise NoSource(f"No module named {modulename!r}")
48 pathname = spec.origin
49 packagename = spec.name
50 if spec.submodule_search_locations:
51 mod_main = modulename + ".__main__"
52 spec = importlib.util.find_spec(mod_main)
44 if not spec: 53 if not spec:
45 raise NoSource("No module named %r" % (modulename,)) 54 raise NoSource(
55 f"No module named {mod_main}; " +
56 f"{modulename!r} is a package and cannot be directly executed"
57 )
46 pathname = spec.origin 58 pathname = spec.origin
47 packagename = spec.name 59 packagename = spec.name
48 if spec.submodule_search_locations: 60 packagename = packagename.rpartition(".")[0]
49 mod_main = modulename + ".__main__" 61 return pathname, packagename, spec
50 spec = importlib_util_find_spec(mod_main) 62
51 if not spec: 63
52 raise NoSource( 64 class PyRunner:
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. 65 """Multi-stage execution of Python code.
107 66
108 This is meant to emulate real Python execution as closely as possible. 67 This is meant to emulate real Python execution as closely as possible.
109 68
110 """ 69 """
174 elif os.path.isdir(self.arg0): 133 elif os.path.isdir(self.arg0):
175 # Running a directory means running the __main__.py file in that 134 # Running a directory means running the __main__.py file in that
176 # directory. 135 # directory.
177 for ext in [".py", ".pyc", ".pyo"]: 136 for ext in [".py", ".pyc", ".pyo"]:
178 try_filename = os.path.join(self.arg0, "__main__" + ext) 137 try_filename = os.path.join(self.arg0, "__main__" + ext)
138 # 3.8.10 changed how files are reported when running a
139 # directory. But I'm not sure how far this change is going to
140 # spread, so I'll just hard-code it here for now.
141 if env.PYVERSION >= (3, 8, 10):
142 try_filename = os.path.abspath(try_filename)
179 if os.path.exists(try_filename): 143 if os.path.exists(try_filename):
180 self.arg0 = try_filename 144 self.arg0 = try_filename
181 break 145 break
182 else: 146 else:
183 raise NoSource("Can't find '__main__' module in '%s'" % self.arg0) 147 raise NoSource("Can't find '__main__' module in '%s'" % self.arg0)
184 148
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. 149 # Make a spec. I don't know if this is the right way to do it.
189 try: 150 try_filename = python_reported_file(try_filename)
190 import importlib.machinery 151 self.spec = importlib.machinery.ModuleSpec("__main__", None, origin=try_filename)
191 except ImportError: 152 self.spec.has_location = True
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 = "" 153 self.package = ""
198 self.loader = DummyLoader("__main__") 154 self.loader = DummyLoader("__main__")
199 else: 155 else:
200 if env.PY3: 156 self.loader = DummyLoader("__main__")
201 self.loader = DummyLoader("__main__")
202 157
203 self.arg0 = python_reported_file(self.arg0) 158 self.arg0 = python_reported_file(self.arg0)
204 159
205 def run(self): 160 def run(self):
206 """Run the Python code!""" 161 """Run the Python code!"""
218 main_mod.__package__ = self.package 173 main_mod.__package__ = self.package
219 main_mod.__loader__ = self.loader 174 main_mod.__loader__ = self.loader
220 if self.spec is not None: 175 if self.spec is not None:
221 main_mod.__spec__ = self.spec 176 main_mod.__spec__ = self.spec
222 177
223 main_mod.__builtins__ = BUILTINS 178 main_mod.__builtins__ = sys.modules['builtins']
224 179
225 sys.modules['__main__'] = main_mod 180 sys.modules['__main__'] = main_mod
226 181
227 # Set sys.argv properly. 182 # Set sys.argv properly.
228 sys.argv = self.args 183 sys.argv = self.args
234 else: 189 else:
235 code = make_code_from_py(self.arg0) 190 code = make_code_from_py(self.arg0)
236 except CoverageException: 191 except CoverageException:
237 raise 192 raise
238 except Exception as exc: 193 except Exception as exc:
239 msg = "Couldn't run '{filename}' as Python code: {exc.__class__.__name__}: {exc}" 194 msg = f"Couldn't run '{self.arg0}' as Python code: {exc.__class__.__name__}: {exc}"
240 raise CoverageException(msg.format(filename=self.arg0, exc=exc)) 195 raise CoverageException(msg) from exc
241 196
242 # Execute the code object. 197 # Execute the code object.
243 # Return to the original directory in case the test code exits in 198 # Return to the original directory in case the test code exits in
244 # a non-existent directory. 199 # a non-existent directory.
245 cwd = os.getcwd() 200 cwd = os.getcwd()
263 # it somehow? https://bitbucket.org/pypy/pypy/issue/1903 218 # it somehow? https://bitbucket.org/pypy/pypy/issue/1903
264 getattr(err, '__context__', None) 219 getattr(err, '__context__', None)
265 220
266 # Call the excepthook. 221 # Call the excepthook.
267 try: 222 try:
268 if hasattr(err, "__traceback__"): 223 err.__traceback__ = err.__traceback__.tb_next
269 err.__traceback__ = err.__traceback__.tb_next
270 sys.excepthook(typ, err, tb.tb_next) 224 sys.excepthook(typ, err, tb.tb_next)
271 except SystemExit: # pylint: disable=try-except-raise 225 except SystemExit: # pylint: disable=try-except-raise
272 raise 226 raise
273 except Exception: 227 except Exception as exc:
274 # Getting the output right in the case of excepthook 228 # Getting the output right in the case of excepthook
275 # shenanigans is kind of involved. 229 # shenanigans is kind of involved.
276 sys.stderr.write("Error in sys.excepthook:\n") 230 sys.stderr.write("Error in sys.excepthook:\n")
277 typ2, err2, tb2 = sys.exc_info() 231 typ2, err2, tb2 = sys.exc_info()
278 err2.__suppress_context__ = True 232 err2.__suppress_context__ = True
279 if hasattr(err2, "__traceback__"): 233 err2.__traceback__ = err2.__traceback__.tb_next
280 err2.__traceback__ = err2.__traceback__.tb_next
281 sys.__excepthook__(typ2, err2, tb2.tb_next) 234 sys.__excepthook__(typ2, err2, tb2.tb_next)
282 sys.stderr.write("\nOriginal exception was:\n") 235 sys.stderr.write("\nOriginal exception was:\n")
283 raise ExceptionDuringRun(typ, err, tb.tb_next) 236 raise ExceptionDuringRun(typ, err, tb.tb_next) from exc
284 else: 237 else:
285 sys.exit(1) 238 sys.exit(1)
286 finally: 239 finally:
287 os.chdir(cwd) 240 os.chdir(cwd)
288 241
319 def make_code_from_py(filename): 272 def make_code_from_py(filename):
320 """Get source from `filename` and make a code object of it.""" 273 """Get source from `filename` and make a code object of it."""
321 # Open the source file. 274 # Open the source file.
322 try: 275 try:
323 source = get_python_source(filename) 276 source = get_python_source(filename)
324 except (IOError, NoSource): 277 except (OSError, NoSource) as exc:
325 raise NoSource("No file to run: '%s'" % filename) 278 raise NoSource(f"No file to run: '{filename}'") from exc
326 279
327 code = compile_unicode(source, filename, "exec") 280 code = compile_unicode(source, filename, "exec")
328 return code 281 return code
329 282
330 283
331 def make_code_from_pyc(filename): 284 def make_code_from_pyc(filename):
332 """Get a code object from a .pyc file.""" 285 """Get a code object from a .pyc file."""
333 try: 286 try:
334 fpyc = open(filename, "rb") 287 fpyc = open(filename, "rb")
335 except IOError: 288 except OSError as exc:
336 raise NoCode("No file to run: '%s'" % filename) 289 raise NoCode(f"No file to run: '{filename}'") from exc
337 290
338 with fpyc: 291 with fpyc:
339 # First four bytes are a version-specific magic number. It has to 292 # First four bytes are a version-specific magic number. It has to
340 # match or we won't run the file. 293 # match or we won't run the file.
341 magic = fpyc.read(4) 294 magic = fpyc.read(4)
342 if magic != PYC_MAGIC_NUMBER: 295 if magic != PYC_MAGIC_NUMBER:
343 raise NoCode("Bad magic number in .pyc file: {} != {}".format(magic, PYC_MAGIC_NUMBER)) 296 raise NoCode(f"Bad magic number in .pyc file: {magic} != {PYC_MAGIC_NUMBER}")
344 297
345 date_based = True 298 date_based = True
346 if env.PYBEHAVIOR.hashed_pyc_pep552: 299 if env.PYBEHAVIOR.hashed_pyc_pep552:
347 flags = struct.unpack('<L', fpyc.read(4))[0] 300 flags = struct.unpack('<L', fpyc.read(4))[0]
348 hash_based = flags & 0x01 301 hash_based = flags & 0x01
350 fpyc.read(8) # Skip the hash. 303 fpyc.read(8) # Skip the hash.
351 date_based = False 304 date_based = False
352 if date_based: 305 if date_based:
353 # Skip the junk in the header that we don't need. 306 # Skip the junk in the header that we don't need.
354 fpyc.read(4) # Skip the moddate. 307 fpyc.read(4) # Skip the moddate.
355 if env.PYBEHAVIOR.size_in_pyc: 308 # 3.3 added another long to the header (size), skip it.
356 # 3.3 added another long to the header (size), skip it. 309 fpyc.read(4)
357 fpyc.read(4)
358 310
359 # The rest of the file is the code object we want. 311 # The rest of the file is the code object we want.
360 code = marshal.load(fpyc) 312 code = marshal.load(fpyc)
361 313
362 return code 314 return code

eric ide

mercurial