diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 95d12bf78..4fe718f6e 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -13,9 +13,9 @@ A clear and concise description of the bug. **To Reproduce** How can we reproduce the problem? Please *be specific*. Don't just link to a failing CI job. Answer the questions below: 1. What version of Python are you using? -1. What version of coverage.py are you using? The output of `coverage debug sys` is helpful. +1. What version of coverage.py shows the problem? The output of `coverage debug sys` is helpful. 1. What versions of what packages do you have installed? The output of `pip freeze` is helpful. -1. What code are you running? Give us a specific commit of a specific repo that we can check out. +1. What code are you running? Give us a specific commit of a specific repo that we can check out. If you've already worked around the problem, please provide a commit before that fix. 1. What commands did you run? **Expected behavior** diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index e7dd828fb..3e12f01be 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -38,6 +38,7 @@ jobs: - "3.8" - "3.9" - "3.10" + - "3.11.0-alpha.2" - "pypy3" exclude: # Windows PyPy doesn't seem to work? @@ -62,10 +63,7 @@ jobs: set -xe python -VV python -m site - # Need to install setuptools first so that ci.pip will succeed. - python -m pip install -c requirements/pins.pip setuptools wheel - python -m pip install -r requirements/ci.pip - python -m pip install -c requirements/pins.pip tox-gh-actions + python -m pip install -r requirements/tox.pip - name: "Run tox coverage for ${{ matrix.python-version }}" env: @@ -95,14 +93,14 @@ jobs: - name: "Set up Python" uses: "actions/setup-python@v2" with: - python-version: "3.9" + python-version: "3.8" - name: "Install dependencies" run: | set -xe python -VV python -m site - python setup.py --quiet clean develop + python -m pip install -e . python igor.py zip_mods - name: "Download coverage data" @@ -119,6 +117,7 @@ jobs: run: | set -xe python -m igor combine_html + rm htmlcov/.gitignore python -m coverage json echo "::set-output name=total::$(python -c "import json;print(json.load(open('coverage.json'))['totals']['percent_covered_display'])")" diff --git a/.github/workflows/kit.yml b/.github/workflows/kit.yml index c6b9e43da..3aa67dde5 100644 --- a/.github/workflows/kit.yml +++ b/.github/workflows/kit.yml @@ -4,6 +4,15 @@ # Based on: # https://github.com/joerick/cibuildwheel/blob/master/examples/github-deploy.yml +# To test installing wheels without uploading them to PyPI: +# +# $ mkdir /tmp/pypi +# $ cp dist/* /tmp/pypi +# $ python -m pip install piprepo +# $ piprepo build /tmp/pypi +# $ python -m pip install -v coverage --index-url=file:///tmp/pypi/simple +# + name: "Kits" on: @@ -22,22 +31,75 @@ env: jobs: wheels: - name: "Build wheels on ${{ matrix.os }}" - runs-on: ${{ matrix.os }} + name: "Build ${{ matrix.os }} ${{ matrix.py }} ${{ matrix.arch }} wheels" + runs-on: ${{ matrix.os }}-latest strategy: matrix: include: - - os: ubuntu-latest - cibw_arch: x86_64 i686 aarch64 - - os: windows-latest - cibw_arch: x86 AMD64 - - os: macos-latest - cibw_arch: x86_64 + # To change the matrix, edit the choices, then process this file with cog: + # + # $ python -m pip install cogapp + # $ python -m cogapp -rP .github/workflows/kit.yml + # + # + # [[[cog + # #----- vvv Choices for the matrix vvv ----- + # oss = ["ubuntu", "macos", "windows"] + # pys = ["cp36", "cp37", "cp38", "cp39", "cp310"] + # archs = { + # "ubuntu": ["x86_64", "i686", "aarch64"], + # "macos": ["x86_64"], + # "windows": ["x86", "AMD64"], + # } + # #----- ^^^ ---------------------- ^^^ ----- + # + # import json + # for the_os in oss: + # for the_py in pys: + # for the_arch in archs[the_os]: + # them = { + # "os": the_os, + # "py": the_py, + # "arch": the_arch, + # } + # print(f"- {json.dumps(them)}") + # ]]] + - {"os": "ubuntu", "py": "cp36", "arch": "x86_64"} + - {"os": "ubuntu", "py": "cp36", "arch": "i686"} + - {"os": "ubuntu", "py": "cp36", "arch": "aarch64"} + - {"os": "ubuntu", "py": "cp37", "arch": "x86_64"} + - {"os": "ubuntu", "py": "cp37", "arch": "i686"} + - {"os": "ubuntu", "py": "cp37", "arch": "aarch64"} + - {"os": "ubuntu", "py": "cp38", "arch": "x86_64"} + - {"os": "ubuntu", "py": "cp38", "arch": "i686"} + - {"os": "ubuntu", "py": "cp38", "arch": "aarch64"} + - {"os": "ubuntu", "py": "cp39", "arch": "x86_64"} + - {"os": "ubuntu", "py": "cp39", "arch": "i686"} + - {"os": "ubuntu", "py": "cp39", "arch": "aarch64"} + - {"os": "ubuntu", "py": "cp310", "arch": "x86_64"} + - {"os": "ubuntu", "py": "cp310", "arch": "i686"} + - {"os": "ubuntu", "py": "cp310", "arch": "aarch64"} + - {"os": "macos", "py": "cp36", "arch": "x86_64"} + - {"os": "macos", "py": "cp37", "arch": "x86_64"} + - {"os": "macos", "py": "cp38", "arch": "x86_64"} + - {"os": "macos", "py": "cp39", "arch": "x86_64"} + - {"os": "macos", "py": "cp310", "arch": "x86_64"} + - {"os": "windows", "py": "cp36", "arch": "x86"} + - {"os": "windows", "py": "cp36", "arch": "AMD64"} + - {"os": "windows", "py": "cp37", "arch": "x86"} + - {"os": "windows", "py": "cp37", "arch": "AMD64"} + - {"os": "windows", "py": "cp38", "arch": "x86"} + - {"os": "windows", "py": "cp38", "arch": "AMD64"} + - {"os": "windows", "py": "cp39", "arch": "x86"} + - {"os": "windows", "py": "cp39", "arch": "AMD64"} + - {"os": "windows", "py": "cp310", "arch": "x86"} + - {"os": "windows", "py": "cp310", "arch": "AMD64"} + # [[[end]]] fail-fast: false steps: - name: Setup QEMU - if: matrix.os == 'ubuntu-latest' + if: matrix.os == 'ubuntu' uses: docker/setup-qemu-action@v1 with: platforms: arm64 @@ -45,24 +107,35 @@ jobs: - name: "Check out the repo" uses: actions/checkout@v2 - - name: "Install Python 3.7" + - name: "Install Python 3.8" uses: actions/setup-python@v2 with: - python-version: "3.7" + python-version: "3.8" - - name: "Install cibuildwheel" + - name: "Install tools" run: | - python -m pip install -c requirements/pins.pip cibuildwheel + python -m pip install -r requirements/kit.pip - name: "Build wheels" env: - # Don't build wheels for PyPy. - CIBW_SKIP: pp* - CIBW_ARCHS: ${{ matrix.cibw_arch }} + CIBW_BUILD: ${{ matrix.py }}-* + CIBW_ARCHS: ${{ matrix.arch }} + CIBW_ENVIRONMENT: PIP_DISABLE_PIP_VERSION_CHECK=1 + CIBW_TEST_COMMAND: python -c "from coverage.tracer import CTracer; print('CTracer OK!')" run: | python -m cibuildwheel --output-dir wheelhouse + + - name: "List wheels" + run: | ls -al wheelhouse/ + - name: "Remove unwanted wheels" + if: matrix.os == 'ubuntu' && matrix.py == 'cp39' + run: | + # Python 3.9 musllinux wheels don't work in some places, so delete them. + # https://github.com/nedbat/coveragepy/issues/1268 + rm -f wheelhouse/*-cp39-musllinux*.whl + - name: "Upload wheels" uses: actions/upload-artifact@v2 with: @@ -76,14 +149,21 @@ jobs: - name: "Check out the repo" uses: actions/checkout@v2 - - name: "Install Python 3.7" + - name: "Install Python 3.8" uses: actions/setup-python@v2 with: - python-version: "3.7" + python-version: "3.8" + + - name: "Install tools" + run: | + python -m pip install -r requirements/kit.pip - name: "Build sdist" run: | - python setup.py sdist + python -m build + + - name: "List tarballs" + run: | ls -al dist/ - name: "Upload sdist" @@ -93,7 +173,7 @@ jobs: path: dist/*.tar.gz pypy: - name: "Build PyPy wheels" + name: "Build PyPy wheel" runs-on: ubuntu-latest steps: - name: "Check out the repo" @@ -106,12 +186,16 @@ jobs: - name: "Install requirements" run: | - pypy3 -m pip install -r requirements/wheel.pip + pypy3 -m pip install -r requirements/kit.pip - - name: "Build wheels" + - name: "Build wheel" + run: | + # One wheel works for all PyPy versions. + # yes, this is weird syntax: https://github.com/pypa/build/issues/202 + pypy3 -m build -w -C="--global-option=--python-tag" -C="--global-option=pp36.pp37.pp38" + + - name: "List wheels" run: | - pypy3 setup.py bdist_wheel --python-tag pp36 - pypy3 setup.py bdist_wheel --python-tag pp37 ls -al dist/ - name: "Upload wheels" @@ -121,35 +205,35 @@ jobs: path: dist/*.whl prerel: - name: "Build ${{ matrix.python-version }} wheels on ${{ matrix.os }}" - if: ${{ false }} # disable for now, since there are no pre-rel Python versions. - runs-on: ${{ matrix.os }} + name: "Build pre-rel ${{ matrix.os }} ${{ matrix.py }} wheels" + if: ${{ true }} # true when there are pre-rel, false when not. + runs-on: "${{ matrix.os }}-latest" strategy: matrix: os: - - ubuntu-latest - - windows-latest - - macos-latest - python-version: - - "3.10.0-rc.2" + - ubuntu + - windows + - macos + py: + - "3.11.0-alpha.2" fail-fast: false steps: - name: "Check out the repo" uses: actions/checkout@v2 - - name: "Install Python ${{ matrix.python-version }}" + - name: "Install Python ${{ matrix.py }}" uses: actions/setup-python@v2 with: - python-version: ${{ matrix.python-version }} + python-version: ${{ matrix.py }} - name: "Install wheel tools" run: | - python -m pip install -r requirements/wheel.pip + python -m pip install -r requirements/kit.pip - name: "Build wheel" run: | - python setup.py bdist_wheel + python -m build - name: "Convert to manylinux wheel" if: runner.os == 'Linux' @@ -161,6 +245,9 @@ jobs: auditwheel show wheelhouse/*.whl rm dist/*.whl mv wheelhouse/*.whl dist/ + + - name: "List wheels" + run: | ls -al dist/ - name: "Upload wheels" diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 7df3c9886..fe7b31fe3 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -7,6 +7,7 @@ on: push: branches: - master + - nedbat/* pull_request: workflow_dispatch: @@ -39,7 +40,7 @@ jobs: set -xe python -VV python -m site - python -m pip install -c requirements/pins.pip tox + python -m pip install -r requirements/tox.pip - name: "Tox lint" run: | @@ -63,7 +64,7 @@ jobs: set -xe python -VV python -m site - python -m pip install -c requirements/pins.pip tox + python -m pip install -r requirements/tox.pip - name: "Tox doc" run: | diff --git a/.github/workflows/testsuite.yml b/.github/workflows/testsuite.yml index 6b0de1b3d..6f31f48f7 100644 --- a/.github/workflows/testsuite.yml +++ b/.github/workflows/testsuite.yml @@ -7,6 +7,7 @@ on: push: branches: - master + - nedbat/* pull_request: workflow_dispatch: @@ -36,6 +37,7 @@ jobs: - "3.8" - "3.9" - "3.10" + - "3.11.0-alpha.2" - "pypy3" exclude: # Windows PyPy doesn't seem to work? @@ -57,10 +59,7 @@ jobs: set -xe python -VV python -m site - # Need to install setuptools first so that ci.pip will succeed. - python -m pip install -c requirements/pins.pip setuptools wheel - python -m pip install -r requirements/ci.pip - python -m pip install -c requirements/pins.pip tox-gh-actions + python -m pip install -r requirements/tox.pip - name: "Run tox for ${{ matrix.python-version }}" continue-on-error: true diff --git a/.gitignore b/.gitignore index c8b41f499..2373d5dc7 100644 --- a/.gitignore +++ b/.gitignore @@ -8,12 +8,14 @@ .coverage .coverage.* coverage.xml +coverage.json .metacov .metacov.* *.swp # Stuff generated by editors. .idea/ +.vscode/ .vimtags # Stuff in the root. @@ -29,11 +31,13 @@ setuptools-*.egg .pytest_cache .hypothesis .ruby-version +.venv # Stuff in the test directory. covmain.zip zipmods.zip zip1.zip +tests/actual # Stuff in the doc directory. doc/_build diff --git a/CHANGES.rst b/CHANGES.rst index 12f15d81a..7943d9724 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -16,14 +16,123 @@ This list is detailed and covers changes in each pre-release version. .. .. .. _changes_981: .. - .. Version 9.8.1 --- 2027-07-27 - .. ---------------------------- + .. Version 9.8.1 — 2027-07-27 + .. -------------------------- + +.. _changes_612: + +Version 6.1.2 — 2021-11-10 +-------------------------- + +- Python 3.11 is supported (tested with 3.11.0a2). One still-open issue has to + do with `exits through with-statements `_. + +- Fix: When remapping file paths through the ``[paths]`` setting while + combining, the ``[run] relative_files`` setting was ignored, resulting in + absolute paths for remapped file names (`issue 1147`_). This is now fixed. + +- Fix: Complex conditionals over excluded lines could have incorrectly reported + a missing branch (`issue 1271`_). This is now fixed. + +- Fix: More exceptions are now handled when trying to parse source files for + reporting. Problems that used to terminate coverage.py can now be handled + with ``[report] ignore_errors``. This helps with plugins failing to read + files (`django_coverage_plugin issue 78`_). + +- Fix: Removed another vestige of jQuery from the source tarball + (`issue 840`_). + +- Fix: Added a default value for a new-to-6.x argument of an internal class. + This unsupported class is being used by coveralls (`issue 1273`_). Although + I'd rather not "fix" unsupported interfaces, it's actually nicer with a + default value. + +.. _django_coverage_plugin issue 78: https://github.com/nedbat/django_coverage_plugin/issues/78 +.. _issue 1147: https://github.com/nedbat/coveragepy/issues/1147 +.. _issue 1270: https://github.com/nedbat/coveragepy/issues/1270 +.. _issue 1271: https://github.com/nedbat/coveragepy/issues/1271 +.. _issue 1273: https://github.com/nedbat/coveragepy/issues/1273 + + +.. _changes_611: + +Version 6.1.1 — 2021-10-31 +-------------------------- + +- Fix: The sticky header on the HTML report didn't work unless you had branch + coverage enabled. This is now fixed: the sticky header works for everyone. + (Do people still use coverage without branch measurement!? j/k) + +- Fix: When using explicitly declared namespace packages, the "already imported + a file that will be measured" warning would be issued (`issue 888`_). This + is now fixed. + +.. _issue 888: https://github.com/nedbat/coveragepy/issues/888 + + +.. _changes_61: + +Version 6.1 — 2021-10-30 +------------------------ + +- Deprecated: The ``annotate`` command and the ``Coverage.annotate`` function + will be removed in a future version, unless people let me know that they are + using it. Instead, the ``html`` command gives better-looking (and more + accurate) output, and the ``report -m`` command will tell you line numbers of + missing lines. Please get in touch if you have a reason to use ``annotate`` + over those better options: ned@nedbatchelder.com. + +- Feature: Coverage now sets an environment variable, ``COVERAGE_RUN`` when + running your code with the ``coverage run`` command. The value is not + important, and may change in the future. Closes `issue 553`_. + +- Feature: The HTML report pages for Python source files now have a sticky + header so the file name and controls are always visible. + +- Feature: The ``xml`` and ``json`` commands now describe what they wrote + where. + +- Feature: The ``html``, ``combine``, ``xml``, and ``json`` commands all accept + a ``-q/--quiet`` option to suppress the messages they write to stdout about + what they are doing (`issue 1254`_). + +- Feature: The ``html`` command writes a ``.gitignore`` file into the HTML + output directory, to prevent the report from being committed to git. If you + want to commit it, you will need to delete that file. Closes `issue 1244`_. + +- Feature: Added support for PyPy 3.8. + +- Fix: More generated code is now excluded from measurement. Code such as + `attrs`_ boilerplate, or doctest code, was being measured though the + synthetic line numbers meant they were never reported. Once Cython was + involved though, the generated .so files were parsed as Python, raising + syntax errors, as reported in `issue 1160`_. This is now fixed. + +- Fix: When sorting human-readable names, numeric components are sorted + correctly: file10.py will appear after file9.py. This applies to file names, + module names, environment variables, and test contexts. + +- Performance: Branch coverage measurement is faster, though you might only + notice on code that is executed many times, such as long-running loops. + +- Build: jQuery is no longer used or vendored (`issue 840`_ and `issue 1118`_). + Huge thanks to Nils Kattenbeck (septatrix) for the conversion to vanilla + JavaScript in `pull request 1248`_. + +.. _issue 553: https://github.com/nedbat/coveragepy/issues/553 +.. _issue 840: https://github.com/nedbat/coveragepy/issues/840 +.. _issue 1118: https://github.com/nedbat/coveragepy/issues/1118 +.. _issue 1160: https://github.com/nedbat/coveragepy/issues/1160 +.. _issue 1244: https://github.com/nedbat/coveragepy/issues/1244 +.. _pull request 1248: https://github.com/nedbat/coveragepy/pull/1248 +.. _issue 1254: https://github.com/nedbat/coveragepy/issues/1254 +.. _attrs: https://www.attrs.org/ .. _changes_602: -Version 6.0.2 --- 2021-10-11 ----------------------------- +Version 6.0.2 — 2021-10-11 +-------------------------- - Namespace packages being measured weren't properly handled by the new code that ignores third-party packages. If the namespace package was installed, it @@ -48,8 +157,8 @@ Version 6.0.2 --- 2021-10-11 .. _changes_601: -Version 6.0.1 --- 2021-10-06 ----------------------------- +Version 6.0.1 — 2021-10-06 +-------------------------- - In 6.0, the coverage.py exceptions moved from coverage.misc to coverage.exceptions. These exceptions are not part of the public supported @@ -71,8 +180,8 @@ Version 6.0.1 --- 2021-10-06 .. _changes_60: -Version 6.0 --- 2021-10-03 --------------------------- +Version 6.0 — 2021-10-03 +------------------------ - The ``coverage html`` command now prints a message indicating where the HTML report was written. Fixes `issue 1195`_. @@ -108,8 +217,8 @@ Version 6.0 --- 2021-10-03 .. _changes_60b1: -Version 6.0b1 --- 2021-07-18 ----------------------------- +Version 6.0b1 — 2021-07-18 +-------------------------- - Dropped support for Python 2.7, PyPy 2, and Python 3.5. @@ -158,8 +267,8 @@ Version 6.0b1 --- 2021-07-18 .. _changes_56b1: -Version 5.6b1 --- 2021-04-13 ----------------------------- +Version 5.6b1 — 2021-04-13 +-------------------------- Note: 5.6 final was never released. These changes are part of 6.0. @@ -187,8 +296,8 @@ Note: 5.6 final was never released. These changes are part of 6.0. .. _changes_55: -Version 5.5 --- 2021-02-28 --------------------------- +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 @@ -223,8 +332,8 @@ Version 5.5 --- 2021-02-28 .. _changes_54: -Version 5.4 --- 2021-01-24 --------------------------- +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 @@ -258,8 +367,8 @@ Version 5.4 --- 2021-01-24 .. _changes_531: -Version 5.3.1 --- 2020-12-19 ----------------------------- +Version 5.3.1 — 2020-12-19 +-------------------------- - When using ``--source`` on a large source tree, v5.x was slower than previous versions. This performance regression is now fixed, closing `issue 1037`_. @@ -284,8 +393,8 @@ Version 5.3.1 --- 2020-12-19 .. _changes_53: -Version 5.3 --- 2020-09-13 --------------------------- +Version 5.3 — 2020-09-13 +------------------------ - The ``source`` setting has always been interpreted as either a file path or a module, depending on which existed. If both interpretations were valid, it @@ -301,2785 +410,11 @@ Version 5.3 --- 2020-09-13 .. _issue 1011: https://github.com/nedbat/coveragepy/issues/1011 -.. _changes_521: - -Version 5.2.1 --- 2020-07-23 ----------------------------- - -- The dark mode HTML report still used light colors for the context listing, - making them unreadable (`issue 1009`_). This is now fixed. - -- The time stamp on the HTML report now includes the time zone. Thanks, Xie - Yanbo (`pull request 960`_). - -.. _pull request 960: https://github.com/nedbat/coveragepy/pull/960 -.. _issue 1009: https://github.com/nedbat/coveragepy/issues/1009 - - -.. _changes_52: - -Version 5.2 --- 2020-07-05 --------------------------- - -- The HTML report has been redesigned by Vince Salvino. There is now a dark - mode, the code text is larger, and system sans serif fonts are used, in - addition to other small changes (`issue 858`_ and `pull request 931`_). - -- The ``coverage report`` and ``coverage html`` commands now accept a - ``--precision`` option to control the number of decimal points displayed. - Thanks, Teake Nutma (`pull request 982`_). - -- The ``coverage report`` and ``coverage html`` commands now accept a - ``--no-skip-covered`` option to negate ``--skip-covered``. Thanks, Anthony - Sottile (`issue 779`_ and `pull request 932`_). - -- The ``--skip-empty`` option is now available for the XML report, closing - `issue 976`_. - -- The ``coverage report`` command now accepts a ``--sort`` option to specify - how to sort the results. Thanks, Jerin Peter George (`pull request 1005`_). - -- If coverage fails due to the coverage total not reaching the ``--fail-under`` - value, it will now print a message making the condition clear. Thanks, - Naveen Yadav (`pull request 977`_). - -- TOML configuration files with non-ASCII characters would cause errors on - Windows (`issue 990`_). This is now fixed. - -- The output of ``--debug=trace`` now includes information about how the - ``--source`` option is being interpreted, and the module names being - considered. - -.. _pull request 931: https://github.com/nedbat/coveragepy/pull/931 -.. _pull request 932: https://github.com/nedbat/coveragepy/pull/932 -.. _pull request 977: https://github.com/nedbat/coveragepy/pull/977 -.. _pull request 982: https://github.com/nedbat/coveragepy/pull/982 -.. _pull request 1005: https://github.com/nedbat/coveragepy/pull/1005 -.. _issue 779: https://github.com/nedbat/coveragepy/issues/779 -.. _issue 858: https://github.com/nedbat/coveragepy/issues/858 -.. _issue 976: https://github.com/nedbat/coveragepy/issues/976 -.. _issue 990: https://github.com/nedbat/coveragepy/issues/990 - - -.. _changes_51: - -Version 5.1 --- 2020-04-12 --------------------------- - -- The JSON report now includes counts of covered and missing branches. Thanks, - Salvatore Zagaria. - -- On Python 3.8, try-finally-return reported wrong branch coverage with - decorated async functions (`issue 964`_). This is now fixed. Thanks, Kjell - Braden. - -- The :meth:`~coverage.Coverage.get_option` and - :meth:`~coverage.Coverage.set_option` methods can now manipulate the - ``[paths]`` configuration setting. Thanks to Bernát Gábor for the fix for - `issue 967`_. - -.. _issue 964: https://github.com/nedbat/coveragepy/issues/964 -.. _issue 967: https://github.com/nedbat/coveragepy/issues/967 - - -.. _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 ----------------------------- - -- A performance improvement in 5.0.2 didn't work for test suites that changed - directory before combining data, causing "Couldn't use data file: no such - table: meta" errors (`issue 916`_). This is now fixed. - -- Coverage could fail to run your program with some form of "ModuleNotFound" or - "ImportError" trying to import from the current directory. This would happen - if coverage had been packaged into a zip file (for example, on Windows), or - was found indirectly (for example, by pyenv-virtualenv). A number of - different scenarios were described in `issue 862`_ which is now fixed. Huge - thanks to Agbonze O. Jeremiah for reporting it, and Alexander Waters and - George-Cristian Bîrzan for protracted debugging sessions. - -- Added the "premain" debug option. - -- Added SQLite compile-time options to the "debug sys" output. - -.. _issue 862: https://github.com/nedbat/coveragepy/issues/862 -.. _issue 916: https://github.com/nedbat/coveragepy/issues/916 - - -.. _changes_502: - -Version 5.0.2 --- 2020-01-05 ----------------------------- - -- Programs that used multiprocessing and changed directories would fail under - coverage. This is now fixed (`issue 890`_). A side effect is that debug - information about the config files read now shows absolute paths to the - files. - -- When running programs as modules (``coverage run -m``) with ``--source``, - some measured modules were imported before coverage starts. This resulted in - unwanted warnings ("Already imported a file that will be measured") and a - reduction in coverage totals (`issue 909`_). This is now fixed. - -- If no data was collected, an exception about "No data to report" could happen - instead of a 0% report being created (`issue 884`_). This is now fixed. - -- The handling of source files with non-encodable file names has changed. - Previously, if a file name could not be encoded as UTF-8, an error occurred, - as described in `issue 891`_. Now, those files will not be measured, since - their data would not be recordable. - -- A new warning ("dynamic-conflict") is issued if two mechanisms are trying to - change the dynamic context. Closes `issue 901`_. - -- ``coverage run --debug=sys`` would fail with an AttributeError. This is now - fixed (`issue 907`_). - -.. _issue 884: https://github.com/nedbat/coveragepy/issues/884 -.. _issue 890: https://github.com/nedbat/coveragepy/issues/890 -.. _issue 891: https://github.com/nedbat/coveragepy/issues/891 -.. _issue 901: https://github.com/nedbat/coveragepy/issues/901 -.. _issue 907: https://github.com/nedbat/coveragepy/issues/907 -.. _issue 909: https://github.com/nedbat/coveragepy/issues/909 - - -.. _changes_501: - -Version 5.0.1 --- 2019-12-22 ----------------------------- - -- If a 4.x data file is the cause of a "file is not a database" error, then use - a more specific error message, "Looks like a coverage 4.x data file, are you - mixing versions of coverage?" Helps diagnose the problems described in - `issue 886`_. - -- Measurement contexts and relative file names didn't work together, as - reported in `issue 899`_ and `issue 900`_. This is now fixed, thanks to - David Szotten. - -- When using ``coverage run --concurrency=multiprocessing``, all data files - should be named with parallel-ready suffixes. 5.0 mistakenly named the main - process' file with no suffix when using ``--append``. This is now fixed, - closing `issue 880`_. - -- Fixed a problem on Windows when the current directory is changed to a - different drive (`issue 895`_). Thanks, Olivier Grisel. - -- Updated Python 3.9 support to 3.9a2. - -.. _issue 880: https://github.com/nedbat/coveragepy/issues/880 -.. _issue 886: https://github.com/nedbat/coveragepy/issues/886 -.. _issue 895: https://github.com/nedbat/coveragepy/issues/895 -.. _issue 899: https://github.com/nedbat/coveragepy/issues/899 -.. _issue 900: https://github.com/nedbat/coveragepy/issues/900 - - -.. _changes_50: - -Version 5.0 --- 2019-12-14 --------------------------- - -Nothing new beyond 5.0b2. - -A summary of major changes in 5.0 since 4.5.x is in see :ref:`whatsnew5x`. - - - -.. _changes_50b2: - -Version 5.0b2 --- 2019-12-08 ----------------------------- - -- An experimental ``[run] relative_files`` setting tells coverage to store - relative file names in the data file. This makes it easier to run tests in - one (or many) environments, and then report in another. It has not had much - real-world testing, so it may change in incompatible ways in the future. - -- When constructing a :class:`coverage.Coverage` object, `data_file` can be - specified as None to prevent writing any data file at all. In previous - versions, an explicit `data_file=None` argument would use the default of - ".coverage". Fixes `issue 871`_. - -- Python files run with ``-m`` now have ``__spec__`` defined properly. This - fixes `issue 745`_ (about not being able to run unittest tests that spawn - subprocesses), and `issue 838`_, which described the problem directly. - -- The ``[paths]`` configuration section is now ordered. If you specify more - than one list of patterns, the first one that matches will be used. Fixes - `issue 649`_. - -- The :func:`.coverage.numbits.register_sqlite_functions` function now also - registers `numbits_to_nums` for use in SQLite queries. Thanks, Simon - Willison. - -- Python 3.9a1 is supported. - -- Coverage.py has a mascot: :ref:`Sleepy Snake `. - -.. _issue 649: https://github.com/nedbat/coveragepy/issues/649 -.. _issue 745: https://github.com/nedbat/coveragepy/issues/745 -.. _issue 838: https://github.com/nedbat/coveragepy/issues/838 -.. _issue 871: https://github.com/nedbat/coveragepy/issues/871 - - -.. _changes_50b1: - -Version 5.0b1 --- 2019-11-11 ----------------------------- - -- The HTML and textual reports now have a ``--skip-empty`` option that skips - files with no statements, notably ``__init__.py`` files. Thanks, Reya B. - -- Configuration can now be read from `TOML`_ files. This requires installing - coverage.py with the ``[toml]`` extra. The standard "pyproject.toml" file - will be read automatically if no other configuration file is found, with - settings in the ``[tool.coverage.]`` namespace. Thanks to Frazer McLean for - implementation and persistence. Finishes `issue 664`_. - -- The ``[run] note`` setting has been deprecated. Using it will result in a - warning, and the note will not be written to the data file. The - corresponding :class:`.CoverageData` methods have been removed. - -- The HTML report has been reimplemented (no more table around the source - code). This allowed for a better presentation of the context information, - hopefully resolving `issue 855`_. - -- Added sqlite3 module version information to ``coverage debug sys`` output. - -- Asking the HTML report to show contexts (``[html] show_contexts=True`` or - ``coverage html --show-contexts``) will issue a warning if there were no - contexts measured (`issue 851`_). - -.. _TOML: https://toml.io/ -.. _issue 664: https://github.com/nedbat/coveragepy/issues/664 -.. _issue 851: https://github.com/nedbat/coveragepy/issues/851 -.. _issue 855: https://github.com/nedbat/coveragepy/issues/855 - - -.. _changes_50a8: - -Version 5.0a8 --- 2019-10-02 ----------------------------- - -- The :class:`.CoverageData` API has changed how queries are limited to - specific contexts. Now you use :meth:`.CoverageData.set_query_context` to - set a single exact-match string, or :meth:`.CoverageData.set_query_contexts` - to set a list of regular expressions to match contexts. This changes the - command-line ``--contexts`` option to use regular expressions instead of - filename-style wildcards. - - -.. _changes_50a7: - -Version 5.0a7 --- 2019-09-21 ----------------------------- - -- Data can now be "reported" in JSON format, for programmatic use, as requested - in `issue 720`_. The new ``coverage json`` command writes raw and summarized - data to a JSON file. Thanks, Matt Bachmann. - -- Dynamic contexts are now supported in the Python tracer, which is important - for PyPy users. Closes `issue 846`_. - -- The compact line number representation introduced in 5.0a6 is called a - "numbits." The :mod:`coverage.numbits` module provides functions for working - with them. - -- The reporting methods used to permanently apply their arguments to the - configuration of the Coverage object. Now they no longer do. The arguments - affect the operation of the method, but do not persist. - -- A class named "test_something" no longer confuses the ``test_function`` - dynamic context setting. Fixes `issue 829`_. - -- Fixed an unusual tokenizing issue with backslashes in comments. Fixes - `issue 822`_. - -- ``debug=plugin`` didn't properly support configuration or dynamic context - plugins, but now it does, closing `issue 834`_. - -.. _issue 720: https://github.com/nedbat/coveragepy/issues/720 -.. _issue 822: https://github.com/nedbat/coveragepy/issues/822 -.. _issue 834: https://github.com/nedbat/coveragepy/issues/834 -.. _issue 829: https://github.com/nedbat/coveragepy/issues/829 -.. _issue 846: https://github.com/nedbat/coveragepy/issues/846 - - -.. _changes_50a6: - -Version 5.0a6 --- 2019-07-16 ----------------------------- - -- Reporting on contexts. Big thanks to Stephan Richter and Albertas Agejevas - for the contribution. - - - The ``--contexts`` option is available on the ``report`` and ``html`` - commands. It's a comma-separated list of shell-style wildcards, selecting - the contexts to report on. Only contexts matching one of the wildcards - will be included in the report. - - - The ``--show-contexts`` option for the ``html`` command adds context - information to each covered line. Hovering over the "ctx" marker at the - end of the line reveals a list of the contexts that covered the line. - -- Database changes: - - - Line numbers are now stored in a much more compact way. For each file and - context, a single binary string is stored with a bit per line number. This - greatly improves memory use, but makes ad-hoc use difficult. - - - Dynamic contexts with no data are no longer written to the database. - - - SQLite data storage is now faster. There's no longer a reason to keep the - JSON data file code, so it has been removed. - -- Changes to the :class:`.CoverageData` interface: - - - The new :meth:`.CoverageData.dumps` method serializes the data to a string, - and a corresponding :meth:`.CoverageData.loads` method reconstitutes this - data. The format of the data string is subject to change at any time, and - so should only be used between two installations of the same version of - coverage.py. - - - The :meth:`CoverageData constructor<.CoverageData.__init__>` has a new - argument, `no_disk` (default: False). Setting it to True prevents writing - any data to the disk. This is useful for transient data objects. - -- Added the classmethod :meth:`.Coverage.current` to get the latest started - Coverage instance. - -- Multiprocessing support in Python 3.8 was broken, but is now fixed. Closes - `issue 828`_. - -- Error handling during reporting has changed slightly. All reporting methods - now behave the same. The ``--ignore-errors`` option keeps errors from - stopping the reporting, but files that couldn't parse as Python will always - be reported as warnings. As with other warnings, you can suppress them with - the ``[run] disable_warnings`` configuration setting. - -- Coverage.py no longer fails if the user program deletes its current - directory. Fixes `issue 806`_. Thanks, Dan Hemberger. - -- The scrollbar markers in the HTML report now accurately show the highlighted - lines, regardless of what categories of line are highlighted. - -- The hack to accommodate ShiningPanda_ looking for an obsolete internal data - file has been removed, since ShiningPanda 0.22 fixed it four years ago. - -- The deprecated `Reporter.file_reporters` property has been removed. - -.. _ShiningPanda: https://plugins.jenkins.io/shiningpanda/ -.. _issue 806: https://github.com/nedbat/coveragepy/pull/806 -.. _issue 828: https://github.com/nedbat/coveragepy/issues/828 - - -.. _changes_50a5: - -Version 5.0a5 --- 2019-05-07 ----------------------------- - -- Drop support for Python 3.4 - -- Dynamic contexts can now be set two new ways, both thanks to Justas - Sadzevičius. - - - A plugin can implement a ``dynamic_context`` method to check frames for - whether a new context should be started. See - :ref:`dynamic_context_plugins` for more details. - - - Another tool (such as a test runner) can use the new - :meth:`.Coverage.switch_context` method to explicitly change the context. - -- The ``dynamic_context = test_function`` setting now works with Python 2 - old-style classes, though it only reports the method name, not the class it - was defined on. Closes `issue 797`_. - -- ``fail_under`` values more than 100 are reported as errors. Thanks to Mike - Fiedler for closing `issue 746`_. - -- The "missing" values in the text output are now sorted by line number, so - that missing branches are reported near the other lines they affect. The - values used to show all missing lines, and then all missing branches. - -- Access to the SQLite database used for data storage is now thread-safe. - Thanks, Stephan Richter. This closes `issue 702`_. - -- Combining data stored in SQLite is now about twice as fast, fixing `issue - 761`_. Thanks, Stephan Richter. - -- The ``filename`` attribute on :class:`.CoverageData` objects has been made - private. You can use the ``data_filename`` method to get the actual file - name being used to store data, and the ``base_filename`` method to get the - original filename before parallelizing suffixes were added. This is part of - fixing `issue 708`_. - -- Line numbers in the HTML report now align properly with source lines, even - when Chrome's minimum font size is set, fixing `issue 748`_. Thanks Wen Ye. - -.. _issue 702: https://github.com/nedbat/coveragepy/issues/702 -.. _issue 708: https://github.com/nedbat/coveragepy/issues/708 -.. _issue 746: https://github.com/nedbat/coveragepy/issues/746 -.. _issue 748: https://github.com/nedbat/coveragepy/issues/748 -.. _issue 761: https://github.com/nedbat/coveragepy/issues/761 -.. _issue 797: https://github.com/nedbat/coveragepy/issues/797 - - -.. _changes_50a4: - -Version 5.0a4 --- 2018-11-25 ----------------------------- - -- You can specify the command line to run your program with the ``[run] - command_line`` configuration setting, as requested in `issue 695`_. - -- Coverage will create directories as needed for the data file if they don't - exist, closing `issue 721`_. - -- The ``coverage run`` command has always adjusted the first entry in sys.path, - to properly emulate how Python runs your program. Now this adjustment is - skipped if sys.path[0] is already different than Python's default. This - fixes `issue 715`_. - -- Improvements to context support: - - - The "no such table: meta" error is fixed.: `issue 716`_. - - - Combining data files is now much faster. - -- Python 3.8 (as of today!) passes all tests. - -.. _issue 695: https://github.com/nedbat/coveragepy/issues/695 -.. _issue 715: https://github.com/nedbat/coveragepy/issues/715 -.. _issue 716: https://github.com/nedbat/coveragepy/issues/716 -.. _issue 721: https://github.com/nedbat/coveragepy/issues/721 - - -.. _changes_50a3: - -Version 5.0a3 --- 2018-10-06 ----------------------------- - -- Context support: static contexts let you specify a label for a coverage run, - which is recorded in the data, and retained when you combine files. See - :ref:`contexts` for more information. - -- Dynamic contexts: specifying ``[run] dynamic_context = test_function`` in the - config file will record the test function name as a dynamic context during - execution. This is the core of "Who Tests What" (`issue 170`_). Things to - note: - - - There is no reporting support yet. Use SQLite to query the .coverage file - for information. Ideas are welcome about how reporting could be extended - to use this data. - - - There's a noticeable slow-down before any test is run. - - - Data files will now be roughly N times larger, where N is the number of - tests you have. Combining data files is therefore also N times slower. - - - No other values for ``dynamic_context`` are recognized yet. Let me know - what else would be useful. I'd like to use a pytest plugin to get better - information directly from pytest, for example. - -.. _issue 170: https://github.com/nedbat/coveragepy/issues/170 - -- Environment variable substitution in configuration files now supports two - syntaxes for controlling the behavior of undefined variables: if ``VARNAME`` - is not defined, ``${VARNAME?}`` will raise an error, and ``${VARNAME-default - value}`` will use "default value". - -- Partial support for Python 3.8, which has not yet released an alpha. Fixes - `issue 707`_ and `issue 714`_. - -.. _issue 707: https://github.com/nedbat/coveragepy/issues/707 -.. _issue 714: https://github.com/nedbat/coveragepy/issues/714 - - -.. _changes_50a2: - -Version 5.0a2 --- 2018-09-03 ----------------------------- - -- Coverage's data storage has changed. In version 4.x, .coverage files were - basically JSON. Now, they are SQLite databases. This means the data file - can be created earlier than it used to. A large amount of code was - refactored to support this change. - - - Because the data file is created differently than previous releases, you - may need ``parallel=true`` where you didn't before. - - - The old data format is still available (for now) by setting the environment - variable COVERAGE_STORAGE=json. Please tell me if you think you need to - keep the JSON format. - - - The database schema is guaranteed to change in the future, to support new - features. I'm looking for opinions about making the schema part of the - public API to coverage.py or not. - -- Development moved from `Bitbucket`_ to `GitHub`_. - -- HTML files no longer have trailing and extra whitespace. - -- The sort order in the HTML report is stored in local storage rather than - cookies, closing `issue 611`_. Thanks, Federico Bond. - -- pickle2json, for converting v3 data files to v4 data files, has been removed. - -.. _Bitbucket: https://bitbucket.org -.. _GitHub: https://github.com/nedbat/coveragepy - -.. _issue 611: https://github.com/nedbat/coveragepy/issues/611 - - -.. _changes_50a1: - -Version 5.0a1 --- 2018-06-05 ----------------------------- - -- Coverage.py no longer supports Python 2.6 or 3.3. - -- The location of the configuration file can now be specified with a - ``COVERAGE_RCFILE`` environment variable, as requested in `issue 650`_. - -- Namespace packages are supported on Python 3.7, where they used to cause - TypeErrors about path being None. Fixes `issue 700`_. - -- A new warning (``already-imported``) is issued if measurable files have - already been imported before coverage.py started measurement. See - :ref:`cmd_warnings` for more information. - -- Running coverage many times for small runs in a single process should be - faster, closing `issue 625`_. Thanks, David MacIver. - -- Large HTML report pages load faster. Thanks, Pankaj Pandey. - -.. _issue 625: https://github.com/nedbat/coveragepy/issues/625 -.. _issue 650: https://github.com/nedbat/coveragepy/issues/650 -.. _issue 700: https://github.com/nedbat/coveragepy/issues/700 - - -.. _changes_454: - -Version 4.5.4 --- 2019-07-29 ----------------------------- - -- Multiprocessing support in Python 3.8 was broken, but is now fixed. Closes - `issue 828`_. - -.. _issue 828: https://github.com/nedbat/coveragepy/issues/828 - - -.. _changes_453: - -Version 4.5.3 --- 2019-03-09 ----------------------------- - -- Only packaging metadata changes. - - -.. _changes_452: - -Version 4.5.2 --- 2018-11-12 ----------------------------- - -- Namespace packages are supported on Python 3.7, where they used to cause - TypeErrors about path being None. Fixes `issue 700`_. - -- Python 3.8 (as of today!) passes all tests. Fixes `issue 707`_ and - `issue 714`_. - -- Development moved from `Bitbucket`_ to `GitHub`_. - -.. _issue 700: https://github.com/nedbat/coveragepy/issues/700 -.. _issue 707: https://github.com/nedbat/coveragepy/issues/707 -.. _issue 714: https://github.com/nedbat/coveragepy/issues/714 - -.. _Bitbucket: https://bitbucket.org -.. _GitHub: https://github.com/nedbat/coveragepy - - -.. _changes_451: - -Version 4.5.1 --- 2018-02-10 ----------------------------- - -- Now that 4.5 properly separated the ``[run] omit`` and ``[report] omit`` - settings, an old bug has become apparent. If you specified a package name - for ``[run] source``, then omit patterns weren't matched inside that package. - This bug (`issue 638`_) is now fixed. - -- On Python 3.7, reporting about a decorated function with no body other than a - docstring would crash coverage.py with an IndexError (`issue 640`_). This is - now fixed. - -- Configurer plugins are now reported in the output of ``--debug=sys``. - -.. _issue 638: https://github.com/nedbat/coveragepy/issues/638 -.. _issue 640: https://github.com/nedbat/coveragepy/issues/640 - - -.. _changes_45: - -Version 4.5 --- 2018-02-03 --------------------------- - -- A new kind of plugin is supported: configurers are invoked at start-up to - allow more complex configuration than the .coveragerc file can easily do. - See :ref:`api_plugin` for details. This solves the complex configuration - problem described in `issue 563`_. - -- The ``fail_under`` option can now be a float. Note that you must specify the - ``[report] precision`` configuration option for the fractional part to be - used. Thanks to Lars Hupfeldt Nielsen for help with the implementation. - Fixes `issue 631`_. - -- The ``include`` and ``omit`` options can be specified for both the ``[run]`` - and ``[report]`` phases of execution. 4.4.2 introduced some incorrect - interactions between those phases, where the options for one were confused - for the other. This is now corrected, fixing `issue 621`_ and `issue 622`_. - Thanks to Daniel Hahler for seeing more clearly than I could. - -- The ``coverage combine`` command used to always overwrite the data file, even - when no data had been read from apparently combinable files. Now, an error - is raised if we thought there were files to combine, but in fact none of them - could be used. Fixes `issue 629`_. - -- The ``coverage combine`` command could get confused about path separators - when combining data collected on Windows with data collected on Linux, as - described in `issue 618`_. This is now fixed: the result path always uses - the path separator specified in the ``[paths]`` result. - -- On Windows, the HTML report could fail when source trees are deeply nested, - due to attempting to create HTML filenames longer than the 250-character - maximum. Now filenames will never get much larger than 200 characters, - fixing `issue 627`_. Thanks to Alex Sandro for helping with the fix. - -.. _issue 563: https://github.com/nedbat/coveragepy/issues/563 -.. _issue 618: https://github.com/nedbat/coveragepy/issues/618 -.. _issue 621: https://github.com/nedbat/coveragepy/issues/621 -.. _issue 622: https://github.com/nedbat/coveragepy/issues/622 -.. _issue 627: https://github.com/nedbat/coveragepy/issues/627 -.. _issue 629: https://github.com/nedbat/coveragepy/issues/629 -.. _issue 631: https://github.com/nedbat/coveragepy/issues/631 - - -.. _changes_442: - -Version 4.4.2 --- 2017-11-05 ----------------------------- - -- Support for Python 3.7. In some cases, class and module docstrings are no - longer counted in statement totals, which could slightly change your total - results. - -- Specifying both ``--source`` and ``--include`` no longer silently ignores the - include setting, instead it displays a warning. Thanks, Loïc Dachary. Closes - `issue 265`_ and `issue 101`_. - -- Fixed a race condition when saving data and multiple threads are tracing - (`issue 581`_). It could produce a "dictionary changed size during iteration" - RuntimeError. I believe this mostly but not entirely fixes the race - condition. A true fix would likely be too expensive. Thanks, Peter Baughman - for the debugging, and Olivier Grisel for the fix with tests. - -- Configuration values which are file paths will now apply tilde-expansion, - closing `issue 589`_. - -- Now secondary config files like tox.ini and setup.cfg can be specified - explicitly, and prefixed sections like `[coverage:run]` will be read. Fixes - `issue 588`_. - -- Be more flexible about the command name displayed by help, fixing - `issue 600`_. Thanks, Ben Finney. - -.. _issue 101: https://github.com/nedbat/coveragepy/issues/101 -.. _issue 581: https://github.com/nedbat/coveragepy/issues/581 -.. _issue 588: https://github.com/nedbat/coveragepy/issues/588 -.. _issue 589: https://github.com/nedbat/coveragepy/issues/589 -.. _issue 600: https://github.com/nedbat/coveragepy/issues/600 - - -.. _changes_441: - -Version 4.4.1 --- 2017-05-14 ----------------------------- - -- No code changes: just corrected packaging for Python 2.7 Linux wheels. - - -.. _changes_44: - -Version 4.4 --- 2017-05-07 --------------------------- - -- Reports could produce the wrong file names for packages, reporting ``pkg.py`` - instead of the correct ``pkg/__init__.py``. This is now fixed. Thanks, Dirk - Thomas. - -- XML reports could produce ```` and ```` lines that together - didn't specify a valid source file path. This is now fixed. (`issue 526`_) - -- Namespace packages are no longer warned as having no code. (`issue 572`_) - -- Code that uses ``sys.settrace(sys.gettrace())`` in a file that wasn't being - coverage-measured would prevent correct coverage measurement in following - code. An example of this was running doctests programmatically. This is now - fixed. (`issue 575`_) - -- Errors printed by the ``coverage`` command now go to stderr instead of - stdout. - -- Running ``coverage xml`` in a directory named with non-ASCII characters would - fail under Python 2. This is now fixed. (`issue 573`_) - -.. _issue 526: https://github.com/nedbat/coveragepy/issues/526 -.. _issue 572: https://github.com/nedbat/coveragepy/issues/572 -.. _issue 573: https://github.com/nedbat/coveragepy/issues/573 -.. _issue 575: https://github.com/nedbat/coveragepy/issues/575 - - -Version 4.4b1 --- 2017-04-04 ----------------------------- - -- Some warnings can now be individually disabled. Warnings that can be - disabled have a short name appended. The ``[run] disable_warnings`` setting - takes a list of these warning names to disable. Closes both `issue 96`_ and - `issue 355`_. - -- The XML report now includes attributes from version 4 of the Cobertura XML - format, fixing `issue 570`_. - -- In previous versions, calling a method that used collected data would prevent - further collection. For example, `save()`, `report()`, `html_report()`, and - others would all stop collection. An explicit `start()` was needed to get it - going again. This is no longer true. Now you can use the collected data and - also continue measurement. Both `issue 79`_ and `issue 448`_ described this - problem, and have been fixed. - -- Plugins can now find unexecuted files if they choose, by implementing the - `find_executable_files` method. Thanks, Emil Madsen. - -- Minimal IronPython support. You should be able to run IronPython programs - under ``coverage run``, though you will still have to do the reporting phase - with CPython. - -- Coverage.py has long had a special hack to support CPython's need to measure - the coverage of the standard library tests. This code was not installed by - kitted versions of coverage.py. Now it is. - -.. _issue 79: https://github.com/nedbat/coveragepy/issues/79 -.. _issue 96: https://github.com/nedbat/coveragepy/issues/96 -.. _issue 355: https://github.com/nedbat/coveragepy/issues/355 -.. _issue 448: https://github.com/nedbat/coveragepy/issues/448 -.. _issue 570: https://github.com/nedbat/coveragepy/issues/570 - - -.. _changes_434: - -Version 4.3.4 --- 2017-01-17 ----------------------------- - -- Fixing 2.6 in version 4.3.3 broke other things, because the too-tricky - exception wasn't properly derived from Exception, described in `issue 556`_. - A newb mistake; it hasn't been a good few days. - -.. _issue 556: https://github.com/nedbat/coveragepy/issues/556 - - -.. _changes_433: - -Version 4.3.3 --- 2017-01-17 ----------------------------- - -- Python 2.6 support was broken due to a testing exception imported for the - benefit of the coverage.py test suite. Properly conditionalizing it fixed - `issue 554`_ so that Python 2.6 works again. - -.. _issue 554: https://github.com/nedbat/coveragepy/issues/554 - - -.. _changes_432: - -Version 4.3.2 --- 2017-01-16 ----------------------------- - -- Using the ``--skip-covered`` option on an HTML report with 100% coverage - would cause a "No data to report" error, as reported in `issue 549`_. This is - now fixed; thanks, Loïc Dachary. - -- If-statements can be optimized away during compilation, for example, `if 0:` - or `if __debug__:`. Coverage.py had problems properly understanding these - statements which existed in the source, but not in the compiled bytecode. - This problem, reported in `issue 522`_, is now fixed. - -- If you specified ``--source`` as a directory, then coverage.py would look for - importable Python files in that directory, and could identify ones that had - never been executed at all. But if you specified it as a package name, that - detection wasn't performed. Now it is, closing `issue 426`_. Thanks to Loïc - Dachary for the fix. - -- If you started and stopped coverage measurement thousands of times in your - process, you could crash Python with a "Fatal Python error: deallocating - None" error. This is now fixed. Thanks to Alex Groce for the bug report. - -- On PyPy, measuring coverage in subprocesses could produce a warning: "Trace - function changed, measurement is likely wrong: None". This was spurious, and - has been suppressed. - -- Previously, coverage.py couldn't start on Jython, due to that implementation - missing the multiprocessing module (`issue 551`_). This problem has now been - fixed. Also, `issue 322`_ about not being able to invoke coverage - conveniently, seems much better: ``jython -m coverage run myprog.py`` works - properly. - -- Let's say you ran the HTML report over and over again in the same output - directory, with ``--skip-covered``. And imagine due to your heroic - test-writing efforts, a file just achieved the goal of 100% coverage. With - coverage.py 4.3, the old HTML file with the less-than-100% coverage would be - left behind. This file is now properly deleted. - -.. _issue 322: https://github.com/nedbat/coveragepy/issues/322 -.. _issue 426: https://github.com/nedbat/coveragepy/issues/426 -.. _issue 522: https://github.com/nedbat/coveragepy/issues/522 -.. _issue 549: https://github.com/nedbat/coveragepy/issues/549 -.. _issue 551: https://github.com/nedbat/coveragepy/issues/551 - - -.. _changes_431: - -Version 4.3.1 --- 2016-12-28 ----------------------------- - -- Some environments couldn't install 4.3, as described in `issue 540`_. This is - now fixed. - -- The check for conflicting ``--source`` and ``--include`` was too simple in a - few different ways, breaking a few perfectly reasonable use cases, described - in `issue 541`_. The check has been reverted while we re-think the fix for - `issue 265`_. - -.. _issue 540: https://github.com/nedbat/coveragepy/issues/540 -.. _issue 541: https://github.com/nedbat/coveragepy/issues/541 - - -.. _changes_43: - -Version 4.3 --- 2016-12-27 --------------------------- - -Special thanks to **Loïc Dachary**, who took an extraordinary interest in -coverage.py and contributed a number of improvements in this release. - -- Subprocesses that are measured with `automatic subprocess measurement`_ used - to read in any pre-existing data file. This meant data would be incorrectly - carried forward from run to run. Now those files are not read, so each - subprocess only writes its own data. Fixes `issue 510`_. - -- The ``coverage combine`` command will now fail if there are no data files to - combine. The combine changes in 4.2 meant that multiple combines could lose - data, leaving you with an empty .coverage data file. Fixes - `issue 525`_, `issue 412`_, `issue 516`_, and probably `issue 511`_. - -- Coverage.py wouldn't execute `sys.excepthook`_ when an exception happened in - your program. Now it does, thanks to Andrew Hoos. Closes `issue 535`_. - -- Branch coverage fixes: - - - Branch coverage could misunderstand a finally clause on a try block that - never continued on to the following statement, as described in `issue - 493`_. This is now fixed. Thanks to Joe Doherty for the report and Loïc - Dachary for the fix. - - - A while loop with a constant condition (while True) and a continue - statement would be mis-analyzed, as described in `issue 496`_. This is now - fixed, thanks to a bug report by Eli Skeggs and a fix by Loïc Dachary. - - - While loops with constant conditions that were never executed could result - in a non-zero coverage report. Artem Dayneko reported this in `issue - 502`_, and Loïc Dachary provided the fix. - -- The HTML report now supports a ``--skip-covered`` option like the other - reporting commands. Thanks, Loïc Dachary for the implementation, closing - `issue 433`_. - -- Options can now be read from a tox.ini file, if any. Like setup.cfg, sections - are prefixed with "coverage:", so ``[run]`` options will be read from the - ``[coverage:run]`` section of tox.ini. Implements part of `issue 519`_. - Thanks, Stephen Finucane. - -- Specifying both ``--source`` and ``--include`` no longer silently ignores the - include setting, instead it fails with a message. Thanks, Nathan Land and - Loïc Dachary. Closes `issue 265`_. - -- The ``Coverage.combine`` method has a new parameter, ``strict=False``, to - support failing if there are no data files to combine. - -- When forking subprocesses, the coverage data files would have the same random - number appended to the file name. This didn't cause problems, because the - file names had the process id also, making collisions (nearly) impossible. - But it was disconcerting. This is now fixed. - -- The text report now properly sizes headers when skipping some files, fixing - `issue 524`_. Thanks, Anthony Sottile and Loïc Dachary. - -- Coverage.py can now search .pex files for source, just as it can .zip and - .egg. Thanks, Peter Ebden. - -- Data files are now about 15% smaller. - -- Improvements in the ``[run] debug`` setting: - - - The "dataio" debug setting now also logs when data files are deleted during - combining or erasing. - - - A new debug option, "multiproc", for logging the behavior of - ``concurrency=multiprocessing``. - - - If you used the debug options "config" and "callers" together, you'd get a - call stack printed for every line in the multi-line config output. This is - now fixed. - -- Fixed an unusual bug involving multiple coding declarations affecting code - containing code in multi-line strings: `issue 529`_. - -- Coverage.py will no longer be misled into thinking that a plain file is a - package when interpreting ``--source`` options. Thanks, Cosimo Lupo. - -- If you try to run a non-Python file with coverage.py, you will now get a more - useful error message. `Issue 514`_. - -- The default pragma regex changed slightly, but this will only matter to you - if you are deranged and use mixed-case pragmas. - -- Deal properly with non-ASCII file names in an ASCII-only world, `issue 533`_. - -- Programs that set Unicode configuration values could cause UnicodeErrors when - generating HTML reports. Pytest-cov is one example. This is now fixed. - -- Prevented deprecation warnings from configparser that happened in some - circumstances, closing `issue 530`_. - -- Corrected the name of the jquery.ba-throttle-debounce.js library. Thanks, - Ben Finney. Closes `issue 505`_. - -- Testing against PyPy 5.6 and PyPy3 5.5. - -- Switched to pytest from nose for running the coverage.py tests. - -- Renamed AUTHORS.txt to CONTRIBUTORS.txt, since there are other ways to - contribute than by writing code. Also put the count of contributors into the - author string in setup.py, though this might be too cute. - -.. _sys.excepthook: https://docs.python.org/3/library/sys.html#sys.excepthook -.. _issue 265: https://github.com/nedbat/coveragepy/issues/265 -.. _issue 412: https://github.com/nedbat/coveragepy/issues/412 -.. _issue 433: https://github.com/nedbat/coveragepy/issues/433 -.. _issue 493: https://github.com/nedbat/coveragepy/issues/493 -.. _issue 496: https://github.com/nedbat/coveragepy/issues/496 -.. _issue 502: https://github.com/nedbat/coveragepy/issues/502 -.. _issue 505: https://github.com/nedbat/coveragepy/issues/505 -.. _issue 514: https://github.com/nedbat/coveragepy/issues/514 -.. _issue 510: https://github.com/nedbat/coveragepy/issues/510 -.. _issue 511: https://github.com/nedbat/coveragepy/issues/511 -.. _issue 516: https://github.com/nedbat/coveragepy/issues/516 -.. _issue 519: https://github.com/nedbat/coveragepy/issues/519 -.. _issue 524: https://github.com/nedbat/coveragepy/issues/524 -.. _issue 525: https://github.com/nedbat/coveragepy/issues/525 -.. _issue 529: https://github.com/nedbat/coveragepy/issues/529 -.. _issue 530: https://github.com/nedbat/coveragepy/issues/530 -.. _issue 533: https://github.com/nedbat/coveragepy/issues/533 -.. _issue 535: https://github.com/nedbat/coveragepy/issues/535 - - -.. _changes_42: - -Version 4.2 --- 2016-07-26 --------------------------- - -- Since ``concurrency=multiprocessing`` uses subprocesses, options specified on - the coverage.py command line will not be communicated down to them. Only - options in the configuration file will apply to the subprocesses. - Previously, the options didn't apply to the subprocesses, but there was no - indication. Now it is an error to use ``--concurrency=multiprocessing`` and - other run-affecting options on the command line. This prevents - failures like those reported in `issue 495`_. - -- Filtering the HTML report is now faster, thanks to Ville Skyttä. - -.. _issue 495: https://github.com/nedbat/coveragepy/issues/495 - - -Version 4.2b1 --- 2016-07-04 ----------------------------- - -Work from the PyCon 2016 Sprints! - -- BACKWARD INCOMPATIBILITY: the ``coverage combine`` command now ignores an - existing ``.coverage`` data file. It used to include that file in its - combining. This caused confusing results, and extra tox "clean" steps. If - you want the old behavior, use the new ``coverage combine --append`` option. - -- The ``concurrency`` option can now take multiple values, to support programs - using multiprocessing and another library such as eventlet. This is only - possible in the configuration file, not from the command line. The - configuration file is the only way for sub-processes to all run with the same - options. Fixes `issue 484`_. Thanks to Josh Williams for prototyping. - -- Using a ``concurrency`` setting of ``multiprocessing`` now implies - ``--parallel`` so that the main program is measured similarly to the - sub-processes. - -- When using `automatic subprocess measurement`_, running coverage commands - would create spurious data files. This is now fixed, thanks to diagnosis and - testing by Dan Riti. Closes `issue 492`_. - -- A new configuration option, ``report:sort``, controls what column of the - text report is used to sort the rows. Thanks to Dan Wandschneider, this - closes `issue 199`_. - -- The HTML report has a more-visible indicator for which column is being - sorted. Closes `issue 298`_, thanks to Josh Williams. - -- If the HTML report cannot find the source for a file, the message now - suggests using the ``-i`` flag to allow the report to continue. Closes - `issue 231`_, thanks, Nathan Land. - -- When reports are ignoring errors, there's now a warning if a file cannot be - parsed, rather than being silently ignored. Closes `issue 396`_. Thanks, - Matthew Boehm. - -- A new option for ``coverage debug`` is available: ``coverage debug config`` - shows the current configuration. Closes `issue 454`_, thanks to Matthew - Boehm. - -- Running coverage as a module (``python -m coverage``) no longer shows the - program name as ``__main__.py``. Fixes `issue 478`_. Thanks, Scott Belden. - -- The `test_helpers` module has been moved into a separate pip-installable - package: `unittest-mixins`_. - -.. _automatic subprocess measurement: https://coverage.readthedocs.io/en/latest/subprocess.html -.. _issue 199: https://github.com/nedbat/coveragepy/issues/199 -.. _issue 231: https://github.com/nedbat/coveragepy/issues/231 -.. _issue 298: https://github.com/nedbat/coveragepy/issues/298 -.. _issue 396: https://github.com/nedbat/coveragepy/issues/396 -.. _issue 454: https://github.com/nedbat/coveragepy/issues/454 -.. _issue 478: https://github.com/nedbat/coveragepy/issues/478 -.. _issue 484: https://github.com/nedbat/coveragepy/issues/484 -.. _issue 492: https://github.com/nedbat/coveragepy/issues/492 -.. _unittest-mixins: https://pypi.org/project/unittest-mixins/ - - -.. _changes_41: - -Version 4.1 --- 2016-05-21 --------------------------- - -- The internal attribute `Reporter.file_reporters` was removed in 4.1b3. It - should have come has no surprise that there were third-party tools out there - using that attribute. It has been restored, but with a deprecation warning. - - -Version 4.1b3 --- 2016-05-10 ----------------------------- - -- When running your program, execution can jump from an ``except X:`` line to - some other line when an exception other than ``X`` happens. This jump is no - longer considered a branch when measuring branch coverage. - -- When measuring branch coverage, ``yield`` statements that were never resumed - were incorrectly marked as missing, as reported in `issue 440`_. This is now - fixed. - -- During branch coverage of single-line callables like lambdas and generator - expressions, coverage.py can now distinguish between them never being called, - or being called but not completed. Fixes `issue 90`_, `issue 460`_ and - `issue 475`_. - -- The HTML report now has a map of the file along the rightmost edge of the - page, giving an overview of where the missed lines are. Thanks, Dmitry - Shishov. - -- The HTML report now uses different monospaced fonts, favoring Consolas over - Courier. Along the way, `issue 472`_ about not properly handling one-space - indents was fixed. The index page also has slightly different styling, to - try to make the clickable detail pages more apparent. - -- Missing branches reported with ``coverage report -m`` will now say ``->exit`` - for missed branches to the exit of a function, rather than a negative number. - Fixes `issue 469`_. - -- ``coverage --help`` and ``coverage --version`` now mention which tracer is - installed, to help diagnose problems. The docs mention which features need - the C extension. (`issue 479`_) - -- Officially support PyPy 5.1, which required no changes, just updates to the - docs. - -- The `Coverage.report` function had two parameters with non-None defaults, - which have been changed. `show_missing` used to default to True, but now - defaults to None. If you had been calling `Coverage.report` without - specifying `show_missing`, you'll need to explicitly set it to True to keep - the same behavior. `skip_covered` used to default to False. It is now None, - which doesn't change the behavior. This fixes `issue 485`_. - -- It's never been possible to pass a namespace module to one of the analysis - functions, but now at least we raise a more specific error message, rather - than getting confused. (`issue 456`_) - -- The `coverage.process_startup` function now returns the `Coverage` instance - it creates, as suggested in `issue 481`_. - -- Make a small tweak to how we compare threads, to avoid buggy custom - comparison code in thread classes. (`issue 245`_) - -.. _issue 90: https://github.com/nedbat/coveragepy/issues/90 -.. _issue 245: https://github.com/nedbat/coveragepy/issues/245 -.. _issue 440: https://github.com/nedbat/coveragepy/issues/440 -.. _issue 456: https://github.com/nedbat/coveragepy/issues/456 -.. _issue 460: https://github.com/nedbat/coveragepy/issues/460 -.. _issue 469: https://github.com/nedbat/coveragepy/issues/469 -.. _issue 472: https://github.com/nedbat/coveragepy/issues/472 -.. _issue 475: https://github.com/nedbat/coveragepy/issues/475 -.. _issue 479: https://github.com/nedbat/coveragepy/issues/479 -.. _issue 481: https://github.com/nedbat/coveragepy/issues/481 -.. _issue 485: https://github.com/nedbat/coveragepy/issues/485 - - -Version 4.1b2 --- 2016-01-23 ----------------------------- - -- Problems with the new branch measurement in 4.1 beta 1 were fixed: - - - Class docstrings were considered executable. Now they no longer are. - - - ``yield from`` and ``await`` were considered returns from functions, since - they could transfer control to the caller. This produced unhelpful - "missing branch" reports in a number of circumstances. Now they no longer - are considered returns. - - - In unusual situations, a missing branch to a negative number was reported. - This has been fixed, closing `issue 466`_. - -- The XML report now produces correct package names for modules found in - directories specified with ``source=``. Fixes `issue 465`_. - -- ``coverage report`` won't produce trailing whitespace. - -.. _issue 465: https://github.com/nedbat/coveragepy/issues/465 -.. _issue 466: https://github.com/nedbat/coveragepy/issues/466 - - -Version 4.1b1 --- 2016-01-10 ----------------------------- - -- Branch analysis has been rewritten: it used to be based on bytecode, but now - uses AST analysis. This has changed a number of things: - - - More code paths are now considered runnable, especially in - ``try``/``except`` structures. This may mean that coverage.py will - identify more code paths as uncovered. This could either raise or lower - your overall coverage number. - - - Python 3.5's ``async`` and ``await`` keywords are properly supported, - fixing `issue 434`_. - - - Some long-standing branch coverage bugs were fixed: - - - `issue 129`_: functions with only a docstring for a body would - incorrectly report a missing branch on the ``def`` line. - - - `issue 212`_: code in an ``except`` block could be incorrectly marked as - a missing branch. - - - `issue 146`_: context managers (``with`` statements) in a loop or ``try`` - block could confuse the branch measurement, reporting incorrect partial - branches. - - - `issue 422`_: in Python 3.5, an actual partial branch could be marked as - complete. - -- Pragmas to disable coverage measurement can now be used on decorator lines, - and they will apply to the entire function or class being decorated. This - implements the feature requested in `issue 131`_. - -- Multiprocessing support is now available on Windows. Thanks, Rodrigue - Cloutier. - -- Files with two encoding declarations are properly supported, fixing - `issue 453`_. Thanks, Max Linke. - -- Non-ascii characters in regexes in the configuration file worked in 3.7, but - stopped working in 4.0. Now they work again, closing `issue 455`_. - -- Form-feed characters would prevent accurate determination of the beginning of - statements in the rest of the file. This is now fixed, closing `issue 461`_. - -.. _issue 129: https://github.com/nedbat/coveragepy/issues/129 -.. _issue 131: https://github.com/nedbat/coveragepy/issues/131 -.. _issue 146: https://github.com/nedbat/coveragepy/issues/146 -.. _issue 212: https://github.com/nedbat/coveragepy/issues/212 -.. _issue 422: https://github.com/nedbat/coveragepy/issues/422 -.. _issue 434: https://github.com/nedbat/coveragepy/issues/434 -.. _issue 453: https://github.com/nedbat/coveragepy/issues/453 -.. _issue 455: https://github.com/nedbat/coveragepy/issues/455 -.. _issue 461: https://github.com/nedbat/coveragepy/issues/461 - - -.. _changes_403: - -Version 4.0.3 --- 2015-11-24 ----------------------------- - -- Fixed a mysterious problem that manifested in different ways: sometimes - hanging the process (`issue 420`_), sometimes making database connections - fail (`issue 445`_). - -- The XML report now has correct ```` elements when using a - ``--source=`` option somewhere besides the current directory. This fixes - `issue 439`_. Thanks, Arcadiy Ivanov. - -- Fixed an unusual edge case of detecting source encodings, described in - `issue 443`_. - -- Help messages that mention the command to use now properly use the actual - command name, which might be different than "coverage". Thanks to Ben - Finney, this closes `issue 438`_. - -.. _issue 420: https://github.com/nedbat/coveragepy/issues/420 -.. _issue 438: https://github.com/nedbat/coveragepy/issues/438 -.. _issue 439: https://github.com/nedbat/coveragepy/issues/439 -.. _issue 443: https://github.com/nedbat/coveragepy/issues/443 -.. _issue 445: https://github.com/nedbat/coveragepy/issues/445 - - -.. _changes_402: - -Version 4.0.2 --- 2015-11-04 ----------------------------- - -- More work on supporting unusually encoded source. Fixed `issue 431`_. - -- Files or directories with non-ASCII characters are now handled properly, - fixing `issue 432`_. - -- Setting a trace function with sys.settrace was broken by a change in 4.0.1, - as reported in `issue 436`_. This is now fixed. - -- Officially support PyPy 4.0, which required no changes, just updates to the - docs. - -.. _issue 431: https://github.com/nedbat/coveragepy/issues/431 -.. _issue 432: https://github.com/nedbat/coveragepy/issues/432 -.. _issue 436: https://github.com/nedbat/coveragepy/issues/436 - - -.. _changes_401: - -Version 4.0.1 --- 2015-10-13 ----------------------------- - -- When combining data files, unreadable files will now generate a warning - instead of failing the command. This is more in line with the older - coverage.py v3.7.1 behavior, which silently ignored unreadable files. - Prompted by `issue 418`_. - -- The --skip-covered option would skip reporting on 100% covered files, but - also skipped them when calculating total coverage. This was wrong, it should - only remove lines from the report, not change the final answer. This is now - fixed, closing `issue 423`_. - -- In 4.0, the data file recorded a summary of the system on which it was run. - Combined data files would keep all of those summaries. This could lead to - enormous data files consisting of mostly repetitive useless information. That - summary is now gone, fixing `issue 415`_. If you want summary information, - get in touch, and we'll figure out a better way to do it. - -- Test suites that mocked os.path.exists would experience strange failures, due - to coverage.py using their mock inadvertently. This is now fixed, closing - `issue 416`_. - -- Importing a ``__init__`` module explicitly would lead to an error: - ``AttributeError: 'module' object has no attribute '__path__'``, as reported - in `issue 410`_. This is now fixed. - -- Code that uses ``sys.settrace(sys.gettrace())`` used to incur a more than 2x - speed penalty. Now there's no penalty at all. Fixes `issue 397`_. - -- Pyexpat C code will no longer be recorded as a source file, fixing - `issue 419`_. - -- The source kit now contains all of the files needed to have a complete source - tree, re-fixing `issue 137`_ and closing `issue 281`_. - -.. _issue 281: https://github.com/nedbat/coveragepy/issues/281 -.. _issue 397: https://github.com/nedbat/coveragepy/issues/397 -.. _issue 410: https://github.com/nedbat/coveragepy/issues/410 -.. _issue 415: https://github.com/nedbat/coveragepy/issues/415 -.. _issue 416: https://github.com/nedbat/coveragepy/issues/416 -.. _issue 418: https://github.com/nedbat/coveragepy/issues/418 -.. _issue 419: https://github.com/nedbat/coveragepy/issues/419 -.. _issue 423: https://github.com/nedbat/coveragepy/issues/423 - - -.. _changes_40: - -Version 4.0 --- 2015-09-20 --------------------------- - -No changes from 4.0b3 - - -Version 4.0b3 --- 2015-09-07 ----------------------------- - -- Reporting on an unmeasured file would fail with a traceback. This is now - fixed, closing `issue 403`_. - -- The Jenkins ShiningPanda_ plugin looks for an obsolete file name to find the - HTML reports to publish, so it was failing under coverage.py 4.0. Now we - create that file if we are running under Jenkins, to keep things working - smoothly. `issue 404`_. - -- Kits used to include tests and docs, but didn't install them anywhere, or - provide all of the supporting tools to make them useful. Kits no longer - include tests and docs. If you were using them from the older packages, get - in touch and help me understand how. - -.. _issue 403: https://github.com/nedbat/coveragepy/issues/403 -.. _issue 404: https://github.com/nedbat/coveragepy/issues/404 - - -Version 4.0b2 --- 2015-08-22 ----------------------------- - -- 4.0b1 broke ``--append`` creating new data files. This is now fixed, closing - `issue 392`_. - -- ``py.test --cov`` can write empty data, then touch files due to ``--source``, - which made coverage.py mistakenly force the data file to record lines instead - of arcs. This would lead to a "Can't combine line data with arc data" error - message. This is now fixed, and changed some method names in the - CoverageData interface. Fixes `issue 399`_. - -- `CoverageData.read_fileobj` and `CoverageData.write_fileobj` replace the - `.read` and `.write` methods, and are now properly inverses of each other. - -- When using ``report --skip-covered``, a message will now be included in the - report output indicating how many files were skipped, and if all files are - skipped, coverage.py won't accidentally scold you for having no data to - report. Thanks, Krystian Kichewko. - -- A new conversion utility has been added: ``python -m coverage.pickle2json`` - will convert v3.x pickle data files to v4.x JSON data files. Thanks, - Alexander Todorov. Closes `issue 395`_. - -- A new version identifier is available, `coverage.version_info`, a plain tuple - of values similar to `sys.version_info`_. - -.. _issue 392: https://github.com/nedbat/coveragepy/issues/392 -.. _issue 395: https://github.com/nedbat/coveragepy/issues/395 -.. _issue 399: https://github.com/nedbat/coveragepy/issues/399 -.. _sys.version_info: https://docs.python.org/3/library/sys.html#sys.version_info - - -Version 4.0b1 --- 2015-08-02 ----------------------------- - -- Coverage.py is now licensed under the Apache 2.0 license. See NOTICE.txt for - details. Closes `issue 313`_. - -- The data storage has been completely revamped. The data file is now - JSON-based instead of a pickle, closing `issue 236`_. The `CoverageData` - class is now a public supported documented API to the data file. - -- A new configuration option, ``[run] note``, lets you set a note that will be - stored in the `runs` section of the data file. You can use this to annotate - the data file with any information you like. - -- Unrecognized configuration options will now print an error message and stop - coverage.py. This should help prevent configuration mistakes from passing - silently. Finishes `issue 386`_. - -- In parallel mode, ``coverage erase`` will now delete all of the data files, - fixing `issue 262`_. - -- Coverage.py now accepts a directory name for ``coverage run`` and will run a - ``__main__.py`` found there, just like Python will. Fixes `issue 252`_. - Thanks, Dmitry Trofimov. - -- The XML report now includes a ``missing-branches`` attribute. Thanks, Steve - Peak. This is not a part of the Cobertura DTD, so the XML report no longer - references the DTD. - -- Missing branches in the HTML report now have a bit more information in the - right-hand annotations. Hopefully this will make their meaning clearer. - -- All the reporting functions now behave the same if no data had been - collected, exiting with a status code of 1. Fixed ``fail_under`` to be - applied even when the report is empty. Thanks, Ionel Cristian Mărieș. - -- Plugins are now initialized differently. Instead of looking for a class - called ``Plugin``, coverage.py looks for a function called ``coverage_init``. - -- A file-tracing plugin can now ask to have built-in Python reporting by - returning `"python"` from its `file_reporter()` method. - -- Code that was executed with `exec` would be mis-attributed to the file that - called it. This is now fixed, closing `issue 380`_. - -- The ability to use item access on `Coverage.config` (introduced in 4.0a2) has - been changed to a more explicit `Coverage.get_option` and - `Coverage.set_option` API. - -- The ``Coverage.use_cache`` method is no longer supported. - -- The private method ``Coverage._harvest_data`` is now called - ``Coverage.get_data``, and returns the ``CoverageData`` containing the - collected data. - -- The project is consistently referred to as "coverage.py" throughout the code - and the documentation, closing `issue 275`_. - -- Combining data files with an explicit configuration file was broken in 4.0a6, - but now works again, closing `issue 385`_. - -- ``coverage combine`` now accepts files as well as directories. - -- The speed is back to 3.7.1 levels, after having slowed down due to plugin - support, finishing up `issue 387`_. - -.. _issue 236: https://github.com/nedbat/coveragepy/issues/236 -.. _issue 252: https://github.com/nedbat/coveragepy/issues/252 -.. _issue 262: https://github.com/nedbat/coveragepy/issues/262 -.. _issue 275: https://github.com/nedbat/coveragepy/issues/275 -.. _issue 313: https://github.com/nedbat/coveragepy/issues/313 -.. _issue 380: https://github.com/nedbat/coveragepy/issues/380 -.. _issue 385: https://github.com/nedbat/coveragepy/issues/385 -.. _issue 386: https://github.com/nedbat/coveragepy/issues/386 -.. _issue 387: https://github.com/nedbat/coveragepy/issues/387 - -.. 40 issues closed in 4.0 below here - - -Version 4.0a6 --- 2015-06-21 ----------------------------- - -- Python 3.5b2 and PyPy 2.6.0 are supported. - -- The original module-level function interface to coverage.py is no longer - supported. You must now create a ``coverage.Coverage`` object, and use - methods on it. - -- The ``coverage combine`` command now accepts any number of directories as - arguments, and will combine all the data files from those directories. This - means you don't have to copy the files to one directory before combining. - Thanks, Christine Lytwynec. Finishes `issue 354`_. - -- Branch coverage couldn't properly handle certain extremely long files. This - is now fixed (`issue 359`_). - -- Branch coverage didn't understand yield statements properly. Mickie Betz - persisted in pursuing this despite Ned's pessimism. Fixes `issue 308`_ and - `issue 324`_. - -- The COVERAGE_DEBUG environment variable can be used to set the - ``[run] debug`` configuration option to control what internal operations are - logged. - -- HTML reports were truncated at formfeed characters. This is now fixed - (`issue 360`_). It's always fun when the problem is due to a `bug in the - Python standard library `_. - -- Files with incorrect encoding declaration comments are no longer ignored by - the reporting commands, fixing `issue 351`_. - -- HTML reports now include a timestamp in the footer, closing `issue 299`_. - Thanks, Conrad Ho. - -- HTML reports now begrudgingly use double-quotes rather than single quotes, - because there are "software engineers" out there writing tools that read HTML - and somehow have no idea that single quotes exist. Capitulates to the absurd - `issue 361`_. Thanks, Jon Chappell. - -- The ``coverage annotate`` command now handles non-ASCII characters properly, - closing `issue 363`_. Thanks, Leonardo Pistone. - -- Drive letters on Windows were not normalized correctly, now they are. Thanks, - Ionel Cristian Mărieș. - -- Plugin support had some bugs fixed, closing `issue 374`_ and `issue 375`_. - Thanks, Stefan Behnel. - -.. _issue 299: https://github.com/nedbat/coveragepy/issues/299 -.. _issue 308: https://github.com/nedbat/coveragepy/issues/308 -.. _issue 324: https://github.com/nedbat/coveragepy/issues/324 -.. _issue 351: https://github.com/nedbat/coveragepy/issues/351 -.. _issue 354: https://github.com/nedbat/coveragepy/issues/354 -.. _issue 359: https://github.com/nedbat/coveragepy/issues/359 -.. _issue 360: https://github.com/nedbat/coveragepy/issues/360 -.. _issue 361: https://github.com/nedbat/coveragepy/issues/361 -.. _issue 363: https://github.com/nedbat/coveragepy/issues/363 -.. _issue 374: https://github.com/nedbat/coveragepy/issues/374 -.. _issue 375: https://github.com/nedbat/coveragepy/issues/375 - - -Version 4.0a5 --- 2015-02-16 ----------------------------- - -- Plugin support is now implemented in the C tracer instead of the Python - tracer. This greatly improves the speed of tracing projects using plugins. - -- Coverage.py now always adds the current directory to sys.path, so that - plugins can import files in the current directory (`issue 358`_). - -- If the `config_file` argument to the Coverage constructor is specified as - ".coveragerc", it is treated as if it were True. This means setup.cfg is - also examined, and a missing file is not considered an error (`issue 357`_). - -- Wildly experimental: support for measuring processes started by the - multiprocessing module. To use, set ``--concurrency=multiprocessing``, - either on the command line or in the .coveragerc file (`issue 117`_). Thanks, - Eduardo Schettino. Currently, this does not work on Windows. - -- A new warning is possible, if a desired file isn't measured because it was - imported before coverage.py was started (`issue 353`_). - -- The `coverage.process_startup` function now will start coverage measurement - only once, no matter how many times it is called. This fixes problems due - to unusual virtualenv configurations (`issue 340`_). - -- Added 3.5.0a1 to the list of supported CPython versions. - -.. _issue 117: https://github.com/nedbat/coveragepy/issues/117 -.. _issue 340: https://github.com/nedbat/coveragepy/issues/340 -.. _issue 353: https://github.com/nedbat/coveragepy/issues/353 -.. _issue 357: https://github.com/nedbat/coveragepy/issues/357 -.. _issue 358: https://github.com/nedbat/coveragepy/issues/358 - - -Version 4.0a4 --- 2015-01-25 ----------------------------- - -- Plugins can now provide sys_info for debugging output. - -- Started plugins documentation. - -- Prepared to move the docs to readthedocs.org. - - -Version 4.0a3 --- 2015-01-20 ----------------------------- - -- Reports now use file names with extensions. Previously, a report would - describe a/b/c.py as "a/b/c". Now it is shown as "a/b/c.py". This allows - for better support of non-Python files, and also fixed `issue 69`_. - -- The XML report now reports each directory as a package again. This was a bad - regression, I apologize. This was reported in `issue 235`_, which is now - fixed. - -- A new configuration option for the XML report: ``[xml] package_depth`` - controls which directories are identified as packages in the report. - Directories deeper than this depth are not reported as packages. - The default is that all directories are reported as packages. - Thanks, Lex Berezhny. - -- When looking for the source for a frame, check if the file exists. On - Windows, .pyw files are no longer recorded as .py files. Along the way, this - fixed `issue 290`_. - -- Empty files are now reported as 100% covered in the XML report, not 0% - covered (`issue 345`_). - -- Regexes in the configuration file are now compiled as soon as they are read, - to provide error messages earlier (`issue 349`_). - -.. _issue 69: https://github.com/nedbat/coveragepy/issues/69 -.. _issue 235: https://github.com/nedbat/coveragepy/issues/235 -.. _issue 290: https://github.com/nedbat/coveragepy/issues/290 -.. _issue 345: https://github.com/nedbat/coveragepy/issues/345 -.. _issue 349: https://github.com/nedbat/coveragepy/issues/349 - - -Version 4.0a2 --- 2015-01-14 ----------------------------- - -- Officially support PyPy 2.4, and PyPy3 2.4. Drop support for - CPython 3.2 and older versions of PyPy. The code won't work on CPython 3.2. - It will probably still work on older versions of PyPy, but I'm not testing - against them. - -- Plugins! - -- The original command line switches (`-x` to run a program, etc) are no - longer supported. - -- A new option: `coverage report --skip-covered` will reduce the number of - files reported by skipping files with 100% coverage. Thanks, Krystian - Kichewko. This means that empty `__init__.py` files will be skipped, since - they are 100% covered, closing `issue 315`_. - -- You can now specify the ``--fail-under`` option in the ``.coveragerc`` file - as the ``[report] fail_under`` option. This closes `issue 314`_. - -- The ``COVERAGE_OPTIONS`` environment variable is no longer supported. It was - a hack for ``--timid`` before configuration files were available. - -- The HTML report now has filtering. Type text into the Filter box on the - index page, and only modules with that text in the name will be shown. - Thanks, Danny Allen. - -- The textual report and the HTML report used to report partial branches - differently for no good reason. Now the text report's "missing branches" - column is a "partial branches" column so that both reports show the same - numbers. This closes `issue 342`_. - -- If you specify a ``--rcfile`` that cannot be read, you will get an error - message. Fixes `issue 343`_. - -- The ``--debug`` switch can now be used on any command. - -- You can now programmatically adjust the configuration of coverage.py by - setting items on `Coverage.config` after construction. - -- A module run with ``-m`` can be used as the argument to ``--source``, fixing - `issue 328`_. Thanks, Buck Evan. - -- The regex for matching exclusion pragmas has been fixed to allow more kinds - of whitespace, fixing `issue 334`_. - -- Made some PyPy-specific tweaks to improve speed under PyPy. Thanks, Alex - Gaynor. - -- In some cases, with a source file missing a final newline, coverage.py would - count statements incorrectly. This is now fixed, closing `issue 293`_. - -- The status.dat file that HTML reports use to avoid re-creating files that - haven't changed is now a JSON file instead of a pickle file. This obviates - `issue 287`_ and `issue 237`_. - -.. _issue 237: https://github.com/nedbat/coveragepy/issues/237 -.. _issue 287: https://github.com/nedbat/coveragepy/issues/287 -.. _issue 293: https://github.com/nedbat/coveragepy/issues/293 -.. _issue 314: https://github.com/nedbat/coveragepy/issues/314 -.. _issue 315: https://github.com/nedbat/coveragepy/issues/315 -.. _issue 328: https://github.com/nedbat/coveragepy/issues/328 -.. _issue 334: https://github.com/nedbat/coveragepy/issues/334 -.. _issue 342: https://github.com/nedbat/coveragepy/issues/342 -.. _issue 343: https://github.com/nedbat/coveragepy/issues/343 - - -Version 4.0a1 --- 2014-09-27 ----------------------------- - -- Python versions supported are now CPython 2.6, 2.7, 3.2, 3.3, and 3.4, and - PyPy 2.2. - -- Gevent, eventlet, and greenlet are now supported, closing `issue 149`_. - The ``concurrency`` setting specifies the concurrency library in use. Huge - thanks to Peter Portante for initial implementation, and to Joe Jevnik for - the final insight that completed the work. - -- Options are now also read from a setup.cfg file, if any. Sections are - prefixed with "coverage:", so the ``[run]`` options will be read from the - ``[coverage:run]`` section of setup.cfg. Finishes `issue 304`_. - -- The ``report -m`` command can now show missing branches when reporting on - branch coverage. Thanks, Steve Leonard. Closes `issue 230`_. - -- The XML report now contains a element, fixing `issue 94`_. Thanks - Stan Hu. - -- The class defined in the coverage module is now called ``Coverage`` instead - of ``coverage``, though the old name still works, for backward compatibility. - -- The ``fail-under`` value is now rounded the same as reported results, - preventing paradoxical results, fixing `issue 284`_. - -- The XML report will now create the output directory if need be, fixing - `issue 285`_. Thanks, Chris Rose. - -- HTML reports no longer raise UnicodeDecodeError if a Python file has - undecodable characters, fixing `issue 303`_ and `issue 331`_. - -- The annotate command will now annotate all files, not just ones relative to - the current directory, fixing `issue 57`_. - -- The coverage module no longer causes deprecation warnings on Python 3.4 by - importing the imp module, fixing `issue 305`_. - -- Encoding declarations in source files are only considered if they are truly - comments. Thanks, Anthony Sottile. - -.. _issue 57: https://github.com/nedbat/coveragepy/issues/57 -.. _issue 94: https://github.com/nedbat/coveragepy/issues/94 -.. _issue 149: https://github.com/nedbat/coveragepy/issues/149 -.. _issue 230: https://github.com/nedbat/coveragepy/issues/230 -.. _issue 284: https://github.com/nedbat/coveragepy/issues/284 -.. _issue 285: https://github.com/nedbat/coveragepy/issues/285 -.. _issue 303: https://github.com/nedbat/coveragepy/issues/303 -.. _issue 304: https://github.com/nedbat/coveragepy/issues/304 -.. _issue 305: https://github.com/nedbat/coveragepy/issues/305 -.. _issue 331: https://github.com/nedbat/coveragepy/issues/331 - - -.. _changes_371: - -Version 3.7.1 --- 2013-12-13 ----------------------------- - -- Improved the speed of HTML report generation by about 20%. - -- Fixed the mechanism for finding OS-installed static files for the HTML report - so that it will actually find OS-installed static files. - - -.. _changes_37: - -Version 3.7 --- 2013-10-06 --------------------------- - -- Added the ``--debug`` switch to ``coverage run``. It accepts a list of - options indicating the type of internal activity to log to stderr. - -- Improved the branch coverage facility, fixing `issue 92`_ and `issue 175`_. - -- Running code with ``coverage run -m`` now behaves more like Python does, - setting sys.path properly, which fixes `issue 207`_ and `issue 242`_. - -- Coverage.py can now run .pyc files directly, closing `issue 264`_. - -- Coverage.py properly supports .pyw files, fixing `issue 261`_. - -- Omitting files within a tree specified with the ``source`` option would - cause them to be incorrectly marked as unexecuted, as described in - `issue 218`_. This is now fixed. - -- When specifying paths to alias together during data combining, you can now - specify relative paths, fixing `issue 267`_. - -- Most file paths can now be specified with username expansion (``~/src``, or - ``~build/src``, for example), and with environment variable expansion - (``build/$BUILDNUM/src``). - -- Trying to create an XML report with no files to report on, would cause a - ZeroDivideError, but no longer does, fixing `issue 250`_. - -- When running a threaded program under the Python tracer, coverage.py no - longer issues a spurious warning about the trace function changing: "Trace - function changed, measurement is likely wrong: None." This fixes `issue - 164`_. - -- Static files necessary for HTML reports are found in system-installed places, - to ease OS-level packaging of coverage.py. Closes `issue 259`_. - -- Source files with encoding declarations, but a blank first line, were not - decoded properly. Now they are. Thanks, Roger Hu. - -- The source kit now includes the ``__main__.py`` file in the root coverage - directory, fixing `issue 255`_. - -.. _issue 92: https://github.com/nedbat/coveragepy/issues/92 -.. _issue 164: https://github.com/nedbat/coveragepy/issues/164 -.. _issue 175: https://github.com/nedbat/coveragepy/issues/175 -.. _issue 207: https://github.com/nedbat/coveragepy/issues/207 -.. _issue 242: https://github.com/nedbat/coveragepy/issues/242 -.. _issue 218: https://github.com/nedbat/coveragepy/issues/218 -.. _issue 250: https://github.com/nedbat/coveragepy/issues/250 -.. _issue 255: https://github.com/nedbat/coveragepy/issues/255 -.. _issue 259: https://github.com/nedbat/coveragepy/issues/259 -.. _issue 261: https://github.com/nedbat/coveragepy/issues/261 -.. _issue 264: https://github.com/nedbat/coveragepy/issues/264 -.. _issue 267: https://github.com/nedbat/coveragepy/issues/267 - - -.. _changes_36: - -Version 3.6 --- 2013-01-05 --------------------------- - -- Added a page to the docs about troublesome situations, closing `issue 226`_, - and added some info to the TODO file, closing `issue 227`_. - -.. _issue 226: https://github.com/nedbat/coveragepy/issues/226 -.. _issue 227: https://github.com/nedbat/coveragepy/issues/227 - - -Version 3.6b3 --- 2012-12-29 ----------------------------- - -- Beta 2 broke the nose plugin. It's fixed again, closing `issue 224`_. - -.. _issue 224: https://github.com/nedbat/coveragepy/issues/224 - - -Version 3.6b2 --- 2012-12-23 ----------------------------- - -- Coverage.py runs on Python 2.3 and 2.4 again. It was broken in 3.6b1. - -- The C extension is optionally compiled using a different more widely-used - technique, taking another stab at fixing `issue 80`_ once and for all. - -- Combining data files would create entries for phantom files if used with - ``source`` and path aliases. It no longer does. - -- ``debug sys`` now shows the configuration file path that was read. - -- If an oddly-behaved package claims that code came from an empty-string - file name, coverage.py no longer associates it with the directory name, - fixing `issue 221`_. - -.. _issue 221: https://github.com/nedbat/coveragepy/issues/221 - - -Version 3.6b1 --- 2012-11-28 ----------------------------- - -- Wildcards in ``include=`` and ``omit=`` arguments were not handled properly - in reporting functions, though they were when running. Now they are handled - uniformly, closing `issue 143`_ and `issue 163`_. **NOTE**: it is possible - that your configurations may now be incorrect. If you use ``include`` or - ``omit`` during reporting, whether on the command line, through the API, or - in a configuration file, please check carefully that you were not relying on - the old broken behavior. - -- The **report**, **html**, and **xml** commands now accept a ``--fail-under`` - switch that indicates in the exit status whether the coverage percentage was - less than a particular value. Closes `issue 139`_. - -- The reporting functions coverage.report(), coverage.html_report(), and - coverage.xml_report() now all return a float, the total percentage covered - measurement. - -- The HTML report's title can now be set in the configuration file, with the - ``--title`` switch on the command line, or via the API. - -- Configuration files now support substitution of environment variables, using - syntax like ``${WORD}``. Closes `issue 97`_. - -- Embarrassingly, the ``[xml] output=`` setting in the .coveragerc file simply - didn't work. Now it does. - -- The XML report now consistently uses file names for the file name attribute, - rather than sometimes using module names. Fixes `issue 67`_. - Thanks, Marcus Cobden. - -- Coverage percentage metrics are now computed slightly differently under - branch coverage. This means that completely unexecuted files will now - correctly have 0% coverage, fixing `issue 156`_. This also means that your - total coverage numbers will generally now be lower if you are measuring - branch coverage. - -- When installing, now in addition to creating a "coverage" command, two new - aliases are also installed. A "coverage2" or "coverage3" command will be - created, depending on whether you are installing in Python 2.x or 3.x. - A "coverage-X.Y" command will also be created corresponding to your specific - version of Python. Closes `issue 111`_. - -- The coverage.py installer no longer tries to bootstrap setuptools or - Distribute. You must have one of them installed first, as `issue 202`_ - recommended. - -- The coverage.py kit now includes docs (closing `issue 137`_) and tests. - -- On Windows, files are now reported in their correct case, fixing `issue 89`_ - and `issue 203`_. - -- If a file is missing during reporting, the path shown in the error message - is now correct, rather than an incorrect path in the current directory. - Fixes `issue 60`_. - -- Running an HTML report in Python 3 in the same directory as an old Python 2 - HTML report would fail with a UnicodeDecodeError. This issue (`issue 193`_) - is now fixed. - -- Fixed yet another error trying to parse non-Python files as Python, this - time an IndentationError, closing `issue 82`_ for the fourth time... - -- If `coverage xml` fails because there is no data to report, it used to - create a zero-length XML file. Now it doesn't, fixing `issue 210`_. - -- Jython files now work with the ``--source`` option, fixing `issue 100`_. - -- Running coverage.py under a debugger is unlikely to work, but it shouldn't - fail with "TypeError: 'NoneType' object is not iterable". Fixes `issue - 201`_. - -- On some Linux distributions, when installed with the OS package manager, - coverage.py would report its own code as part of the results. Now it won't, - fixing `issue 214`_, though this will take some time to be repackaged by the - operating systems. - -- Docstrings for the legacy singleton methods are more helpful. Thanks Marius - Gedminas. Closes `issue 205`_. - -- The pydoc tool can now show documentation for the class `coverage.coverage`. - Closes `issue 206`_. - -- Added a page to the docs about contributing to coverage.py, closing - `issue 171`_. - -- When coverage.py ended unsuccessfully, it may have reported odd errors like - ``'NoneType' object has no attribute 'isabs'``. It no longer does, - so kiss `issue 153`_ goodbye. - -.. _issue 60: https://github.com/nedbat/coveragepy/issues/60 -.. _issue 67: https://github.com/nedbat/coveragepy/issues/67 -.. _issue 89: https://github.com/nedbat/coveragepy/issues/89 -.. _issue 97: https://github.com/nedbat/coveragepy/issues/97 -.. _issue 100: https://github.com/nedbat/coveragepy/issues/100 -.. _issue 111: https://github.com/nedbat/coveragepy/issues/111 -.. _issue 137: https://github.com/nedbat/coveragepy/issues/137 -.. _issue 139: https://github.com/nedbat/coveragepy/issues/139 -.. _issue 143: https://github.com/nedbat/coveragepy/issues/143 -.. _issue 153: https://github.com/nedbat/coveragepy/issues/153 -.. _issue 156: https://github.com/nedbat/coveragepy/issues/156 -.. _issue 163: https://github.com/nedbat/coveragepy/issues/163 -.. _issue 171: https://github.com/nedbat/coveragepy/issues/171 -.. _issue 193: https://github.com/nedbat/coveragepy/issues/193 -.. _issue 201: https://github.com/nedbat/coveragepy/issues/201 -.. _issue 202: https://github.com/nedbat/coveragepy/issues/202 -.. _issue 203: https://github.com/nedbat/coveragepy/issues/203 -.. _issue 205: https://github.com/nedbat/coveragepy/issues/205 -.. _issue 206: https://github.com/nedbat/coveragepy/issues/206 -.. _issue 210: https://github.com/nedbat/coveragepy/issues/210 -.. _issue 214: https://github.com/nedbat/coveragepy/issues/214 - - -.. _changes_353: - -Version 3.5.3 --- 2012-09-29 ----------------------------- - -- Line numbers in the HTML report line up better with the source lines, fixing - `issue 197`_, thanks Marius Gedminas. - -- When specifying a directory as the source= option, the directory itself no - longer needs to have a ``__init__.py`` file, though its sub-directories do, - to be considered as source files. - -- Files encoded as UTF-8 with a BOM are now properly handled, fixing - `issue 179`_. Thanks, Pablo Carballo. - -- Fixed more cases of non-Python files being reported as Python source, and - then not being able to parse them as Python. Closes `issue 82`_ (again). - Thanks, Julian Berman. - -- Fixed memory leaks under Python 3, thanks, Brett Cannon. Closes `issue 147`_. - -- Optimized .pyo files may not have been handled correctly, `issue 195`_. - Thanks, Marius Gedminas. - -- Certain unusually named file paths could have been mangled during reporting, - `issue 194`_. Thanks, Marius Gedminas. - -- Try to do a better job of the impossible task of detecting when we can't - build the C extension, fixing `issue 183`_. - -- Testing is now done with `tox`_, thanks, Marc Abramowitz. - -.. _issue 147: https://github.com/nedbat/coveragepy/issues/147 -.. _issue 179: https://github.com/nedbat/coveragepy/issues/179 -.. _issue 183: https://github.com/nedbat/coveragepy/issues/183 -.. _issue 194: https://github.com/nedbat/coveragepy/issues/194 -.. _issue 195: https://github.com/nedbat/coveragepy/issues/195 -.. _issue 197: https://github.com/nedbat/coveragepy/issues/197 -.. _tox: https://tox.readthedocs.io/ - - -.. _changes_352: - -Version 3.5.2 --- 2012-05-04 ----------------------------- - -No changes since 3.5.2.b1 - - -Version 3.5.2b1 --- 2012-04-29 ------------------------------- - -- The HTML report has slightly tweaked controls: the buttons at the top of - the page are color-coded to the source lines they affect. - -- Custom CSS can be applied to the HTML report by specifying a CSS file as - the ``extra_css`` configuration value in the ``[html]`` section. - -- Source files with custom encodings declared in a comment at the top are now - properly handled during reporting on Python 2. Python 3 always handled them - properly. This fixes `issue 157`_. - -- Backup files left behind by editors are no longer collected by the source= - option, fixing `issue 168`_. - -- If a file doesn't parse properly as Python, we don't report it as an error - if the file name seems like maybe it wasn't meant to be Python. This is a - pragmatic fix for `issue 82`_. - -- The ``-m`` switch on ``coverage report``, which includes missing line numbers - in the summary report, can now be specified as ``show_missing`` in the - config file. Closes `issue 173`_. - -- When running a module with ``coverage run -m ``, certain details - of the execution environment weren't the same as for - ``python -m ``. This had the unfortunate side-effect of making - ``coverage run -m unittest discover`` not work if you had tests in a - directory named "test". This fixes `issue 155`_ and `issue 142`_. - -- Now the exit status of your product code is properly used as the process - status when running ``python -m coverage run ...``. Thanks, JT Olds. - -- When installing into pypy, we no longer attempt (and fail) to compile - the C tracer function, closing `issue 166`_. - -.. _issue 142: https://github.com/nedbat/coveragepy/issues/142 -.. _issue 155: https://github.com/nedbat/coveragepy/issues/155 -.. _issue 157: https://github.com/nedbat/coveragepy/issues/157 -.. _issue 166: https://github.com/nedbat/coveragepy/issues/166 -.. _issue 168: https://github.com/nedbat/coveragepy/issues/168 -.. _issue 173: https://github.com/nedbat/coveragepy/issues/173 - - -.. _changes_351: - -Version 3.5.1 --- 2011-09-23 ----------------------------- - -- The ``[paths]`` feature unfortunately didn't work in real world situations - where you wanted to, you know, report on the combined data. Now all paths - stored in the combined file are canonicalized properly. - - -Version 3.5.1b1 --- 2011-08-28 ------------------------------- - -- When combining data files from parallel runs, you can now instruct - coverage.py about which directories are equivalent on different machines. A - ``[paths]`` section in the configuration file lists paths that are to be - considered equivalent. Finishes `issue 17`_. - -- for-else constructs are understood better, and don't cause erroneous partial - branch warnings. Fixes `issue 122`_. - -- Branch coverage for ``with`` statements is improved, fixing `issue 128`_. - -- The number of partial branches reported on the HTML summary page was - different than the number reported on the individual file pages. This is - now fixed. - -- An explicit include directive to measure files in the Python installation - wouldn't work because of the standard library exclusion. Now the include - directive takes precedence, and the files will be measured. Fixes - `issue 138`_. - -- The HTML report now handles Unicode characters in Python source files - properly. This fixes `issue 124`_ and `issue 144`_. Thanks, Devin - Jeanpierre. - -- In order to help the core developers measure the test coverage of the - standard library, Brandon Rhodes devised an aggressive hack to trick Python - into running some coverage.py code before anything else in the process. - See the coverage/fullcoverage directory if you are interested. - -.. _issue 17: https://github.com/nedbat/coveragepy/issues/17 -.. _issue 122: https://github.com/nedbat/coveragepy/issues/122 -.. _issue 124: https://github.com/nedbat/coveragepy/issues/124 -.. _issue 128: https://github.com/nedbat/coveragepy/issues/128 -.. _issue 138: https://github.com/nedbat/coveragepy/issues/138 -.. _issue 144: https://github.com/nedbat/coveragepy/issues/144 - - -.. _changes_35: - -Version 3.5 --- 2011-06-29 --------------------------- - -- The HTML report hotkeys now behave slightly differently when the current - chunk isn't visible at all: a chunk on the screen will be selected, - instead of the old behavior of jumping to the literal next chunk. - The hotkeys now work in Google Chrome. Thanks, Guido van Rossum. - - -Version 3.5b1 --- 2011-06-05 ----------------------------- - -- The HTML report now has hotkeys. Try ``n``, ``s``, ``m``, ``x``, ``b``, - ``p``, and ``c`` on the overview page to change the column sorting. - On a file page, ``r``, ``m``, ``x``, and ``p`` toggle the run, missing, - excluded, and partial line markings. You can navigate the highlighted - sections of code by using the ``j`` and ``k`` keys for next and previous. - The ``1`` (one) key jumps to the first highlighted section in the file, - and ``0`` (zero) scrolls to the top of the file. - -- The ``--omit`` and ``--include`` switches now interpret their values more - usefully. If the value starts with a wildcard character, it is used as-is. - If it does not, it is interpreted relative to the current directory. - Closes `issue 121`_. - -- Partial branch warnings can now be pragma'd away. The configuration option - ``partial_branches`` is a list of regular expressions. Lines matching any of - those expressions will never be marked as a partial branch. In addition, - there's a built-in list of regular expressions marking statements which - should never be marked as partial. This list includes ``while True:``, - ``while 1:``, ``if 1:``, and ``if 0:``. - -- The ``coverage()`` constructor accepts single strings for the ``omit=`` and - ``include=`` arguments, adapting to a common error in programmatic use. - -- Modules can now be run directly using ``coverage run -m modulename``, to - mirror Python's ``-m`` flag. Closes `issue 95`_, thanks, Brandon Rhodes. - -- ``coverage run`` didn't emulate Python accurately in one small detail: the - current directory inserted into ``sys.path`` was relative rather than - absolute. This is now fixed. - -- HTML reporting is now incremental: a record is kept of the data that - produced the HTML reports, and only files whose data has changed will - be generated. This should make most HTML reporting faster. - -- Pathological code execution could disable the trace function behind our - backs, leading to incorrect code measurement. Now if this happens, - coverage.py will issue a warning, at least alerting you to the problem. - Closes `issue 93`_. Thanks to Marius Gedminas for the idea. - -- The C-based trace function now behaves properly when saved and restored - with ``sys.gettrace()`` and ``sys.settrace()``. This fixes `issue 125`_ - and `issue 123`_. Thanks, Devin Jeanpierre. - -- Source files are now opened with Python 3.2's ``tokenize.open()`` where - possible, to get the best handling of Python source files with encodings. - Closes `issue 107`_, thanks, Brett Cannon. - -- Syntax errors in supposed Python files can now be ignored during reporting - with the ``-i`` switch just like other source errors. Closes `issue 115`_. - -- Installation from source now succeeds on machines without a C compiler, - closing `issue 80`_. - -- Coverage.py can now be run directly from a working tree by specifying - the directory name to python: ``python coverage_py_working_dir run ...``. - Thanks, Brett Cannon. - -- A little bit of Jython support: `coverage run` can now measure Jython - execution by adapting when $py.class files are traced. Thanks, Adi Roiban. - Jython still doesn't provide the Python libraries needed to make - coverage reporting work, unfortunately. - -- Internally, files are now closed explicitly, fixing `issue 104`_. Thanks, - Brett Cannon. - -.. _issue 80: https://github.com/nedbat/coveragepy/issues/80 -.. _issue 93: https://github.com/nedbat/coveragepy/issues/93 -.. _issue 95: https://github.com/nedbat/coveragepy/issues/95 -.. _issue 104: https://github.com/nedbat/coveragepy/issues/104 -.. _issue 107: https://github.com/nedbat/coveragepy/issues/107 -.. _issue 115: https://github.com/nedbat/coveragepy/issues/115 -.. _issue 121: https://github.com/nedbat/coveragepy/issues/121 -.. _issue 123: https://github.com/nedbat/coveragepy/issues/123 -.. _issue 125: https://github.com/nedbat/coveragepy/issues/125 - - -.. _changes_34: - -Version 3.4 --- 2010-09-19 --------------------------- - -- The XML report is now sorted by package name, fixing `issue 88`_. - -- Programs that exited with ``sys.exit()`` with no argument weren't handled - properly, producing a coverage.py stack trace. That is now fixed. - -.. _issue 88: https://github.com/nedbat/coveragepy/issues/88 - - -Version 3.4b2 --- 2010-09-06 ----------------------------- - -- Completely unexecuted files can now be included in coverage results, reported - as 0% covered. This only happens if the --source option is specified, since - coverage.py needs guidance about where to look for source files. - -- The XML report output now properly includes a percentage for branch coverage, - fixing `issue 65`_ and `issue 81`_. - -- Coverage percentages are now displayed uniformly across reporting methods. - Previously, different reports could round percentages differently. Also, - percentages are only reported as 0% or 100% if they are truly 0 or 100, and - are rounded otherwise. Fixes `issue 41`_ and `issue 70`_. - -- The precision of reported coverage percentages can be set with the - ``[report] precision`` config file setting. Completes `issue 16`_. - -- Threads derived from ``threading.Thread`` with an overridden `run` method - would report no coverage for the `run` method. This is now fixed, closing - `issue 85`_. - -.. _issue 16: https://github.com/nedbat/coveragepy/issues/16 -.. _issue 41: https://github.com/nedbat/coveragepy/issues/41 -.. _issue 65: https://github.com/nedbat/coveragepy/issues/65 -.. _issue 70: https://github.com/nedbat/coveragepy/issues/70 -.. _issue 81: https://github.com/nedbat/coveragepy/issues/81 -.. _issue 85: https://github.com/nedbat/coveragepy/issues/85 - - -Version 3.4b1 --- 2010-08-21 ----------------------------- - -- BACKWARD INCOMPATIBILITY: the ``--omit`` and ``--include`` switches now take - file patterns rather than file prefixes, closing `issue 34`_ and `issue 36`_. - -- BACKWARD INCOMPATIBILITY: the `omit_prefixes` argument is gone throughout - coverage.py, replaced with `omit`, a list of file name patterns suitable for - `fnmatch`. A parallel argument `include` controls what files are included. - -- The run command now has a ``--source`` switch, a list of directories or - module names. If provided, coverage.py will only measure execution in those - source files. - -- Various warnings are printed to stderr for problems encountered during data - measurement: if a ``--source`` module has no Python source to measure, or is - never encountered at all, or if no data is collected. - -- The reporting commands (report, annotate, html, and xml) now have an - ``--include`` switch to restrict reporting to modules matching those file - patterns, similar to the existing ``--omit`` switch. Thanks, Zooko. - -- The run command now supports ``--include`` and ``--omit`` to control what - modules it measures. This can speed execution and reduce the amount of data - during reporting. Thanks Zooko. - -- Since coverage.py 3.1, using the Python trace function has been slower than - it needs to be. A cache of tracing decisions was broken, but has now been - fixed. - -- Python 2.7 and 3.2 have introduced new opcodes that are now supported. - -- Python files with no statements, for example, empty ``__init__.py`` files, - are now reported as having zero statements instead of one. Fixes `issue 1`_. - -- Reports now have a column of missed line counts rather than executed line - counts, since developers should focus on reducing the missed lines to zero, - rather than increasing the executed lines to varying targets. Once - suggested, this seemed blindingly obvious. - -- Line numbers in HTML source pages are clickable, linking directly to that - line, which is highlighted on arrival. Added a link back to the index page - at the bottom of each HTML page. - -- Programs that call ``os.fork`` will properly collect data from both the child - and parent processes. Use ``coverage run -p`` to get two data files that can - be combined with ``coverage combine``. Fixes `issue 56`_. - -- Coverage.py is now runnable as a module: ``python -m coverage``. Thanks, - Brett Cannon. - -- When measuring code running in a virtualenv, most of the system library was - being measured when it shouldn't have been. This is now fixed. - -- Doctest text files are no longer recorded in the coverage data, since they - can't be reported anyway. Fixes `issue 52`_ and `issue 61`_. - -- Jinja HTML templates compile into Python code using the HTML file name, - which confused coverage.py. Now these files are no longer traced, fixing - `issue 82`_. - -- Source files can have more than one dot in them (foo.test.py), and will be - treated properly while reporting. Fixes `issue 46`_. - -- Source files with DOS line endings are now properly tokenized for syntax - coloring on non-DOS machines. Fixes `issue 53`_. - -- Unusual code structure that confused exits from methods with exits from - classes is now properly analyzed. See `issue 62`_. - -- Asking for an HTML report with no files now shows a nice error message rather - than a cryptic failure ('int' object is unsubscriptable). Fixes `issue 59`_. - -.. _issue 1: https://github.com/nedbat/coveragepy/issues/1 -.. _issue 34: https://github.com/nedbat/coveragepy/issues/34 -.. _issue 36: https://github.com/nedbat/coveragepy/issues/36 -.. _issue 46: https://github.com/nedbat/coveragepy/issues/46 -.. _issue 53: https://github.com/nedbat/coveragepy/issues/53 -.. _issue 52: https://github.com/nedbat/coveragepy/issues/52 -.. _issue 56: https://github.com/nedbat/coveragepy/issues/56 -.. _issue 61: https://github.com/nedbat/coveragepy/issues/61 -.. _issue 62: https://github.com/nedbat/coveragepy/issues/62 -.. _issue 59: https://github.com/nedbat/coveragepy/issues/59 -.. _issue 82: https://github.com/nedbat/coveragepy/issues/82 - - -.. _changes_331: - -Version 3.3.1 --- 2010-03-06 ----------------------------- - -- Using `parallel=True` in .coveragerc file prevented reporting, but now does - not, fixing `issue 49`_. - -- When running your code with "coverage run", if you call `sys.exit()`, - coverage.py will exit with that status code, fixing `issue 50`_. - -.. _issue 49: https://github.com/nedbat/coveragepy/issues/49 -.. _issue 50: https://github.com/nedbat/coveragepy/issues/50 - - -.. _changes_33: - -Version 3.3 --- 2010-02-24 --------------------------- - -- Settings are now read from a .coveragerc file. A specific file can be - specified on the command line with --rcfile=FILE. The name of the file can - be programmatically set with the `config_file` argument to the coverage() - constructor, or reading a config file can be disabled with - `config_file=False`. - -- Fixed a problem with nested loops having their branch possibilities - mischaracterized: `issue 39`_. - -- Added coverage.process_start to enable coverage measurement when Python - starts. - -- Parallel data file names now have a random number appended to them in - addition to the machine name and process id. - -- Parallel data files combined with "coverage combine" are deleted after - they're combined, to clean up unneeded files. Fixes `issue 40`_. - -- Exceptions thrown from product code run with "coverage run" are now displayed - without internal coverage.py frames, so the output is the same as when the - code is run without coverage.py. - -- The `data_suffix` argument to the coverage constructor is now appended with - an added dot rather than simply appended, so that .coveragerc files will not - be confused for data files. - -- Python source files that don't end with a newline can now be executed, fixing - `issue 47`_. - -- Added an AUTHORS.txt file. - -.. _issue 39: https://github.com/nedbat/coveragepy/issues/39 -.. _issue 40: https://github.com/nedbat/coveragepy/issues/40 -.. _issue 47: https://github.com/nedbat/coveragepy/issues/47 - - -.. _changes_32: - -Version 3.2 --- 2009-12-05 --------------------------- - -- Added a ``--version`` option on the command line. - - -Version 3.2b4 --- 2009-12-01 ----------------------------- - -- Branch coverage improvements: - - - The XML report now includes branch information. - -- Click-to-sort HTML report columns are now persisted in a cookie. Viewing - a report will sort it first the way you last had a coverage report sorted. - Thanks, `Chris Adams`_. - -- On Python 3.x, setuptools has been replaced by `Distribute`_. - -.. _Distribute: https://pypi.org/project/distribute/ - - -Version 3.2b3 --- 2009-11-23 ----------------------------- - -- Fixed a memory leak in the C tracer that was introduced in 3.2b1. - -- Branch coverage improvements: - - - Branches to excluded code are ignored. - -- The table of contents in the HTML report is now sortable: click the headers - on any column. Thanks, `Chris Adams`_. - -.. _Chris Adams: http://chris.improbable.org - - -Version 3.2b2 --- 2009-11-19 ----------------------------- - -- Branch coverage improvements: - - - Classes are no longer incorrectly marked as branches: `issue 32`_. - - - "except" clauses with types are no longer incorrectly marked as branches: - `issue 35`_. - -- Fixed some problems syntax coloring sources with line continuations and - source with tabs: `issue 30`_ and `issue 31`_. - -- The --omit option now works much better than before, fixing `issue 14`_ and - `issue 33`_. Thanks, Danek Duvall. - -.. _issue 14: https://github.com/nedbat/coveragepy/issues/14 -.. _issue 30: https://github.com/nedbat/coveragepy/issues/30 -.. _issue 31: https://github.com/nedbat/coveragepy/issues/31 -.. _issue 32: https://github.com/nedbat/coveragepy/issues/32 -.. _issue 33: https://github.com/nedbat/coveragepy/issues/33 -.. _issue 35: https://github.com/nedbat/coveragepy/issues/35 - - -Version 3.2b1 --- 2009-11-10 ----------------------------- - -- Branch coverage! - -- XML reporting has file paths that let Cobertura find the source code. - -- The tracer code has changed, it's a few percent faster. - -- Some exceptions reported by the command line interface have been cleaned up - so that tracebacks inside coverage.py aren't shown. Fixes `issue 23`_. - -.. _issue 23: https://github.com/nedbat/coveragepy/issues/23 - - -.. _changes_31: - -Version 3.1 --- 2009-10-04 --------------------------- - -- Source code can now be read from eggs. Thanks, Ross Lawley. Fixes - `issue 25`_. - -.. _issue 25: https://github.com/nedbat/coveragepy/issues/25 - - -Version 3.1b1 --- 2009-09-27 ----------------------------- - -- Python 3.1 is now supported. - -- Coverage.py has a new command line syntax with sub-commands. This expands - the possibilities for adding features and options in the future. The old - syntax is still supported. Try "coverage help" to see the new commands. - Thanks to Ben Finney for early help. - -- Added an experimental "coverage xml" command for producing coverage reports - in a Cobertura-compatible XML format. Thanks, Bill Hart. - -- Added the --timid option to enable a simpler slower trace function that works - for DecoratorTools projects, including TurboGears. Fixed `issue 12`_ and - `issue 13`_. - -- HTML reports show modules from other directories. Fixed `issue 11`_. - -- HTML reports now display syntax-colored Python source. - -- Programs that change directory will still write .coverage files in the - directory where execution started. Fixed `issue 24`_. - -- Added a "coverage debug" command for getting diagnostic information about the - coverage.py installation. - -.. _issue 11: https://github.com/nedbat/coveragepy/issues/11 -.. _issue 12: https://github.com/nedbat/coveragepy/issues/12 -.. _issue 13: https://github.com/nedbat/coveragepy/issues/13 -.. _issue 24: https://github.com/nedbat/coveragepy/issues/24 - - -.. _changes_301: - -Version 3.0.1 --- 2009-07-07 ----------------------------- - -- Removed the recursion limit in the tracer function. Previously, code that - ran more than 500 frames deep would crash. Fixed `issue 9`_. - -- Fixed a bizarre problem involving pyexpat, whereby lines following XML parser - invocations could be overlooked. Fixed `issue 10`_. - -- On Python 2.3, coverage.py could mis-measure code with exceptions being - raised. This is now fixed. - -- The coverage.py code itself will now not be measured by coverage.py, and no - coverage.py modules will be mentioned in the nose --with-cover plug-in. - Fixed `issue 8`_. - -- When running source files, coverage.py now opens them in universal newline - mode just like Python does. This lets it run Windows files on Mac, for - example. - -.. _issue 9: https://github.com/nedbat/coveragepy/issues/9 -.. _issue 10: https://github.com/nedbat/coveragepy/issues/10 -.. _issue 8: https://github.com/nedbat/coveragepy/issues/8 - - -.. _changes_30: - -Version 3.0 --- 2009-06-13 --------------------------- - -- Fixed the way the Python library was ignored. Too much code was being - excluded the old way. - -- Tabs are now properly converted in HTML reports. Previously indentation was - lost. Fixed `issue 6`_. - -- Nested modules now get a proper flat_rootname. Thanks, Christian Heimes. - -.. _issue 6: https://github.com/nedbat/coveragepy/issues/6 - - -Version 3.0b3 --- 2009-05-16 ----------------------------- - -- Added parameters to coverage.__init__ for options that had been set on the - coverage object itself. - -- Added clear_exclude() and get_exclude_list() methods for programmatic - manipulation of the exclude regexes. - -- Added coverage.load() to read previously-saved data from the data file. - -- Improved the finding of code files. For example, .pyc files that have been - installed after compiling are now located correctly. Thanks, Detlev - Offenbach. - -- When using the object API (that is, constructing a coverage() object), data - is no longer saved automatically on process exit. You can re-enable it with - the auto_data=True parameter on the coverage() constructor. The module-level - interface still uses automatic saving. - - -Version 3.0b --- 2009-04-30 ---------------------------- - -HTML reporting, and continued refactoring. - -- HTML reports and annotation of source files: use the new -b (browser) switch. - Thanks to George Song for code, inspiration and guidance. - -- Code in the Python standard library is not measured by default. If you need - to measure standard library code, use the -L command-line switch during - execution, or the cover_pylib=True argument to the coverage() constructor. - -- Source annotation into a directory (-a -d) behaves differently. The - annotated files are named with their hierarchy flattened so that same-named - files from different directories no longer collide. Also, only files in the - current tree are included. - -- coverage.annotate_file is no longer available. - -- Programs executed with -x now behave more as they should, for example, - __file__ has the correct value. - -- .coverage data files have a new pickle-based format designed for better - extensibility. - -- Removed the undocumented cache_file argument to coverage.usecache(). - - -Version 3.0b1 --- 2009-03-07 ----------------------------- - -Major overhaul. - -- Coverage.py is now a package rather than a module. Functionality has been - split into classes. - -- The trace function is implemented in C for speed. Coverage.py runs are now - much faster. Thanks to David Christian for productive micro-sprints and - other encouragement. - -- Executable lines are identified by reading the line number tables in the - compiled code, removing a great deal of complicated analysis code. - -- Precisely which lines are considered executable has changed in some cases. - Therefore, your coverage stats may also change slightly. - -- The singleton coverage object is only created if the module-level functions - are used. This maintains the old interface while allowing better - programmatic use of coverage.py. - -- The minimum supported Python version is 2.3. - - -Version 2.85 --- 2008-09-14 ---------------------------- - -- Add support for finding source files in eggs. Don't check for - morf's being instances of ModuleType, instead use duck typing so that - pseudo-modules can participate. Thanks, Imri Goldberg. - -- Use os.realpath as part of the fixing of file names so that symlinks won't - confuse things. Thanks, Patrick Mezard. - - -Version 2.80 --- 2008-05-25 ---------------------------- - -- Open files in rU mode to avoid line ending craziness. Thanks, Edward Loper. - - -Version 2.78 --- 2007-09-30 ---------------------------- - -- Don't try to predict whether a file is Python source based on the extension. - Extension-less files are often Pythons scripts. Instead, simply parse the - file and catch the syntax errors. Hat tip to Ben Finney. - - -Version 2.77 --- 2007-07-29 ---------------------------- - -- Better packaging. - - -Version 2.76 --- 2007-07-23 ---------------------------- - -- Now Python 2.5 is *really* fully supported: the body of the new with - statement is counted as executable. - - -Version 2.75 --- 2007-07-22 ---------------------------- - -- Python 2.5 now fully supported. The method of dealing with multi-line - statements is now less sensitive to the exact line that Python reports during - execution. Pass statements are handled specially so that their disappearance - during execution won't throw off the measurement. - - -Version 2.7 --- 2007-07-21 --------------------------- - -- "#pragma: nocover" is excluded by default. - -- Properly ignore docstrings and other constant expressions that appear in the - middle of a function, a problem reported by Tim Leslie. - -- coverage.erase() shouldn't clobber the exclude regex. Change how parallel - mode is invoked, and fix erase() so that it erases the cache when called - programmatically. - -- In reports, ignore code executed from strings, since we can't do anything - useful with it anyway. - -- Better file handling on Linux, thanks Guillaume Chazarain. - -- Better shell support on Windows, thanks Noel O'Boyle. - -- Python 2.2 support maintained, thanks Catherine Proulx. - -- Minor changes to avoid lint warnings. - - -Version 2.6 --- 2006-08-23 --------------------------- - -- Applied Joseph Tate's patch for function decorators. - -- Applied Sigve Tjora and Mark van der Wal's fixes for argument handling. - -- Applied Geoff Bache's parallel mode patch. - -- Refactorings to improve testability. Fixes to command-line logic for parallel - mode and collect. - - -Version 2.5 --- 2005-12-04 --------------------------- - -- Call threading.settrace so that all threads are measured. Thanks Martin - Fuzzey. - -- Add a file argument to report so that reports can be captured to a different - destination. - -- Coverage.py can now measure itself. - -- Adapted Greg Rogers' patch for using relative file names, and sorting and - omitting files to report on. - - -Version 2.2 --- 2004-12-31 --------------------------- - -- Allow for keyword arguments in the module global functions. Thanks, Allen. - - -Version 2.1 --- 2004-12-14 --------------------------- - -- Return 'analysis' to its original behavior and add 'analysis2'. Add a global - for 'annotate', and factor it, adding 'annotate_file'. - - -Version 2.0 --- 2004-12-12 --------------------------- - -Significant code changes. - -- Finding executable statements has been rewritten so that docstrings and - other quirks of Python execution aren't mistakenly identified as missing - lines. - -- Lines can be excluded from consideration, even entire suites of lines. - -- The file system cache of covered lines can be disabled programmatically. - -- Modernized the code. - - -Earlier History ---------------- - -2001-12-04 GDR Created. - -2001-12-06 GDR Added command-line interface and source code annotation. - -2001-12-09 GDR Moved design and interface to separate documents. - -2001-12-10 GDR Open cache file as binary on Windows. Allow simultaneous -e and --x, or -a and -r. - -2001-12-12 GDR Added command-line help. Cache analysis so that it only needs to -be done once when you specify -a and -r. +.. endchangesinclude -2001-12-13 GDR Improved speed while recording. Portable between Python 1.5.2 -and 2.1.1. +Older changes +------------- -2002-01-03 GDR Module-level functions work correctly. +The complete history is available in the `coverage.py docs`__. -2002-01-07 GDR Update sys.path when running a file with the -x option, so that -it matches the value the program would get if it were run on its own. +__ https://coverage.readthedocs.io/en/latest/changes.html diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 1c1fe0e9c..81080d51f 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -108,6 +108,7 @@ Mickie Betz Mike Fiedler Naveen Yadav Nathan Land +Nils Kattenbeck Noel O'Boyle Olivier Grisel Ori Avtalion @@ -126,6 +127,7 @@ Salvatore Zagaria Sandra Martocchia Scott Belden Sebastián Ramírez +Sergey B Kirpichev Sigve Tjora Simon Willison Stan Hu diff --git a/MANIFEST.in b/MANIFEST.in index 50c1f790c..e03084e54 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -32,12 +32,12 @@ recursive-include .github * recursive-include coverage/fullcoverage *.py recursive-include coverage/ctracer *.c *.h -recursive-include doc *.py *.pip *.rst *.txt *.png +recursive-include doc *.py *.in *.pip *.rst *.txt *.png recursive-include doc/_static * prune doc/_build prune doc/_spell -recursive-include requirements *.pip +recursive-include requirements *.in *.pip recursive-include tests *.py *.tok recursive-include tests/gold * diff --git a/Makefile b/Makefile index 4a5927531..909ca4d6e 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ clean: clean_platform ## Remove artifacts of test execution, i @rm -f *$$py.class */*$$py.class */*/*$$py.class */*/*/*$$py.class */*/*/*/*$$py.class */*/*/*/*/*$$py.class @rm -f coverage/*,cover @rm -f MANIFEST - @rm -f .coverage .coverage.* coverage.xml .metacov* + @rm -f .coverage .coverage.* coverage.xml coverage.json .metacov* @rm -f .tox/*/lib/*/site-packages/zzz_metacov.pth @rm -f */.coverage */*/.coverage */*/*/.coverage */*/*/*/.coverage */*/*/*/*/.coverage */*/*/*/*/*/.coverage @rm -f tests/covmain.zip tests/zipmods.zip tests/zip1.zip @@ -44,24 +44,13 @@ $(CSS): $(SCSS) pysassc --style=compact $(SCSS) $@ cp $@ tests/gold/html/styled -LINTABLE = coverage tests igor.py setup.py __main__.py - lint: ## Run linters and checkers. tox -q -e lint -test: - tox -q -e py39 $(ARGS) - -PYTEST_SMOKE_ARGS = -n 6 -m "not expensive" --maxfail=3 $(ARGS) +PYTEST_SMOKE_ARGS = -n auto -m "not expensive" --maxfail=3 $(ARGS) smoke: ## Run tests quickly with the C tracer in the lowest supported Python versions. - COVERAGE_NO_PYTRACER=1 tox -q -e py39 -- $(PYTEST_SMOKE_ARGS) - -pysmoke: ## Run tests quickly with the Python tracer in the lowest supported Python versions. - COVERAGE_NO_CTRACER=1 tox -q -e py39 -- $(PYTEST_SMOKE_ARGS) - -metasmoke: - COVERAGE_NO_PYTRACER=1 ARGS="-e py39" make clean metacov metahtml + COVERAGE_NO_PYTRACER=1 tox -q -e py36 -- $(PYTEST_SMOKE_ARGS) # Coverage measurement of coverage.py itself (meta-coverage). See metacov.ini # for details. @@ -72,10 +61,25 @@ metacov: ## Run meta-coverage, measuring ourself. metahtml: ## Produce meta-coverage HTML reports. python igor.py combine_html +metasmoke: + COVERAGE_NO_PYTRACER=1 ARGS="-e py39" make clean metacov metahtml + +PIP_COMPILE = pip-compile --upgrade --allow-unsafe +upgrade: export CUSTOM_COMPILE_COMMAND=make upgrade +upgrade: ## update the *.pip files with the latest packages satisfying *.in files + pip install -q -r requirements/pip-tools.pip + $(PIP_COMPILE) -o requirements/pip-tools.pip requirements/pip-tools.in + $(PIP_COMPILE) -o requirements/pip.pip requirements/pip.in + $(PIP_COMPILE) -o requirements/pytest.pip requirements/pytest.in + $(PIP_COMPILE) -o requirements/kit.pip requirements/kit.in + $(PIP_COMPILE) -o requirements/tox.pip requirements/tox.in + $(PIP_COMPILE) -o requirements/dev.pip requirements/dev.in + $(PIP_COMPILE) -o doc/requirements.pip doc/requirements.in + # Kitting kit: ## Make the source distribution. - python setup.py sdist + python -m build kit_upload: ## Upload the built distributions to PyPI. twine upload --verbose dist/* @@ -155,7 +159,7 @@ RELNOTES_JSON = tmp/relnotes.json $(CHANGES_MD): CHANGES.rst $(DOCBIN) $(SPHINXBUILD) -b rst doc tmp/rst_rst - pandoc -frst -tmarkdown_strict --atx-headers --wrap=none tmp/rst_rst/changes.rst > $(CHANGES_MD) + pandoc -frst -tmarkdown_strict --markdown-headings=atx --wrap=none tmp/rst_rst/changes.rst > $(CHANGES_MD) relnotes_json: $(RELNOTES_JSON) ## Convert changelog to JSON for further parsing. $(RELNOTES_JSON): $(CHANGES_MD) diff --git a/README.rst b/README.rst index de045a9f1..777bbb989 100644 --- a/README.rst +++ b/README.rst @@ -9,7 +9,7 @@ Code coverage testing for Python. | |license| |versions| |status| | |test-status| |quality-status| |docs| |codecov| -| |kit| |format| |repos| |downloads| +| |kit| |downloads| |format| |repos| | |stars| |forks| |contributors| | |tidelift| |twitter-coveragepy| |twitter-nedbat| @@ -19,8 +19,8 @@ library to determine which lines are executable, and which have been executed. Coverage.py runs on these versions of Python: -* CPython 3.6 through 3.10. -* PyPy3 7.3.3. +* CPython 3.6 through 3.11. +* PyPy3 7.3.7. Documentation is on `Read the Docs`_. Code repository and issue tracker are on `GitHub`_. @@ -103,9 +103,6 @@ Licensed under the `Apache 2.0 License`_. For details, see `NOTICE.txt`_. .. |docs| image:: https://readthedocs.org/projects/coverage/badge/?version=latest&style=flat :target: https://coverage.readthedocs.io/ :alt: Documentation -.. |reqs| image:: https://requires.io/github/nedbat/coveragepy/requirements.svg?branch=master - :target: https://requires.io/github/nedbat/coveragepy/requirements/?branch=master - :alt: Requirements status .. |kit| image:: https://badge.fury.io/py/coverage.svg :target: https://pypi.org/project/coverage/ :alt: PyPI status @@ -128,7 +125,7 @@ Licensed under the `Apache 2.0 License`_. For details, see `NOTICE.txt`_. :target: https://codecov.io/github/nedbat/coveragepy?branch=master :alt: Coverage! .. |repos| image:: https://repology.org/badge/tiny-repos/python:coverage.svg - :target: https://repology.org/metapackage/python:coverage/versions + :target: https://repology.org/project/python:coverage/versions :alt: Packaging status .. |tidelift| image:: https://tidelift.com/badges/package/pypi/coverage :target: https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme diff --git a/coverage/annotate.py b/coverage/annotate.py index 9ca1b80ac..07ff644dd 100644 --- a/coverage/annotate.py +++ b/coverage/annotate.py @@ -74,8 +74,7 @@ def annotate_file(self, fr, analysis): dest_file = fr.filename + ",cover" with open(dest_file, 'w', encoding='utf-8') as dest: - i = 0 - j = 0 + i = j = 0 covered = True source = fr.source() for lineno, line in enumerate(source.splitlines(True), start=1): @@ -89,9 +88,7 @@ def annotate_file(self, fr, analysis): dest.write(' ') elif self.else_re.match(line): # Special logic for lines containing only 'else:'. - if i >= len(statements) and j >= len(missing): - dest.write('! ') - elif i >= len(statements) or j >= len(missing): + if j >= len(missing): dest.write('> ') elif statements[i] == missing[j]: dest.write('! ') diff --git a/coverage/cmdline.py b/coverage/cmdline.py index 1be155b80..e996fffff 100644 --- a/coverage/cmdline.py +++ b/coverage/cmdline.py @@ -6,6 +6,7 @@ import glob import optparse # pylint: disable=deprecated-module +import os import os.path import shlex import sys @@ -20,6 +21,7 @@ from coverage.debug import info_formatter, info_header, short_stack from coverage.exceptions import BaseCoverageException, ExceptionDuringRun, NoSource from coverage.execfile import PyRunner +from coverage.misc import human_sorted from coverage.results import Numbers, should_fail_under @@ -53,6 +55,14 @@ class Opts: '', '--context', action='store', metavar="LABEL", help="The context label to record for this coverage run.", ) + contexts = optparse.make_option( + '', '--contexts', action='store', + metavar="REGEX1,REGEX2,...", + help=( + "Only display data from lines covered in the given contexts. " + + "Accepts Python regexes, which must be quoted." + ), + ) debug = optparse.make_option( '', '--debug', action='store', metavar="OPTS", help="Debug options, separated by commas. [env: COVERAGE_DEBUG]", @@ -88,30 +98,16 @@ class Opts: "which isn't done by default." ), ) - sort = optparse.make_option( - '--sort', action='store', metavar='COLUMN', - help="Sort the report by the named column: name, stmts, miss, branch, brpart, or cover. " + - "Default is name." - ) show_missing = optparse.make_option( '-m', '--show-missing', action='store_true', help="Show line numbers of statements in each module that weren't executed.", ) - skip_covered = optparse.make_option( - '--skip-covered', action='store_true', - help="Skip files with 100% coverage.", - ) - no_skip_covered = optparse.make_option( - '--no-skip-covered', action='store_false', dest='skip_covered', - help="Disable --skip-covered.", - ) - skip_empty = optparse.make_option( - '--skip-empty', action='store_true', - help="Skip files with no code.", - ) - show_contexts = optparse.make_option( - '--show-contexts', action='store_true', - help="Show contexts for covered lines.", + module = optparse.make_option( + '-m', '--module', action='store_true', + help=( + " is an importable Python module, not a script path, " + + "to be run as 'python -m' would run it." + ), ) omit = optparse.make_option( '', '--omit', action='store', @@ -121,14 +117,6 @@ class Opts: "Accepts shell-style wildcards, which must be quoted." ), ) - contexts = optparse.make_option( - '', '--contexts', action='store', - metavar="REGEX1,REGEX2,...", - help=( - "Only display data from lines covered in the given contexts. " + - "Accepts Python regexes, which must be quoted." - ), - ) output_xml = optparse.make_option( '-o', '', action='store', dest="outfile", metavar="OUTFILE", @@ -151,13 +139,6 @@ class Opts: "many processes." ), ) - module = optparse.make_option( - '-m', '--module', action='store_true', - help=( - " is an importable Python module, not a script path, " + - "to be run as 'python -m' would run it." - ), - ) precision = optparse.make_option( '', '--precision', action='store', metavar='N', type=int, help=( @@ -165,6 +146,10 @@ class Opts: "reported coverage percentages." ), ) + quiet = optparse.make_option( + '-q', '--quiet', action='store_true', + help="Don't print messages about what is happening.", + ) rcfile = optparse.make_option( '', '--rcfile', action='store', help=( @@ -173,6 +158,27 @@ class Opts: "'pyproject.toml' are tried. [env: COVERAGE_RCFILE]" ), ) + show_contexts = optparse.make_option( + '--show-contexts', action='store_true', + help="Show contexts for covered lines.", + ) + skip_covered = optparse.make_option( + '--skip-covered', action='store_true', + help="Skip files with 100% coverage.", + ) + no_skip_covered = optparse.make_option( + '--no-skip-covered', action='store_false', dest='skip_covered', + help="Disable --skip-covered.", + ) + skip_empty = optparse.make_option( + '--skip-empty', action='store_true', + help="Skip files with no code.", + ) + sort = optparse.make_option( + '--sort', action='store', metavar='COLUMN', + help="Sort the report by the named column: name, stmts, miss, branch, brpart, or cover. " + + "Default is name." + ) source = optparse.make_option( '', '--source', action='store', metavar="SRC1,SRC2,...", help="A list of directories or importable names of code to measure.", @@ -212,6 +218,7 @@ def __init__(self, *args, **kwargs): branch=None, concurrency=None, context=None, + contexts=None, debug=None, directory=None, fail_under=None, @@ -221,15 +228,15 @@ def __init__(self, *args, **kwargs): keep=None, module=None, omit=None, - contexts=None, parallel_mode=None, precision=None, pylib=None, + quiet=None, rcfile=True, + show_contexts=None, show_missing=None, skip_covered=None, skip_empty=None, - show_contexts=None, sort=None, source=None, timid=None, @@ -338,6 +345,7 @@ def get_prog_name(self): [ Opts.append, Opts.keep, + Opts.quiet, ] + GLOBAL_ARGS, usage="[options] ... ", description=( @@ -385,6 +393,7 @@ def get_prog_name(self): Opts.include, Opts.omit, Opts.precision, + Opts.quiet, Opts.show_contexts, Opts.skip_covered, Opts.no_skip_covered, @@ -409,6 +418,7 @@ def get_prog_name(self): Opts.omit, Opts.output_json, Opts.json_pretty_print, + Opts.quiet, Opts.show_contexts, ] + GLOBAL_ARGS, usage="[options] [modules]", @@ -461,6 +471,7 @@ def get_prog_name(self): Opts.include, Opts.omit, Opts.output_xml, + Opts.quiet, Opts.skip_empty, ] + GLOBAL_ARGS, usage="[options] [modules]", @@ -574,7 +585,7 @@ def command_line(self, argv): concurrency=options.concurrency, check_preimported=True, context=options.context, - messages=True, + messages=not options.quiet, ) if options.action == "debug": @@ -738,6 +749,8 @@ def do_run(self, options, args): ) return ERR + os.environ["COVERAGE_RUN"] = "true" + runner = PyRunner(args, as_module=bool(options.module)) runner.prepare() @@ -780,7 +793,7 @@ def do_debug(self, args): if data: print(f"has_arcs: {data.has_arcs()!r}") summary = line_counts(data, fullpath=True) - filenames = sorted(summary.keys()) + filenames = human_sorted(summary.keys()) print(f"\n{len(filenames)} files:") for f in filenames: line = f"{f}: {summary[f]} lines" diff --git a/coverage/collector.py b/coverage/collector.py index 73babf44e..6f9aa2c16 100644 --- a/coverage/collector.py +++ b/coverage/collector.py @@ -10,7 +10,7 @@ from coverage.debug import short_stack from coverage.disposition import FileDisposition from coverage.exceptions import CoverageException -from coverage.misc import isolate_module +from coverage.misc import human_sorted, isolate_module from coverage.pytracer import PyTracer os = isolate_module(os) @@ -21,7 +21,7 @@ from coverage.tracer import CTracer, CFileDisposition except ImportError: # Couldn't import the C extension, maybe it isn't built. - if os.getenv('COVERAGE_TEST_TRACER') == 'c': + if os.getenv('COVERAGE_TEST_TRACER') == 'c': # pragma: part covered # During testing, we use the COVERAGE_TEST_TRACER environment variable # to indicate that we've fiddled with the environment to test this # fallback code. If we thought we had a C tracer, but couldn't import @@ -157,9 +157,11 @@ def __init__( if self._trace_class is CTracer: self.file_disposition_class = CFileDisposition self.supports_plugins = True + self.packed_arcs = True else: self.file_disposition_class = FileDisposition self.supports_plugins = False + self.packed_arcs = False def __repr__(self): return f"" @@ -352,7 +354,7 @@ def pause(self): stats = tracer.get_stats() if stats: print("\nCoverage.py tracer stats:") - for k in sorted(stats.keys()): + for k in human_sorted(stats.keys()): print(f"{k:>20}: {stats[k]}") if self.threading: self.threading.settrace(None) @@ -409,15 +411,15 @@ def mapped_file_dict(self, d): # in other threads. We try three times in case of concurrent # access, hoping to get a clean copy. runtime_err = None - for _ in range(3): + for _ in range(3): # pragma: part covered try: items = list(d.items()) - except RuntimeError as ex: + except RuntimeError as ex: # pragma: cant happen runtime_err = ex else: break else: - raise runtime_err + raise runtime_err # pragma: cant happen return {self.cached_mapped_file(k): v for k, v in items if v} @@ -437,7 +439,25 @@ def flush_data(self): return False if self.branch: - self.covdata.add_arcs(self.mapped_file_dict(self.data)) + if self.packed_arcs: + # Unpack the line number pairs packed into integers. See + # tracer.c:CTracer_record_pair for the C code that creates + # these packed ints. + data = {} + for fname, packeds in self.data.items(): + tuples = [] + for packed in packeds: + l1 = packed & 0xFFFFF + l2 = (packed & (0xFFFFF << 20)) >> 20 + if packed & (1 << 40): + l1 *= -1 + if packed & (1 << 41): + l2 *= -1 + tuples.append((l1, l2)) + data[fname] = tuples + else: + data = self.data + self.covdata.add_arcs(self.mapped_file_dict(data)) else: self.covdata.add_lines(self.mapped_file_dict(self.data)) diff --git a/coverage/control.py b/coverage/control.py index 8a55a3174..8a832a20e 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -26,7 +26,7 @@ from coverage.html import HtmlReporter from coverage.inorout import InOrOut from coverage.jsonreport import JsonReporter -from coverage.misc import bool_or_none, join_regex +from coverage.misc import bool_or_none, join_regex, human_sorted, human_sorted_items from coverage.misc import DefaultValue, ensure_dir_for_file, isolate_module from coverage.plugin import FileReporter from coverage.plugin_support import Plugins @@ -309,7 +309,7 @@ def _write_startup_debug(self): wrote_any = False with self._debug.without_callers(): if self._debug.should('config'): - config_info = sorted(self.config.__dict__.items()) + config_info = human_sorted_items(self.config.__dict__.items()) config_info = [(k, v) for k, v in config_info if not k.startswith('_')] write_formatted_info(self._debug, "config", config_info) wrote_any = True @@ -706,7 +706,7 @@ def combine(self, data_paths=None, strict=False, keep=False): aliases = None if self.config.paths: - aliases = PathAliases() + aliases = PathAliases(relative=self.config.relative_files) for paths in self.config.paths.values(): result = paths[0] for pattern in paths[1:]: @@ -932,6 +932,11 @@ def annotate( ): """Annotate a list of modules. + .. note:: + This method has been obsoleted by more modern reporting tools, + including the :meth:`html_report` method. It will be removed in a + future version. + Each module in `morfs` is annotated. The source is written to a new file, named with a ",cover" suffix, with each line prefixed with a marker to indicate the coverage of the line. Covered lines have ">", @@ -940,6 +945,9 @@ def annotate( See :meth:`report` for other arguments. """ + print("The annotate command will be removed in a future version.") + print("Get in touch if you still use it: ned@nedbatchelder.com") + with override_config(self, ignore_errors=ignore_errors, report_omit=omit, report_include=include, report_contexts=contexts, @@ -1006,7 +1014,7 @@ def xml_report( ignore_errors=ignore_errors, report_omit=omit, report_include=include, xml_output=outfile, report_contexts=contexts, skip_empty=skip_empty, ): - return render_report(self.config.xml_output, XmlReporter(self), morfs) + return render_report(self.config.xml_output, XmlReporter(self), morfs, self._message) def json_report( self, morfs=None, outfile=None, ignore_errors=None, @@ -1030,7 +1038,7 @@ def json_report( json_output=outfile, report_contexts=contexts, json_pretty_print=pretty_print, json_show_contexts=show_contexts ): - return render_report(self.config.json_output, JsonReporter(self), morfs) + return render_report(self.config.json_output, JsonReporter(self), morfs, self._message) def sys_info(self): """Return a list of (key, value) pairs showing internal information.""" @@ -1076,7 +1084,7 @@ def plugin_info(plugins): ('pid', os.getpid()), ('cwd', os.getcwd()), ('path', sys.path), - ('environment', sorted( + ('environment', human_sorted( f"{k} = {v}" for k, v in os.environ.items() if ( diff --git a/coverage/ctracer/stats.h b/coverage/ctracer/stats.h index 05173369f..75e5cc740 100644 --- a/coverage/ctracer/stats.h +++ b/coverage/ctracer/stats.h @@ -17,10 +17,8 @@ typedef struct Stats { #if COLLECT_STATS unsigned int lines; unsigned int returns; - unsigned int exceptions; unsigned int others; unsigned int files; - unsigned int missed_returns; unsigned int stack_reallocs; unsigned int errors; unsigned int pycalls; diff --git a/coverage/ctracer/tracer.c b/coverage/ctracer/tracer.c index 00d9f106a..4227e7daf 100644 --- a/coverage/ctracer/tracer.c +++ b/coverage/ctracer/tracer.c @@ -174,22 +174,38 @@ static int CTracer_record_pair(CTracer *self, int l1, int l2) { int ret = RET_ERROR; - - PyObject * t = NULL; - - t = Py_BuildValue("(ii)", l1, l2); - if (t == NULL) { + PyObject * packed_obj = NULL; + uint64 packed = 0; + + // Conceptually, data is a set of tuples (l1, l2), but that literally + // making a set of tuples would require us to construct a tuple just to + // see if we'd already recorded an arc. On many-times-executed code, + // that would mean we construct a tuple, find the tuple is already in the + // set, then discard the tuple. We can avoid that overhead by packing + // the two line numbers into one integer instead. + // See collector.py:flush_data for the Python code that unpacks this. + if (l1 < 0) { + packed |= (1LL << 40); + l1 = -l1; + } + if (l2 < 0) { + packed |= (1LL << 41); + l2 = -l2; + } + packed |= (((uint64)l2) << 20) + (uint64)l1; + packed_obj = PyLong_FromUnsignedLongLong(packed); + if (packed_obj == NULL) { goto error; } - if (PySet_Add(self->pcur_entry->file_data, t) < 0) { + if (PySet_Add(self->pcur_entry->file_data, packed_obj) < 0) { goto error; } ret = RET_OK; error: - Py_XDECREF(t); + Py_XDECREF(packed_obj); return ret; } @@ -279,48 +295,6 @@ CTracer_set_pdata_stack(CTracer *self) * Parts of the trace function. */ -static int -CTracer_check_missing_return(CTracer *self, PyFrameObject *frame) -{ - int ret = RET_ERROR; - - if (self->last_exc_back) { - if (frame == self->last_exc_back) { - /* Looks like someone forgot to send a return event. We'll clear - the exception state and do the RETURN code here. Notice that the - frame we have in hand here is not the correct frame for the RETURN, - that frame is gone. Our handling for RETURN doesn't need the - actual frame, but we do log it, so that will look a little off if - you're looking at the detailed log. - - If someday we need to examine the frame when doing RETURN, then - we'll need to keep more of the missed frame's state. - */ - STATS( self->stats.missed_returns++; ) - if (CTracer_set_pdata_stack(self) < 0) { - goto error; - } - if (self->pdata_stack->depth >= 0) { - if (self->tracing_arcs && self->pcur_entry->file_data) { - if (CTracer_record_pair(self, self->pcur_entry->last_line, -self->last_exc_firstlineno) < 0) { - goto error; - } - } - SHOWLOG(PyFrame_GetLineNumber(frame), MyFrame_GetCode(frame)->co_filename, "missedreturn"); - self->pdata_stack->depth--; - self->pcur_entry = &self->pdata_stack->stack[self->pdata_stack->depth]; - } - } - self->last_exc_back = NULL; - } - - ret = RET_OK; - -error: - - return ret; -} - static int CTracer_handle_call(CTracer *self, PyFrameObject *frame) { @@ -549,7 +523,7 @@ CTracer_handle_call(CTracer *self, PyFrameObject *frame) * re-entering a generator also. f_lasti is -1 for a true call, and a * real byte offset for a generator re-entry. */ - if (frame->f_lasti < 0) { + if (MyFrame_lasti(frame) < 0) { self->pcur_entry->last_line = -MyFrame_GetCode(frame)->co_firstlineno; } else { @@ -757,30 +731,6 @@ CTracer_handle_return(CTracer *self, PyFrameObject *frame) return ret; } -static int -CTracer_handle_exception(CTracer *self, PyFrameObject *frame) -{ - /* Some code (Python 2.3, and pyexpat anywhere) fires an exception event - without a return event. To detect that, we'll keep a copy of the - parent frame for an exception event. If the next event is in that - frame, then we must have returned without a return event. We can - synthesize the missing event then. - - Python itself fixed this problem in 2.4. Pyexpat still has the bug. - I've reported the problem with pyexpat as http://bugs.python.org/issue6359 . - If it gets fixed, this code should still work properly. Maybe some day - the bug will be fixed everywhere coverage.py is supported, and we can - remove this missing-return detection. - - More about this fix: https://nedbatchelder.com/blog/200907/a_nasty_little_bug.html - */ - STATS( self->stats.exceptions++; ) - self->last_exc_back = frame->f_back; - self->last_exc_firstlineno = MyFrame_GetCode(frame)->co_firstlineno; - - return RET_OK; -} - /* * The Trace Function */ @@ -821,11 +771,6 @@ CTracer_trace(CTracer *self, PyFrameObject *frame, int what, PyObject *arg_unuse Py_DECREF(ascii); #endif - /* See below for details on missing-return detection. */ - if (CTracer_check_missing_return(self, frame) < 0) { - goto error; - } - self->activity = TRUE; switch (what) { @@ -847,12 +792,6 @@ CTracer_trace(CTracer *self, PyFrameObject *frame, int what, PyObject *arg_unuse } break; - case PyTrace_EXCEPTION: - if (CTracer_handle_exception(self, frame) < 0) { - goto error; - } - break; - default: STATS( self->stats.others++; ) break; @@ -1034,10 +973,8 @@ CTracer_get_stats(CTracer *self, PyObject *args_unused) "calls", self->stats.calls, "lines", self->stats.lines, "returns", self->stats.returns, - "exceptions", self->stats.exceptions, "others", self->stats.others, "files", self->stats.files, - "missed_returns", self->stats.missed_returns, "stack_reallocs", self->stats.stack_reallocs, "stack_alloc", self->pdata_stack->alloc, "errors", self->stats.errors, diff --git a/coverage/ctracer/tracer.h b/coverage/ctracer/tracer.h index fbbfa202c..65d748ca5 100644 --- a/coverage/ctracer/tracer.h +++ b/coverage/ctracer/tracer.h @@ -60,10 +60,6 @@ typedef struct CTracer { /* The current file's data stack entry. */ DataStackEntry * pcur_entry; - /* The parent frame for the last exception event, to fix missing returns. */ - PyFrameObject * last_exc_back; - int last_exc_firstlineno; - Stats stats; } CTracer; diff --git a/coverage/ctracer/util.h b/coverage/ctracer/util.h index a0b0e236e..58fa1d490 100644 --- a/coverage/ctracer/util.h +++ b/coverage/ctracer/util.h @@ -12,20 +12,26 @@ #undef COLLECT_STATS /* Collect counters: stats are printed when tracer is stopped. */ #undef DO_NOTHING /* Define this to make the tracer do nothing. */ +#if PY_VERSION_HEX >= 0x030B00A0 +// 3.11 moved f_lasti into an internal structure. This is totally the wrong way +// to make this work, but it's all I've got until https://bugs.python.org/issue40421 +// is resolved. +#include +#define MyFrame_lasti(f) ((f)->f_frame->f_lasti * 2) +#elif PY_VERSION_HEX >= 0x030A00A7 // The f_lasti field changed meaning in 3.10.0a7. It had been bytes, but // now is instructions, so we need to adjust it to use it as a byte index. -#if PY_VERSION_HEX >= 0x030A00A7 -#define MyFrame_lasti(f) (f->f_lasti * 2) +#define MyFrame_lasti(f) ((f)->f_lasti * 2) #else -#define MyFrame_lasti(f) f->f_lasti -#endif // 3.10.0a7 +#define MyFrame_lasti(f) ((f)->f_lasti) +#endif // Access f_code should be done through a helper starting in 3.9. #if PY_VERSION_HEX >= 0x03090000 #define MyFrame_GetCode(f) (PyFrame_GetCode(f)) #else #define MyFrame_GetCode(f) ((f)->f_code) -#endif // 3.11 +#endif /* The values returned to indicate ok or error. */ #define RET_OK 0 @@ -36,6 +42,11 @@ typedef int BOOL; #define FALSE 0 #define TRUE 1 +#if SIZEOF_LONG_LONG < 8 +#error long long too small! +#endif +typedef unsigned long long uint64; + /* Only for extreme machete-mode debugging! */ #define CRASH { printf("*** CRASH! ***\n"); *((int*)1) = 1; } diff --git a/coverage/disposition.py b/coverage/disposition.py index dfcc6def3..1c39a9c19 100644 --- a/coverage/disposition.py +++ b/coverage/disposition.py @@ -30,6 +30,8 @@ def disposition_debug_msg(disp): """Make a nice debug message of what the FileDisposition is doing.""" if disp.trace: msg = f"Tracing {disp.original_filename!r}" + if disp.original_filename != disp.source_filename: + msg += f" as {disp.source_filename!r}" if disp.file_tracer: msg += ": will be traced by %r" % disp.file_tracer else: diff --git a/coverage/env.py b/coverage/env.py index c300f8029..a01bb8160 100644 --- a/coverage/env.py +++ b/coverage/env.py @@ -50,6 +50,9 @@ class PYBEHAVIOR: if pep626: optimize_if_not_debug2 = False + # Yet another way to optimize "if not __debug__"? + optimize_if_not_debug3 = (PYPY and PYVERSION >= (3, 8)) + # Can co_lnotab have negative deltas? negative_lnotab = not (PYPY and PYPYVERSION < (7, 2)) @@ -77,7 +80,7 @@ class PYBEHAVIOR: # 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 # (old behavior)? - trace_decorated_def = (PYVERSION >= (3, 8)) + trace_decorated_def = (CPYTHON and PYVERSION >= (3, 8)) # Are while-true loops optimized into absolute jumps with no loop setup? nix_while_true = (PYVERSION >= (3, 8)) @@ -121,3 +124,8 @@ class PYBEHAVIOR: # Even when running tests, you can use COVERAGE_TESTING=0 to disable the # test-specific behavior like contracts. TESTING = os.getenv('COVERAGE_TESTING', '') == 'True' + +# Environment COVERAGE_NO_CONTRACTS=1 can turn off contracts while debugging +# tests to remove noise from stack traces. +# $set_env.py: COVERAGE_NO_CONTRACTS - Disable PyContracts to simplify stack traces. +USE_CONTRACTS = TESTING and not bool(int(os.environ.get("COVERAGE_NO_CONTRACTS", 0))) diff --git a/coverage/files.py b/coverage/files.py index 686717447..a721e533a 100644 --- a/coverage/files.py +++ b/coverage/files.py @@ -14,7 +14,7 @@ from coverage import env from coverage.exceptions import CoverageException -from coverage.misc import contract, join_regex, isolate_module +from coverage.misc import contract, human_sorted, isolate_module, join_regex os = isolate_module(os) @@ -198,8 +198,8 @@ class TreeMatcher: somewhere in a subtree rooted at one of the directories. """ - def __init__(self, paths, name): - self.original_paths = sorted(paths) + def __init__(self, paths, name="unknown"): + self.original_paths = human_sorted(paths) self.paths = list(map(os.path.normcase, paths)) self.name = name @@ -226,7 +226,7 @@ def match(self, fpath): class ModuleMatcher: """A matcher for modules in a tree.""" - def __init__(self, module_names, name): + def __init__(self, module_names, name="unknown"): self.modules = list(module_names) self.name = name @@ -255,7 +255,7 @@ def match(self, module_name): class FnmatchMatcher: """A matcher for files by file name pattern.""" - def __init__(self, pats, name): + def __init__(self, pats, name="unknown"): self.pats = list(pats) self.re = fnmatches_to_regex(self.pats, case_insensitive=env.WINDOWS) self.name = name @@ -326,11 +326,13 @@ class PathAliases: map a path through those aliases to produce a unified path. """ - def __init__(self): + def __init__(self, relative=False): self.aliases = [] + self.relative = relative def pprint(self): # pragma: debugging """Dump the important parts of the PathAliases, for debugging.""" + print(f"Aliases (relative={self.relative}):") for regex, result in self.aliases: print(f"{regex.pattern!r} --> {result!r}") @@ -393,7 +395,8 @@ def map(self, path): if m: new = path.replace(m.group(0), result) new = new.replace(sep(path), sep(result)) - new = canonical_filename(new) + if not self.relative: + new = canonical_filename(new) return new return path diff --git a/coverage/html.py b/coverage/html.py index b095343ed..526890504 100644 --- a/coverage/html.py +++ b/coverage/html.py @@ -15,6 +15,7 @@ from coverage.exceptions import CoverageException from coverage.files import flat_rootname from coverage.misc import ensure_dir, file_be_gone, Hasher, isolate_module, format_local_datetime +from coverage.misc import human_sorted from coverage.report import get_analysis_to_report from coverage.results import Numbers from coverage.templite import Templite @@ -22,42 +23,12 @@ os = isolate_module(os) -# Static files are looked for in a list of places. -STATIC_PATH = [ - # The place Debian puts system Javascript libraries. - "/usr/share/javascript", - - # Our htmlfiles directory. - os.path.join(os.path.dirname(__file__), "htmlfiles"), -] - - -def data_filename(fname, pkgdir=""): - """Return the path to a data file of ours. - - The file is searched for on `STATIC_PATH`, and the first place it's found, - is returned. - - Each directory in `STATIC_PATH` is searched as-is, and also, if `pkgdir` - is provided, at that sub-directory. - +def data_filename(fname): + """Return the path to an "htmlfiles" data file of ours. """ - tried = [] - for static_dir in STATIC_PATH: - static_filename = os.path.join(static_dir, fname) - if os.path.exists(static_filename): - return static_filename - else: - tried.append(static_filename) - if pkgdir: - static_filename = os.path.join(static_dir, pkgdir, fname) - if os.path.exists(static_filename): - return static_filename - else: - tried.append(static_filename) - raise CoverageException( - f"Couldn't find static file {fname!r} from {os.getcwd()!r}, tried: {tried!r}" - ) + static_dir = os.path.join(os.path.dirname(__file__), "htmlfiles") + static_filename = os.path.join(static_dir, fname) + return static_filename def read_data(fname): @@ -123,7 +94,7 @@ def data_for_file(self, fr, analysis): contexts = contexts_label = None context_list = None if category and self.config.show_contexts: - contexts = sorted(c or self.EMPTY for c in contexts_by_lineno.get(lineno, ())) + contexts = human_sorted(c or self.EMPTY for c in contexts_by_lineno.get(lineno, ())) if contexts == [self.EMPTY]: contexts_label = self.EMPTY else: @@ -157,16 +128,11 @@ class HtmlReporter: # These files will be copied from the htmlfiles directory to the output # directory. STATIC_FILES = [ - ("style.css", ""), - ("jquery.min.js", "jquery"), - ("jquery.ba-throttle-debounce.min.js", "jquery-throttle-debounce"), - ("jquery.hotkeys.js", "jquery-hotkeys"), - ("jquery.isonscreen.js", "jquery-isonscreen"), - ("jquery.tablesorter.min.js", "jquery-tablesorter"), - ("coverage_html.js", ""), - ("keybd_closed.png", ""), - ("keybd_open.png", ""), - ("favicon_32.png", ""), + "style.css", + "coverage_html.js", + "keybd_closed.png", + "keybd_open.png", + "favicon_32.png", ] def __init__(self, cov): @@ -255,18 +221,17 @@ def report(self, morfs): def make_local_static_report_files(self): """Make local instances of static files for HTML report.""" # The files we provide must always be copied. - for static, pkgdir in self.STATIC_FILES: - shutil.copyfile( - data_filename(static, pkgdir), - os.path.join(self.directory, static) - ) + for static in self.STATIC_FILES: + shutil.copyfile(data_filename(static), os.path.join(self.directory, static)) + + # .gitignore can't be copied from the source tree because it would + # prevent the static files from being checked in. + with open(os.path.join(self.directory, ".gitignore"), "w") as fgi: + fgi.write("# Created by coverage.py\n*\n") # The user may have extra CSS they want copied. if self.extra_css: - shutil.copyfile( - self.config.extra_css, - os.path.join(self.directory, self.extra_css) - ) + shutil.copyfile(self.config.extra_css, os.path.join(self.directory, self.extra_css)) def html_file(self, fr, analysis): """Generate an HTML file for one source file.""" diff --git a/coverage/htmlfiles/coverage_html.js b/coverage/htmlfiles/coverage_html.js index 30d3a067f..00e18488d 100644 --- a/coverage/htmlfiles/coverage_html.js +++ b/coverage/htmlfiles/coverage_html.js @@ -7,227 +7,191 @@ coverage = {}; -// Find all the elements with shortkey_* class, and use them to assign a shortcut key. -coverage.assign_shortkeys = function () { - $("*[class*='shortkey_']").each(function (i, e) { - $.each($(e).attr("class").split(" "), function (i, c) { - if (/^shortkey_/.test(c)) { - $(document).bind('keydown', c.substr(9), function () { - $(e).click(); - }); - } - }); - }); +// General helpers +function debounce(callback, wait) { + let timeoutId = null; + return function(...args) { + clearTimeout(timeoutId); + timeoutId = setTimeout(() => { + callback.apply(this, args); + }, wait); + }; }; -// Create the events for the help panel. -coverage.wire_up_help_panel = function () { - $("#keyboard_icon").click(function () { - // Show the help panel, and position it so the keyboard icon in the - // panel is in the same place as the keyboard icon in the header. - $(".help_panel").show(); - var koff = $("#keyboard_icon").offset(); - var poff = $("#panel_icon").position(); - $(".help_panel").offset({ - top: koff.top-poff.top-1, - left: koff.left-poff.left-1 +function checkVisible(element) { + const rect = element.getBoundingClientRect(); + const viewBottom = Math.max(document.documentElement.clientHeight, window.innerHeight); + const viewTop = 30; + return !(rect.bottom < viewTop || rect.top >= viewBottom); +} + +// Helpers for table sorting +function getCellValue(row, column = 0) { + const cell = row.cells[column] + if (cell.childElementCount == 1) { + const child = cell.firstElementChild + if (child instanceof HTMLTimeElement && child.dateTime) { + return child.dateTime + } else if (child instanceof HTMLDataElement && child.value) { + return child.value + } + } + return cell.innerText || cell.textContent; +} + +function rowComparator(rowA, rowB, column = 0) { + let valueA = getCellValue(rowA, column); + let valueB = getCellValue(rowB, column); + if (!isNaN(valueA) && !isNaN(valueB)) { + return valueA - valueB + } + return valueA.localeCompare(valueB, undefined, {numeric: true}); +} + +function sortColumn(th) { + // Get the current sorting direction of the selected header, + // clear state on other headers and then set the new sorting direction + const currentSortOrder = th.getAttribute("aria-sort"); + [...th.parentElement.cells].forEach(header => header.setAttribute("aria-sort", "none")); + if (currentSortOrder === "none") { + th.setAttribute("aria-sort", th.dataset.defaultSortOrder || "ascending"); + } else { + th.setAttribute("aria-sort", currentSortOrder === "ascending" ? "descending" : "ascending"); + } + + const column = [...th.parentElement.cells].indexOf(th) + + // Sort all rows and afterwards append them in order to move them in the DOM + Array.from(th.closest("table").querySelectorAll("tbody tr")) + .sort((rowA, rowB) => rowComparator(rowA, rowB, column) * (th.getAttribute("aria-sort") === "ascending" ? 1 : -1)) + .forEach(tr => tr.parentElement.appendChild(tr) ); +} + +// Find all the elements with data-shortcut attribute, and use them to assign a shortcut key. +coverage.assign_shortkeys = function () { + document.querySelectorAll("[data-shortcut]").forEach(element => { + document.addEventListener("keypress", event => { + if (event.target.tagName.toLowerCase() === "input") { + return; // ignore keypress from search filter + } + if (event.key === element.dataset.shortcut) { + element.click(); + } }); }); - $("#panel_icon").click(function () { - $(".help_panel").hide(); - }); }; // Create the events for the filter box. coverage.wire_up_filter = function () { // Cache elements. - var table = $("table.index"); - var table_rows = table.find("tbody tr"); - var table_row_names = table_rows.find("td.name a"); - var no_rows = $("#no_rows"); - - // Create a duplicate table footer that we can modify with dynamic summed values. - var table_footer = $("table.index tfoot tr"); - var table_dynamic_footer = table_footer.clone(); - table_dynamic_footer.attr('class', 'total_dynamic hidden'); - table_footer.after(table_dynamic_footer); + const table = document.querySelector("table.index"); + const table_body_rows = table.querySelectorAll("tbody tr"); + const no_rows = document.getElementById("no_rows"); // Observe filter keyevents. - $("#filter").on("keyup change", $.debounce(150, function (event) { - var filter_value = $(this).val(); - - if (filter_value === "") { - // Filter box is empty, remove all filtering. - table_rows.removeClass("hidden"); - - // Show standard footer, hide dynamic footer. - table_footer.removeClass("hidden"); - table_dynamic_footer.addClass("hidden"); - - // Hide placeholder, show table. - if (no_rows.length > 0) { - no_rows.hide(); + document.getElementById("filter").addEventListener("input", debounce(event => { + // Keep running total of each metric, first index contains number of shown rows + const totals = new Array(table.rows[0].cells.length).fill(0); + // Accumulate the percentage as fraction + totals[totals.length - 1] = { "numer": 0, "denom": 0 }; + + // Hide / show elements. + table_body_rows.forEach(row => { + if (!row.cells[0].textContent.includes(event.target.value)) { + // hide + row.classList.add("hidden"); + return; } - table.show(); - } - else { - // Filter table items by value. - var hidden = 0; - var shown = 0; - - // Hide / show elements. - $.each(table_row_names, function () { - var element = $(this).parents("tr"); - - if ($(this).text().indexOf(filter_value) === -1) { - // hide - element.addClass("hidden"); - hidden++; - } - else { - // show - element.removeClass("hidden"); - shown++; - } - }); - - // Show placeholder if no rows will be displayed. - if (no_rows.length > 0) { - if (shown === 0) { - // Show placeholder, hide table. - no_rows.show(); - table.hide(); - } - else { - // Hide placeholder, show table. - no_rows.hide(); - table.show(); + // show + row.classList.remove("hidden"); + totals[0]++; + + for (let column = 1; column < totals.length; column++) { + // Accumulate dynamic totals + cell = row.cells[column] + if (column === totals.length - 1) { + // Last column contains percentage + const [numer, denom] = cell.dataset.ratio.split(" "); + totals[column]["numer"] += parseInt(numer, 10); + totals[column]["denom"] += parseInt(denom, 10); + } else { + totals[column] += parseInt(cell.textContent, 10); } } + }); - // Manage dynamic header: - if (hidden > 0) { - // Calculate new dynamic sum values based on visible rows. - for (var column = 2; column < 20; column++) { - // Calculate summed value. - var cells = table_rows.find('td:nth-child(' + column + ')'); - if (!cells.length) { - // No more columns...! - break; - } - - var sum = 0, numer = 0, denom = 0; - $.each(cells.filter(':visible'), function () { - var ratio = $(this).data("ratio"); - if (ratio) { - var splitted = ratio.split(" "); - numer += parseInt(splitted[0], 10); - denom += parseInt(splitted[1], 10); - } - else { - sum += parseInt(this.innerHTML, 10); - } - }); - - // Get footer cell element. - var footer_cell = table_dynamic_footer.find('td:nth-child(' + column + ')'); - - // Set value into dynamic footer cell element. - if (cells[0].innerHTML.indexOf('%') > -1) { - // Percentage columns use the numerator and denominator, - // and adapt to the number of decimal places. - var match = /\.([0-9]+)/.exec(cells[0].innerHTML); - var places = 0; - if (match) { - places = match[1].length; - } - var pct = numer * 100 / denom; - footer_cell.text(pct.toFixed(places) + '%'); - } - else { - footer_cell.text(sum); - } - } + // Show placeholder if no rows will be displayed. + if (!totals[0]) { + // Show placeholder, hide table. + no_rows.style.display = "block"; + table.style.display = "none"; + return; + } - // Hide standard footer, show dynamic footer. - table_footer.addClass("hidden"); - table_dynamic_footer.removeClass("hidden"); - } - else { - // Show standard footer, hide dynamic footer. - table_footer.removeClass("hidden"); - table_dynamic_footer.addClass("hidden"); + // Hide placeholder, show table. + no_rows.style.display = null; + table.style.display = null; + + const footer = table.tFoot.rows[0]; + // Calculate new dynamic sum values based on visible rows. + for (let column = 1; column < totals.length; column++) { + // Get footer cell element. + const cell = footer.cells[column]; + + // Set value into dynamic footer cell element. + if (column === totals.length - 1) { + // Percentage column uses the numerator and denominator, + // and adapts to the number of decimal places. + const match = /\.([0-9]+)/.exec(cell.textContent); + const places = match ? match[1].length : 0; + const { numer, denom } = totals[column]; + cell.dataset.ratio = `${numer} ${denom}`; + // Check denom to prevent NaN if filtered files contain no statements + cell.textContent = denom + ? `${(numer * 100 / denom).toFixed(places)}%` + : `${(100).toFixed(places)}%`; + } else { + cell.textContent = totals[column]; } } })); // Trigger change event on setup, to force filter on page refresh // (filter value may still be present). - $("#filter").trigger("change"); + document.getElementById("filter").dispatchEvent(new Event("change")); }; +coverage.INDEX_SORT_STORAGE = "COVERAGE_INDEX_SORT_2"; + // Loaded on index.html -coverage.index_ready = function ($) { +coverage.index_ready = function () { + coverage.assign_shortkeys(); + coverage.wire_up_filter(); + document.querySelectorAll("[data-sortable] th[aria-sort]").forEach( + th => th.addEventListener("click", e => sortColumn(e.target)) + ); + // Look for a localStorage item containing previous sort settings: - var sort_list = []; - var storage_name = "COVERAGE_INDEX_SORT"; - var stored_list = undefined; - try { - stored_list = localStorage.getItem(storage_name); - } catch(err) {} + const stored_list = localStorage.getItem(coverage.INDEX_SORT_STORAGE); if (stored_list) { - sort_list = JSON.parse('[[' + stored_list + ']]'); + const {column, direction} = JSON.parse(stored_list); + const th = document.querySelector("[data-sortable]").tHead.rows[0].cells[column]; + th.setAttribute("aria-sort", direction === "ascending" ? "descending" : "ascending"); + th.click() } - // Create a new widget which exists only to save and restore - // the sort order: - $.tablesorter.addWidget({ - id: "persistentSort", - - // Format is called by the widget before displaying: - format: function (table) { - if (table.config.sortList.length === 0 && sort_list.length > 0) { - // This table hasn't been sorted before - we'll use - // our stored settings: - $(table).trigger('sorton', [sort_list]); - } - else { - // This is not the first load - something has - // already defined sorting so we'll just update - // our stored value to match: - sort_list = table.config.sortList; - } - } - }); - - // Configure our tablesorter to handle the variable number of - // columns produced depending on report options: - var headers = []; - var col_count = $("table.index > thead > tr > th").length; - - headers[0] = { sorter: 'text' }; - for (i = 1; i < col_count-1; i++) { - headers[i] = { sorter: 'digit' }; - } - headers[col_count-1] = { sorter: 'percent' }; - - // Enable the table sorter: - $("table.index").tablesorter({ - widgets: ['persistentSort'], - headers: headers - }); - - coverage.assign_shortkeys(); - coverage.wire_up_help_panel(); - coverage.wire_up_filter(); - // Watch for page unload events so we can save the final sort settings: - $(window).on("unload", function () { - try { - localStorage.setItem(storage_name, sort_list.toString()) - } catch(err) {} + window.addEventListener("unload", function () { + const th = document.querySelector('[data-sortable] th[aria-sort="ascending"], [data-sortable] [aria-sort="descending"]'); + if (!th) { + return; + } + localStorage.setItem(coverage.INDEX_SORT_STORAGE, JSON.stringify({ + column: [...th.parentElement.cells].indexOf(th), + direction: th.getAttribute("aria-sort"), + })); }); }; @@ -235,28 +199,31 @@ coverage.index_ready = function ($) { coverage.LINE_FILTERS_STORAGE = "COVERAGE_LINE_FILTERS"; -coverage.pyfile_ready = function ($) { +coverage.pyfile_ready = function () { // If we're directed to a particular line number, highlight the line. var frag = location.hash; if (frag.length > 2 && frag[1] === 't') { - $(frag).addClass('highlight'); + document.querySelector(frag).closest(".n").classList.add("highlight"); coverage.set_sel(parseInt(frag.substr(2), 10)); - } - else { + } else { coverage.set_sel(0); } - $(document) - .bind('keydown', 'j', coverage.to_next_chunk_nicely) - .bind('keydown', 'k', coverage.to_prev_chunk_nicely) - .bind('keydown', '0', coverage.to_top) - .bind('keydown', '1', coverage.to_first_chunk) - ; + const on_click = function(sel, fn) { + const elt = document.querySelector(sel); + if (elt) { + elt.addEventListener("click", fn); + } + } + on_click(".button_toggle_run", coverage.toggle_lines); + on_click(".button_toggle_mis", coverage.toggle_lines); + on_click(".button_toggle_exc", coverage.toggle_lines); + on_click(".button_toggle_par", coverage.toggle_lines); - $(".button_toggle_run").click(function (evt) {coverage.toggle_lines(evt.target, "run");}); - $(".button_toggle_exc").click(function (evt) {coverage.toggle_lines(evt.target, "exc");}); - $(".button_toggle_mis").click(function (evt) {coverage.toggle_lines(evt.target, "mis");}); - $(".button_toggle_par").click(function (evt) {coverage.toggle_lines(evt.target, "par");}); + on_click(".button_next_chunk", coverage.to_next_chunk_nicely); + on_click(".button_prev_chunk", coverage.to_prev_chunk_nicely); + on_click(".button_top_of_page", coverage.to_top); + on_click(".button_first_chunk", coverage.to_first_chunk); coverage.filters = undefined; try { @@ -275,40 +242,43 @@ coverage.pyfile_ready = function ($) { } coverage.assign_shortkeys(); - coverage.wire_up_help_panel(); - coverage.init_scroll_markers(); + coverage.wire_up_sticky_header(); // Rebuild scroll markers when the window height changes. - $(window).resize(coverage.build_scroll_markers); + window.addEventListener("resize", coverage.build_scroll_markers); }; -coverage.toggle_lines = function (btn, cls) { - var onoff = !$(btn).hasClass("show_" + cls); - coverage.set_line_visibilty(cls, onoff); +coverage.toggle_lines = function (event) { + const btn = event.target.closest("button"); + const category = btn.value + const show = !btn.classList.contains("show_" + category); + coverage.set_line_visibilty(category, show); coverage.build_scroll_markers(); - coverage.filters[cls] = onoff; + coverage.filters[category] = show; try { localStorage.setItem(coverage.LINE_FILTERS_STORAGE, JSON.stringify(coverage.filters)); } catch(err) {} }; -coverage.set_line_visibilty = function (cls, onoff) { - var show = "show_" + cls; - var btn = $(".button_toggle_" + cls); - if (onoff) { - $("#source ." + cls).addClass(show); - btn.addClass(show); - } - else { - $("#source ." + cls).removeClass(show); - btn.removeClass(show); +coverage.set_line_visibilty = function (category, should_show) { + const cls = "show_" + category; + const btn = document.querySelector(".button_toggle_" + category); + if (btn) { + if (should_show) { + document.querySelectorAll("#source ." + category).forEach(e => e.classList.add(cls)); + btn.classList.add(cls); + } + else { + document.querySelectorAll("#source ." + category).forEach(e => e.classList.remove(cls)); + btn.classList.remove(cls); + } } }; // Return the nth line div. coverage.line_elt = function (n) { - return $("#t" + n); + return document.getElementById("t" + n)?.closest("p"); }; // Set the selection. b and e are line numbers. @@ -332,25 +302,26 @@ coverage.to_first_chunk = function () { // Return a string indicating what kind of chunk this line belongs to, // or null if not a chunk. coverage.chunk_indicator = function (line_elt) { - var klass = line_elt.attr('class'); - if (klass) { - var m = klass.match(/\bshow_\w+\b/); - if (m) { - return m[0]; - } + const classes = line_elt?.className; + if (!classes) { + return null; + } + const match = classes.match(/\bshow_\w+\b/); + if (!match) { + return null; } - return null; + return match[0]; }; coverage.to_next_chunk = function () { - var c = coverage; + const c = coverage; // Find the start of the next colored chunk. var probe = c.sel_end; var chunk_indicator, probe_line; while (true) { probe_line = c.line_elt(probe); - if (probe_line.length === 0) { + if (!probe_line) { return; } chunk_indicator = c.chunk_indicator(probe_line); @@ -375,19 +346,19 @@ coverage.to_next_chunk = function () { }; coverage.to_prev_chunk = function () { - var c = coverage; + const c = coverage; // Find the end of the prev colored chunk. var probe = c.sel_begin-1; var probe_line = c.line_elt(probe); - if (probe_line.length === 0) { + if (!probe_line) { return; } var chunk_indicator = c.chunk_indicator(probe_line); - while (probe > 0 && !chunk_indicator) { + while (probe > 1 && !chunk_indicator) { probe--; probe_line = c.line_elt(probe); - if (probe_line.length === 0) { + if (!probe_line) { return; } chunk_indicator = c.chunk_indicator(probe_line); @@ -400,6 +371,9 @@ coverage.to_prev_chunk = function () { var prev_indicator = chunk_indicator; while (prev_indicator === chunk_indicator) { probe--; + if (probe <= 0) { + return; + } probe_line = c.line_elt(probe); prev_indicator = c.chunk_indicator(probe_line); } @@ -407,28 +381,6 @@ coverage.to_prev_chunk = function () { c.show_selection(); }; -// Return the line number of the line nearest pixel position pos -coverage.line_at_pos = function (pos) { - var l1 = coverage.line_elt(1), - l2 = coverage.line_elt(2), - result; - if (l1.length && l2.length) { - var l1_top = l1.offset().top, - line_height = l2.offset().top - l1_top, - nlines = (pos - l1_top) / line_height; - if (nlines < 1) { - result = 1; - } - else { - result = Math.ceil(nlines); - } - } - else { - result = 1; - } - return result; -}; - // Returns 0, 1, or 2: how many of the two ends of the selection are on // the screen right now? coverage.selection_ends_on_screen = function () { @@ -436,31 +388,49 @@ coverage.selection_ends_on_screen = function () { return 0; } - var top = coverage.line_elt(coverage.sel_begin); - var next = coverage.line_elt(coverage.sel_end-1); + const begin = coverage.line_elt(coverage.sel_begin); + const end = coverage.line_elt(coverage.sel_end-1); return ( - (top.isOnScreen() ? 1 : 0) + - (next.isOnScreen() ? 1 : 0) + (checkVisible(begin) ? 1 : 0) + + (checkVisible(end) ? 1 : 0) ); }; coverage.to_next_chunk_nicely = function () { - coverage.finish_scrolling(); if (coverage.selection_ends_on_screen() === 0) { - // The selection is entirely off the screen: select the top line on - // the screen. - var win = $(window); - coverage.select_line_or_chunk(coverage.line_at_pos(win.scrollTop())); + // The selection is entirely off the screen: + // Set the top line on the screen as selection. + + // This will select the top-left of the viewport + // As this is most likely the span with the line number we take the parent + const line = document.elementFromPoint(0, 0).parentElement; + if (line.parentElement !== document.getElementById("source")) { + // The element is not a source line but the header or similar + coverage.select_line_or_chunk(1); + } else { + // We extract the line number from the id + coverage.select_line_or_chunk(parseInt(line.id.substring(1), 10)); + } } coverage.to_next_chunk(); }; coverage.to_prev_chunk_nicely = function () { - coverage.finish_scrolling(); if (coverage.selection_ends_on_screen() === 0) { - var win = $(window); - coverage.select_line_or_chunk(coverage.line_at_pos(win.scrollTop() + win.height())); + // The selection is entirely off the screen: + // Set the lowest line on the screen as selection. + + // This will select the bottom-left of the viewport + // As this is most likely the span with the line number we take the parent + const line = document.elementFromPoint(document.documentElement.clientHeight-1, 0).parentElement; + if (line.parentElement !== document.getElementById("source")) { + // The element is not a source line but the header or similar + coverage.select_line_or_chunk(coverage.lines_len); + } else { + // We extract the line number from the id + coverage.select_line_or_chunk(parseInt(line.id.substring(1), 10)); + } } coverage.to_prev_chunk(); }; @@ -470,7 +440,7 @@ coverage.to_prev_chunk_nicely = function () { coverage.select_line_or_chunk = function (lineno) { var c = coverage; var probe_line = c.line_elt(lineno); - if (probe_line.length === 0) { + if (!probe_line) { return; } var the_indicator = c.chunk_indicator(probe_line); @@ -482,7 +452,7 @@ coverage.select_line_or_chunk = function (lineno) { while (probe > 0 && indicator === the_indicator) { probe--; probe_line = c.line_elt(probe); - if (probe_line.length === 0) { + if (!probe_line) { break; } indicator = c.chunk_indicator(probe_line); @@ -506,106 +476,100 @@ coverage.select_line_or_chunk = function (lineno) { }; coverage.show_selection = function () { - var c = coverage; - // Highlight the lines in the chunk - $("#source .highlight").removeClass("highlight"); - for (var probe = c.sel_begin; probe > 0 && probe < c.sel_end; probe++) { - c.line_elt(probe).addClass("highlight"); + document.querySelectorAll("#source .highlight").forEach(e => e.classList.remove("highlight")); + for (let probe = coverage.sel_begin; probe < coverage.sel_end; probe++) { + coverage.line_elt(probe).querySelector(".n").classList.add("highlight"); } - c.scroll_to_selection(); + coverage.scroll_to_selection(); }; coverage.scroll_to_selection = function () { // Scroll the page if the chunk isn't fully visible. if (coverage.selection_ends_on_screen() < 2) { - // Need to move the page. The html,body trick makes it scroll in all - // browsers, got it from http://stackoverflow.com/questions/3042651 - var top = coverage.line_elt(coverage.sel_begin); - var top_pos = parseInt(top.offset().top, 10); - coverage.scroll_window(top_pos - 30); + const element = coverage.line_elt(coverage.sel_begin); + coverage.scroll_window(element.offsetTop - 60); } }; coverage.scroll_window = function (to_pos) { - $("html,body").animate({scrollTop: to_pos}, 200); -}; - -coverage.finish_scrolling = function () { - $("html,body").stop(true, true); + window.scroll({top: to_pos, behavior: "smooth"}); }; coverage.init_scroll_markers = function () { - var c = coverage; // Init some variables - c.lines_len = $('#source p').length; - c.body_h = $('body').height(); - c.header_h = $('div#header').height(); + coverage.lines_len = document.querySelectorAll('#source > p').length; // Build html - c.build_scroll_markers(); + coverage.build_scroll_markers(); }; coverage.build_scroll_markers = function () { - var c = coverage, - min_line_height = 3, - max_line_height = 10, - visible_window_h = $(window).height(); - - c.lines_to_mark = $('#source').find('p.show_run, p.show_mis, p.show_exc, p.show_exc, p.show_par'); - $('#scroll_marker').remove(); + const temp_scroll_marker = document.getElementById('scroll_marker') + if (temp_scroll_marker) temp_scroll_marker.remove(); // Don't build markers if the window has no scroll bar. - if (c.body_h <= visible_window_h) { + if (document.body.scrollHeight <= window.innerHeight) { return; } - $("body").append("
 
"); - var scroll_marker = $('#scroll_marker'), - marker_scale = scroll_marker.height() / c.body_h, - line_height = scroll_marker.height() / c.lines_len; + const marker_scale = window.innerHeight / document.body.scrollHeight; + const line_height = Math.min(Math.max(3, window.innerHeight / coverage.lines_len), 10); - // Line height must be between the extremes. - if (line_height > min_line_height) { - if (line_height > max_line_height) { - line_height = max_line_height; - } - } - else { - line_height = min_line_height; - } - - var previous_line = -99, - last_mark, - last_top, - offsets = {}; + let previous_line = -99, last_mark, last_top; - // Calculate line offsets outside loop to prevent relayouts - c.lines_to_mark.each(function() { - offsets[this.id] = $(this).offset().top; - }); - c.lines_to_mark.each(function () { - var id_name = $(this).attr('id'), - line_top = Math.round(offsets[id_name] * marker_scale), - line_number = parseInt(id_name.substring(1, id_name.length)); + const scroll_marker = document.createElement("div"); + scroll_marker.id = "scroll_marker"; + document.getElementById('source').querySelectorAll( + 'p.show_run, p.show_mis, p.show_exc, p.show_exc, p.show_par' + ).forEach(element => { + const line_top = Math.floor(element.offsetTop * marker_scale); + const line_number = parseInt(element.id.substr(1)); if (line_number === previous_line + 1) { // If this solid missed block just make previous mark higher. - last_mark.css({ - 'height': line_top + line_height - last_top - }); - } - else { + last_mark.style.height = `${line_top + line_height - last_top}px`; + } else { // Add colored line in scroll_marker block. - scroll_marker.append('
'); - last_mark = $('#m' + line_number); - last_mark.css({ - 'height': line_height, - 'top': line_top - }); + last_mark = document.createElement("div"); + last_mark.id = `m${line_number}`; + last_mark.classList.add("marker"); + last_mark.style.height = `${line_height}px`; + last_mark.style.top = `${line_top}px`; + scroll_marker.append(last_mark); last_top = line_top; } previous_line = line_number; }); + + // Append last to prevent layout calculation + document.body.append(scroll_marker); +}; + +coverage.wire_up_sticky_header = function () { + const header = document.querySelector('header'); + const header_bottom = ( + header.querySelector('.content h2').getBoundingClientRect().top - + header.getBoundingClientRect().top + ); + + function updateHeader() { + if (window.scrollY > header_bottom) { + header.classList.add('sticky'); + } else { + header.classList.remove('sticky'); + } + } + + window.addEventListener('scroll', updateHeader); + updateHeader(); }; + +document.addEventListener("DOMContentLoaded", () => { + if (document.body.classList.contains("indexfile")) { + coverage.index_ready(); + } else { + coverage.pyfile_ready(); + } +}); diff --git a/coverage/htmlfiles/index.html b/coverage/htmlfiles/index.html index 3654d66a0..e1d3e9b59 100644 --- a/coverage/htmlfiles/index.html +++ b/coverage/htmlfiles/index.html @@ -11,79 +11,61 @@ {% if extra_css %} {% endif %} - - - - - - + - + -
- Hide keyboard shortcuts -

Hot-keys on this page

-
-

- n - s - m - x - {% if has_arcs %} - b - p - {% endif %} - c   change column sorting -

-
-
- -
- +
+
{# The title="" attr doesn"t work in Safari. #} - - - - + + + + {% if has_arcs %} - - + + {% endif %} - + - {# HTML syntax requires thead, tfoot, tbody #} - - - - - - - {% if has_arcs %} - - - {% endif %} - - - {% for file in files %} @@ -99,6 +81,19 @@

{{ title|escape }}:

{% endfor %} + + + + + + + {% if has_arcs %} + + + {% endif %} + + +
ModulestatementsmissingexcludedModulestatementsmissingexcludedbranchespartialbranchespartialcoveragecoverage
Total{{totals.n_statements}}{{totals.n_missing}}{{totals.n_excluded}}{{totals.n_branches}}{{totals.n_partial_branches}}{{totals.pc_covered_str}}%
Total{{totals.n_statements}}{{totals.n_missing}}{{totals.n_excluded}}{{totals.n_branches}}{{totals.n_partial_branches}}{{totals.pc_covered_str}}%

@@ -111,16 +106,16 @@

{{ title|escape }}: {% if skipped_empty_msg %}

{{ skipped_empty_msg }}

{% endif %} -

+ - + diff --git a/coverage/htmlfiles/jquery.ba-throttle-debounce.min.js b/coverage/htmlfiles/jquery.ba-throttle-debounce.min.js deleted file mode 100644 index 648fe5d3c..000000000 --- a/coverage/htmlfiles/jquery.ba-throttle-debounce.min.js +++ /dev/null @@ -1,9 +0,0 @@ -/* - * jQuery throttle / debounce - v1.1 - 3/7/2010 - * http://benalman.com/projects/jquery-throttle-debounce-plugin/ - * - * Copyright (c) 2010 "Cowboy" Ben Alman - * Dual licensed under the MIT and GPL licenses. - * http://benalman.com/about/license/ - */ -(function(b,c){var $=b.jQuery||b.Cowboy||(b.Cowboy={}),a;$.throttle=a=function(e,f,j,i){var h,d=0;if(typeof f!=="boolean"){i=j;j=f;f=c}function g(){var o=this,m=+new Date()-d,n=arguments;function l(){d=+new Date();j.apply(o,n)}function k(){h=c}if(i&&!h){l()}h&&clearTimeout(h);if(i===c&&m>e){l()}else{if(f!==true){h=setTimeout(i?k:l,i===c?e-m:e)}}}if($.guid){g.guid=j.guid=j.guid||$.guid++}return g};$.debounce=function(d,e,f){return f===c?a(d,e,false):a(d,f,e!==false)}})(this); diff --git a/coverage/htmlfiles/jquery.hotkeys.js b/coverage/htmlfiles/jquery.hotkeys.js deleted file mode 100644 index 09b21e03c..000000000 --- a/coverage/htmlfiles/jquery.hotkeys.js +++ /dev/null @@ -1,99 +0,0 @@ -/* - * jQuery Hotkeys Plugin - * Copyright 2010, John Resig - * Dual licensed under the MIT or GPL Version 2 licenses. - * - * Based upon the plugin by Tzury Bar Yochay: - * http://github.com/tzuryby/hotkeys - * - * Original idea by: - * Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/ -*/ - -(function(jQuery){ - - jQuery.hotkeys = { - version: "0.8", - - specialKeys: { - 8: "backspace", 9: "tab", 13: "return", 16: "shift", 17: "ctrl", 18: "alt", 19: "pause", - 20: "capslock", 27: "esc", 32: "space", 33: "pageup", 34: "pagedown", 35: "end", 36: "home", - 37: "left", 38: "up", 39: "right", 40: "down", 45: "insert", 46: "del", - 96: "0", 97: "1", 98: "2", 99: "3", 100: "4", 101: "5", 102: "6", 103: "7", - 104: "8", 105: "9", 106: "*", 107: "+", 109: "-", 110: ".", 111 : "/", - 112: "f1", 113: "f2", 114: "f3", 115: "f4", 116: "f5", 117: "f6", 118: "f7", 119: "f8", - 120: "f9", 121: "f10", 122: "f11", 123: "f12", 144: "numlock", 145: "scroll", 191: "/", 224: "meta" - }, - - shiftNums: { - "`": "~", "1": "!", "2": "@", "3": "#", "4": "$", "5": "%", "6": "^", "7": "&", - "8": "*", "9": "(", "0": ")", "-": "_", "=": "+", ";": ": ", "'": "\"", ",": "<", - ".": ">", "/": "?", "\\": "|" - } - }; - - function keyHandler( handleObj ) { - // Only care when a possible input has been specified - if ( typeof handleObj.data !== "string" ) { - return; - } - - var origHandler = handleObj.handler, - keys = handleObj.data.toLowerCase().split(" "); - - handleObj.handler = function( event ) { - // Don't fire in text-accepting inputs that we didn't directly bind to - if ( this !== event.target && (/textarea|select/i.test( event.target.nodeName ) || - event.target.type === "text") ) { - return; - } - - // Keypress represents characters, not special keys - var special = event.type !== "keypress" && jQuery.hotkeys.specialKeys[ event.which ], - character = String.fromCharCode( event.which ).toLowerCase(), - key, modif = "", possible = {}; - - // check combinations (alt|ctrl|shift+anything) - if ( event.altKey && special !== "alt" ) { - modif += "alt+"; - } - - if ( event.ctrlKey && special !== "ctrl" ) { - modif += "ctrl+"; - } - - // TODO: Need to make sure this works consistently across platforms - if ( event.metaKey && !event.ctrlKey && special !== "meta" ) { - modif += "meta+"; - } - - if ( event.shiftKey && special !== "shift" ) { - modif += "shift+"; - } - - if ( special ) { - possible[ modif + special ] = true; - - } else { - possible[ modif + character ] = true; - possible[ modif + jQuery.hotkeys.shiftNums[ character ] ] = true; - - // "$" can be triggered as "Shift+4" or "Shift+$" or just "$" - if ( modif === "shift+" ) { - possible[ jQuery.hotkeys.shiftNums[ character ] ] = true; - } - } - - for ( var i = 0, l = keys.length; i < l; i++ ) { - if ( possible[ keys[i] ] ) { - return origHandler.apply( this, arguments ); - } - } - }; - } - - jQuery.each([ "keydown", "keyup", "keypress" ], function() { - jQuery.event.special[ this ] = { add: keyHandler }; - }); - -})( jQuery ); diff --git a/coverage/htmlfiles/jquery.isonscreen.js b/coverage/htmlfiles/jquery.isonscreen.js deleted file mode 100644 index 28fb99bc6..000000000 --- a/coverage/htmlfiles/jquery.isonscreen.js +++ /dev/null @@ -1,53 +0,0 @@ -/* Copyright (c) 2010 - * @author Laurence Wheway - * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) - * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses. - * - * @version 1.2.0 - */ -(function($) { - jQuery.extend({ - isOnScreen: function(box, container) { - //ensure numbers come in as integers (not strings) and remove 'px' is it's there - for(var i in box){box[i] = parseFloat(box[i])}; - for(var i in container){container[i] = parseFloat(container[i])}; - - if(!container){ - container = { - left: $(window).scrollLeft(), - top: $(window).scrollTop(), - width: $(window).width(), - height: $(window).height() - } - } - - if( box.left+box.width-container.left > 0 && - box.left < container.width+container.left && - box.top+box.height-container.top > 0 && - box.top < container.height+container.top - ) return true; - return false; - } - }) - - - jQuery.fn.isOnScreen = function (container) { - for(var i in container){container[i] = parseFloat(container[i])}; - - if(!container){ - container = { - left: $(window).scrollLeft(), - top: $(window).scrollTop(), - width: $(window).width(), - height: $(window).height() - } - } - - if( $(this).offset().left+$(this).width()-container.left > 0 && - $(this).offset().left < container.width+container.left && - $(this).offset().top+$(this).height()-container.top > 0 && - $(this).offset().top < container.height+container.top - ) return true; - return false; - } -})(jQuery); diff --git a/coverage/htmlfiles/jquery.min.js b/coverage/htmlfiles/jquery.min.js deleted file mode 100644 index d1608e37f..000000000 --- a/coverage/htmlfiles/jquery.min.js +++ /dev/null @@ -1,4 +0,0 @@ -/*! jQuery v1.11.1 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */ -!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l="1.11.1",m=function(a,b){return new m.fn.init(a,b)},n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,o=/^-ms-/,p=/-([\da-z])/gi,q=function(a,b){return b.toUpperCase()};m.fn=m.prototype={jquery:l,constructor:m,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b=a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+-new Date,v=a.document,w=0,x=0,y=gb(),z=gb(),A=gb(),B=function(a,b){return a===b&&(l=!0),0},C="undefined",D=1<<31,E={}.hasOwnProperty,F=[],G=F.pop,H=F.push,I=F.push,J=F.slice,K=F.indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]===a)return b;return-1},L="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",N="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=N.replace("w","w#"),P="\\["+M+"*("+N+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+O+"))|)"+M+"*\\]",Q=":("+N+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+P+")*)|.*)\\)|)",R=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),S=new RegExp("^"+M+"*,"+M+"*"),T=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),V=new RegExp(Q),W=new RegExp("^"+O+"$"),X={ID:new RegExp("^#("+N+")"),CLASS:new RegExp("^\\.("+N+")"),TAG:new RegExp("^("+N.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+Q),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+L+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ab=/[+~]/,bb=/'|\\/g,cb=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),db=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)};try{I.apply(F=J.call(v.childNodes),v.childNodes),F[v.childNodes.length].nodeType}catch(eb){I={apply:F.length?function(a,b){H.apply(a,J.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fb(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],!a||"string"!=typeof a)return d;if(1!==(k=b.nodeType)&&9!==k)return[];if(p&&!e){if(f=_.exec(a))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return I.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName&&b.getElementsByClassName)return I.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=9===k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(bb,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+qb(o[l]);w=ab.test(a)&&ob(b.parentNode)||b,x=o.join(",")}if(x)try{return I.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function gb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function hb(a){return a[u]=!0,a}function ib(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function jb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function kb(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||D)-(~a.sourceIndex||D);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function lb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function mb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function nb(a){return hb(function(b){return b=+b,hb(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function ob(a){return a&&typeof a.getElementsByTagName!==C&&a}c=fb.support={},f=fb.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fb.setDocument=function(a){var b,e=a?a.ownerDocument||a:v,g=e.defaultView;return e!==n&&9===e.nodeType&&e.documentElement?(n=e,o=e.documentElement,p=!f(e),g&&g!==g.top&&(g.addEventListener?g.addEventListener("unload",function(){m()},!1):g.attachEvent&&g.attachEvent("onunload",function(){m()})),c.attributes=ib(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ib(function(a){return a.appendChild(e.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(e.getElementsByClassName)&&ib(function(a){return a.innerHTML="
",a.firstChild.className="i",2===a.getElementsByClassName("i").length}),c.getById=ib(function(a){return o.appendChild(a).id=u,!e.getElementsByName||!e.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if(typeof b.getElementById!==C&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){var c=typeof a.getAttributeNode!==C&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return typeof b.getElementsByTagName!==C?b.getElementsByTagName(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return typeof b.getElementsByClassName!==C&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(e.querySelectorAll))&&(ib(function(a){a.innerHTML="",a.querySelectorAll("[msallowclip^='']").length&&q.push("[*^$]="+M+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+M+"*(?:value|"+L+")"),a.querySelectorAll(":checked").length||q.push(":checked")}),ib(function(a){var b=e.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+M+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ib(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",Q)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===e||a.ownerDocument===v&&t(v,a)?-1:b===e||b.ownerDocument===v&&t(v,b)?1:k?K.call(k,a)-K.call(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,f=a.parentNode,g=b.parentNode,h=[a],i=[b];if(!f||!g)return a===e?-1:b===e?1:f?-1:g?1:k?K.call(k,a)-K.call(k,b):0;if(f===g)return kb(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?kb(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},e):n},fb.matches=function(a,b){return fb(a,null,null,b)},fb.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fb(b,n,null,[a]).length>0},fb.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fb.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&E.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fb.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fb.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fb.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fb.selectors={cacheLength:50,createPseudo:hb,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(cb,db),a[3]=(a[3]||a[4]||a[5]||"").replace(cb,db),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fb.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fb.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(cb,db).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+M+")"+a+"("+M+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||typeof a.getAttribute!==C&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fb.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fb.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?hb(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=K.call(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:hb(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?hb(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:hb(function(a){return function(b){return fb(a,b).length>0}}),contains:hb(function(a){return function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:hb(function(a){return W.test(a||"")||fb.error("unsupported lang: "+a),a=a.replace(cb,db).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:nb(function(){return[0]}),last:nb(function(a,b){return[b-1]}),eq:nb(function(a,b,c){return[0>c?c+b:c]}),even:nb(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:nb(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:nb(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:nb(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function rb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function sb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function tb(a,b,c){for(var d=0,e=b.length;e>d;d++)fb(a,b[d],c);return c}function ub(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function vb(a,b,c,d,e,f){return d&&!d[u]&&(d=vb(d)),e&&!e[u]&&(e=vb(e,f)),hb(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||tb(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ub(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ub(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?K.call(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ub(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):I.apply(g,r)})}function wb(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=rb(function(a){return a===b},h,!0),l=rb(function(a){return K.call(b,a)>-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d))}];f>i;i++)if(c=d.relative[a[i].type])m=[rb(sb(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return vb(i>1&&sb(m),i>1&&qb(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&wb(a.slice(i,e)),f>e&&wb(a=a.slice(e)),f>e&&qb(a))}m.push(c)}return sb(m)}function xb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=G.call(i));s=ub(s)}I.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&fb.uniqueSort(i)}return k&&(w=v,j=t),r};return c?hb(f):f}return h=fb.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wb(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xb(e,d)),f.selector=a}return f},i=fb.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(cb,db),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(cb,db),ab.test(j[0].type)&&ob(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qb(j),!a)return I.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,ab.test(a)&&ob(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ib(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ib(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||jb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ib(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||jb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ib(function(a){return null==a.getAttribute("disabled")})||jb(L,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fb}(a);m.find=s,m.expr=s.selectors,m.expr[":"]=m.expr.pseudos,m.unique=s.uniqueSort,m.text=s.getText,m.isXMLDoc=s.isXML,m.contains=s.contains;var t=m.expr.match.needsContext,u=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,v=/^.[^:#\[\.,]*$/;function w(a,b,c){if(m.isFunction(b))return m.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return m.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(v.test(b))return m.filter(b,a,c);b=m.filter(b,a)}return m.grep(a,function(a){return m.inArray(a,b)>=0!==c})}m.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?m.find.matchesSelector(d,a)?[d]:[]:m.find.matches(a,m.grep(b,function(a){return 1===a.nodeType}))},m.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(m(a).filter(function(){for(b=0;e>b;b++)if(m.contains(d[b],this))return!0}));for(b=0;e>b;b++)m.find(a,d[b],c);return c=this.pushStack(e>1?m.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(w(this,a||[],!1))},not:function(a){return this.pushStack(w(this,a||[],!0))},is:function(a){return!!w(this,"string"==typeof a&&t.test(a)?m(a):a||[],!1).length}});var x,y=a.document,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=m.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||x).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof m?b[0]:b,m.merge(this,m.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:y,!0)),u.test(c[1])&&m.isPlainObject(b))for(c in b)m.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=y.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return x.find(a);this.length=1,this[0]=d}return this.context=y,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):m.isFunction(a)?"undefined"!=typeof x.ready?x.ready(a):a(m):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),m.makeArray(a,this))};A.prototype=m.fn,x=m(y);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};m.extend({dir:function(a,b,c){var d=[],e=a[b];while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!m(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),m.fn.extend({has:function(a){var b,c=m(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(m.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=t.test(a)||"string"!=typeof a?m(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&m.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?m.unique(f):f)},index:function(a){return a?"string"==typeof a?m.inArray(this[0],m(a)):m.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(m.unique(m.merge(this.get(),m(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}m.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return m.dir(a,"parentNode")},parentsUntil:function(a,b,c){return m.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return m.dir(a,"nextSibling")},prevAll:function(a){return m.dir(a,"previousSibling")},nextUntil:function(a,b,c){return m.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return m.dir(a,"previousSibling",c)},siblings:function(a){return m.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return m.sibling(a.firstChild)},contents:function(a){return m.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:m.merge([],a.childNodes)}},function(a,b){m.fn[a]=function(c,d){var e=m.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=m.filter(d,e)),this.length>1&&(C[a]||(e=m.unique(e)),B.test(a)&&(e=e.reverse())),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return m.each(a.match(E)||[],function(a,c){b[c]=!0}),b}m.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):m.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){m.each(b,function(b,c){var d=m.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&m.each(arguments,function(a,c){var d;while((d=m.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?m.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},m.extend({Deferred:function(a){var b=[["resolve","done",m.Callbacks("once memory"),"resolved"],["reject","fail",m.Callbacks("once memory"),"rejected"],["notify","progress",m.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return m.Deferred(function(c){m.each(b,function(b,f){var g=m.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&m.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?m.extend(a,d):d}},e={};return d.pipe=d.then,m.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&m.isFunction(a.promise)?e:0,g=1===f?a:m.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&m.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;m.fn.ready=function(a){return m.ready.promise().done(a),this},m.extend({isReady:!1,readyWait:1,holdReady:function(a){a?m.readyWait++:m.ready(!0)},ready:function(a){if(a===!0?!--m.readyWait:!m.isReady){if(!y.body)return setTimeout(m.ready);m.isReady=!0,a!==!0&&--m.readyWait>0||(H.resolveWith(y,[m]),m.fn.triggerHandler&&(m(y).triggerHandler("ready"),m(y).off("ready")))}}});function I(){y.addEventListener?(y.removeEventListener("DOMContentLoaded",J,!1),a.removeEventListener("load",J,!1)):(y.detachEvent("onreadystatechange",J),a.detachEvent("onload",J))}function J(){(y.addEventListener||"load"===event.type||"complete"===y.readyState)&&(I(),m.ready())}m.ready.promise=function(b){if(!H)if(H=m.Deferred(),"complete"===y.readyState)setTimeout(m.ready);else if(y.addEventListener)y.addEventListener("DOMContentLoaded",J,!1),a.addEventListener("load",J,!1);else{y.attachEvent("onreadystatechange",J),a.attachEvent("onload",J);var c=!1;try{c=null==a.frameElement&&y.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!m.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}I(),m.ready()}}()}return H.promise(b)};var K="undefined",L;for(L in m(k))break;k.ownLast="0"!==L,k.inlineBlockNeedsLayout=!1,m(function(){var a,b,c,d;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",k.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(d))}),function(){var a=y.createElement("div");if(null==k.deleteExpando){k.deleteExpando=!0;try{delete a.test}catch(b){k.deleteExpando=!1}}a=null}(),m.acceptData=function(a){var b=m.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var M=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,N=/([A-Z])/g;function O(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(N,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:M.test(c)?m.parseJSON(c):c}catch(e){}m.data(a,b,c)}else c=void 0}return c}function P(a){var b;for(b in a)if(("data"!==b||!m.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h; -if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthh;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML="
a",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function ab(){return!0}function bb(){return!1}function cb(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=m.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return typeof m===K||a&&m.event.triggered===a.type?void 0:m.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(E)||[""],h=b.length;while(h--)f=_.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=m.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=m.event.special[o]||{},l=m.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&m.expr.match.needsContext.test(e),namespace:p.join(".")},i),(n=g[o])||(n=g[o]=[],n.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?n.splice(n.delegateCount++,0,l):n.push(l),m.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m.hasData(a)&&m._data(a);if(r&&(k=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=_.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=m.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,n=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=n.length;while(f--)g=n[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(n.splice(f,1),g.selector&&n.delegateCount--,l.remove&&l.remove.call(a,g));i&&!n.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||m.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)m.event.remove(a,o+b[j],c,d,!0);m.isEmptyObject(k)&&(delete r.handle,m._removeData(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,n,o=[d||y],p=j.call(b,"type")?b.type:b,q=j.call(b,"namespace")?b.namespace.split("."):[];if(h=l=d=d||y,3!==d.nodeType&&8!==d.nodeType&&!$.test(p+m.event.triggered)&&(p.indexOf(".")>=0&&(q=p.split("."),p=q.shift(),q.sort()),g=p.indexOf(":")<0&&"on"+p,b=b[m.expando]?b:new m.Event(p,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=q.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:m.makeArray(c,[b]),k=m.event.special[p]||{},e||!k.trigger||k.trigger.apply(d,c)!==!1)){if(!e&&!k.noBubble&&!m.isWindow(d)){for(i=k.delegateType||p,$.test(i+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),l=h;l===(d.ownerDocument||y)&&o.push(l.defaultView||l.parentWindow||a)}n=0;while((h=o[n++])&&!b.isPropagationStopped())b.type=n>1?i:k.bindType||p,f=(m._data(h,"events")||{})[b.type]&&m._data(h,"handle"),f&&f.apply(h,c),f=g&&h[g],f&&f.apply&&m.acceptData(h)&&(b.result=f.apply(h,c),b.result===!1&&b.preventDefault());if(b.type=p,!e&&!b.isDefaultPrevented()&&(!k._default||k._default.apply(o.pop(),c)===!1)&&m.acceptData(d)&&g&&d[p]&&!m.isWindow(d)){l=d[g],l&&(d[g]=null),m.event.triggered=p;try{d[p]()}catch(r){}m.event.triggered=void 0,l&&(d[g]=l)}return b.result}},dispatch:function(a){a=m.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(m._data(this,"events")||{})[a.type]||[],k=m.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=m.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,g=0;while((e=f.handlers[g++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(e.namespace))&&(a.handleObj=e,a.data=e.data,c=((m.event.special[e.origType]||{}).handle||e.handler).apply(f.elem,i),void 0!==c&&(a.result=c)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(e=[],f=0;h>f;f++)d=b[f],c=d.selector+" ",void 0===e[c]&&(e[c]=d.needsContext?m(c,this).index(i)>=0:m.find(c,this,null,[i]).length),e[c]&&e.push(d);e.length&&g.push({elem:i,handlers:e})}return h]","i"),hb=/^\s+/,ib=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,jb=/<([\w:]+)/,kb=/\s*$/g,rb={option:[1,""],legend:[1,"
","
"],area:[1,"",""],param:[1,"",""],thead:[1,"","
"],tr:[2,"","
"],col:[2,"","
"],td:[3,"","
"],_default:k.htmlSerialize?[0,"",""]:[1,"X
","
"]},sb=db(y),tb=sb.appendChild(y.createElement("div"));rb.optgroup=rb.option,rb.tbody=rb.tfoot=rb.colgroup=rb.caption=rb.thead,rb.th=rb.td;function ub(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==K?a.getElementsByTagName(b||"*"):typeof a.querySelectorAll!==K?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||m.nodeName(d,b)?f.push(d):m.merge(f,ub(d,b));return void 0===b||b&&m.nodeName(a,b)?m.merge([a],f):f}function vb(a){W.test(a.type)&&(a.defaultChecked=a.checked)}function wb(a,b){return m.nodeName(a,"table")&&m.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function xb(a){return a.type=(null!==m.find.attr(a,"type"))+"/"+a.type,a}function yb(a){var b=pb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function zb(a,b){for(var c,d=0;null!=(c=a[d]);d++)m._data(c,"globalEval",!b||m._data(b[d],"globalEval"))}function Ab(a,b){if(1===b.nodeType&&m.hasData(a)){var c,d,e,f=m._data(a),g=m._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)m.event.add(b,c,h[c][d])}g.data&&(g.data=m.extend({},g.data))}}function Bb(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!k.noCloneEvent&&b[m.expando]){e=m._data(b);for(d in e.events)m.removeEvent(b,d,e.handle);b.removeAttribute(m.expando)}"script"===c&&b.text!==a.text?(xb(b).text=a.text,yb(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),k.html5Clone&&a.innerHTML&&!m.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&W.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}m.extend({clone:function(a,b,c){var d,e,f,g,h,i=m.contains(a.ownerDocument,a);if(k.html5Clone||m.isXMLDoc(a)||!gb.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(tb.innerHTML=a.outerHTML,tb.removeChild(f=tb.firstChild)),!(k.noCloneEvent&&k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||m.isXMLDoc(a)))for(d=ub(f),h=ub(a),g=0;null!=(e=h[g]);++g)d[g]&&Bb(e,d[g]);if(b)if(c)for(h=h||ub(a),d=d||ub(f),g=0;null!=(e=h[g]);g++)Ab(e,d[g]);else Ab(a,f);return d=ub(f,"script"),d.length>0&&zb(d,!i&&ub(a,"script")),d=h=e=null,f},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,l,n=a.length,o=db(b),p=[],q=0;n>q;q++)if(f=a[q],f||0===f)if("object"===m.type(f))m.merge(p,f.nodeType?[f]:f);else if(lb.test(f)){h=h||o.appendChild(b.createElement("div")),i=(jb.exec(f)||["",""])[1].toLowerCase(),l=rb[i]||rb._default,h.innerHTML=l[1]+f.replace(ib,"<$1>")+l[2],e=l[0];while(e--)h=h.lastChild;if(!k.leadingWhitespace&&hb.test(f)&&p.push(b.createTextNode(hb.exec(f)[0])),!k.tbody){f="table"!==i||kb.test(f)?""!==l[1]||kb.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)m.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}m.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),k.appendChecked||m.grep(ub(p,"input"),vb),q=0;while(f=p[q++])if((!d||-1===m.inArray(f,d))&&(g=m.contains(f.ownerDocument,f),h=ub(o.appendChild(f),"script"),g&&zb(h),c)){e=0;while(f=h[e++])ob.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=m.expando,j=m.cache,l=k.deleteExpando,n=m.event.special;null!=(d=a[h]);h++)if((b||m.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)n[e]?m.event.remove(d,e):m.removeEvent(d,e,g.handle);j[f]&&(delete j[f],l?delete d[i]:typeof d.removeAttribute!==K?d.removeAttribute(i):d[i]=null,c.push(f))}}}),m.fn.extend({text:function(a){return V(this,function(a){return void 0===a?m.text(this):this.empty().append((this[0]&&this[0].ownerDocument||y).createTextNode(a))},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?m.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||m.cleanData(ub(c)),c.parentNode&&(b&&m.contains(c.ownerDocument,c)&&zb(ub(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&m.cleanData(ub(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&m.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return m.clone(this,a,b)})},html:function(a){return V(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(fb,""):void 0;if(!("string"!=typeof a||mb.test(a)||!k.htmlSerialize&&gb.test(a)||!k.leadingWhitespace&&hb.test(a)||rb[(jb.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(ib,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(m.cleanData(ub(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,m.cleanData(ub(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,n=this,o=l-1,p=a[0],q=m.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&nb.test(p))return this.each(function(c){var d=n.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(i=m.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=m.map(ub(i,"script"),xb),f=g.length;l>j;j++)d=i,j!==o&&(d=m.clone(d,!0,!0),f&&m.merge(g,ub(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,m.map(g,yb),j=0;f>j;j++)d=g[j],ob.test(d.type||"")&&!m._data(d,"globalEval")&&m.contains(h,d)&&(d.src?m._evalUrl&&m._evalUrl(d.src):m.globalEval((d.text||d.textContent||d.innerHTML||"").replace(qb,"")));i=c=null}return this}}),m.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){m.fn[a]=function(a){for(var c,d=0,e=[],g=m(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),m(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Cb,Db={};function Eb(b,c){var d,e=m(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:m.css(e[0],"display");return e.detach(),f}function Fb(a){var b=y,c=Db[a];return c||(c=Eb(a,b),"none"!==c&&c||(Cb=(Cb||m("