20 from coverage.files import TreeMatcher, FnmatchMatcher |
21 from coverage.files import TreeMatcher, FnmatchMatcher |
21 from coverage.files import PathAliases, find_python_files, prep_patterns |
22 from coverage.files import PathAliases, find_python_files, prep_patterns |
22 from coverage.files import ModuleMatcher, abs_file |
23 from coverage.files import ModuleMatcher, abs_file |
23 from coverage.html import HtmlReporter |
24 from coverage.html import HtmlReporter |
24 from coverage.misc import CoverageException, bool_or_none, join_regex |
25 from coverage.misc import CoverageException, bool_or_none, join_regex |
25 from coverage.misc import file_be_gone |
26 from coverage.misc import file_be_gone, isolate_module |
26 from coverage.monkey import patch_multiprocessing |
27 from coverage.monkey import patch_multiprocessing |
27 from coverage.plugin import FileReporter |
28 from coverage.plugin import FileReporter |
28 from coverage.plugin_support import Plugins |
29 from coverage.plugin_support import Plugins |
29 from coverage.python import PythonFileReporter |
30 from coverage.python import PythonFileReporter |
30 from coverage.results import Analysis, Numbers |
31 from coverage.results import Analysis, Numbers |
31 from coverage.summary import SummaryReporter |
32 from coverage.summary import SummaryReporter |
32 from coverage.xmlreport import XmlReporter |
33 from coverage.xmlreport import XmlReporter |
33 |
34 |
|
35 os = isolate_module(os) |
34 |
36 |
35 # Pypy has some unusual stuff in the "stdlib". Consider those locations |
37 # Pypy has some unusual stuff in the "stdlib". Consider those locations |
36 # when deciding where the stdlib is. |
38 # when deciding where the stdlib is. |
37 try: |
39 try: |
38 import _structseq |
40 import _structseq |
106 `debug` is a list of strings indicating what debugging information is |
108 `debug` is a list of strings indicating what debugging information is |
107 desired. |
109 desired. |
108 |
110 |
109 `concurrency` is a string indicating the concurrency library being used |
111 `concurrency` is a string indicating the concurrency library being used |
110 in the measured code. Without this, coverage.py will get incorrect |
112 in the measured code. Without this, coverage.py will get incorrect |
111 results. Valid strings are "greenlet", "eventlet", "gevent", or |
113 results. Valid strings are "greenlet", "eventlet", "gevent", |
112 "thread" (the default). |
114 "multiprocessing", or "thread" (the default). |
113 |
115 |
114 .. versionadded:: 4.0 |
116 .. versionadded:: 4.0 |
115 The `concurrency` parameter. |
117 The `concurrency` parameter. |
116 |
118 |
117 """ |
119 """ |
277 |
279 |
278 # Create the data file. We do this at construction time so that the |
280 # Create the data file. We do this at construction time so that the |
279 # data file will be written into the directory where the process |
281 # data file will be written into the directory where the process |
280 # started rather than wherever the process eventually chdir'd to. |
282 # started rather than wherever the process eventually chdir'd to. |
281 self.data = CoverageData(debug=self.debug) |
283 self.data = CoverageData(debug=self.debug) |
282 self.data_files = CoverageDataFiles(basename=self.config.data_file) |
284 self.data_files = CoverageDataFiles(basename=self.config.data_file, warn=self._warn) |
283 |
285 |
284 # The directories for files considered "installed with the interpreter". |
286 # The directories for files considered "installed with the interpreter". |
285 self.pylib_dirs = set() |
287 self.pylib_dirs = set() |
286 if not self.config.cover_pylib: |
288 if not self.config.cover_pylib: |
287 # Look at where some standard modules are located. That's the |
289 # Look at where some standard modules are located. That's the |
288 # indication for "installed with the interpreter". In some |
290 # indication for "installed with the interpreter". In some |
289 # environments (virtualenv, for example), these modules may be |
291 # environments (virtualenv, for example), these modules may be |
290 # spread across a few locations. Look at all the candidate modules |
292 # spread across a few locations. Look at all the candidate modules |
291 # we've imported, and take all the different ones. |
293 # we've imported, and take all the different ones. |
292 for m in (atexit, inspect, os, platform, _structseq, traceback): |
294 for m in (atexit, inspect, os, platform, re, _structseq, traceback): |
293 if m is not None and hasattr(m, "__file__"): |
295 if m is not None and hasattr(m, "__file__"): |
294 self.pylib_dirs.add(self._canonical_dir(m)) |
296 self.pylib_dirs.add(self._canonical_dir(m)) |
295 if _structseq and not hasattr(_structseq, '__file__'): |
297 if _structseq and not hasattr(_structseq, '__file__'): |
296 # PyPy 2.4 has no __file__ in the builtin modules, but the code |
298 # PyPy 2.4 has no __file__ in the builtin modules, but the code |
297 # objects still have the file names. So dig into one to find |
299 # objects still have the file names. So dig into one to find |
472 # Lots of non-file execution is represented with artificial |
474 # Lots of non-file execution is represented with artificial |
473 # file names like "<string>", "<doctest readme.txt[0]>", or |
475 # file names like "<string>", "<doctest readme.txt[0]>", or |
474 # "<exec_function>". Don't ever trace these executions, since we |
476 # "<exec_function>". Don't ever trace these executions, since we |
475 # can't do anything with the data later anyway. |
477 # can't do anything with the data later anyway. |
476 return nope(disp, "not a real file name") |
478 return nope(disp, "not a real file name") |
|
479 |
|
480 # pyexpat does a dumb thing, calling the trace function explicitly from |
|
481 # C code with a C file name. |
|
482 if re.search(r"[/\\]Modules[/\\]pyexpat.c", filename): |
|
483 return nope(disp, "pyexpat lies about itself") |
477 |
484 |
478 # Jython reports the .class file to the tracer, use the source file. |
485 # Jython reports the .class file to the tracer, use the source file. |
479 if filename.endswith("$py.class"): |
486 if filename.endswith("$py.class"): |
480 filename = filename[:-9] + ".py" |
487 filename = filename[:-9] + ".py" |
481 |
488 |
941 file_reporters.append(file_reporter) |
939 file_reporters.append(file_reporter) |
942 |
940 |
943 return file_reporters |
941 return file_reporters |
944 |
942 |
945 def report( |
943 def report( |
946 self, morfs=None, show_missing=True, ignore_errors=None, |
944 self, morfs=None, show_missing=None, ignore_errors=None, |
947 file=None, # pylint: disable=redefined-builtin |
945 file=None, # pylint: disable=redefined-builtin |
948 omit=None, include=None, skip_covered=False, |
946 omit=None, include=None, skip_covered=None, |
949 ): |
947 ): |
950 """Write a summary report to `file`. |
948 """Write a summary report to `file`. |
951 |
949 |
952 Each module in `morfs` is listed, with counts of statements, executed |
950 Each module in `morfs` is listed, with counts of statements, executed |
953 statements, missing statements, and a list of lines missed. |
951 statements, missing statements, and a list of lines missed. |
1047 # HTMLReport does this using the Report plumbing because |
1045 # HTMLReport does this using the Report plumbing because |
1048 # its task is more complex, being multiple files. |
1046 # its task is more complex, being multiple files. |
1049 output_dir = os.path.dirname(self.config.xml_output) |
1047 output_dir = os.path.dirname(self.config.xml_output) |
1050 if output_dir and not os.path.isdir(output_dir): |
1048 if output_dir and not os.path.isdir(output_dir): |
1051 os.makedirs(output_dir) |
1049 os.makedirs(output_dir) |
1052 outfile = open(self.config.xml_output, "w") |
1050 open_kwargs = {} |
|
1051 if env.PY3: |
|
1052 open_kwargs['encoding'] = 'utf8' |
|
1053 outfile = open(self.config.xml_output, "w", **open_kwargs) |
1053 file_to_close = outfile |
1054 file_to_close = outfile |
1054 try: |
1055 try: |
1055 reporter = XmlReporter(self, self.config) |
1056 reporter = XmlReporter(self, self.config) |
1056 return reporter.report(morfs, outfile=outfile) |
1057 return reporter.report(morfs, outfile=outfile) |
1057 except CoverageException: |
1058 except CoverageException: |
1163 |
1164 |
1164 #. Create a .pth file in your Python installation containing:: |
1165 #. Create a .pth file in your Python installation containing:: |
1165 |
1166 |
1166 import coverage; coverage.process_startup() |
1167 import coverage; coverage.process_startup() |
1167 |
1168 |
|
1169 Returns the :class:`Coverage` instance that was started, or None if it was |
|
1170 not started by this call. |
|
1171 |
1168 """ |
1172 """ |
1169 cps = os.environ.get("COVERAGE_PROCESS_START") |
1173 cps = os.environ.get("COVERAGE_PROCESS_START") |
1170 if not cps: |
1174 if not cps: |
1171 # No request for coverage, nothing to do. |
1175 # No request for coverage, nothing to do. |
1172 return |
1176 return None |
1173 |
1177 |
1174 # This function can be called more than once in a process. This happens |
1178 # This function can be called more than once in a process. This happens |
1175 # because some virtualenv configurations make the same directory visible |
1179 # because some virtualenv configurations make the same directory visible |
1176 # twice in sys.path. This means that the .pth file will be found twice, |
1180 # twice in sys.path. This means that the .pth file will be found twice, |
1177 # and executed twice, executing this function twice. We set a global |
1181 # and executed twice, executing this function twice. We set a global |
1182 # details. |
1186 # details. |
1183 |
1187 |
1184 if hasattr(process_startup, "done"): |
1188 if hasattr(process_startup, "done"): |
1185 # We've annotated this function before, so we must have already |
1189 # We've annotated this function before, so we must have already |
1186 # started coverage.py in this process. Nothing to do. |
1190 # started coverage.py in this process. Nothing to do. |
1187 return |
1191 return None |
1188 |
1192 |
1189 process_startup.done = True |
1193 process_startup.done = True |
1190 cov = Coverage(config_file=cps, auto_data=True) |
1194 cov = Coverage(config_file=cps, auto_data=True) |
1191 cov.start() |
1195 cov.start() |
1192 cov._warn_no_data = False |
1196 cov._warn_no_data = False |
1193 cov._warn_unimported_source = False |
1197 cov._warn_unimported_source = False |
1194 |
1198 |
|
1199 return cov |
|
1200 |
1195 # |
1201 # |
1196 # eflag: FileType = Python2 |
1202 # eflag: FileType = Python2 |