DebugClients/Python/coverage/control.py

changeset 5051
3586ebd9fac8
parent 4491
0d8612e24fef
equal deleted inserted replaced
5047:04e5dfbd3f3d 5051:3586ebd9fac8
5 5
6 import atexit 6 import atexit
7 import inspect 7 import inspect
8 import os 8 import os
9 import platform 9 import platform
10 import re
10 import sys 11 import sys
11 import traceback 12 import traceback
12 13
13 from coverage import env, files 14 from coverage import env, files
14 from coverage.annotate import AnnotateReporter 15 from coverage.annotate import AnnotateReporter
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
796 .. versionadded:: 4.0 803 .. versionadded:: 4.0
797 804
798 """ 805 """
799 self._init() 806 self._init()
800 if not self._measured: 807 if not self._measured:
801 return 808 return self.data
802 809
803 self.collector.save_data(self.data) 810 self.collector.save_data(self.data)
804 811
805 # If there are still entries in the source_pkgs list, then we never 812 # If there are still entries in the source_pkgs list, then we never
806 # encountered those packages. 813 # encountered those packages.
830 # in as unexecuted. 837 # in as unexecuted.
831 continue 838 continue
832 839
833 self.data.touch_file(py_file) 840 self.data.touch_file(py_file)
834 841
835 # Add run information.
836 self.data.add_run_info(
837 brief_sys=" ".join([
838 platform.python_implementation(),
839 platform.python_version(),
840 platform.system(),
841 ])
842 )
843
844 if self.config.note: 842 if self.config.note:
845 self.data.add_run_info(note=self.config.note) 843 self.data.add_run_info(note=self.config.note)
846 844
847 self._measured = False 845 self._measured = False
848 return self.data 846 return self.data
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

eric ide

mercurial