Wed, 20 Jul 2022 16:13:29 +0200
Third Party packages
- upgraded coverage to 6.4.2
--- a/docs/changelog Wed Jul 20 16:03:55 2022 +0200 +++ b/docs/changelog Wed Jul 20 16:13:29 2022 +0200 @@ -27,6 +27,7 @@ -- added a variant to create a 'setup.cfg' file -- added a variant to create a 'pyproject.toml' file - Third Party packages + -- upgraded coverage to 6.4.2 -- upgraded eradicate to 2.1.0 Version 22.7:
--- a/src/eric7/DebugClients/Python/coverage/data.py Wed Jul 20 16:03:55 2022 +0200 +++ b/src/eric7/DebugClients/Python/coverage/data.py Wed Jul 20 16:13:29 2022 +0200 @@ -30,6 +30,7 @@ """ summ = {} if fullpath: + # pylint: disable=unnecessary-lambda-assignment filename_fn = lambda f: f else: filename_fn = os.path.basename
--- a/src/eric7/DebugClients/Python/coverage/debug.py Wed Jul 20 16:03:55 2022 +0200 +++ b/src/eric7/DebugClients/Python/coverage/debug.py Wed Jul 20 16:13:29 2022 +0200 @@ -12,6 +12,7 @@ import pprint import reprlib import sys +import types import _thread from coverage.misc import isolate_module @@ -282,6 +283,7 @@ self.write(f"New process: pid: {os.getpid()!r}, parent pid: {os.getppid()!r}\n") SYS_MOD_NAME = '$coverage.debug.DebugOutputFile.the_one' + SINGLETON_ATTR = 'the_one_and_is_interim' @classmethod def get_one(cls, fileobj=None, show_process=True, filters=(), interim=False): @@ -310,7 +312,8 @@ # this class can be defined more than once. But we really want # a process-wide singleton. So stash it in sys.modules instead of # on a class attribute. Yes, this is aggressively gross. - the_one, is_interim = sys.modules.get(cls.SYS_MOD_NAME, (None, True)) + singleton_module = sys.modules.get(cls.SYS_MOD_NAME) + the_one, is_interim = getattr(singleton_module, cls.SINGLETON_ATTR, (None, True)) if the_one is None or is_interim: if fileobj is None: debug_file_name = os.environ.get("COVERAGE_DEBUG_FILE", FORCED_DEBUG_FILE) @@ -321,7 +324,9 @@ else: fileobj = sys.stderr the_one = cls(fileobj, show_process, filters) - sys.modules[cls.SYS_MOD_NAME] = (the_one, interim) + singleton_module = types.ModuleType(cls.SYS_MOD_NAME) + setattr(singleton_module, cls.SINGLETON_ATTR, (the_one, interim)) + sys.modules[cls.SYS_MOD_NAME] = singleton_module return the_one def write(self, text):
--- a/src/eric7/DebugClients/Python/coverage/doc/CHANGES.rst Wed Jul 20 16:03:55 2022 +0200 +++ b/src/eric7/DebugClients/Python/coverage/doc/CHANGES.rst Wed Jul 20 16:13:29 2022 +0200 @@ -12,11 +12,50 @@ .. When updating the "Unreleased" header to a specific version, use this .. format. Don't forget the jump target: .. - .. .. _changes_981: + .. .. _changes_9-8-1: .. .. Version 9.8.1 — 2027-07-27 .. -------------------------- +.. _changes_6-4-2: + +Version 6.4.2 — 2022-07-12 +-------------------------- + +- Updated for a small change in Python 3.11.0 beta 4: modules now start with a + line with line number 0, which is ignored. This line cannnot be executed, so + coverage totals were thrown off. This line is now ignored by coverage.py, + but this also means that truly empty modules (like ``__init__.py``) have no + lines in them, rather than one phantom line. Fixes `issue 1419`_. + +- Internal debugging data added to sys.modules is now an actual module, to + avoid confusing code that examines everything in sys.modules. Thanks, + Yilei Yang (`pull 1399`_). + +.. _pull 1399: https://github.com/nedbat/coveragepy/pull/1399 +.. _issue 1419: https://github.com/nedbat/coveragepy/issues/1419 + + +.. _changes_6-4-1: + +Version 6.4.1 — 2022-06-02 +-------------------------- + +- Greatly improved performance on PyPy, and other environments that need the + pure Python trace function. Thanks, Carl Friedrich Bolz-Tereick (`pull + 1381`_ and `pull 1388`_). Slightly improved performance when using the C + trace function, as most environments do. Closes `issue 1339`_. + +- The conditions for using tomllib from the standard library have been made + more precise, so that 3.11 alphas will continue to work. Closes `issue + 1390`_. + +.. _issue 1339: https://github.com/nedbat/coveragepy/issues/1339 +.. _pull 1381: https://github.com/nedbat/coveragepy/pull/1381 +.. _pull 1388: https://github.com/nedbat/coveragepy/pull/1388 +.. _issue 1390: https://github.com/nedbat/coveragepy/issues/1390 + + .. _changes_64: Version 6.4 — 2022-05-22 @@ -79,8 +118,8 @@ decorators like CPython 3.8: both the @-line and the def-line are traced. Fixes `issue 1326`_. -- Debug: added ``pybehave`` to the list of :ref:`cmd_debug` and - :ref:`cmd_run_debug` options. +- Debug: added ``pybehave`` to the list of :ref:`coverage debug <cmd_debug>` + and :ref:`cmd_run_debug` options. - Fix: show an intelligible error message if ``--concurrency=multiprocessing`` is used without a configuration file. Closes `issue 1320`_.
--- a/src/eric7/DebugClients/Python/coverage/doc/CONTRIBUTORS.txt Wed Jul 20 16:03:55 2022 +0200 +++ b/src/eric7/DebugClients/Python/coverage/doc/CONTRIBUTORS.txt Wed Jul 20 16:13:29 2022 +0200 @@ -31,6 +31,7 @@ Bruno P. Kinoshita Buck Evan Calen Pennington +Carl Friedrich Bolz-Tereick Carl Gieringer Catherine Proulx Chris Adams @@ -152,6 +153,7 @@ Vince Salvino Ville Skyttä Xie Yanbo +Yilei "Dolee" Yang Yury Selivanov Zac Hatfield-Dodds Zooko Wilcox-O'Hearn
--- a/src/eric7/DebugClients/Python/coverage/doc/README.rst Wed Jul 20 16:03:55 2022 +0200 +++ b/src/eric7/DebugClients/Python/coverage/doc/README.rst Wed Jul 20 16:13:29 2022 +0200 @@ -27,7 +27,7 @@ .. PYVERSIONS -* CPython 3.7 through 3.11.0b1. +* CPython 3.7 through 3.11.0b4. * PyPy3 7.3.8. Documentation is on `Read the Docs`_. Code repository and issue tracker are on
--- a/src/eric7/DebugClients/Python/coverage/env.py Wed Jul 20 16:03:55 2022 +0200 +++ b/src/eric7/DebugClients/Python/coverage/env.py Wed Jul 20 16:13:29 2022 +0200 @@ -110,6 +110,9 @@ # Some words are keywords in some places, identifiers in other places. soft_keywords = (PYVERSION >= (3, 10)) + # Modules start with a line numbered zero. This means empty modules have + # only a 0-number line, which is ignored, giving a truly empty module. + empty_is_empty = (PYVERSION >= (3, 11, 0, 'beta', 4)) # Coverage.py specifics.
--- a/src/eric7/DebugClients/Python/coverage/files.py Wed Jul 20 16:03:55 2022 +0200 +++ b/src/eric7/DebugClients/Python/coverage/files.py Wed Jul 20 16:13:29 2022 +0200 @@ -270,7 +270,7 @@ """Find the path separator used in this string, or os.sep if none.""" sep_match = re.search(r"[\\/]", s) if sep_match: - the_sep = sep_match.group(0) + the_sep = sep_match[0] else: the_sep = os.sep return the_sep @@ -387,7 +387,7 @@ for regex, result in self.aliases: m = regex.match(path) if m: - new = path.replace(m.group(0), result) + new = path.replace(m[0], result) new = new.replace(sep(path), sep(result)) if not self.relative: new = canonical_filename(new)
--- a/src/eric7/DebugClients/Python/coverage/misc.py Wed Jul 20 16:03:55 2022 +0200 +++ b/src/eric7/DebugClients/Python/coverage/misc.py Wed Jul 20 16:13:29 2022 +0200 @@ -324,17 +324,17 @@ def dollar_replace(match): """Called for each $replacement.""" - # Only one of the groups will have matched, just get its text. + # Only one of the dollar_groups will have matched, just get its text. word = next(g for g in match.group(*dollar_groups) if g) # pragma: always breaks if word == "$": return "$" elif word in variables: return variables[word] - elif match.group('strict'): + elif match['strict']: msg = f"Variable {word} is undefined: {text!r}" raise CoverageException(msg) else: - return match.group('defval') + return match['defval'] text = re.sub(dollar_pattern, dollar_replace, text) return text
--- a/src/eric7/DebugClients/Python/coverage/parser.py Wed Jul 20 16:03:55 2022 +0200 +++ b/src/eric7/DebugClients/Python/coverage/parser.py Wed Jul 20 16:13:29 2022 +0200 @@ -377,7 +377,7 @@ """ if hasattr(self.code, "co_lines"): for _, _, line in self.code.co_lines(): - if line is not None: + if line: yield line else: # Adapted from dis.py in the standard library.
--- a/src/eric7/DebugClients/Python/coverage/pytracer.py Wed Jul 20 16:03:55 2022 +0200 +++ b/src/eric7/DebugClients/Python/coverage/pytracer.py Wed Jul 20 16:13:29 2022 +0200 @@ -67,6 +67,10 @@ # On exit, self.in_atexit = True atexit.register(setattr, self, 'in_atexit', True) + # Cache a bound method on the instance, so that we don't have to + # re-create a bound method object all the time. + self._cached_bound_method_trace = self._trace + def __repr__(self): return "<PyTracer at 0x{:x}: {} lines in {} files>".format( id(self), @@ -105,7 +109,7 @@ #self.log(":", frame.f_code.co_filename, frame.f_lineno, frame.f_code.co_name + "()", event) - if (self.stopped and sys.gettrace() == self._trace): # pylint: disable=comparison-with-callable + if (self.stopped and sys.gettrace() == self._cached_bound_method_trace): # pylint: disable=comparison-with-callable # The PyTrace.stop() method has been called, possibly by another # thread, let's deactivate ourselves now. if 0: @@ -129,12 +133,13 @@ context_maybe = self.should_start_context(frame) if context_maybe is not None: self.context = context_maybe - self.started_context = True + started_context = True self.switch_context(self.context) else: - self.started_context = False + started_context = False else: - self.started_context = False + started_context = False + self.started_context = started_context # Entering a new frame. Decide if we should trace in this file. self._activity = True @@ -143,22 +148,35 @@ self.cur_file_data, self.cur_file_name, self.last_line, - self.started_context, + started_context, ) ) - filename = frame.f_code.co_filename - self.cur_file_name = filename - disp = self.should_trace_cache.get(filename) - if disp is None: - disp = self.should_trace(filename, frame) - self.should_trace_cache[filename] = disp - self.cur_file_data = None - if disp.trace: - tracename = disp.source_filename - if tracename not in self.data: - self.data[tracename] = set() - self.cur_file_data = self.data[tracename] + # Improve tracing performance: when calling a function, both caller + # and callee are often within the same file. if that's the case, we + # don't have to re-check whether to trace the corresponding + # function (which is a little bit espensive since it involves + # dictionary lookups). This optimization is only correct if we + # didn't start a context. + filename = frame.f_code.co_filename + if filename != self.cur_file_name or started_context: + self.cur_file_name = filename + disp = self.should_trace_cache.get(filename) + if disp is None: + disp = self.should_trace(filename, frame) + self.should_trace_cache[filename] = disp + + self.cur_file_data = None + if disp.trace: + tracename = disp.source_filename + if tracename not in self.data: + self.data[tracename] = set() + self.cur_file_data = self.data[tracename] + else: + frame.f_trace_lines = False + elif not self.cur_file_data: + frame.f_trace_lines = False + # The call event is really a "start frame" event, and happens for # function calls and re-entering generators. The f_lasti field is # -1 for calls, and a real offset for generators. Use <0 as the @@ -174,6 +192,7 @@ self.last_line = -frame.f_code.co_firstlineno else: self.last_line = frame.f_lineno + elif event == 'line': # Record an executed line. if self.cur_file_data is not None: @@ -184,6 +203,7 @@ else: self.cur_file_data.add(lineno) self.last_line = lineno + elif event == 'return': if self.trace_arcs and self.cur_file_data: # Record an arc leaving the function, but beware that a @@ -211,6 +231,7 @@ if real_return: first = frame.f_code.co_firstlineno self.cur_file_data.add((self.last_line, -first)) + # Leaving this function, pop the filename stack. self.cur_file_data, self.cur_file_name, self.last_line, self.started_context = ( self.data_stack.pop() @@ -219,7 +240,7 @@ if self.started_context: self.context = None self.switch_context(None) - return self._trace + return self._cached_bound_method_trace def start(self): """Start this Tracer. @@ -237,10 +258,10 @@ # function, but we are marked as running again, so maybe it # will be ok? #self.log("~", "starting on different threads") - return self._trace + return self._cached_bound_method_trace - sys.settrace(self._trace) - return self._trace + sys.settrace(self._cached_bound_method_trace) + return self._cached_bound_method_trace def stop(self): """Stop this Tracer.""" @@ -265,9 +286,10 @@ # so don't warn if we are in atexit on PyPy and the trace function # has changed to None. dont_warn = (env.PYPY and env.PYPYVERSION >= (5, 4) and self.in_atexit and tf is None) - if (not dont_warn) and tf != self._trace: # pylint: disable=comparison-with-callable + if (not dont_warn) and tf != self._cached_bound_method_trace: # pylint: disable=comparison-with-callable self.warn( - f"Trace function changed, data is likely wrong: {tf!r} != {self._trace!r}", + "Trace function changed, data is likely wrong: " + + f"{tf!r} != {self._cached_bound_method_trace!r}", slug="trace-changed", )
--- a/src/eric7/DebugClients/Python/coverage/sqldata.py Wed Jul 20 16:03:55 2022 +0200 +++ b/src/eric7/DebugClients/Python/coverage/sqldata.py Wed Jul 20 16:13:29 2022 +0200 @@ -1064,7 +1064,7 @@ except sqlite3.Error as exc: raise DataError(f"Couldn't use data file {self.filename!r}: {exc}") from exc - self.con.create_function("REGEXP", 2, _regexp) + self.con.create_function("REGEXP", 2, lambda txt, pat: re.search(txt, pat) is not None) # This pragma makes writing faster. It disables rollbacks, but we never need them. # PyPy needs the .close() calls here, or sqlite gets twisted up: @@ -1181,8 +1181,3 @@ def dump(self): """Return a multi-line string, the SQL dump of the database.""" return "\n".join(self.con.iterdump()) - - -def _regexp(text, pattern): - """A regexp function for SQLite.""" - return re.search(text, pattern) is not None
--- a/src/eric7/DebugClients/Python/coverage/tomlconfig.py Wed Jul 20 16:03:55 2022 +0200 +++ b/src/eric7/DebugClients/Python/coverage/tomlconfig.py Wed Jul 20 16:13:29 2022 +0200 @@ -12,7 +12,7 @@ from coverage.misc import import_third_party, substitute_variables -if env.PYVERSION >= (3, 11): +if env.PYVERSION >= (3, 11, 0, "alpha", 7): import tomllib # pylint: disable=import-error else: # TOML support on Python 3.10 and below is an install-time extra option.
--- a/src/eric7/DebugClients/Python/coverage/version.py Wed Jul 20 16:03:55 2022 +0200 +++ b/src/eric7/DebugClients/Python/coverage/version.py Wed Jul 20 16:13:29 2022 +0200 @@ -5,15 +5,13 @@ # This file is exec'ed in setup.py, don't import anything! # Same semantics as sys.version_info. -version_info = (6, 4, 0, "final", 0) +version_info = (6, 4, 2, "final", 0) def _make_version(major, minor, micro, releaselevel, serial): """Create a readable version string from version_info tuple components.""" assert releaselevel in ['alpha', 'beta', 'candidate', 'final'] - version = "%d.%d" % (major, minor) - if micro: - version += ".%d" % (micro,) + version = "%d.%d.%d" % (major, minor, micro) if releaselevel != 'final': short = {'alpha': 'a', 'beta': 'b', 'candidate': 'rc'}[releaselevel] version += f"{short}{serial}"