question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Using pytest-cov for coverage results in top-level package name of "." when using coverage.py doesn't

See original GitHub issue

First, versions:

$ python --version
Python 2.7.11
$ pip freeze|grep coverage
coverage==4.4.1
$ pip freeze|grep pytest-cov
pytest-cov==2.5.1

Happens on Ubuntu 12.04, Ubuntu 16.04, and Mac OS X El Capitan 10.11.6. Can’t test anything else, but seems safe to say it would be the same.

I have a project laid out like this:

proj/ex_secure/__init__.py
proj/ex_secure/base.py
proj/ex_secure/metrics.py
proj/ex_secure/keys.py
proj/ex_secure/utils.py
proj/tests/test_base.py
proj/tests/test_metrics.py
proj/tests/test_keys.py
proj/.gitignore
proj/.pep8
proj/README.rst
proj/setup.cfg
proj/setup.py

All the example commands below I run from within the proj directory.

First, I run coverage directly:

$ coverage run --branch --source=ex_secure -m pytest -s --junitxml=pytests.xml
... output that doesn't matter ...
$ coverage xml
$ coverage report
Name                              Stmts   Miss Branch BrPart  Cover
-------------------------------------------------------------------
ex_secure/__init__.py                 1      0      0      0   100%
ex_secure/base.py                   274     55     74     12    74%
ex_secure/keys.py                    31      6      6      0    78%
ex_secure/metrics.py                 10      2      4      0    57%
ex_secure/util.py                   238    238     98      0     0%

And the top package of the XML file looks like so:

<package branch-rate="0.3058" complexity="0" line-rate="0.4769" name="ex_secure">

Next, I run pytest with the pytest-cov plugin:

$ pytest -s --junitxml=pytests.xml --cov-report xml --cov-report term --cov-branch --cov=ex_secure
... output that doesn't matter ...
---------- coverage: platform darwin, python 2.7.11-final-0 ----------
Name                              Stmts   Miss Branch BrPart  Cover
-------------------------------------------------------------------
ex_secure/__init__.py                 1      0      0      0   100%
ex_secure/base.py                   274     55     74     12    74%
ex_secure/keys.py                    31      6      6      0    78%
ex_secure/metrics.py                 10      2      4      0    57%
ex_secure/util.py                   238    238     98      0     0%

And the top package of the XML file looks like so:

<package branch-rate="0.3058" complexity="0" line-rate="0.4769" name=".">

Notice the name of the package is now . instead of ex_secure. However, if I then run coverage xml to re-generate the report:

$ coverage xml

The XML file looks right again:

<package branch-rate="0.3058" complexity="0" line-rate="0.4769" name="ex_secure">

So I tried running pytest a different way:

$ pytest -s --junitxml=pytests.xml --cov-report xml --cov-report term --cov-branch --cov=ex_secure.base --cov=ex_secure.keys --cov=ex_secure.metrics --cov=ex_secure.util
... output that doesn't matter ...
---------- coverage: platform darwin, python 2.7.11-final-0 ----------
Name                              Stmts   Miss Branch BrPart  Cover
-------------------------------------------------------------------
ex_secure/base.py                   274     55     74     12    74%
ex_secure/keys.py                    31      6      6      0    78%
ex_secure/metrics.py                 10      2      4      0    57%

Now the XML also looks right:

<package branch-rate="0.4722" complexity="0" line-rate="0.6766" name="ex_secure">

But this has several downsides:

  • I have to enumerate everything, and I miss coverage info for new files added if they are not enumerated.
  • Can’t cover __init__.py
  • utils.py is not in the report anymore because none of its lines are covered, resulting in incorrectly high coverage results
  • Branch rate and line rate are now different (because of missing __init__.py and utils.py

Issue Analytics

  • State:open
  • Created 6 years ago
  • Reactions:11
  • Comments:21 (4 by maintainers)

github_iconTop GitHub Comments

3reactions
tonnydouradocommented, Dec 29, 2017

Just to add, I’m having the same problem. We have a slightly weird package layout, with a main package and an extra module in the same level, and pytest-cov does the same name='.' thing, which breaks our Sonar integration.

3reactions
beamerblvdcommented, Oct 27, 2017

I did some debugging and I might have an answer:

This is the XML report generation using pytest-cov:

# In coverage/control.py xml_report()
ipdb> self.source_match
<TreeMatcher [u'/Users/nwilliams/proj/ex_secure']>
# In coverage/xmlreport.py report()
ipdb> morfs, outfile
(None, <open file 'coverage.xml', mode 'w' at 0x10cdd3e40>)
ipdb> self.packages
{}
# In coverage/xmlreport.py xml_file()
ipdb> filename
u'/Users/nwilliams/proj/ex_secure/__init__.py'
ipdb> self.source_paths
set([u'/Users/nwilliams/proj/ex_secure'])
ipdb> dirname
u'.'
ipdb> package_name
u'.'
# Back in coverage/xmlreport.py report()
ipdb> self.packages
{'.': [{}, 0, 0, 0, 0]}

This is the XML report generation using coverage xml immediately after running pytest with pytest-cov:

# In coverage/control.py xml_report()
ipdb> self.source_match
None
# In coverage/xmlreport.py report()
ipdb> morfs, outfile
([], <open file 'coverage.xml', mode 'w' at 0x10537e5d0>)
ipdb> self.packages
{}
# In coverage/xmlreport.py xml_file()
ipdb> filename
u'/Users/nwilliams/proj/ex_secure/__init__.py'
ipdb> self.source_paths
set([])
ipdb> dirname
u'ex_secure'
ipdb> package_name
u'ex_secure'
# Back in coverage/xmlreport.py report()
ipdb> self.packages
{u'ex_secure': [{}, 0, 0, 0, 0]}

Upon further examination of the code, coverage xml does not allow the --sources argument and has no way to pass sources into xml_report. That’s why it works. On the other hand, pytest-cov is creating a single Coverage object and setting its sources, which get passed on to the xml_report, and so it strips that from all the file names before figuring out their package names.

So one could argue Coverage should be a little smarter here, but also pytest-cov is not calling xml_report the same way that coverage xml would, and that seems like a legit bug in pytest-cov to me.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Python Coverage turns top-level package name into period
It is described in detail on the PyTest-Cov GitHub and the Py-Coverage BitBucket. In short, calling coverage xml from the command line doesn't...
Read more >
Release 7.0.0b1 unknown - Coverage.py
Coverage.py is a tool for measuring code coverage of Python programs. It monitors your program, noting which parts.
Read more >
Testing Python in Visual Studio Code
The Python extension supports testing with Python's built-in unittest ... Note If you have the pytest-cov coverage module installed, VS Code doesn't stop...
Read more >
Effective Python Testing With Pytest
If you want to measure how well your tests cover your implementation code, then you can use the coverage package. pytest-cov integrates ...
Read more >
Good Integration Practices — pytest documentation
Install package with pip¶ ... where PACKAGENAME is the name of your package. ... Within Python modules, pytest also discovers tests using the...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found