diff --git a/.travis.yml b/.travis.yml index c68e816ff..2135dae02 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,7 @@ dist: xenial language: python cache: pip + python: - '2.7' - '3.5' @@ -17,6 +18,18 @@ python: - 'pypy2.7-6.0' - 'pypy3.5-6.0' +# Only testing it for python3.8 on aarch64 platform, since it already has a lot of jobs to test and takes long time. +matrix: + include: + - python: 3.8 + arch: arm64 + env: + - COVERAGE_COVERAGE=no + - python: 3.8 + arch: arm64 + env: + - COVERAGE_COVERAGE=yes + env: matrix: - COVERAGE_COVERAGE=no diff --git a/CHANGES.rst b/CHANGES.rst index 943f03ac9..8e4a561d4 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -22,6 +22,34 @@ want to know what's different in 5.0 since 4.5.x, see :ref:`whatsnew5x`. .. ---------------------------- +.. _changes_504: + +Version 5.0.4 --- 2020-03-16 +---------------------------- + +- If using the ``[run] relative_files`` setting, the XML report will use + relative files in the ```` elements indicating the location of source + code. Closes `issue 948`_. + +- The textual summary report could report missing lines with negative line + numbers on PyPy3 7.1 (`issue 943`_). This is now fixed. + +- Windows wheels for Python 3.8 were incorrectly built, but are now fixed. + (`issue 949`_) + +- Updated Python 3.9 support to 3.9a4. + +- HTML reports couldn't be sorted if localStorage wasn't available. This is now + fixed: sorting works even though the sorting setting isn't retained. (`issue + 944`_ and `pull request 945`_). Thanks, Abdeali Kothari. + +.. _issue 943: https://github.com/nedbat/coveragepy/issues/943 +.. _issue 944: https://github.com/nedbat/coveragepy/issues/944 +.. _pull request 945: https://github.com/nedbat/coveragepy/pull/945 +.. _issue 948: https://github.com/nedbat/coveragepy/issues/948 +.. _issue 949: https://github.com/nedbat/coveragepy/issues/949 + + .. _changes_503: Version 5.0.3 --- 2020-01-12 diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 53846eafc..a89c3bfa6 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -4,6 +4,7 @@ extended and maintained by Ned Batchelder. Other contributions, including writing code, updating docs, and submitting useful bug reports, have been made by: +Abdeali Kothari Adi Roiban Agbonze O. Jeremiah Albertas Agejevas @@ -22,6 +23,7 @@ Ben Finney Bill Hart Brandon Rhodes Brett Cannon +Bruno P. Kinoshita Buck Evan Calen Pennington Carl Gieringer diff --git a/Makefile b/Makefile index 368a07ec8..e1675d9b1 100644 --- a/Makefile +++ b/Makefile @@ -101,9 +101,6 @@ wheel: ## Make the wheels for distribution. kit_linux: ## Make the Linux wheels. $(RUN_MANYLINUX_X86) build $(RUN_MANYLINUX_I686) build - # The manylinux image has Python 3.4, but we don't support it, delete - # those wheels. - rm -f dist/*cp34* kit_upload: ## Upload the built distributions to PyPI. twine upload --verbose dist/* diff --git a/README.rst b/README.rst index a811a049c..4534bc92a 100644 --- a/README.rst +++ b/README.rst @@ -20,8 +20,8 @@ library to determine which lines are executable, and which have been executed. Coverage.py runs on many versions of Python: * CPython 2.7. -* CPython 3.5 through 3.9 alpha 2. -* PyPy2 7.0 and PyPy3 7.0. +* CPython 3.5 through 3.9 alpha 4. +* PyPy2 7.3.0 and PyPy3 7.3.0. Documentation is on `Read the Docs`_. Code repository and issue tracker are on `GitHub`_. diff --git a/appveyor.yml b/appveyor.yml index a8b4d56e9..76e21dad5 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -53,7 +53,7 @@ environment: - JOB: "3.9 64-bit" TOXENV: "py39" PYTHON: "C:\\Python39-x64" - PYTHON_VERSION: "3.9.0a2" + PYTHON_VERSION: "3.9.0a3" PYTHON_ARCH: "64" # 32-bit jobs don't run the tests under the Python tracer, since that should @@ -96,7 +96,7 @@ environment: - JOB: "3.9 32-bit" TOXENV: "py39" PYTHON: "C:\\Python39" - PYTHON_VERSION: "3.9.0a2" + PYTHON_VERSION: "3.9.0a3" PYTHON_ARCH: "32" COVERAGE_NO_PYTRACER: "1" diff --git a/ci/manylinux.sh b/ci/manylinux.sh index 99ea598ec..c5dc42a30 100755 --- a/ci/manylinux.sh +++ b/ci/manylinux.sh @@ -16,7 +16,13 @@ if [[ $action == "build" ]]; then # Compile wheels cd /io for PYBIN in /opt/python/*/bin; do + if [[ $PYBIN == *cp34* ]]; then + # manylinux docker images have Python 3.4, but we don't use it. + continue + fi "$PYBIN/pip" install -r requirements/wheel.pip + # pin so auditwheel will work: https://github.com/pypa/auditwheel/issues/102 + "$PYBIN/pip" install wheel==0.31.1 "$PYBIN/python" setup.py clean -a "$PYBIN/python" setup.py bdist_wheel -d ~/wheelhouse/ done @@ -30,6 +36,10 @@ if [[ $action == "build" ]]; then elif [[ $action == "test" ]]; then # Create "pythonX.Y" links for PYBIN in /opt/python/*/bin/; do + if [[ $PYBIN == *cp34* ]]; then + # manylinux docker images have Python 3.4, but we don't use it. + continue + fi PYNAME=$("$PYBIN/python" -c "import sys; print('python{0[0]}.{0[1]}'.format(sys.version_info))") ln -sf "$PYBIN/$PYNAME" /usr/local/bin/$PYNAME done diff --git a/coverage/cmdline.py b/coverage/cmdline.py index 57266e73c..9fddb6bb8 100644 --- a/coverage/cmdline.py +++ b/coverage/cmdline.py @@ -18,7 +18,7 @@ from coverage import env from coverage.collector import CTracer from coverage.data import line_counts -from coverage.debug import info_formatter, info_header +from coverage.debug import info_formatter, info_header, short_stack from coverage.execfile import PyRunner from coverage.misc import BaseCoverageException, ExceptionDuringRun, NoSource, output_encoding from coverage.results import should_fail_under @@ -749,7 +749,6 @@ def do_debug(self, args): print(" %s" % line) elif info == "premain": print(info_header("premain")) - from coverage.debug import short_stack print(short_stack()) else: show_help("Don't know what you mean by %r" % info) diff --git a/coverage/control.py b/coverage/control.py index e8d4bc958..f7db26e94 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -545,7 +545,7 @@ def stop(self): def _atexit(self): """Clean up on process shutdown.""" if self._debug.should("process"): - self._debug.write("atexit: {!r}".format(self)) + self._debug.write("atexit: pid: {}, instance: {!r}".format(os.getpid(), self)) if self._started: self.stop() if self._auto_save: diff --git a/coverage/env.py b/coverage/env.py index cf91f7476..b5da3b471 100644 --- a/coverage/env.py +++ b/coverage/env.py @@ -57,7 +57,7 @@ class PYBEHAVIOR(object): unpackings_pep448 = (PYVERSION >= (3, 5)) # Can co_lnotab have negative deltas? - negative_lnotab = (PYVERSION >= (3, 6)) + negative_lnotab = (PYVERSION >= (3, 6)) and not (PYPY and PYPYVERSION < (7, 2)) # Do .pyc files conform to PEP 552? Hash-based pyc's. hashed_pyc_pep552 = (PYVERSION >= (3, 7, 0, 'alpha', 4)) @@ -85,10 +85,6 @@ class PYBEHAVIOR(object): # Python 3.9a1 made sys.argv[0] and other reported files absolute paths. report_absolute_files = (PYVERSION >= (3, 9)) - # Python 3.9a2 changed how return/finally was traced, but it was - # temporary. - bpo39114 = (PYVERSION == (3, 9, 0, 'alpha', 2, 0)) - # Coverage.py specifics. # Are we using the C-implemented trace function? diff --git a/coverage/htmlfiles/coverage_html.js b/coverage/htmlfiles/coverage_html.js index 22152333e..3bf04bf92 100644 --- a/coverage/htmlfiles/coverage_html.js +++ b/coverage/htmlfiles/coverage_html.js @@ -172,7 +172,10 @@ coverage.index_ready = function ($) { // Look for a localStorage item containing previous sort settings: var sort_list = []; var storage_name = "COVERAGE_INDEX_SORT"; - var stored_list = localStorage.getItem(storage_name); + var stored_list = undefined; + try { + stored_list = localStorage.getItem(storage_name); + } catch(err) {} if (stored_list) { sort_list = JSON.parse('[[' + stored_list + ']]'); @@ -222,7 +225,9 @@ coverage.index_ready = function ($) { // Watch for page unload events so we can save the final sort settings: $(window).unload(function () { - localStorage.setItem(storage_name, sort_list.toString()) + try { + localStorage.setItem(storage_name, sort_list.toString()) + } catch(err) {} }); }; diff --git a/coverage/plugin.py b/coverage/plugin.py index b9cb7238c..6997b489b 100644 --- a/coverage/plugin.py +++ b/coverage/plugin.py @@ -496,6 +496,7 @@ def source_token_lines(self): * ``'num'``: a number * ``'op'``: an operator * ``'str'``: a string literal + * ``'ws'``: some white space * ``'txt'``: some other kind of text If you concatenate all the token texts, and then join them with diff --git a/coverage/sqldata.py b/coverage/sqldata.py index b1adeb966..b8ee88532 100644 --- a/coverage/sqldata.py +++ b/coverage/sqldata.py @@ -268,7 +268,7 @@ def _read_db(self): """Read the metadata from a database so that we are ready to use it.""" with self._dbs[get_thread_id()] as db: try: - schema_version, = db.execute("select version from coverage_schema").fetchone() + schema_version, = db.execute_one("select version from coverage_schema") except Exception as exc: raise CoverageException( "Data file {!r} doesn't seem to be a coverage data file: {}".format( @@ -374,7 +374,7 @@ def _context_id(self, context): assert context is not None self._start_using() with self._connect() as con: - row = con.execute("select id from context where context = ?", (context,)).fetchone() + row = con.execute_one("select id from context where context = ?", (context,)) if row is not None: return row[0] else: @@ -789,7 +789,7 @@ def file_tracer(self, filename): file_id = self._file_id(filename) if file_id is None: return None - row = con.execute("select tracer from tracer where file_id = ?", (file_id,)).fetchone() + row = con.execute_one("select tracer from tracer where file_id = ?", (file_id,)) if row is not None: return row[0] or "" return "" # File was measured, but no tracer associated. @@ -952,11 +952,13 @@ def sys_info(cls): """ with SqliteDb(":memory:", debug=NoDebugging()) as db: + temp_store = [row[0] for row in db.execute("pragma temp_store")] compile_options = [row[0] for row in db.execute("pragma compile_options")] return [ ('sqlite3_version', sqlite3.version), ('sqlite3_sqlite_version', sqlite3.sqlite_version), + ('sqlite3_temp_store', temp_store), ('sqlite3_compile_options', compile_options), ] @@ -1062,6 +1064,23 @@ def execute(self, sql, parameters=()): self.debug.write("EXCEPTION from execute: {}".format(msg)) raise CoverageException("Couldn't use data file {!r}: {}".format(self.filename, msg)) + def execute_one(self, sql, parameters=()): + """Execute a statement and return the one row that results. + + This is like execute(sql, parameters).fetchone(), except it is + correct in reading the entire result set. This will raise an + exception if more than one row results. + + Returns a row, or None if there were no rows. + """ + rows = list(self.execute(sql, parameters)) + if len(rows) == 0: + return None + elif len(rows) == 1: + return rows[0] + else: + raise CoverageException("Sql {!r} shouldn't return {} rows".format(sql, len(rows))) + def executemany(self, sql, data): """Same as :meth:`python:sqlite3.Connection.executemany`.""" if self.debug: diff --git a/coverage/version.py b/coverage/version.py index b7145f05e..9cef0cf1e 100644 --- a/coverage/version.py +++ b/coverage/version.py @@ -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, 0, 3, 'final', 0) +version_info = (5, 0, 4, 'final', 0) def _make_version(major, minor, micro, releaselevel, serial): diff --git a/coverage/xmlreport.py b/coverage/xmlreport.py index 265bf02cf..ad44775f2 100644 --- a/coverage/xmlreport.py +++ b/coverage/xmlreport.py @@ -41,7 +41,9 @@ def __init__(self, coverage): if self.config.source: for src in self.config.source: if os.path.exists(src): - self.source_paths.add(files.canonical_filename(src)) + if not self.config.relative_files: + src = files.canonical_filename(src) + self.source_paths.add(src) self.packages = {} self.xml_out = None @@ -144,18 +146,18 @@ def xml_file(self, fr, analysis, has_arcs): # are populated later. Note that a package == a directory. filename = fr.filename.replace("\\", "/") for source_path in self.source_paths: + source_path = files.canonical_filename(source_path) if filename.startswith(source_path.replace("\\", "/") + "/"): rel_name = filename[len(source_path)+1:] break else: rel_name = fr.relative_filename() + self.source_paths.add(fr.filename[:-len(rel_name)].rstrip(r"\/")) dirname = os.path.dirname(rel_name) or u"." dirname = "/".join(dirname.split("/")[:self.config.xml_package_depth]) package_name = dirname.replace("/", ".") - if rel_name != fr.filename: - self.source_paths.add(fr.filename[:-len(rel_name)].rstrip(r"\/")) package = self.packages.setdefault(package_name, [{}, 0, 0, 0, 0]) xclass = self.xml_out.createElement("class") diff --git a/doc/conf.py b/doc/conf.py index 414a8be4c..67e09307c 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -68,9 +68,9 @@ # The short X.Y version. version = '5.0' # CHANGEME # The full version, including alpha/beta/rc tags. -release = '5.0.3' # CHANGEME +release = '5.0.4' # CHANGEME # The date of release, in "monthname day, year" format. -release_date = 'January 12, 2020' # CHANGEME +release_date = 'March 16, 2020' # CHANGEME rst_epilog = """ .. |release_date| replace:: {release_date} @@ -222,8 +222,8 @@ 'github': ('https://github.com/nedbat/coveragepy/issues/%s', 'issue '), } -# When auto-doc'ing a class, write the class' docstring and the __init__ docstring -# into the class docs. +# When auto-doc'ing a class, only write the class' docstring into the class docs, +# don't automatically include the __init__ docstring. autoclass_content = "class" prerelease = bool(max(release).isalpha()) diff --git a/doc/config.rst b/doc/config.rst index 6091867d8..bac2f0bd4 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -240,8 +240,8 @@ patterns to match against the paths of collected data, or they can be absolute or relative file paths on the current machine. In this example, data collected for "/jenkins/build/1234/src/module.py" will be -combined with data for "c:\myproj\src\module.py", and will be reported against -the source file found at "src/module.py". +combined with data for "c:\\myproj\\src\\module.py", and will be reported +against the source file found at "src/module.py". If you specify more than one list of paths, they will be considered in order. The first list that has a match will be used. diff --git a/doc/index.rst b/doc/index.rst index afc4e1bcc..36e024a0f 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -18,7 +18,7 @@ supported on: * Python versions 2.7, 3.5, 3.6, 3.7, 3.8, and 3.9 alpha. -* PyPy2 7.2.0 and PyPy3 7.2.0. +* PyPy2 7.3.0 and PyPy3 7.3.0. .. ifconfig:: prerelease diff --git a/doc/plugins.rst b/doc/plugins.rst index b15e31a54..38990d946 100644 --- a/doc/plugins.rst +++ b/doc/plugins.rst @@ -63,6 +63,14 @@ Some coverage.py plug-ins you might find useful: .. __: https://pypi.org/project/django_coverage_plugin/ +* `Conditional coverage plug-in`__: for measuring coverage based + on any rules you define! + Can exclude different lines of code that are only executed + on different platforms, python versions, + and with different dependencies installed. + + .. __: https://github.com/wemake-services/coverage-conditional-plugin + * `Mako template coverage plug-in`__: for measuring coverage in Mako templates. Doesn't work yet, probably needs some changes in Mako itself. diff --git a/doc/requirements.pip b/doc/requirements.pip index c102984a5..6d8891777 100644 --- a/doc/requirements.pip +++ b/doc/requirements.pip @@ -4,7 +4,7 @@ doc8==0.8.0 pyenchant==2.0.0 -sphinx==2.2.0 +sphinx==2.4.3 sphinx-rst-builder==0.0.1 sphinxcontrib-spelling==4.3.0 sphinx_rtd_theme==0.4.3 diff --git a/doc/sample_html/cogapp___init___py.html b/doc/sample_html/cogapp___init___py.html index b12c38a58..c90ba1712 100644 --- a/doc/sample_html/cogapp___init___py.html +++ b/doc/sample_html/cogapp___init___py.html @@ -65,8 +65,8 @@

diff --git a/doc/sample_html/cogapp___main___py.html b/doc/sample_html/cogapp___main___py.html index 5c1432273..da7857e2b 100644 --- a/doc/sample_html/cogapp___main___py.html +++ b/doc/sample_html/cogapp___main___py.html @@ -61,8 +61,8 @@

diff --git a/doc/sample_html/cogapp_backward_py.html b/doc/sample_html/cogapp_backward_py.html index cb2e9a4cf..855394627 100644 --- a/doc/sample_html/cogapp_backward_py.html +++ b/doc/sample_html/cogapp_backward_py.html @@ -98,8 +98,8 @@

diff --git a/doc/sample_html/cogapp_cogapp_py.html b/doc/sample_html/cogapp_cogapp_py.html index 925c1c798..09d49a3b8 100644 --- a/doc/sample_html/cogapp_cogapp_py.html +++ b/doc/sample_html/cogapp_cogapp_py.html @@ -864,8 +864,8 @@

diff --git a/doc/sample_html/cogapp_makefiles_py.html b/doc/sample_html/cogapp_makefiles_py.html index 8e7e7372b..6cdb3af56 100644 --- a/doc/sample_html/cogapp_makefiles_py.html +++ b/doc/sample_html/cogapp_makefiles_py.html @@ -102,8 +102,8 @@

diff --git a/doc/sample_html/cogapp_test_cogapp_py.html b/doc/sample_html/cogapp_test_cogapp_py.html index dab18acc2..dcc1f2893 100644 --- a/doc/sample_html/cogapp_test_cogapp_py.html +++ b/doc/sample_html/cogapp_test_cogapp_py.html @@ -2534,8 +2534,8 @@

diff --git a/doc/sample_html/cogapp_test_makefiles_py.html b/doc/sample_html/cogapp_test_makefiles_py.html index a4a2f05aa..20a31775e 100644 --- a/doc/sample_html/cogapp_test_makefiles_py.html +++ b/doc/sample_html/cogapp_test_makefiles_py.html @@ -178,8 +178,8 @@

diff --git a/doc/sample_html/cogapp_test_whiteutils_py.html b/doc/sample_html/cogapp_test_whiteutils_py.html index 4c1124712..f9094a0e5 100644 --- a/doc/sample_html/cogapp_test_whiteutils_py.html +++ b/doc/sample_html/cogapp_test_whiteutils_py.html @@ -157,8 +157,8 @@

diff --git a/doc/sample_html/cogapp_whiteutils_py.html b/doc/sample_html/cogapp_whiteutils_py.html index 3fe3ba22e..af6181ced 100644 --- a/doc/sample_html/cogapp_whiteutils_py.html +++ b/doc/sample_html/cogapp_whiteutils_py.html @@ -129,8 +129,8 @@

diff --git a/doc/sample_html/coverage_html.js b/doc/sample_html/coverage_html.js index 22152333e..3bf04bf92 100644 --- a/doc/sample_html/coverage_html.js +++ b/doc/sample_html/coverage_html.js @@ -172,7 +172,10 @@ coverage.index_ready = function ($) { // Look for a localStorage item containing previous sort settings: var sort_list = []; var storage_name = "COVERAGE_INDEX_SORT"; - var stored_list = localStorage.getItem(storage_name); + var stored_list = undefined; + try { + stored_list = localStorage.getItem(storage_name); + } catch(err) {} if (stored_list) { sort_list = JSON.parse('[[' + stored_list + ']]'); @@ -222,7 +225,9 @@ coverage.index_ready = function ($) { // Watch for page unload events so we can save the final sort settings: $(window).unload(function () { - localStorage.setItem(storage_name, sort_list.toString()) + try { + localStorage.setItem(storage_name, sort_list.toString()) + } catch(err) {} }); }; diff --git a/doc/sample_html/index.html b/doc/sample_html/index.html index 9728bb1c4..fea042b58 100644 --- a/doc/sample_html/index.html +++ b/doc/sample_html/index.html @@ -155,8 +155,8 @@

Coverage report: diff --git a/doc/sample_html/status.json b/doc/sample_html/status.json index c0d86343a..34acd51c4 100644 --- a/doc/sample_html/status.json +++ b/doc/sample_html/status.json @@ -1 +1 @@ -{"format":2,"version":"5.0.2","globals":"1ebe6fe5d7f653a4b8e6dc7e6991302c","files":{"cogapp___init___py":{"hash":"394223dfdd89e833f305580aa57a8249","index":{"nums":[1,2,0,0,0,0,0],"html_filename":"cogapp___init___py.html","relative_filename":"cogapp/__init__.py"}},"cogapp___main___py":{"hash":"2cec3551dfd9a5818a6550318658ccd4","index":{"nums":[1,3,0,3,0,0,0],"html_filename":"cogapp___main___py.html","relative_filename":"cogapp/__main__.py"}},"cogapp_backward_py":{"hash":"a9fac271913998141885e738e94e4366","index":{"nums":[1,22,0,6,4,2,2],"html_filename":"cogapp_backward_py.html","relative_filename":"cogapp/backward.py"}},"cogapp_cogapp_py":{"hash":"fe4ad7ebb464cbf9343ce967237d49af","index":{"nums":[1,485,1,215,200,28,132],"html_filename":"cogapp_cogapp_py.html","relative_filename":"cogapp/cogapp.py"}},"cogapp_makefiles_py":{"hash":"551431f9308a0836e0a365e64ff7c333","index":{"nums":[1,27,0,20,14,0,14],"html_filename":"cogapp_makefiles_py.html","relative_filename":"cogapp/makefiles.py"}},"cogapp_test_cogapp_py":{"hash":"cb0eb6015bcf540023c227a30835c5d6","index":{"nums":[1,790,6,549,20,0,18],"html_filename":"cogapp_test_cogapp_py.html","relative_filename":"cogapp/test_cogapp.py"}},"cogapp_test_makefiles_py":{"hash":"0447c2f176e1ea621505e8452b9d5038","index":{"nums":[1,71,0,53,6,0,6],"html_filename":"cogapp_test_makefiles_py.html","relative_filename":"cogapp/test_makefiles.py"}},"cogapp_test_whiteutils_py":{"hash":"5af4aaae2ad4317a361eb40dd79d4ea0","index":{"nums":[1,69,0,50,0,0,0],"html_filename":"cogapp_test_whiteutils_py.html","relative_filename":"cogapp/test_whiteutils.py"}},"cogapp_whiteutils_py":{"hash":"4068aefb16d502186c6baa27cc775d4a","index":{"nums":[1,45,0,5,34,4,4],"html_filename":"cogapp_whiteutils_py.html","relative_filename":"cogapp/whiteutils.py"}}}} \ No newline at end of file +{"format":2,"version":"5.0.4","globals":"1ebe6fe5d7f653a4b8e6dc7e6991302c","files":{"cogapp___init___py":{"hash":"394223dfdd89e833f305580aa57a8249","index":{"nums":[1,2,0,0,0,0,0],"html_filename":"cogapp___init___py.html","relative_filename":"cogapp/__init__.py"}},"cogapp___main___py":{"hash":"2cec3551dfd9a5818a6550318658ccd4","index":{"nums":[1,3,0,3,0,0,0],"html_filename":"cogapp___main___py.html","relative_filename":"cogapp/__main__.py"}},"cogapp_backward_py":{"hash":"a9fac271913998141885e738e94e4366","index":{"nums":[1,22,0,6,4,2,2],"html_filename":"cogapp_backward_py.html","relative_filename":"cogapp/backward.py"}},"cogapp_cogapp_py":{"hash":"fe4ad7ebb464cbf9343ce967237d49af","index":{"nums":[1,485,1,215,200,28,132],"html_filename":"cogapp_cogapp_py.html","relative_filename":"cogapp/cogapp.py"}},"cogapp_makefiles_py":{"hash":"551431f9308a0836e0a365e64ff7c333","index":{"nums":[1,27,0,20,14,0,14],"html_filename":"cogapp_makefiles_py.html","relative_filename":"cogapp/makefiles.py"}},"cogapp_test_cogapp_py":{"hash":"cb0eb6015bcf540023c227a30835c5d6","index":{"nums":[1,790,6,549,20,0,18],"html_filename":"cogapp_test_cogapp_py.html","relative_filename":"cogapp/test_cogapp.py"}},"cogapp_test_makefiles_py":{"hash":"0447c2f176e1ea621505e8452b9d5038","index":{"nums":[1,71,0,53,6,0,6],"html_filename":"cogapp_test_makefiles_py.html","relative_filename":"cogapp/test_makefiles.py"}},"cogapp_test_whiteutils_py":{"hash":"5af4aaae2ad4317a361eb40dd79d4ea0","index":{"nums":[1,69,0,50,0,0,0],"html_filename":"cogapp_test_whiteutils_py.html","relative_filename":"cogapp/test_whiteutils.py"}},"cogapp_whiteutils_py":{"hash":"4068aefb16d502186c6baa27cc775d4a","index":{"nums":[1,45,0,5,34,4,4],"html_filename":"cogapp_whiteutils_py.html","relative_filename":"cogapp/whiteutils.py"}}}} \ No newline at end of file diff --git a/howto.txt b/howto.txt index f73dee046..3653e830a 100644 --- a/howto.txt +++ b/howto.txt @@ -76,20 +76,24 @@ - Update Tidelift: - make upload_relnotes - Update readthedocs - - visit https://readthedocs.org/projects/coverage/versions/ - - find the latest tag in the inactive list, edit it, make it active. - - readthedocs won't find the tag until a commit is made on master. - - keep just the latest version of each x.y release, make the rest inactive. - IF NOT PRE-RELEASE: - update git "stable" branch to point to latest release + - git branch -f stable + - git push --all - visit https://readthedocs.org/projects/coverage/builds/ - wait for the new tag build to finish successfully. - visit https://readthedocs.org/dashboard/coverage/advanced/ - change the default version to the new version + - visit https://readthedocs.org/projects/coverage/versions/ + - find the latest tag in the inactive list, edit it, make it active. + - readthedocs won't find the tag until a commit is made on master. + - keep just the latest version of each x.y release, make the rest inactive. - Visit the fixed issues on GitHub and mention the version it was fixed in. -- Update nedbatchelder.com - - Blog post? -- Announce on TIP. + - make a milestone for the next release and move open issues into it. +- Announce: + - twitter @coveragepy + - nedbatchelder.com blog post? + - testing-in-python mailing list? * Testing diff --git a/requirements/dev.pip b/requirements/dev.pip index 3fe109549..a11729cdb 100644 --- a/requirements/dev.pip +++ b/requirements/dev.pip @@ -4,10 +4,10 @@ # Requirements for doing local development work on coverage.py. # https://requires.io/github/nedbat/coveragepy/requirements/ -pip==19.3.1 -virtualenv==16.7.8 +pip==20.0.2 +virtualenv==16.7.9 -pluggy==0.13.0 +pluggy==0.13.1 # PyPI requirements for running tests. -r tox.pip @@ -15,7 +15,7 @@ pluggy==0.13.0 # for linting. greenlet==0.4.15 -pylint==2.4.3 +pylint==2.4.4 check-manifest==0.40 readme_renderer==24.0 diff --git a/requirements/tox.pip b/requirements/tox.pip index 8dfcc669d..a6279c325 100644 --- a/requirements/tox.pip +++ b/requirements/tox.pip @@ -2,6 +2,6 @@ # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt # The version of tox used by coverage.py -tox==3.14.1 +tox==3.14.3 # Adds env recreation on requirements file changes. -tox-battery==0.5.1 +tox-battery==0.5.2 diff --git a/requirements/wheel.pip b/requirements/wheel.pip index 5dba911f0..abef9db4d 100644 --- a/requirements/wheel.pip +++ b/requirements/wheel.pip @@ -4,5 +4,4 @@ # Things needed to make wheels for coverage.py setuptools==41.4.0 -# pin so auditwheel will work: https://github.com/pypa/auditwheel/issues/102 -wheel==0.31.1 +wheel==0.34.2 diff --git a/tests/coveragetest.py b/tests/coveragetest.py index f9091a8d0..58cfb3dc6 100644 --- a/tests/coveragetest.py +++ b/tests/coveragetest.py @@ -101,10 +101,6 @@ def setUp(self): self.last_command_output = None self.last_module_name = None - def xfail(self, msg): - """Mark this test as an expected failure.""" - pytest.xfail(msg) - def clean_local_file_imports(self): """Clean up the results of calls to `import_local_file`. @@ -501,3 +497,8 @@ def command_line(args): script = CoverageScript() ret = script.command_line(shlex.split(args)) return ret + + +def xfail(condition, reason): + """A decorator to mark as test as expected to fail.""" + return pytest.mark.xfail(condition, reason=reason, strict=True) diff --git a/tests/test_arcs.py b/tests/test_arcs.py index 545145a51..bf17f7123 100644 --- a/tests/test_arcs.py +++ b/tests/test_arcs.py @@ -630,8 +630,6 @@ def test_finally_in_loop(self): def test_break_through_finally(self): - if env.PYBEHAVIOR.bpo39114: - self.xfail("https://bugs.python.org/issue39114") if env.PYBEHAVIOR.finally_jumps_back: arcz = ".1 12 23 34 3D 45 56 67 68 7A 7D 8A A3 A7 BC CD D." else: @@ -656,8 +654,6 @@ def test_break_through_finally(self): ) def test_continue_through_finally(self): - if env.PYBEHAVIOR.bpo39114: - self.xfail("https://bugs.python.org/issue39114") if env.PYBEHAVIOR.finally_jumps_back: arcz = ".1 12 23 34 3D 45 56 67 68 73 7A 8A A3 A7 BC CD D." else: @@ -695,8 +691,6 @@ def test_finally_in_loop_bug_92(self): ) def test_bug_212(self): - if env.PYBEHAVIOR.bpo39114: - self.xfail("https://bugs.python.org/issue39114") # "except Exception as e" is crucial here. # Bug 212 said that the "if exc" line was incorrectly marked as only # partially covered. @@ -819,8 +813,6 @@ def test_multiple_except_clauses(self): ) def test_return_finally(self): - if env.PYBEHAVIOR.bpo39114: - self.xfail("https://bugs.python.org/issue39114") if env.PYBEHAVIOR.finally_jumps_back: arcz = ".1 12 29 9A AB BC C-1 -23 34 45 5-2 57 75 38 8-2" else: @@ -843,8 +835,6 @@ def check_token(data): ) def test_except_jump_finally(self): - if env.PYBEHAVIOR.bpo39114: - self.xfail("https://bugs.python.org/issue39114") if env.PYBEHAVIOR.finally_jumps_back: arcz = ( ".1 1Q QR RS ST TU U. " @@ -899,8 +889,6 @@ def func(x): ) def test_else_jump_finally(self): - if env.PYBEHAVIOR.bpo39114: - self.xfail("https://bugs.python.org/issue39114") if env.PYBEHAVIOR.finally_jumps_back: arcz = ( ".1 1S ST TU UV VW W. " @@ -1523,8 +1511,6 @@ async def print_sum(x, y): # 8 self.assertEqual(self.stdout(), "Compute 1 + 2 ...\n1 + 2 = 3\n") def test_async_for(self): - if env.PYVERSION == (3, 9, 0, 'alpha', 2, 0): - self.xfail("https://bugs.python.org/issue39166") self.check_coverage("""\ import asyncio diff --git a/tests/test_parser.py b/tests/test_parser.py index 8e7295a10..192640439 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -9,7 +9,7 @@ from coverage.misc import NotPython from coverage.parser import PythonParser -from tests.coveragetest import CoverageTest +from tests.coveragetest import CoverageTest, xfail from tests.helpers import arcz_to_arcs @@ -137,9 +137,12 @@ def test_token_error(self): ''' """) + + @xfail( + env.PYPY3 and env.PYPYVERSION >= (7, 3, 0), + "https://bitbucket.org/pypy/pypy/issues/3139", + ) def test_decorator_pragmas(self): - if env.PYPY3 and env.PYPYVERSION >= (7, 3, 0): # pragma: obscure - self.xfail("https://bitbucket.org/pypy/pypy/issues/3139") parser = self.parse_source("""\ # 1 diff --git a/tests/test_process.py b/tests/test_process.py index 6ca4571c6..a9b8e00a6 100644 --- a/tests/test_process.py +++ b/tests/test_process.py @@ -4,13 +4,13 @@ """Tests for process behavior of coverage.py.""" -import distutils.sysconfig import glob import os import os.path import re import stat import sys +import sysconfig import textwrap import time from xml.etree import ElementTree @@ -23,7 +23,7 @@ from coverage.files import python_reported_file from coverage.misc import output_encoding -from tests.coveragetest import CoverageTest, TESTS_DIR +from tests.coveragetest import CoverageTest, TESTS_DIR, xfail from tests.helpers import re_lines @@ -742,12 +742,14 @@ def test_fullcoverage(self): # pragma: no metacov # about 5. self.assertGreater(line_counts(data)['os.py'], 50) + @xfail( + env.PYPY3 and env.PYPYVERSION >= (7, 1, 1), + "https://bitbucket.org/pypy/pypy/issues/3074" + ) def test_lang_c(self): if env.JYTHON: # Jython as of 2.7.1rc3 won't compile a filename that isn't utf8. self.skipTest("Jython can't handle this test") - if env.PYPY3 and env.PYPYVERSION >= (7, 1, 1): # pragma: obscure - self.xfail("https://bitbucket.org/pypy/pypy/issues/3074") # LANG=C forces getfilesystemencoding on Linux to 'ascii', which causes # failures with non-ascii file names. We don't want to make a real file # with strange characters, though, because that gets the test runners @@ -1397,7 +1399,7 @@ def possible_pth_dirs(): # If we're still looking, then try the Python library directory. # https://bitbucket.org/ned/coveragepy/issue/339/pth-test-malfunctions - yield distutils.sysconfig.get_python_lib() # pragma: cant happen + yield sysconfig.get_python_lib() # pragma: cant happen def find_writable_pth_directory(): diff --git a/tests/test_xml.py b/tests/test_xml.py index 93fee9b7b..0d789fca2 100644 --- a/tests/test_xml.py +++ b/tests/test_xml.py @@ -370,6 +370,19 @@ def test_source_prefix(self): dom = ElementTree.parse("coverage.xml") self.assert_source(dom, "src") + def test_relative_source(self): + self.make_file("src/mod.py", "print(17)") + cov = coverage.Coverage(source=["src"]) + cov.set_option("run:relative_files", True) + self.start_import_stop(cov, "mod", modfile="src/mod.py") + cov.xml_report() + + with open("coverage.xml") as x: + print(x.read()) + dom = ElementTree.parse("coverage.xml") + elts = dom.findall(".//sources/source") + assert [elt.text for elt in elts] == ["src"] + def compare_xml(expected, actual, **kwargs): """Specialized compare function for our XML files.""" diff --git a/tox.ini b/tox.ini index c96d541ac..57c4d4bca 100644 --- a/tox.ini +++ b/tox.ini @@ -15,7 +15,7 @@ deps = # Check here for what might be out of date: # https://requires.io/github/nedbat/coveragepy/requirements/ -r requirements/pytest.pip - pip==19.3.1 + pip==20.0.2 setuptools==41.4.0 # gevent 1.3 causes a failure: https://github.com/nedbat/coveragepy/issues/663 py{27,35,36}: gevent==1.2.2