FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

fix(xml): keep same-named files from different source roots (closes #1291) by Kayvan-Zahiri · Pull Request #2258 · coveragepy/coveragepy · GitHub

fix(xml): keep same-named files from different source roots (closes #1291) - #2258

Open
Kayvan-Zahiri wants to merge 2 commits into
coveragepy:mainfrom
Kayvan-Zahiri:fix/xml-duplicate-package-names
Open

fix(xml): keep same-named files from different source roots (closes #1291)#2258
Kayvan-Zahiri wants to merge 2 commits into
coveragepy:mainfrom
Kayvan-Zahiri:fix/xml-duplicate-package-names

Conversation

Copy link
Copy Markdown

Closes #1291.

The bug

coverage report and coverage xml disagree about which files exist. With two
source roots that each contain a package of the same name:

project/settings/__init__.py    3 statements
testing/settings/__init__.py    3 statements
.coveragerc:  [run]  source = project, testing/

coverage report lists both files, TOTAL 6 statements. coverage xml emits one
<class filename="settings/__init__.py"> holding 3 <line> elements. The other
file is absent.

The report is also internally inconsistent, which is the part that needs no
judgement call:

<coverage ... lines-valid="6" lines-covered="6">   <!-- says 6 -->

The document contains 3 <line> elements. Cobertura XML is what CI services read,
so a file silently vanishes from the artifact that gates merges while the totals
still count it.

Cause

xml_file() computes rel_name relative to whichever source root matched, then
stores the element as package.elements[rel_name]. Both files reduce to
settings/__init__.py, so the second replaces the first. The counters just below
it (package.lines, package.hits) are accumulated separately and still see both
files, which is exactly why the header and the body disagree.

Fix

Key on filename, the absolute path, which is unique per file. The <class filename=...> attribute still carries rel_name, so consumers resolve it against
the <sources> roots that are already emitted, and both roots are listed there.

Emitting two classes with the same relative name is not a new convention: as the
reporter notes, source = . already produces settings twice today.

Ordering is unchanged for the ordinary single-root case. The key is only used
for sorting, and with one root filename is source_path + "/" + rel_name, a
constant prefix, so the sort is identical. I checked this on a project with
pkg/zeta.py, pkg/alpha.py, pkg/mid.py and pkg/sub/deep.py, and the emitted
order is byte-for-byte the same before and after. Only the multi-root case
reorders, and that is the case that was broken.

Verification

  • The reproduction above now emits 2 classes and 6 <line> elements, and
    lines-valid matches.
  • tests/test_xml.py: 31 passed, 1 skipped, against 30 passed, 1 skipped on an
    unmodified checkout, the difference being the new test.
  • The new test fails on main with assert 1 == 2.

The regression test asserts both that two classes are emitted and that
lines-valid equals the number of <line> elements, since the second is the
clearest statement of the defect.

The XML writer keyed each package's class elements on the file's name relative
to its own source root. Two source roots can each contain a package with the
same name, and their files then share that relative name, so the second one
replaced the first in the dict.

The line counts were accumulated separately and still included both files, so
the report advertised lines-valid=6 while containing only 3 line elements.
coverage report listed both files the whole time.

Key on the absolute filename instead. The class filename attribute still holds
the relative name, which consumers resolve against the sources roots already
emitted, and single-root output is byte-identical since the absolute paths share
a prefix there.

Closes coveragepy#1291
ElementTree's get() returns str | None, so assert before int().
Kayvan-Zahiri force-pushed the fix/xml-duplicate-package-names branch from 3b0e1e3 to 14c8db5 Compare August 24, 2026 18:45
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Missing package with duplicate name in coverage-xml

1 participant


Back | FazBrowse Home | New Git URL