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