Python coverage with sources set to subdirectory results in error and 0% coverage

Using:

  • Community Edition
  • Version 10.1 (build 73491)

I am trying to follow the config instructions here: Python test coverage

My project layout looks similar to this:

.
├── sonar-project.properties
├── src
│   ├── file1.py
│   └── file2.py
├── tests
└── tox.ini

My .coveragerc file looks like this:

[run]
relative_files = True
source = src/

However, this produces coverage.xml reports that look similar to this:

<?xml version="1.0" ?>
<coverage version="7.6.3" timestamp="1729294473688" lines-valid="2426" lines-covered="2204" line-rate="0.9085" branches-covered="0" branches-valid="0" branch-rate="0" complexity="0">
	<!-- Generated by coverage.py: https://coverage.readthedocs.io/en/7.6.3 -->
	<!-- Based on https://raw.githubusercontent.com/cobertura/web/master/htdocs/xml/coverage-04.dtd -->
	<sources>
		<source>src</source>
	</sources>
	<packages>
		<package name="." line-rate="0.9006" branch-rate="0" complexity="0">
			<classes>
				<class name="file1.py" filename="file1.py" complexity="0" line-rate="0.973" branch-rate="0">
...

Other applications can read this XML just fine but in Sonar I get an error:

The following error(s) occurred while trying to import coverage report:
Invalid directory path in 'source' element: src%nCannot resolve the file path 'clients.py' of the coverage report, the file does not exist in all 'source'.%nCannot resolve 15 file paths, ignoring coverage measures for those files

Not sure what the error is saying since src is the proper path to the sources, e.g. the first file to analyze would be src/file1.py.

Any assistance here is appreciated.

Hi,

Welcome to the community!

Can you share the contents of your sonar-project.properties file, redacted as necessary? I’m particularly interested in your sonar.sources definition.

 
Thx,
Ann

Sure - here it is:

sonar.python.coverage.reportPaths=test-reports/coverage/coverage.xml
sonar.python.version=3.11
sonar.python.xunit.reportPath=test-reports/unit/junit.xml
sonar.python.xunit.skipDetails=true
sonar.sources=src
sonar.test.inclusions=src/test/**
sonar.tests=src/test

Hi,

Okay, nothing super-obvious is jumping out at me here. And yet…

First, there’s no need for the double-specification of sonar.test.inclusions and sonar.tests when they describe the same set. And since the test files are a subset of sonar.sources, they should be excluded. So, can you adjust that to this:

sonar.sources=src
sonar.exclusions=src/test/**/*
sonar.tests=src/test

And then give us the full analysis log, please?

 
Thx,
Ann

Hi Ann,

I made the above changes - one minor note, the directory layout is actually with tests under src which matches the sonar config more accurately:

.
├── sonar-project.properties
├── src
│   ├── file1.py
│   ├── file2.py
│   └── tests
└── tox.ini

I am still getting the same error, see below:

sonar-log.md.log (6.7 KB)

Specifically this seems to be the issue that gives me a 0% coverage report in Sonar:

INFO: Parsing report '/github/workspace/main-repo/test-reports/coverage/coverage.xml'
WARN: Invalid directory path in 'source' element: src
ERROR: Cannot resolve the file path 'clients.py' of the coverage report, the file does not exist in all 'source'.
ERROR: Cannot resolve 17 file paths, ignoring coverage measures for those files

Here is the full coverage.xml:

coverage 7.xml.log (56.4 KB)

If I had to guess it seems like it doesn’t like the . in the package in the XML:

	<sources>
		<source>src</source>
	</sources>
        <packages>
		<package name="." line-rate="0.6779" branch-rate="0" complexity="0">

I am not sure how you wouldn’t have that when setting the source attribute in the coverage XML config.

Please let me know if you need more information.

Thanks,
Paul

Hi Paul,

Thanks for the log. As you know, you had already excerpted the relevant pieces, but having the whole thing helps me make sure I haven’t missed anything,

So… Let’s go back to that error:

WARN: Invalid directory path in 'source' element: src
ERROR: Cannot resolve the file path 'clients.py' of the coverage report, the file does not exist in all 'source'.
ERROR: Cannot resolve 17 file paths, ignoring coverage measures for those files

And to the report

	<sources>
		<source>src</source>
	</sources>
	<packages>
		<package name="." line-rate="0.6779" branch-rate="0" complexity="0">
			<classes>
				<class name="clients.py" filename="clients.py" complexity="0" line-rate="0.625" branch-rate="0">

I’m reading this excerpt to mean

clients.py is located in the src directory.

I think that’s how analysis is reading it to. But I think analysis is already in the src directory because that’s where sonar.sources is set to. So the structure that’s expected ends up being

Which, of course, doesn’t exist.

Can you try this without that <source>src</source> element?

 
Ann

Hi Ann,

I will have to see what commands I can use in generating the coverage report to make a file that matches what you’re describing above.

What’s interesting is that if I use include instead of source in the .coveragerc, e.g:

[run]
relative_files = True
include = src/*

I get a coverage.xml that looks like this:

	<sources>
		<source></source>
	</sources>
	<packages>
		<package name="src" line-rate="0.7341" branch-rate="0" complexity="0">
			<classes>
				<class name="clients.py" filename="src/clients.py" complexity="0" line-rate="0.625" branch-rate="0">
...

Sonar is perfectly happy with this and I read this as meaning ./src/clients.py - unless sonar is just ignoring the package name?

Also, using the include directive instead of the source directive means that files that are not analyzed but that are still source files don’t show up in the coverage report - from the documentation: Specifying source files — Coverage.py 7.6.4 documentation

If the source option is specified, only code in those locations will be measured. Specifying the source option also enables coverage.py to report on un-executed files, since it can search the source tree for files that haven’t been measured at all.

While Sonar does pick up unexecuted files in its final reporting, it makes it harder to harmonize the number in the report from the number in Sonar post-build (it also doesn’t reflect the Sonar documentation on how to set up coverage).

Hi Paul,

I really couldn’t tell you. That would be my guess.

Sorry, I’m not following this.

Is that important? (This is an actual question. I’m not being a smartass.)

Could you point me to what’s not matching?

 
Thx,
Ann