Upgraded the included code coverage library to v5.5.0. eric7

Sat, 21 Aug 2021 14:21:44 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sat, 21 Aug 2021 14:21:44 +0200
branch
eric7
changeset 8527
2bd1325d727e
parent 8526
587202572b10
child 8528
2175f268ad9b

Upgraded the included code coverage library to v5.5.0.

docs/changelog file | annotate | diff | comparison | revisions
eric7.epj file | annotate | diff | comparison | revisions
eric7/DebugClients/Python/coverage/backunittest.py file | annotate | diff | comparison | revisions
eric7/DebugClients/Python/coverage/backward.py file | annotate | diff | comparison | revisions
eric7/DebugClients/Python/coverage/cmdline.py file | annotate | diff | comparison | revisions
eric7/DebugClients/Python/coverage/collector.py file | annotate | diff | comparison | revisions
eric7/DebugClients/Python/coverage/config.py file | annotate | diff | comparison | revisions
eric7/DebugClients/Python/coverage/control.py file | annotate | diff | comparison | revisions
eric7/DebugClients/Python/coverage/data.py file | annotate | diff | comparison | revisions
eric7/DebugClients/Python/coverage/doc/CHANGES.rst file | annotate | diff | comparison | revisions
eric7/DebugClients/Python/coverage/doc/CONTRIBUTORS.txt file | annotate | diff | comparison | revisions
eric7/DebugClients/Python/coverage/doc/README.rst file | annotate | diff | comparison | revisions
eric7/DebugClients/Python/coverage/env.py file | annotate | diff | comparison | revisions
eric7/DebugClients/Python/coverage/files.py file | annotate | diff | comparison | revisions
eric7/DebugClients/Python/coverage/html.py file | annotate | diff | comparison | revisions
eric7/DebugClients/Python/coverage/misc.py file | annotate | diff | comparison | revisions
eric7/DebugClients/Python/coverage/multiproc.py file | annotate | diff | comparison | revisions
eric7/DebugClients/Python/coverage/optional.py file | annotate | diff | comparison | revisions
eric7/DebugClients/Python/coverage/parser.py file | annotate | diff | comparison | revisions
eric7/DebugClients/Python/coverage/phystokens.py file | annotate | diff | comparison | revisions
eric7/DebugClients/Python/coverage/pytracer.py file | annotate | diff | comparison | revisions
eric7/DebugClients/Python/coverage/results.py file | annotate | diff | comparison | revisions
eric7/DebugClients/Python/coverage/sqldata.py file | annotate | diff | comparison | revisions
eric7/DebugClients/Python/coverage/summary.py file | annotate | diff | comparison | revisions
eric7/DebugClients/Python/coverage/tomlconfig.py file | annotate | diff | comparison | revisions
eric7/DebugClients/Python/coverage/version.py file | annotate | diff | comparison | revisions
--- a/docs/changelog	Fri Aug 20 19:56:17 2021 +0200
+++ b/docs/changelog	Sat Aug 21 14:21:44 2021 +0200
@@ -14,6 +14,8 @@
      dialog
 - Shell
   -- added capability to save the contents of the shell window into a file
+- Third Party packages
+  -- upgraded coverage to 5.5.0
 
 Version 21.9:
 - bug fixes
--- a/eric7.epj	Fri Aug 20 19:56:17 2021 +0200
+++ b/eric7.epj	Sat Aug 21 14:21:44 2021 +0200
@@ -1005,7 +1005,6 @@
       "eric7/DebugClients/Python/coverage/__init__.py",
       "eric7/DebugClients/Python/coverage/__main__.py",
       "eric7/DebugClients/Python/coverage/annotate.py",
-      "eric7/DebugClients/Python/coverage/backunittest.py",
       "eric7/DebugClients/Python/coverage/backward.py",
       "eric7/DebugClients/Python/coverage/bytecode.py",
       "eric7/DebugClients/Python/coverage/cmdline.py",
@@ -1025,7 +1024,6 @@
       "eric7/DebugClients/Python/coverage/misc.py",
       "eric7/DebugClients/Python/coverage/multiproc.py",
       "eric7/DebugClients/Python/coverage/numbits.py",
-      "eric7/DebugClients/Python/coverage/optional.py",
       "eric7/DebugClients/Python/coverage/parser.py",
       "eric7/DebugClients/Python/coverage/phystokens.py",
       "eric7/DebugClients/Python/coverage/plugin.py",
--- a/eric7/DebugClients/Python/coverage/backunittest.py	Fri Aug 20 19:56:17 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Implementations of unittest features from the future."""
-
-import unittest
-
-
-def unittest_has(method):
-    """Does `unittest.TestCase` have `method` defined?"""
-    return hasattr(unittest.TestCase, method)
-
-
-class TestCase(unittest.TestCase):
-    """Just like unittest.TestCase, but with assert methods added.
-
-    Designed to be compatible with 3.1 unittest.  Methods are only defined if
-    `unittest` doesn't have them.
-
-    """
-    # pylint: disable=signature-differs, deprecated-method
-
-    if not unittest_has('assertCountEqual'):
-        def assertCountEqual(self, *args, **kwargs):
-            return self.assertItemsEqual(*args, **kwargs)
-
-    if not unittest_has('assertRaisesRegex'):
-        def assertRaisesRegex(self, *args, **kwargs):
-            return self.assertRaisesRegexp(*args, **kwargs)
-
-    if not unittest_has('assertRegex'):
-        def assertRegex(self, *args, **kwargs):
-            return self.assertRegexpMatches(*args, **kwargs)
--- a/eric7/DebugClients/Python/coverage/backward.py	Fri Aug 20 19:56:17 2021 +0200
+++ b/eric7/DebugClients/Python/coverage/backward.py	Sat Aug 21 14:21:44 2021 +0200
@@ -78,7 +78,9 @@
 
 try:
     import reprlib
-except ImportError:
+except ImportError:             # pragma: not covered
+    # We need this on Python 2, but in testing environments, a backport is
+    # installed, so this import isn't used.
     import repr as reprlib
 
 # A function to iterate listlessly over a dict's items, and one to get the
@@ -215,9 +217,6 @@
             items = ("{}={!r}".format(k, self.__dict__[k]) for k in keys)
             return "{}({})".format(type(self).__name__, ", ".join(items))
 
-        def __eq__(self, other):
-            return self.__dict__ == other.__dict__
-
 
 def format_local_datetime(dt):
     """Return a string with local timezone representing the date.
@@ -245,15 +244,17 @@
 
     """
     try:
-        from importlib.machinery import SourceFileLoader
+        import importlib.util as importlib_util
     except ImportError:
-        SourceFileLoader = None
+        importlib_util = None
 
     if modfile is None:
         modfile = modname + '.py'
-    if SourceFileLoader:
-        # pylint: disable=no-value-for-parameter, deprecated-method
-        mod = SourceFileLoader(modname, modfile).load_module()
+    if importlib_util:
+        spec = importlib_util.spec_from_file_location(modname, modfile)
+        mod = importlib_util.module_from_spec(spec)
+        sys.modules[modname] = mod
+        spec.loader.exec_module(mod)
     else:
         for suff in imp.get_suffixes():                 # pragma: part covered
             if suff[0] == '.py':
--- a/eric7/DebugClients/Python/coverage/cmdline.py	Fri Aug 20 19:56:17 2021 +0200
+++ b/eric7/DebugClients/Python/coverage/cmdline.py	Sat Aug 21 14:21:44 2021 +0200
@@ -31,6 +31,10 @@
         '-a', '--append', action='store_true',
         help="Append coverage data to .coverage, otherwise it starts clean each time.",
     )
+    keep = optparse.make_option(
+        '', '--keep', action='store_true',
+        help="Keep original coverage files, otherwise they are deleted.",
+    )
     branch = optparse.make_option(
         '', '--branch', action='store_true',
         help="Measure branch coverage in addition to statement coverage.",
@@ -215,6 +219,7 @@
             help=None,
             ignore_errors=None,
             include=None,
+            keep=None,
             module=None,
             omit=None,
             contexts=None,
@@ -333,6 +338,7 @@
         "combine",
         [
             Opts.append,
+            Opts.keep,
             ] + GLOBAL_ARGS,
         usage="[options] <path1> <path2> ... <pathN>",
         description=(
@@ -585,7 +591,7 @@
             if options.append:
                 self.coverage.load()
             data_dirs = args or None
-            self.coverage.combine(data_dirs, strict=True)
+            self.coverage.combine(data_dirs, strict=True, keep=bool(options.keep))
             self.coverage.save()
             return OK
 
@@ -765,7 +771,7 @@
                 self.coverage.load()
                 data = self.coverage.get_data()
                 print(info_header("data"))
-                print("path: %s" % self.coverage.get_data().data_filename())
+                print("path: %s" % data.data_filename())
                 if data:
                     print("has_arcs: %r" % data.has_arcs())
                     summary = line_counts(data, fullpath=True)
--- a/eric7/DebugClients/Python/coverage/collector.py	Fri Aug 20 19:56:17 2021 +0200
+++ b/eric7/DebugClients/Python/coverage/collector.py	Sat Aug 21 14:21:44 2021 +0200
@@ -55,7 +55,7 @@
     _collectors = []
 
     # The concurrency settings we support here.
-    SUPPORTED_CONCURRENCIES = set(["greenlet", "eventlet", "gevent", "thread"])
+    SUPPORTED_CONCURRENCIES = {"greenlet", "eventlet", "gevent", "thread"}
 
     def __init__(
         self, should_trace, check_include, should_start_context, file_mapper,
--- a/eric7/DebugClients/Python/coverage/config.py	Fri Aug 20 19:56:17 2021 +0200
+++ b/eric7/DebugClients/Python/coverage/config.py	Sat Aug 21 14:21:44 2021 +0200
@@ -63,7 +63,7 @@
             real_section = section_prefix + section
             if configparser.RawConfigParser.has_section(self, real_section):
                 return configparser.RawConfigParser.options(self, real_section)
-        raise configparser.NoSectionError
+        raise configparser.NoSectionError(section)
 
     def get_section(self, section):
         """Get the contents of a section, as a dictionary."""
@@ -87,7 +87,7 @@
             if configparser.RawConfigParser.has_option(self, real_section, option):
                 break
         else:
-            raise configparser.NoOptionError
+            raise configparser.NoOptionError(option, section)
 
         v = configparser.RawConfigParser.get(self, real_section, option, *args, **kwargs)
         v = substitute_variables(v, os.environ)
@@ -217,6 +217,8 @@
         # Defaults for [html]
         self.extra_css = None
         self.html_dir = "htmlcov"
+        self.html_skip_covered = None
+        self.html_skip_empty = None
         self.html_title = "Coverage report"
         self.show_contexts = False
 
@@ -384,6 +386,8 @@
         # [html]
         ('extra_css', 'html:extra_css'),
         ('html_dir', 'html:directory'),
+        ('html_skip_covered', 'html:skip_covered', 'boolean'),
+        ('html_skip_empty', 'html:skip_empty', 'boolean'),
         ('html_title', 'html:title'),
         ('show_contexts', 'html:show_contexts', 'boolean'),
 
@@ -473,6 +477,20 @@
         # If we get here, we didn't find the option.
         raise CoverageException("No such option: %r" % option_name)
 
+    def post_process_file(self, path):
+        """Make final adjustments to a file path to make it usable."""
+        return os.path.expanduser(path)
+
+    def post_process(self):
+        """Make final adjustments to settings to make them usable."""
+        self.data_file = self.post_process_file(self.data_file)
+        self.html_dir = self.post_process_file(self.html_dir)
+        self.xml_output = self.post_process_file(self.xml_output)
+        self.paths = collections.OrderedDict(
+            (k, [self.post_process_file(f) for f in v])
+            for k, v in self.paths.items()
+        )
+
 
 def config_files_to_try(config_file):
     """What config files should we try to read?
@@ -547,12 +565,6 @@
 
     # Once all the config has been collected, there's a little post-processing
     # to do.
-    config.data_file = os.path.expanduser(config.data_file)
-    config.html_dir = os.path.expanduser(config.html_dir)
-    config.xml_output = os.path.expanduser(config.xml_output)
-    config.paths = collections.OrderedDict(
-        (k, [os.path.expanduser(f) for f in v])
-        for k, v in config.paths.items()
-    )
+    config.post_process()
 
     return config
--- a/eric7/DebugClients/Python/coverage/control.py	Fri Aug 20 19:56:17 2021 +0200
+++ b/eric7/DebugClients/Python/coverage/control.py	Sat Aug 21 14:21:44 2021 +0200
@@ -659,7 +659,7 @@
         data = self.get_data()
         data.write()
 
-    def combine(self, data_paths=None, strict=False):
+    def combine(self, data_paths=None, strict=False, keep=False):
         """Combine together a number of similarly-named coverage data files.
 
         All coverage data files whose name starts with `data_file` (from the
@@ -674,12 +674,16 @@
         If `strict` is true, then it is an error to attempt to combine when
         there are no data files to combine.
 
+        If `keep` is true, then original input data files won't be deleted.
+
         .. versionadded:: 4.0
             The `data_paths` parameter.
 
         .. versionadded:: 4.3
             The `strict` parameter.
 
+        .. versionadded: 5.5
+            The `keep` parameter.
         """
         self._init()
         self._init_data(suffix=None)
@@ -694,7 +698,13 @@
                 for pattern in paths[1:]:
                     aliases.add(pattern, result)
 
-        combine_parallel_data(self._data, aliases=aliases, data_paths=data_paths, strict=strict)
+        combine_parallel_data(
+            self._data,
+            aliases=aliases,
+            data_paths=data_paths,
+            strict=strict,
+            keep=keep,
+        )
 
     def get_data(self):
         """Get the collected data.
@@ -955,8 +965,8 @@
         with override_config(self,
             ignore_errors=ignore_errors, report_omit=omit, report_include=include,
             html_dir=directory, extra_css=extra_css, html_title=title,
-            skip_covered=skip_covered, show_contexts=show_contexts, report_contexts=contexts,
-            skip_empty=skip_empty, precision=precision,
+            html_skip_covered=skip_covered, show_contexts=show_contexts, report_contexts=contexts,
+            html_skip_empty=skip_empty, precision=precision,
         ):
             reporter = HtmlReporter(self)
             return reporter.report(morfs)
--- a/eric7/DebugClients/Python/coverage/data.py	Fri Aug 20 19:56:17 2021 +0200
+++ b/eric7/DebugClients/Python/coverage/data.py	Sat Aug 21 14:21:44 2021 +0200
@@ -52,7 +52,7 @@
     hasher.update(data.file_tracer(filename))
 
 
-def combine_parallel_data(data, aliases=None, data_paths=None, strict=False):
+def combine_parallel_data(data, aliases=None, data_paths=None, strict=False, keep=False):
     """Combine a number of data files together.
 
     Treat `data.filename` as a file prefix, and combine the data from all
@@ -68,7 +68,7 @@
     If `data_paths` is not provided, then the directory portion of
     `data.filename` is used as the directory to search for data files.
 
-    Every data file found and combined is then deleted from disk. If a file
+    Unless `keep` is True every data file found and combined is then deleted from disk. If a file
     cannot be read, a warning will be issued, and the file will not be
     deleted.
 
@@ -116,9 +116,10 @@
         else:
             data.update(new_data, aliases=aliases)
             files_combined += 1
-            if data._debug.should('dataio'):
-                data._debug.write("Deleting combined data file %r" % (f,))
-            file_be_gone(f)
+            if not keep:
+                if data._debug.should('dataio'):
+                    data._debug.write("Deleting combined data file %r" % (f,))
+                file_be_gone(f)
 
     if strict and not files_combined:
         raise CoverageException("No usable data files")
--- a/eric7/DebugClients/Python/coverage/doc/CHANGES.rst	Fri Aug 20 19:56:17 2021 +0200
+++ b/eric7/DebugClients/Python/coverage/doc/CHANGES.rst	Sat Aug 21 14:21:44 2021 +0200
@@ -21,6 +21,77 @@
     ..  Version 9.8.1 --- 2027-07-27
     ..  ----------------------------
 
+.. _changes_55:
+
+Version 5.5 --- 2021-02-28
+--------------------------
+
+- ``coverage combine`` has a new option, ``--keep`` to keep the original data
+  files after combining them.  The default is still to delete the files after
+  they have been combined.  This was requested in `issue 1108`_ and implemented
+  in `pull request 1110`_.  Thanks, Éric Larivière.
+
+- When reporting missing branches in ``coverage report``, branches aren't
+  reported that jump to missing lines.  This adds to the long-standing behavior
+  of not reporting branches from missing lines.  Now branches are only reported
+  if both the source and destination lines are executed.  Closes both `issue
+  1065`_ and `issue 955`_.
+
+- Minor improvements to the HTML report:
+
+  - The state of the line visibility selector buttons is saved in local storage
+    so you don't have to fiddle with them so often, fixing `issue 1123`_.
+
+  - It has a little more room for line numbers so that 4-digit numbers work
+    well, fixing `issue 1124`_.
+
+- Improved the error message when combining line and branch data, so that users
+  will be more likely to understand what's happening, closing `issue 803`_.
+
+.. _issue 803: https://github.com/nedbat/coveragepy/issues/803
+.. _issue 955: https://github.com/nedbat/coveragepy/issues/955
+.. _issue 1065: https://github.com/nedbat/coveragepy/issues/1065
+.. _issue 1108: https://github.com/nedbat/coveragepy/issues/1108
+.. _pull request 1110: https://github.com/nedbat/coveragepy/pull/1110
+.. _issue 1123: https://github.com/nedbat/coveragepy/issues/1123
+.. _issue 1124: https://github.com/nedbat/coveragepy/issues/1124
+
+
+.. _changes_54:
+
+Version 5.4 --- 2021-01-24
+--------------------------
+
+- The text report produced by ``coverage report`` now always outputs a TOTAL
+  line, even if only one Python file is reported.  This makes regex parsing
+  of the output easier.  Thanks, Judson Neer.  This had been requested a number
+  of times (`issue 1086`_, `issue 922`_, `issue 732`_).
+
+- The ``skip_covered`` and ``skip_empty`` settings in the configuration file
+  can now be specified in the ``[html]`` section, so that text reports and HTML
+  reports can use separate settings.  The HTML report will still use the
+  ``[report]`` settings if there isn't a value in the ``[html]`` section.
+  Closes `issue 1090`_.
+
+- Combining files on Windows across drives now works properly, fixing `issue
+  577`_.  Thanks, `Valentin Lab <pr1080_>`_.
+
+- Fix an obscure warning from deep in the _decimal module, as reported in
+  `issue 1084`_.
+
+- Update to support Python 3.10 alphas in progress, including `PEP 626: Precise
+  line numbers for debugging and other tools <pep626_>`_.
+
+.. _issue 577: https://github.com/nedbat/coveragepy/issues/577
+.. _issue 732: https://github.com/nedbat/coveragepy/issues/732
+.. _issue 922: https://github.com/nedbat/coveragepy/issues/922
+.. _issue 1084: https://github.com/nedbat/coveragepy/issues/1084
+.. _issue 1086: https://github.com/nedbat/coveragepy/issues/1086
+.. _issue 1090: https://github.com/nedbat/coveragepy/issues/1090
+.. _pr1080: https://github.com/nedbat/coveragepy/pull/1080
+.. _pep626: https://www.python.org/dev/peps/pep-0626/
+
+
 .. _changes_531:
 
 Version 5.3.1 --- 2020-12-19
--- a/eric7/DebugClients/Python/coverage/doc/CONTRIBUTORS.txt	Fri Aug 20 19:56:17 2021 +0200
+++ b/eric7/DebugClients/Python/coverage/doc/CONTRIBUTORS.txt	Sat Aug 21 14:21:44 2021 +0200
@@ -19,6 +19,7 @@
 Arcadiy Ivanov
 Aron Griffis
 Artem Dayneko
+Arthur Deygin
 Ben Finney
 Bernát Gábor
 Bill Hart
@@ -54,9 +55,10 @@
 Dmitry Shishov
 Dmitry Trofimov
 Eduardo Schettino
+Edward Loper
 Eli Skeggs
 Emil Madsen
-Edward Loper
+Éric Larivière
 Federico Bond
 Frazer McLean
 Geoff Bache
@@ -79,6 +81,7 @@
 Jon Dufresne
 Joseph Tate
 Josh Williams
+Judson Neer
 Julian Berman
 Julien Voisin
 Justas Sadzevičius
@@ -135,6 +138,7 @@
 Thijs Triemstra
 Thomas Grainger
 Titus Brown
+Valentin Lab
 Vince Salvino
 Ville Skyttä
 Xie Yanbo
--- a/eric7/DebugClients/Python/coverage/doc/README.rst	Fri Aug 20 19:56:17 2021 +0200
+++ b/eric7/DebugClients/Python/coverage/doc/README.rst	Sat Aug 21 14:21:44 2021 +0200
@@ -8,7 +8,7 @@
 Code coverage testing for Python.
 
 |  |license| |versions| |status|
-|  |ci-status| |docs| |codecov|
+|  |test-status| |quality-status| |docs| |codecov|
 |  |kit| |format| |repos| |downloads|
 |  |stars| |forks| |contributors|
 |  |tidelift| |twitter-coveragepy| |twitter-nedbat|
@@ -21,7 +21,7 @@
 
 * CPython 2.7.
 * CPython 3.5 through 3.10 alpha.
-* PyPy2 7.3.1 and PyPy3 7.3.1.
+* PyPy2 7.3.3 and PyPy3 7.3.3.
 
 Documentation is on `Read the Docs`_.  Code repository and issue tracker are on
 `GitHub`_.
@@ -95,9 +95,12 @@
 .. _NOTICE.txt: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
 
 
-.. |ci-status| image:: https://github.com/nedbat/coveragepy/workflows/Test%20Suite/badge.svg
-    :target: https://github.com/nedbat/coveragepy/actions?query=workflow%3A%22Test+Suite%22
-    :alt: Build status
+.. |test-status| image:: https://github.com/nedbat/coveragepy/actions/workflows/testsuite.yml/badge.svg?branch=master&event=push
+    :target: https://github.com/nedbat/coveragepy/actions/workflows/testsuite.yml
+    :alt: Test suite status
+.. |quality-status| image:: https://github.com/nedbat/coveragepy/actions/workflows/quality.yml/badge.svg?branch=master&event=push
+    :target: https://github.com/nedbat/coveragepy/actions/workflows/quality.yml
+    :alt: Quality check status
 .. |docs| image:: https://readthedocs.org/projects/coverage/badge/?version=latest&style=flat
     :target: https://coverage.readthedocs.io/
     :alt: Documentation
--- a/eric7/DebugClients/Python/coverage/env.py	Fri Aug 20 19:56:17 2021 +0200
+++ b/eric7/DebugClients/Python/coverage/env.py	Sat Aug 21 14:21:44 2021 +0200
@@ -11,35 +11,49 @@
 WINDOWS = sys.platform == "win32"
 LINUX = sys.platform.startswith("linux")
 
+# Python implementations.
+CPYTHON = (platform.python_implementation() == "CPython")
+PYPY = (platform.python_implementation() == "PyPy")
+JYTHON = (platform.python_implementation() == "Jython")
+IRONPYTHON = (platform.python_implementation() == "IronPython")
+
 # Python versions. We amend version_info with one more value, a zero if an
 # official version, or 1 if built from source beyond an official version.
 PYVERSION = sys.version_info + (int(platform.python_version()[-1] == "+"),)
 PY2 = PYVERSION < (3, 0)
 PY3 = PYVERSION >= (3, 0)
 
-# Python implementations.
-PYPY = (platform.python_implementation() == 'PyPy')
 if PYPY:
     PYPYVERSION = sys.pypy_version_info
 
 PYPY2 = PYPY and PY2
 PYPY3 = PYPY and PY3
 
-JYTHON = (platform.python_implementation() == 'Jython')
-IRONPYTHON = (platform.python_implementation() == 'IronPython')
-
-# Python behavior
+# Python behavior.
 class PYBEHAVIOR(object):
     """Flags indicating this Python's behavior."""
 
+    pep626 = CPYTHON and (PYVERSION > (3, 10, 0, 'alpha', 4))
+
     # Is "if __debug__" optimized away?
-    optimize_if_debug = (not PYPY)
+    if PYPY3:
+        optimize_if_debug = True
+    elif PYPY2:
+        optimize_if_debug = False
+    else:
+        optimize_if_debug = not pep626
 
     # Is "if not __debug__" optimized away?
     optimize_if_not_debug = (not PYPY) and (PYVERSION >= (3, 7, 0, 'alpha', 4))
+    if pep626:
+        optimize_if_not_debug = False
+    if PYPY3:
+        optimize_if_not_debug = True
 
     # Is "if not __debug__" optimized away even better?
     optimize_if_not_debug2 = (not PYPY) and (PYVERSION >= (3, 8, 0, 'beta', 1))
+    if pep626:
+        optimize_if_not_debug2 = False
 
     # Do we have yield-from?
     yield_from = (PYVERSION >= (3, 3))
@@ -66,13 +80,16 @@
     # used to be an empty string (meaning the current directory). It changed
     # to be the actual path to the current directory, so that os.chdir wouldn't
     # affect the outcome.
-    actual_syspath0_dash_m = (not PYPY) and (PYVERSION >= (3, 7, 0, 'beta', 3))
+    actual_syspath0_dash_m = CPYTHON and (PYVERSION >= (3, 7, 0, 'beta', 3))
+
+    # 3.7 changed how functions with only docstrings are numbered.
+    docstring_only_function = (not PYPY) and ((3, 7, 0, 'beta', 5) <= PYVERSION <= (3, 10))
 
     # When a break/continue/return statement in a try block jumps to a finally
     # block, does the finally block do the break/continue/return (pre-3.8), or
     # does the finally jump back to the break/continue/return (3.8) to do the
     # work?
-    finally_jumps_back = (PYVERSION >= (3, 8))
+    finally_jumps_back = ((3, 8) <= PYVERSION < (3, 10))
 
     # When a function is decorated, does the trace function get called for the
     # @-line and also the def-line (new behavior in 3.8)? Or just the @-line
@@ -85,6 +102,20 @@
     # Python 3.9a1 made sys.argv[0] and other reported files absolute paths.
     report_absolute_files = (PYVERSION >= (3, 9))
 
+    # Lines after break/continue/return/raise are no longer compiled into the
+    # bytecode.  They used to be marked as missing, now they aren't executable.
+    omit_after_jump = pep626
+
+    # PyPy has always omitted statements after return.
+    omit_after_return = omit_after_jump or PYPY
+
+    # Modules used to have firstlineno equal to the line number of the first
+    # real line of code.  Now they always start at 1.
+    module_firstline_1 = pep626
+
+    # Are "if 0:" lines (and similar) kept in the compiled code?
+    keep_constant_test = pep626
+
 # Coverage.py specifics.
 
 # Are we using the C-implemented trace function?
--- a/eric7/DebugClients/Python/coverage/files.py	Fri Aug 20 19:56:17 2021 +0200
+++ b/eric7/DebugClients/Python/coverage/files.py	Sat Aug 21 14:21:44 2021 +0200
@@ -359,17 +359,19 @@
         match an entire tree, and not just its root.
 
         """
+        pattern_sep = sep(pattern)
+
         if len(pattern) > 1:
             pattern = pattern.rstrip(r"\/")
 
         # The pattern can't end with a wildcard component.
         if pattern.endswith("*"):
             raise CoverageException("Pattern must not end with wildcards.")
-        pattern_sep = sep(pattern)
 
         # The pattern is meant to match a filepath.  Let's make it absolute
         # unless it already is, or is meant to match any prefix.
-        if not pattern.startswith('*') and not isabs_anywhere(pattern):
+        if not pattern.startswith('*') and not isabs_anywhere(pattern +
+                                                              pattern_sep):
             pattern = abs_file(pattern)
         if not pattern.endswith(pattern_sep):
             pattern += pattern_sep
--- a/eric7/DebugClients/Python/coverage/html.py	Fri Aug 20 19:56:17 2021 +0200
+++ b/eric7/DebugClients/Python/coverage/html.py	Sat Aug 21 14:21:44 2021 +0200
@@ -84,7 +84,7 @@
         data = self.coverage.get_data()
         self.has_arcs = data.has_arcs()
         if self.config.show_contexts:
-            if data.measured_contexts() == set([""]):
+            if data.measured_contexts() == {""}:
                 self.coverage._warn("No contexts were measured")
         data.set_query_contexts(self.config.report_contexts)
 
@@ -173,6 +173,14 @@
         self.coverage = cov
         self.config = self.coverage.config
         self.directory = self.config.html_dir
+
+        self.skip_covered = self.config.html_skip_covered
+        if self.skip_covered is None:
+            self.skip_covered = self.config.skip_covered
+        self.skip_empty = self.config.html_skip_empty
+        if self.skip_empty is None:
+            self.skip_empty= self.config.skip_empty
+
         title = self.config.html_title
         if env.PY2:
             title = title.decode("utf8")
@@ -271,7 +279,7 @@
         nums = analysis.numbers
         self.all_files_nums.append(nums)
 
-        if self.config.skip_covered:
+        if self.skip_covered:
             # Don't report on 100% files.
             no_missing_lines = (nums.n_missing == 0)
             no_missing_branches = (nums.n_partial_branches == 0)
@@ -280,7 +288,7 @@
                 file_be_gone(html_path)
                 return
 
-        if self.config.skip_empty:
+        if self.skip_empty:
             # Don't report on empty files.
             if nums.n_statements == 0:
                 file_be_gone(html_path)
--- a/eric7/DebugClients/Python/coverage/misc.py	Fri Aug 20 19:56:17 2021 +0200
+++ b/eric7/DebugClients/Python/coverage/misc.py	Sat Aug 21 14:21:44 2021 +0200
@@ -63,7 +63,7 @@
     def new_contract(*args, **kwargs):
         """A proxy for contracts.new_contract that doesn't mind happening twice."""
         try:
-            return raw_new_contract(*args, **kwargs)
+            raw_new_contract(*args, **kwargs)
         except ValueError:
             # During meta-coverage, this module is imported twice, and
             # PyContracts doesn't like redefining contracts. It's OK.
@@ -77,7 +77,7 @@
     def one_of(argnames):
         """Ensure that only one of the argnames is non-None."""
         def _decorator(func):
-            argnameset = set(name.strip() for name in argnames.split(","))
+            argnameset = {name.strip() for name in argnames.split(",")}
             def _wrapper(*args, **kwargs):
                 vals = [kwargs.get(name) for name in argnameset]
                 assert sum(val is not None for val in vals) == 1
--- a/eric7/DebugClients/Python/coverage/multiproc.py	Fri Aug 20 19:56:17 2021 +0200
+++ b/eric7/DebugClients/Python/coverage/multiproc.py	Sat Aug 21 14:21:44 2021 +0200
@@ -28,7 +28,7 @@
 class ProcessWithCoverage(OriginalProcess):         # pylint: disable=abstract-method
     """A replacement for multiprocess.Process that starts coverage."""
 
-    def _bootstrap(self, *args, **kwargs):          # pylint: disable=signature-differs
+    def _bootstrap(self, *args, **kwargs):
         """Wrapper around _bootstrap to start coverage."""
         try:
             from coverage import Coverage       # avoid circular import
--- a/eric7/DebugClients/Python/coverage/optional.py	Fri Aug 20 19:56:17 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,76 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""
-Imports that we need at runtime, but might not be present.
-
-When importing one of these modules, always do it in the function where you
-need the module.  Some tests will need to remove the module.  If you import
-it at the top level of your module, then the test won't be able to simulate
-the module being unimportable.
-
-The import will always succeed, but the value will be None if the module is
-unavailable.
-
-Bad::
-
-    # MyModule.py
-    import unsure
-
-    def use_unsure():
-        unsure.something()
-
-Also bad::
-
-    # MyModule.py
-    from coverage.optional import unsure
-
-    def use_unsure():
-        unsure.something()
-
-Good::
-
-    # MyModule.py
-
-    def use_unsure():
-        from coverage.optional import unsure
-        if unsure is None:
-            raise Exception("Module unsure isn't available!")
-
-        unsure.something()
-
-"""
-
-import contextlib
-
-# This file's purpose is to provide modules to be imported from here.
-# pylint: disable=unused-import
-
-# TOML support is an install-time extra option.
-try:
-    import toml
-except ImportError:         # pragma: not covered
-    toml = None
-
-
-@contextlib.contextmanager
-def without(modname):
-    """Hide a module for testing.
-
-    Use this in a test function to make an optional module unavailable during
-    the test::
-
-        with coverage.optional.without('toml'):
-            use_toml_somehow()
-
-    Arguments:
-        modname (str): the name of a module importable from
-            `coverage.optional`.
-
-    """
-    real_module = globals()[modname]
-    try:
-        globals()[modname] = None
-        yield
-    finally:
-        globals()[modname] = real_module
--- a/eric7/DebugClients/Python/coverage/parser.py	Fri Aug 20 19:56:17 2021 +0200
+++ b/eric7/DebugClients/Python/coverage/parser.py	Sat Aug 21 14:21:44 2021 +0200
@@ -205,6 +205,12 @@
         if not empty:
             self.raw_statements.update(self.byte_parser._find_statements())
 
+        # The first line of modules can lie and say 1 always, even if the first
+        # line of code is later. If so, map 1 to the actual first line of the
+        # module.
+        if env.PYBEHAVIOR.module_firstline_1 and self._multiline:
+            self._multiline[1] = min(self.raw_statements)
+
     def first_line(self, line):
         """Return the first line number of the statement including `line`."""
         if line < 0:
@@ -220,7 +226,7 @@
         Returns a set of the first lines.
 
         """
-        return set(self.first_line(l) for l in lines)
+        return {self.first_line(l) for l in lines}
 
     def translate_lines(self, lines):
         """Implement `FileReporter.translate_lines`."""
@@ -332,9 +338,7 @@
         fragment_pairs = self._missing_arc_fragments.get((start, end), [(None, None)])
 
         msgs = []
-        for fragment_pair in fragment_pairs:
-            smsg, emsg = fragment_pair
-
+        for smsg, emsg in fragment_pairs:
             if emsg is None:
                 if end < 0:
                     # Hmm, maybe we have a one-line callable, let's check.
@@ -389,34 +393,35 @@
         """
         return (ByteParser(self.text, code=c) for c in code_objects(self.code))
 
-    def _bytes_lines(self):
-        """Map byte offsets to line numbers in `code`.
-
-        Uses co_lnotab described in Python/compile.c to map byte offsets to
-        line numbers.  Produces a sequence: (b0, l0), (b1, l1), ...
+    def _line_numbers(self):
+        """Yield the line numbers possible in this code object.
 
-        Only byte offsets that correspond to line numbers are included in the
-        results.
-
+        Uses co_lnotab described in Python/compile.c to find the
+        line numbers.  Produces a sequence: l0, l1, ...
         """
-        # Adapted from dis.py in the standard library.
-        byte_increments = bytes_to_ints(self.code.co_lnotab[0::2])
-        line_increments = bytes_to_ints(self.code.co_lnotab[1::2])
+        if hasattr(self.code, "co_lines"):
+            for _, _, line in self.code.co_lines():
+                if line is not None:
+                    yield line
+        else:
+            # Adapted from dis.py in the standard library.
+            byte_increments = bytes_to_ints(self.code.co_lnotab[0::2])
+            line_increments = bytes_to_ints(self.code.co_lnotab[1::2])
 
-        last_line_num = None
-        line_num = self.code.co_firstlineno
-        byte_num = 0
-        for byte_incr, line_incr in zip(byte_increments, line_increments):
-            if byte_incr:
-                if line_num != last_line_num:
-                    yield (byte_num, line_num)
-                    last_line_num = line_num
-                byte_num += byte_incr
-            if env.PYBEHAVIOR.negative_lnotab and line_incr >= 0x80:
-                line_incr -= 0x100
-            line_num += line_incr
-        if line_num != last_line_num:
-            yield (byte_num, line_num)
+            last_line_num = None
+            line_num = self.code.co_firstlineno
+            byte_num = 0
+            for byte_incr, line_incr in zip(byte_increments, line_increments):
+                if byte_incr:
+                    if line_num != last_line_num:
+                        yield line_num
+                        last_line_num = line_num
+                    byte_num += byte_incr
+                if env.PYBEHAVIOR.negative_lnotab and line_incr >= 0x80:
+                    line_incr -= 0x100
+                line_num += line_incr
+            if line_num != last_line_num:
+                yield line_num
 
     def _find_statements(self):
         """Find the statements in `self.code`.
@@ -427,7 +432,7 @@
         """
         for bp in self.child_parsers():
             # Get all of the lineno information from this code.
-            for _, l in bp._bytes_lines():
+            for l in bp._line_numbers():
                 yield l
 
 
@@ -520,7 +525,7 @@
     def __init__(self, text, statements, multiline):
         self.root_node = ast.parse(neuter_encoding_declaration(text))
         # TODO: I think this is happening in too many places.
-        self.statements = set(multiline.get(l, l) for l in statements)
+        self.statements = {multiline.get(l, l) for l in statements}
         self.multiline = multiline
 
         if AST_DUMP:                                # pragma: debugging
@@ -619,17 +624,19 @@
             return node.lineno
 
     def _line__Module(self, node):
-        if node.body:
+        if env.PYBEHAVIOR.module_firstline_1:
+            return 1
+        elif node.body:
             return self.line_for_node(node.body[0])
         else:
             # Empty modules have no line number, they always start at 1.
             return 1
 
     # The node types that just flow to the next node with no complications.
-    OK_TO_DEFAULT = set([
+    OK_TO_DEFAULT = {
         "Assign", "Assert", "AugAssign", "Delete", "Exec", "Expr", "Global",
         "Import", "ImportFrom", "Nonlocal", "Pass", "Print",
-    ])
+    }
 
     @contract(returns='ArcStarts')
     def add_arcs(self, node):
@@ -661,7 +668,7 @@
                     print("*** Unhandled: {}".format(node))
 
             # Default for simple statements: one exit from this node.
-            return set([ArcStart(self.line_for_node(node))])
+            return {ArcStart(self.line_for_node(node))}
 
     @one_of("from_start, prev_starts")
     @contract(returns='ArcStarts')
@@ -677,7 +684,7 @@
 
         """
         if prev_starts is None:
-            prev_starts = set([from_start])
+            prev_starts = {from_start}
         for body_node in body:
             lineno = self.line_for_node(body_node)
             first_line = self.multiline.get(lineno, lineno)
@@ -890,7 +897,7 @@
                         self.add_arc(last, lineno)
                         last = lineno
         # The body is handled in collect_arcs.
-        return set([ArcStart(last)])
+        return {ArcStart(last)}
 
     _handle__ClassDef = _handle_decorated
 
@@ -984,7 +991,7 @@
                 # If there are `except` clauses, then raises in the try body
                 # will already jump to them.  Start this set over for raises in
                 # `except` and `else`.
-                try_block.raise_from = set([])
+                try_block.raise_from = set()
         else:
             self.block_stack.pop()
 
@@ -1079,7 +1086,7 @@
             if start.cause is not None:
                 causes.append(start.cause.format(lineno=start.lineno))
         cause = " or ".join(causes)
-        exits = set(ArcStart(xit.lineno, cause) for xit in exits)
+        exits = {ArcStart(xit.lineno, cause) for xit in exits}
         return exits
 
     @contract(returns='ArcStarts')
@@ -1109,9 +1116,14 @@
 
     @contract(returns='ArcStarts')
     def _handle__While(self, node):
+        start = to_top = self.line_for_node(node.test)
         constant_test = self.is_constant_expr(node.test)
-        start = to_top = self.line_for_node(node.test)
+        top_is_body0 = False
         if constant_test and (env.PY3 or constant_test == "Num"):
+            top_is_body0 = True
+        if env.PYBEHAVIOR.keep_constant_test:
+            top_is_body0 = False
+        if top_is_body0:
             to_top = self.line_for_node(node.body[0])
         self.block_stack.append(LoopBlock(start=to_top))
         from_start = ArcStart(start, cause="the condition on line {lineno} was never true")
--- a/eric7/DebugClients/Python/coverage/phystokens.py	Fri Aug 20 19:56:17 2021 +0200
+++ b/eric7/DebugClients/Python/coverage/phystokens.py	Sat Aug 21 14:21:44 2021 +0200
@@ -87,7 +87,7 @@
 
     """
 
-    ws_tokens = set([token.INDENT, token.DEDENT, token.NEWLINE, tokenize.NL])
+    ws_tokens = {token.INDENT, token.DEDENT, token.NEWLINE, tokenize.NL}
     line = []
     col = 0
 
--- a/eric7/DebugClients/Python/coverage/pytracer.py	Fri Aug 20 19:56:17 2021 +0200
+++ b/eric7/DebugClients/Python/coverage/pytracer.py	Sat Aug 21 14:21:44 2021 +0200
@@ -14,6 +14,11 @@
 if env.PY2:
     YIELD_VALUE = chr(YIELD_VALUE)
 
+# When running meta-coverage, this file can try to trace itself, which confuses
+# everything.  Don't trace ourselves.
+
+THIS_FILE = __file__.rstrip("co")
+
 
 class PyTracer(object):
     """Python implementation of the raw data tracer."""
@@ -72,25 +77,47 @@
     def log(self, marker, *args):
         """For hard-core logging of what this tracer is doing."""
         with open("/tmp/debug_trace.txt", "a") as f:
-            f.write("{} {:x}.{:x}[{}] {:x} {}\n".format(
+            f.write("{} {}[{}]".format(
                 marker,
                 id(self),
-                self.thread.ident,
                 len(self.data_stack),
-                self.threading.currentThread().ident,
-                " ".join(map(str, args))
             ))
+            if 0:
+                f.write(".{:x}.{:x}".format(
+                    self.thread.ident,
+                    self.threading.currentThread().ident,
+                ))
+            f.write(" {}".format(" ".join(map(str, args))))
+            if 0:
+                f.write(" | ")
+                stack = " / ".join(
+                    (fname or "???").rpartition("/")[-1]
+                    for _, fname, _, _ in self.data_stack
+                )
+                f.write(stack)
+            f.write("\n")
 
     def _trace(self, frame, event, arg_unused):
         """The trace function passed to sys.settrace."""
 
-        #self.log(":", frame.f_code.co_filename, frame.f_lineno, event)
+        if THIS_FILE in frame.f_code.co_filename:
+            return None
+
+        #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
             # The PyTrace.stop() method has been called, possibly by another
             # thread, let's deactivate ourselves now.
-            #self.log("X", frame.f_code.co_filename, frame.f_lineno)
+            if 0:
+                self.log("---\nX", frame.f_code.co_filename, frame.f_lineno)
+                f = frame
+                while f:
+                    self.log(">", f.f_code.co_filename, f.f_lineno, f.f_code.co_name, f.f_trace)
+                    f = f.f_back
             sys.settrace(None)
+            self.cur_file_dict, self.cur_file_name, self.last_line, self.started_context = (
+                self.data_stack.pop()
+            )
             return None
 
         if self.last_exc_back:
@@ -104,10 +131,13 @@
                 )
             self.last_exc_back = None
 
+        # if event != 'call' and frame.f_code.co_filename != self.cur_file_name:
+        #     self.log("---\n*", frame.f_code.co_filename, self.cur_file_name, frame.f_lineno)
+
         if event == 'call':
             # Should we start a new context?
             if self.should_start_context and self.context is None:
-                context_maybe = self.should_start_context(frame)    # pylint: disable=not-callable
+                context_maybe = self.should_start_context(frame)
                 if context_maybe is not None:
                     self.context = context_maybe
                     self.started_context = True
@@ -132,15 +162,15 @@
             self.cur_file_name = filename
             disp = self.should_trace_cache.get(filename)
             if disp is None:
-                disp = self.should_trace(filename, frame)   # pylint: disable=not-callable
-                self.should_trace_cache[filename] = disp    # pylint: disable=unsupported-assignment-operation
+                disp = self.should_trace(filename, frame)
+                self.should_trace_cache[filename] = disp
 
             self.cur_file_dict = None
             if disp.trace:
                 tracename = disp.source_filename
-                if tracename not in self.data:              # pylint: disable=unsupported-membership-test
-                    self.data[tracename] = {}               # pylint: disable=unsupported-assignment-operation
-                self.cur_file_dict = self.data[tracename]   # pylint: disable=unsubscriptable-object
+                if tracename not in self.data:
+                    self.data[tracename] = {}
+                self.cur_file_dict = self.data[tracename]
             # 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
@@ -153,8 +183,7 @@
             # Record an executed line.
             if self.cur_file_dict is not None:
                 lineno = frame.f_lineno
-                #if frame.f_code.co_filename != self.cur_file_name:
-                #    self.log("*", frame.f_code.co_filename, self.cur_file_name, lineno)
+
                 if self.trace_arcs:
                     self.cur_file_dict[(self.last_line, lineno)] = None
                 else:
@@ -227,7 +256,7 @@
             # 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
-                self.warn(                              # pylint: disable=not-callable
+                self.warn(
                     "Trace function changed, measurement is likely wrong: %r" % (tf,),
                     slug="trace-changed",
                 )
--- a/eric7/DebugClients/Python/coverage/results.py	Fri Aug 20 19:56:17 2021 +0200
+++ b/eric7/DebugClients/Python/coverage/results.py	Sat Aug 21 14:21:44 2021 +0200
@@ -146,10 +146,7 @@
         stats = {}
         for lnum in self._branch_lines():
             exits = self.exit_counts[lnum]
-            try:
-                missing = len(missing_arcs[lnum])
-            except KeyError:
-                missing = 0
+            missing = len(missing_arcs[lnum])
             stats[lnum] = (exits, exits - missing)
         return stats
 
@@ -265,7 +262,7 @@
         # Implementing 0+Numbers allows us to sum() a list of Numbers.
         if other == 0:
             return self
-        return NotImplemented
+        return NotImplemented       # pragma: not covered (we never call it this way)
 
 
 def _line_ranges(statements, lines):
@@ -315,7 +312,7 @@
         line_exits = sorted(arcs)
         for line, exits in line_exits:
             for ex in sorted(exits):
-                if line not in lines:
+                if line not in lines and ex not in lines:
                     dest = (ex if ex > 0 else "exit")
                     line_items.append((line, "%d->%s" % (line, dest)))
 
--- a/eric7/DebugClients/Python/coverage/sqldata.py	Fri Aug 20 19:56:17 2021 +0200
+++ b/eric7/DebugClients/Python/coverage/sqldata.py	Sat Aug 21 14:21:44 2021 +0200
@@ -486,9 +486,9 @@
         assert lines or arcs
         assert not (lines and arcs)
         if lines and self._has_arcs:
-            raise CoverageException("Can't add lines to existing arc data")
+            raise CoverageException("Can't add line measurements to existing branch data")
         if arcs and self._has_lines:
-            raise CoverageException("Can't add arcs to existing line data")
+            raise CoverageException("Can't add branch measurements to existing line data")
         if not self._has_arcs and not self._has_lines:
             self._has_lines = lines
             self._has_arcs = arcs
@@ -784,7 +784,7 @@
         """
         self._start_using()
         with self._connect() as con:
-            contexts = set(row[0] for row in con.execute("select distinct(context) from context"))
+            contexts = {row[0] for row in con.execute("select distinct(context) from context")}
         return contexts
 
     def file_tracer(self, filename):
@@ -857,7 +857,7 @@
             arcs = self.arcs(filename)
             if arcs is not None:
                 all_lines = itertools.chain.from_iterable(arcs)
-                return list(set(l for l in all_lines if l > 0))
+                return list({l for l in all_lines if l > 0})
 
         with self._connect() as con:
             file_id = self._file_id(filename)
--- a/eric7/DebugClients/Python/coverage/summary.py	Fri Aug 20 19:56:17 2021 +0200
+++ b/eric7/DebugClients/Python/coverage/summary.py	Sat Aug 21 14:21:44 2021 +0200
@@ -8,7 +8,7 @@
 from coverage import env
 from coverage.report import get_analysis_to_report
 from coverage.results import Numbers
-from coverage.misc import NotPython, CoverageException, output_encoding
+from coverage.misc import CoverageException, output_encoding
 
 
 class SummaryReporter(object):
@@ -78,29 +78,18 @@
         lines = []
 
         for (fr, analysis) in self.fr_analysis:
-            try:
-                nums = analysis.numbers
+            nums = analysis.numbers
 
-                args = (fr.relative_filename(), nums.n_statements, nums.n_missing)
-                if self.branches:
-                    args += (nums.n_branches, nums.n_partial_branches)
-                args += (nums.pc_covered_str,)
-                if self.config.show_missing:
-                    args += (analysis.missing_formatted(branches=True),)
-                text = fmt_coverage % args
-                # Add numeric percent coverage so that sorting makes sense.
-                args += (nums.pc_covered,)
-                lines.append((text, args))
-            except Exception:
-                report_it = not self.config.ignore_errors
-                if report_it:
-                    typ, msg = sys.exc_info()[:2]
-                    # NotPython is only raised by PythonFileReporter, which has a
-                    # should_be_python() method.
-                    if typ is NotPython and not fr.should_be_python():
-                        report_it = False
-                if report_it:
-                    self.writeout(self.fmt_err % (fr.relative_filename(), typ.__name__, msg))
+            args = (fr.relative_filename(), nums.n_statements, nums.n_missing)
+            if self.branches:
+                args += (nums.n_branches, nums.n_partial_branches)
+            args += (nums.pc_covered_str,)
+            if self.config.show_missing:
+                args += (analysis.missing_formatted(branches=True),)
+            text = fmt_coverage % args
+            # Add numeric percent coverage so that sorting makes sense.
+            args += (nums.pc_covered,)
+            lines.append((text, args))
 
         # Sort the lines and write them out.
         if getattr(self.config, 'sort', None):
@@ -120,8 +109,8 @@
         for line in lines:
             self.writeout(line[0])
 
-        # Write a TOTAl line if we had more than one file.
-        if self.total.n_files > 1:
+        # Write a TOTAL line if we had at least one file.
+        if self.total.n_files > 0:
             self.writeout(rule)
             args = ("TOTAL", self.total.n_statements, self.total.n_missing)
             if self.branches:
--- a/eric7/DebugClients/Python/coverage/tomlconfig.py	Fri Aug 20 19:56:17 2021 +0200
+++ b/eric7/DebugClients/Python/coverage/tomlconfig.py	Sat Aug 21 14:21:44 2021 +0200
@@ -11,6 +11,12 @@
 from coverage.backward import configparser, path_types
 from coverage.misc import CoverageException, substitute_variables
 
+# TOML support is an install-time extra option.
+try:
+    import toml
+except ImportError:         # pragma: not covered
+    toml = None
+
 
 class TomlDecodeError(Exception):
     """An exception class that exists even when toml isn't installed."""
@@ -29,8 +35,6 @@
         self.data = None
 
     def read(self, filenames):
-        from coverage.optional import toml
-
         # RawConfigParser takes a filename or list of filenames, but we only
         # ever call this with a single filename.
         assert isinstance(filenames, path_types)
--- a/eric7/DebugClients/Python/coverage/version.py	Fri Aug 20 19:56:17 2021 +0200
+++ b/eric7/DebugClients/Python/coverage/version.py	Sat Aug 21 14:21:44 2021 +0200
@@ -5,7 +5,7 @@
 # This file is exec'ed in setup.py, don't import anything!
 
 # Same semantics as sys.version_info.
-version_info = (5, 3, 1, "final", 0)
+version_info = (5, 5, 0, "final", 0)
 
 
 def _make_version(major, minor, micro, releaselevel, serial):

eric ide

mercurial