From 7b5457967f256696d3b6c936e81436aa60b4b409 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Wed, 24 Sep 2014 21:03:16 -0400 Subject: "concurrency" is a better name that "coroutine" --HG-- rename : tests/test_coroutine.py => tests/test_concurrency.py --- coverage/control.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'coverage/control.py') diff --git a/coverage/control.py b/coverage/control.py index 86a2ae23..510ced7b 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -45,7 +45,7 @@ class Coverage(object): def __init__(self, data_file=None, data_suffix=None, cover_pylib=None, auto_data=False, timid=None, branch=None, config_file=True, source=None, omit=None, include=None, debug=None, - debug_file=None, coroutine=None, plugins=None): + debug_file=None, concurrency=None, plugins=None): """ `data_file` is the base name of the data file to use, defaulting to ".coverage". `data_suffix` is appended (with a dot) to `data_file` to @@ -84,7 +84,7 @@ class Coverage(object): desired. `debug_file` is the file to write debug messages to, defaulting to stderr. - `coroutine` is a string indicating the coroutining library being used + `concurrency` is a string indicating the concurrency library being used in the measured code. Without this, coverage.py will get incorrect results. Valid strings are "greenlet", "eventlet", or "gevent", which are all equivalent. TODO: really? @@ -128,7 +128,7 @@ class Coverage(object): data_file=data_file, cover_pylib=cover_pylib, timid=timid, branch=branch, parallel=bool_or_none(data_suffix), source=source, omit=omit, include=include, debug=debug, - coroutine=coroutine, plugins=plugins, + concurrency=concurrency, plugins=plugins, ) # Create and configure the debugging controller. @@ -170,7 +170,7 @@ class Coverage(object): timid=self.config.timid, branch=self.config.branch, warn=self._warn, - coroutine=self.config.coroutine, + concurrency=self.config.concurrency, ) # Suffixes are a bit tricky. We want to use the data suffix only when -- cgit v1.2.1 From bee4695b9dd3ac9aee5d2dc2746a49a37aae0742 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Thu, 25 Sep 2014 06:40:09 -0400 Subject: Some error checking and more tests for concurrency control. --- coverage/control.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'coverage/control.py') diff --git a/coverage/control.py b/coverage/control.py index 510ced7b..cc1ad36c 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -86,8 +86,8 @@ class Coverage(object): `concurrency` is a string indicating the concurrency library being used in the measured code. Without this, coverage.py will get incorrect - results. Valid strings are "greenlet", "eventlet", or "gevent", which - are all equivalent. TODO: really? + results. Valid strings are "greenlet", "eventlet", "gevent", or + "thread" (the default). `plugins` TODO. -- cgit v1.2.1 From d33fe1f90216e24039d5cbd003219543d1671313 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 28 Sep 2014 18:42:30 -0400 Subject: Remove support for COVERAGE_OPTIONS environment variable. --- coverage/control.py | 1 - 1 file changed, 1 deletion(-) (limited to 'coverage/control.py') diff --git a/coverage/control.py b/coverage/control.py index cc1ad36c..fe4e5f15 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -118,7 +118,6 @@ class Coverage(object): self.config.from_file("setup.cfg", section_prefix="coverage:") # 3: from environment variables: - self.config.from_environment('COVERAGE_OPTIONS') env_data_file = os.environ.get('COVERAGE_FILE') if env_data_file: self.config.data_file = env_data_file -- cgit v1.2.1 From 9453fa062539ac28b60c325e39c0fcbfd3b2ea10 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 28 Sep 2014 19:44:20 -0400 Subject: Pragmas for uncovered code --- coverage/control.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'coverage/control.py') diff --git a/coverage/control.py b/coverage/control.py index fe4e5f15..b2ec64ab 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -550,10 +550,9 @@ class Coverage(object): # `save()` at the last minute so that the pid will be correct even # if the process forks. extra = "" - if _TEST_NAME_FILE: - f = open(_TEST_NAME_FILE) - test_name = f.read() - f.close() + if _TEST_NAME_FILE: # pragma: debugging + with open(_TEST_NAME_FILE) as f: + test_name = f.read() extra = "." + test_name data_suffix = "%s%s.%s.%06d" % ( socket.gethostname(), extra, os.getpid(), -- cgit v1.2.1 From 99c52f24c5db26a98d77816a3c7ada0f4f7f8797 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Fri, 10 Oct 2014 14:58:03 -0400 Subject: Move lots of initing to _init, so we can tweak settings before starting. --- coverage/control.py | 109 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 75 insertions(+), 34 deletions(-) (limited to 'coverage/control.py') diff --git a/coverage/control.py b/coverage/control.py index b2ec64ab..af9cbcc7 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -45,7 +45,7 @@ class Coverage(object): def __init__(self, data_file=None, data_suffix=None, cover_pylib=None, auto_data=False, timid=None, branch=None, config_file=True, source=None, omit=None, include=None, debug=None, - debug_file=None, concurrency=None, plugins=None): + concurrency=None, plugins=None): """ `data_file` is the base name of the data file to use, defaulting to ".coverage". `data_suffix` is appended (with a dot) to `data_file` to @@ -81,8 +81,7 @@ class Coverage(object): will also accept a single string argument. `debug` is a list of strings indicating what debugging information is - desired. `debug_file` is the file to write debug messages to, - defaulting to stderr. + desired. `concurrency` is a string indicating the concurrency library being used in the measured code. Without this, coverage.py will get incorrect @@ -92,11 +91,6 @@ class Coverage(object): `plugins` TODO. """ - from coverage import __version__ - - # A record of all the warnings that have been issued. - self._warnings = [] - # Build our configuration from a number of sources: # 1: defaults: self.config = CoverageConfig() @@ -130,8 +124,57 @@ class Coverage(object): concurrency=concurrency, plugins=plugins, ) + self._debug_file = None + self._auto_data = auto_data + self._data_suffix = data_suffix + + # The matchers for _should_trace. + self.source_match = None + self.pylib_match = self.cover_match = None + self.include_match = self.omit_match = None + + # Is it ok for no data to be collected? + self._warn_no_data = True + self._warn_unimported_source = True + + # A record of all the warnings that have been issued. + self._warnings = [] + + # Other instance attributes, set later. + self.omit = self.include = self.source = None + self.source_pkgs = self.file_locator = None + self.data = self.collector = None + self.plugins = self.trace_judges = None + self.pylib_dirs = self.cover_dir = None + self.data_suffix = self.run_suffix = None + self._exclude_re = None + self.debug = None + + # State machine variables: + # Have we initialized everything? + self._inited = False + # Have we started collecting and not stopped it? + self._started = False + # Have we measured some data and not harvested it? + self._measured = False + + def _init(self): + """Set all the initial state. + + This is called by the public methods to initialize state. This lets us + construct a Coverage object, then tweak its state before this function + is called. + + """ + from coverage import __version__ + + if self._inited: + return + # Create and configure the debugging controller. - self.debug = DebugControl(self.config.debug, debug_file or sys.stderr) + if self._debug_file is None: + self._debug_file = sys.stderr + self.debug = DebugControl(self.config.debug, self._debug_file) # Load plugins self.plugins = Plugins.load_plugins(self.config.plugins, self.config) @@ -142,8 +185,6 @@ class Coverage(object): self.trace_judges.append(plugin) self.trace_judges.append(None) # The Python case. - self.auto_data = auto_data - # _exclude_re is a dict mapping exclusion list names to compiled # regexes. self._exclude_re = {} @@ -176,14 +217,14 @@ class Coverage(object): # collecting data, not when combining data. So we save it as # `self.run_suffix` now, and promote it to `self.data_suffix` if we # find that we are collecting data later. - if data_suffix or self.config.parallel: - if not isinstance(data_suffix, string_class): + if self._data_suffix or self.config.parallel: + if not isinstance(self._data_suffix, string_class): # if data_suffix=True, use .machinename.pid.random - data_suffix = True + self._data_suffix = True else: - data_suffix = None + self._data_suffix = None self.data_suffix = None - self.run_suffix = data_suffix + self.run_suffix = self._data_suffix # Create the data file. We do this at construction time so that the # data file will be written into the directory where the process @@ -210,26 +251,13 @@ class Coverage(object): # where we are. self.cover_dir = self._canonical_dir(__file__) - # The matchers for _should_trace. - self.source_match = None - self.pylib_match = self.cover_match = None - self.include_match = self.omit_match = None - # Set the reporting precision. Numbers.set_precision(self.config.precision) - # Is it ok for no data to be collected? - self._warn_no_data = True - self._warn_unimported_source = True - - # State machine variables: - # Have we started collecting and not stopped it? - self._started = False - # Have we measured some data and not harvested it? - self._measured = False - atexit.register(self._atexit) + self._inited = True + def _canonical_dir(self, morf): """Return the canonical directory of the module or file `morf`.""" morf_filename = PythonCodeUnit(morf, self.file_locator).filename @@ -424,10 +452,12 @@ class Coverage(object): `usecache` is true or false, whether to read and write data on disk. """ + self._init() self.data.usefile(usecache) def load(self): """Load previously-collected coverage data from the data file.""" + self._init() self.collector.reset() self.data.read() @@ -441,11 +471,12 @@ class Coverage(object): process might not shut down cleanly. """ + self._init() if self.run_suffix: # Calling start() means we're running code, so use the run_suffix # as the data_suffix when we eventually save the data. self.data_suffix = self.run_suffix - if self.auto_data: + if self._auto_data: self.load() # Create the matchers we need for _should_trace @@ -477,14 +508,15 @@ class Coverage(object): def stop(self): """Stop measuring code coverage.""" + if self._started: + self.collector.stop() self._started = False - self.collector.stop() def _atexit(self): """Clean up on process shutdown.""" if self._started: self.stop() - if self.auto_data: + if self._auto_data: self.save() def erase(self): @@ -494,11 +526,13 @@ class Coverage(object): discarding the data file. """ + self._init() self.collector.reset() self.data.erase() def clear_exclude(self, which='exclude'): """Clear the exclude list.""" + self._init() setattr(self.config, which + "_list", []) self._exclude_regex_stale() @@ -517,6 +551,7 @@ class Coverage(object): is marked for special treatment during reporting. """ + self._init() excl_list = getattr(self.config, which + "_list") excl_list.append(regex) self._exclude_regex_stale() @@ -539,10 +574,12 @@ class Coverage(object): that are available, and their meaning. """ + self._init() return getattr(self.config, which + "_list") def save(self): """Save the collected coverage data to the data file.""" + self._init() data_suffix = self.data_suffix if data_suffix is True: # If data_suffix was a simple true value, then make a suffix with @@ -570,6 +607,7 @@ class Coverage(object): current measurements. """ + self._init() aliases = None if self.config.paths: aliases = PathAliases(self.file_locator) @@ -585,6 +623,7 @@ class Coverage(object): Also warn about various problems collecting data. """ + self._init() if not self._measured: return @@ -642,6 +681,7 @@ class Coverage(object): coverage data. """ + self._init() analysis = self._analyze(morf) return ( analysis.filename, @@ -792,6 +832,7 @@ class Coverage(object): import coverage as covmod + self._init() try: implementation = platform.python_implementation() except AttributeError: -- cgit v1.2.1 From 066a693d77b0b950653cf5b577d308da594fa471 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sat, 11 Oct 2014 08:06:17 -0400 Subject: Can change config after construction. --- coverage/control.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'coverage/control.py') diff --git a/coverage/control.py b/coverage/control.py index af9cbcc7..7b033793 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -45,7 +45,7 @@ class Coverage(object): def __init__(self, data_file=None, data_suffix=None, cover_pylib=None, auto_data=False, timid=None, branch=None, config_file=True, source=None, omit=None, include=None, debug=None, - concurrency=None, plugins=None): + concurrency=None): """ `data_file` is the base name of the data file to use, defaulting to ".coverage". `data_suffix` is appended (with a dot) to `data_file` to @@ -88,8 +88,6 @@ class Coverage(object): results. Valid strings are "greenlet", "eventlet", "gevent", or "thread" (the default). - `plugins` TODO. - """ # Build our configuration from a number of sources: # 1: defaults: @@ -121,7 +119,7 @@ class Coverage(object): data_file=data_file, cover_pylib=cover_pylib, timid=timid, branch=branch, parallel=bool_or_none(data_suffix), source=source, omit=omit, include=include, debug=debug, - concurrency=concurrency, plugins=plugins, + concurrency=concurrency, ) self._debug_file = None -- cgit v1.2.1 From 022d2c0e0dd1b20f4797236692d8bf162d99d5da Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sat, 11 Oct 2014 09:57:02 -0400 Subject: Fix the case --- coverage/control.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'coverage/control.py') diff --git a/coverage/control.py b/coverage/control.py index 7b033793..43e440b8 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -33,7 +33,7 @@ class Coverage(object): To use:: - from coverage import coverage + from coverage import Coverage cov = Coverage() cov.start() -- cgit v1.2.1 From 845c03dfad682c931dd55cfcff81d5612a803df6 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sat, 11 Oct 2014 22:10:22 -0400 Subject: Fix a problem with PyPy 2.4 --- coverage/control.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'coverage/control.py') diff --git a/coverage/control.py b/coverage/control.py index 43e440b8..e351b005 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -244,6 +244,12 @@ class Coverage(object): for m in (atexit, os, platform, random, socket, _structseq): if m is not None and hasattr(m, "__file__"): self.pylib_dirs.add(self._canonical_dir(m)) + if _structseq and not hasattr(_structseq, '__file__'): + # Pypy 2.4 has no __file__ in the builtin modules, but the code + # objects still have the filenames. So dig into one to find + # the path to exclude. + structseq_file = _structseq.structseq_new.func_code.co_filename + self.pylib_dirs.add(self._canonical_dir(structseq_file)) # To avoid tracing the coverage code itself, we skip anything located # where we are. -- cgit v1.2.1 From 2150bdc270fa92d56a2fadea4c8d1f76fe6ab531 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sat, 11 Oct 2014 22:15:05 -0400 Subject: second p is captialized :-) --- coverage/control.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'coverage/control.py') diff --git a/coverage/control.py b/coverage/control.py index e351b005..c31f15fa 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -245,7 +245,7 @@ class Coverage(object): if m is not None and hasattr(m, "__file__"): self.pylib_dirs.add(self._canonical_dir(m)) if _structseq and not hasattr(_structseq, '__file__'): - # Pypy 2.4 has no __file__ in the builtin modules, but the code + # PyPy 2.4 has no __file__ in the builtin modules, but the code # objects still have the filenames. So dig into one to find # the path to exclude. structseq_file = _structseq.structseq_new.func_code.co_filename -- cgit v1.2.1 From cd015c45c278aca757263746ed2e64c46d578ddd Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 19 Oct 2014 11:51:26 -0400 Subject: More plugin re-shaping --- coverage/control.py | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) (limited to 'coverage/control.py') diff --git a/coverage/control.py b/coverage/control.py index c31f15fa..66979c33 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -142,7 +142,7 @@ class Coverage(object): self.omit = self.include = self.source = None self.source_pkgs = self.file_locator = None self.data = self.collector = None - self.plugins = self.trace_judges = None + self.plugins = self.file_tracers = None self.pylib_dirs = self.cover_dir = None self.data_suffix = self.run_suffix = None self._exclude_re = None @@ -177,11 +177,11 @@ class Coverage(object): # Load plugins self.plugins = Plugins.load_plugins(self.config.plugins, self.config) - self.trace_judges = [] + self.file_tracers = [] for plugin in self.plugins: - if plugin_implements(plugin, "trace_judge"): - self.trace_judges.append(plugin) - self.trace_judges.append(None) # The Python case. + if plugin_implements(plugin, "file_tracer"): + self.file_tracers.append(plugin) + self.file_tracers.append(None) # The Python case. # _exclude_re is a dict mapping exclusion list names to compiled # regexes. @@ -325,15 +325,27 @@ class Coverage(object): disp.canonical_filename = canonical # Try the plugins, see if they have an opinion about the file. - for plugin in self.trace_judges: + for plugin in self.file_tracers: if plugin: - plugin.trace_judge(disp) + #plugin.trace_judge(disp) + file_tracer = plugin.file_tracer(canonical) + if file_tracer is not None: + file_tracer.plugin_name = plugin.plugin_name + disp.trace = True + disp.file_tracer = file_tracer + disp.source_filename = self.file_locator.canonical_filename(file_tracer.source_filename()) else: disp.trace = True disp.source_filename = canonical + file_tracer = None if disp.trace: - disp.plugin = plugin - + if file_tracer: + disp.file_tracer = file_tracer + if disp.source_filename is None: + raise CoverageException( + "Plugin %r didn't set source_filename for %r" % + (plugin, disp.original_filename) + ) if disp.check_filters: reason = self._check_include_omit_etc(disp.source_filename) if reason: @@ -886,7 +898,7 @@ class FileDisposition(object): self.check_filters = True self.trace = False self.reason = "" - self.plugin = None + self.file_tracer = None def debug_message(self): """Produce a debugging message explaining the outcome.""" -- cgit v1.2.1