Contributing to coverage.py — Coverage.py 7.8.0 documentation (original) (raw)

I welcome contributions to coverage.py. Over the years, hundreds of people have provided contributions of various sizes to add features, fix bugs, or just help diagnose thorny issues. This page should have all the information you need to make a contribution.

One source of history or ideas are the bug reports against coverage.py. There you can find ideas for requested features, or the remains of rejected ideas.

Before you begin

If you have an idea for coverage.py, run it by me before you begin writing code. This way, I can get you going in the right direction, or point you to previous work in the area. Things are not always as straightforward as they seem, and having the benefit of lessons learned by those before you can save you frustration.

We have a #coverage channel in the Python Discord that can be a good place to explore ideas, get help, or help people with coverage.py.Join us!

Getting the code

The coverage.py code is hosted on a GitHub repository athttps://github.com/nedbat/coveragepy. To get a working environment, follow these steps:

  1. Fork the repo into your own GitHub account. The coverage.py code will then be copied into a GitHub repository athttps://github.com/GITHUB_USER/coveragepy where GITHUB_USER is your GitHub username.
  2. (Optional) Create a virtualenv to work in, and activate it. There are a number of ways to do this. Use the method you are comfortable with. Ideally, use Python 3.9 (the lowest version coverage.py supports).
  3. Clone the repository:
    $ git clone https://github.com/GITHUB_USER/coveragepy
    $ cd coveragepy
  4. Install the requirements with either of these commands:
    $ make install
    $ python3 -m pip install -r requirements/dev.pip
    Note: You may need to upgrade pip to install the requirements.

Running the tests

The tests are written mostly as standard unittest-style tests, and are run with pytest running under tox:

% python3 -m tox -e py39 py39: wheel-0.45.1-py3-none-any.whl already present in /Users/ned/Library/Application Support/virtualenv/wheel/3.9/embed/3/wheel.json py39: pip-25.0.1-py3-none-any.whl already present in /Users/ned/Library/Application Support/virtualenv/wheel/3.9/embed/3/pip.json py39: setuptools-78.1.0-py3-none-any.whl already present in /Users/ned/Library/Application Support/virtualenv/wheel/3.9/embed/3/setuptools.json py39: install_deps> python -m pip install -U -r requirements/pip.pip -r requirements/pytest.pip -r requirements/light-threads.pip .pkg: install_requires> python -I -m pip install setuptools .pkg: _optional_hooks> python /usr/local/virtualenvs/coverage/lib/python3.9/site-packages/pyproject_api/_backend.py True setuptools.build_meta .pkg: get_requires_for_build_editable> python /usr/local/virtualenvs/coverage/lib/python3.9/site-packages/pyproject_api/_backend.py True setuptools.build_meta .pkg: build_editable> python /usr/local/virtualenvs/coverage/lib/python3.9/site-packages/pyproject_api/_backend.py True setuptools.build_meta py39: install_package_deps> python -m pip install -U 'tomli; python_full_version <= "3.11.0a6"' py39: install_package> python -m pip install -U --force-reinstall --no-deps .tox/.tmp/package/1/coverage-7.8.1a0.dev1-0.editable-cp39-cp39-macosx_15_0_arm64.whl py39: commands[0]> python igor.py zip_mods py39: commands[1]> python igor.py remove_extension py39: commands[2]> python igor.py test_with_core pytrace === CPython 3.9.21 (gil) with Python tracer (.tox/py39/bin/python) === bringing up nodes... ....................................................................................... [ 5%] ..................................................................x................s... [ 11%] ......s...s.....s....s......s.s.s.s.................................................... [ 17%] ...........................................s..ss...ss.ss.ss............................ [ 23%] ....................................................................................... [ 29%] ....................................................................................... [ 35%] ....................................................................................... [ 41%] ................................................s...................................... [ 47%] ....................................................................................... [ 53%] ...................................................s..........s........................ [ 59%] ....................................................................................... [ 65%] ..........................ssss......................................................... [ 71%] ..s.....s.ss..........................ss...................s.s..sssssss.ssssss.sssss... [ 77%] .........ss..........................s...s.s......s........s........................s.. [ 83%] .............................s......................................................... [ 88%] ....................................................................................... [ 94%] .............................................s.......................ss.... [100%] 1403 passed, 63 skipped, 1 xfailed in 15.05s py39: commands[3]> python setup.py --quiet build_ext --inplace py39: commands[4]> python -m pip install -q -e . py39: commands[5]> python igor.py test_with_core ctrace === CPython 3.9.21 (gil) with C tracer (.tox/py39/bin/python) === bringing up nodes... ....................................................................................... [ 5%] ..........................................sx................................s.......... [ 11%] ..........ss........s....................................s............................. [ 17%] ..............................sss...................................................... [ 23%] ..............................................................s........................ [ 29%] ....................................................................................... [ 35%] ....................................................................................... [ 41%] ......................................................s................................ [ 47%] .............................................s......................................... [ 53%] .......s..................s............................................................ [ 59%] ....................................................................................s.. [ 65%] .......................................................ss.......................s...... [ 71%] ....................................................s............................ss.... [ 77%] ..........................s...................s........................................ [ 83%] ....................................................................................... [ 88%] ............................s......s................................................... [ 94%] .................................................................s......... [100%] 1440 passed, 26 skipped, 1 xfailed in 12.38s py39: OK (40.04=setup[9.03]+cmd[0.17,0.09,15.40,0.13,2.47,12.77] seconds) congratulations :) (40.61 seconds)

Tox runs the complete test suite a few times for each version of Python you have installed. The first run uses the C implementation of the trace function, the second uses the Python implementation. If sys.monitoring is available, the suite will be run again with that core.

To limit tox to just a few versions of Python, use the -e switch:

$ python3 -m tox -e py38,py39

On the tox command line, options after -- are passed to pytest. To run just a few tests, you can use pytest test selectors:

$ python3 -m tox -- tests/test_misc.py $ python3 -m tox -- tests/test_misc.py::HasherTest $ python3 -m tox -- tests/test_misc.py::HasherTest::test_string_hashing

These commands run the tests in one file, one class, and just one test, respectively. The pytest -k option selects tests based on a word in their name, which can be very convenient for ad-hoc test selection. Of course you can combine tox and pytest options:

% python3 -m tox -q -e py310 -- -n 0 -vv -k hash Skipping tests with Python tracer: Only one core: not running pytrace === CPython 3.10.16 (gil) with C tracer (.tox/py310/bin/python) === ===================================== test session starts ===================================== platform darwin -- Python 3.10.16, pytest-8.3.5, pluggy-1.5.0 -- /Users/ned/coverage/trunk/.tox/py310/bin/python cachedir: .tox/py310/.pytest_cache hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase(PosixPath('/Users/ned/coverage/trunk/.hypothesis/examples')) rootdir: /Users/ned/coverage/trunk configfile: pyproject.toml plugins: flaky-3.8.1, hypothesis-6.128.1, xdist-3.6.1 collected 1467 items / 1457 deselected / 10 selected run-last-failure: no previously failed tests, not deselecting items.

tests/test_data.py::CoverageDataTest::test_add_to_hash_with_lines PASSED [ 10%] tests/test_data.py::CoverageDataTest::test_add_to_hash_with_arcs PASSED [ 20%] tests/test_data.py::CoverageDataTest::test_add_to_lines_hash_with_missing_file PASSED [ 30%] tests/test_data.py::CoverageDataTest::test_add_to_arcs_hash_with_missing_file PASSED [ 40%] tests/test_execfile.py::RunPycFileTest::test_running_hashed_pyc PASSED [ 50%] tests/test_misc.py::HasherTest::test_string_hashing PASSED [ 60%] tests/test_misc.py::HasherTest::test_bytes_hashing PASSED [ 70%] tests/test_misc.py::HasherTest::test_unicode_hashing PASSED [ 80%] tests/test_misc.py::HasherTest::test_dict_hashing PASSED [ 90%] tests/test_misc.py::HasherTest::test_dict_collision PASSED [100%]

============================= 10 passed, 1457 deselected in 3.13s ============================= py310: OK (16.62 seconds) congratulations :) (16.97 seconds)

You can also affect the test runs with environment variables:

There are other environment variables that affect tests. I use set_env.pyas a simple terminal interface to see and set them.

Of course, run all the tests on every version of Python you have before submitting a change.

Lint, etc

I try to keep the coverage.py source as clean as possible. I use pylint to alert me to possible problems:

The source is pylint-clean, even if it’s because there are pragmas quieting some warnings. Please try to keep it that way, but don’t let pylint warnings keep you from sending patches. I can clean them up.

Lines should be kept to a 100-character maximum length. I recommend aneditorconfig.org plugin for your editor of choice, which will also help with indentation, line endings and so on.

Other style questions are best answered by looking at the existing code. Formatting of docstrings, comments, long lines, and so on, should match the code that already exists.

Many people love auto-formatting with black or ruff, but I would prefer not to on coverage.py.

Continuous integration

When you make a pull request, GitHub actions will run all of the tests and quality checks on your changes. If any fail, either fix them or ask for help.

Dependencies

Coverage.py has no direct runtime dependencies, and I would like to keep it that way.

It has many development dependencies. These are specified generically in therequirements/*.in files. The .in files should have no versions specified in them. The specific versions to use are pinned in requirements/*.pipfiles. These are created by running make upgrade.

It’s important to use Python 3.9 to run make upgrade so that the pinned versions will work on all of the Python versions currently supported by coverage.py.

If for some reason we need to constrain a version of a dependency, the constraint should be specified in the requirements/pins.pip file, with a detailed reason for the pin.

Coverage testing coverage.py

Coverage.py can measure itself, but it’s complicated. The process has been packaged up to make it easier:

Then look at htmlcov/index.html. Note that due to the recursive nature of coverage.py measuring itself, there are some parts of the code that will never appear as covered, even though they are executed.

Contributing

When you are ready to contribute a change, any way you can get it to me is probably fine. A pull request on GitHub is great, but a simple diff or patch works too.

All contributions are expected to include tests for new functionality and fixes. If you need help writing tests, please ask.