Skip to content

With relative_files = true, I'm still getting absolute paths in .coverage due to $PWD-sensitivity #1674

Open
@jab

Description

@jab

(Same symptom as #1147 but different cause.)

I need to produce relative paths in the coverage data that coverage.py produces for smoother interoperability with various consumers of this data (e.g., the VSCode Coverage Gutters extension, Code Coverage for Bitbucket Server, etc.).

I have the constraint that coverage.py is invoked inside a temporary working directory (specifically, a bazel test sandbox) that does not contain the package to be measured. Rather, the package to be measured is installed in some virtualenv's site-packages directory.

I tried using coverage.py's path remapping with relative_files = true to produce relative paths in coverage.py's output, but because coverage.py's path remapping is sensitive to the current working directory, and the package to be measured is not inside the current working directory, this is not working.

It seems this could be fixed by allowing the base directory for source resolution to be explicitly configured (defaulting to $PWD if not provided), and/or adding support for an additional, final path remapping step that applies configured substitutions without first checking for file existence.

Reproduction steps:
~cd $(mktemp -d)

xpfjyxwx727_zrtwcrhyfyjm0000gn/T/tmp.QW8ElweRvim pyproject.toml

xpfjyxwx727_zrtwcrhyfyjm0000gn/T/tmp.QW8ElweRcat pyproject.toml
[tool.coverage.run]
source_pkgs = ["demo"]
relative_files = true

[tool.coverage.paths]
source = [
  "demo",
  "/**/demo",
]

xpfjyxwx727_zrtwcrhyfyjm0000gn/T/tmp.QW8ElweRmkdir demo

xpfjyxwx727_zrtwcrhyfyjm0000gn/T/tmp.QW8ElweRvim demo/__init__.py

xpfjyxwx727_zrtwcrhyfyjm0000gn/T/tmp.QW8ElweRcat demo/__init__.py
def foo():
    return 42

xpfjyxwx727_zrtwcrhyfyjm0000gn/T/tmp.QW8ElweRvim test_demo.py

xpfjyxwx727_zrtwcrhyfyjm0000gn/T/tmp.QW8ElweRcat test_demo.py
from unittest import TestCase, main

from demo import foo


class TestFoo(TestCase):
    def test_foo(self):
        self.assertEqual(foo(), 42)


if __name__ == "__main__":
    main()


xpfjyxwx727_zrtwcrhyfyjm0000gn/T/tmp.QW8ElweRpython3 -m venv .venv

xpfjyxwx727_zrtwcrhyfyjm0000gn/T/tmp.QW8ElweR.venv/bin/pip install -q coverage[toml]

xpfjyxwx727_zrtwcrhyfyjm0000gn/T/tmp.QW8ElweRset -gx SRC_DIR $(pwd)


xpfjyxwx727_zrtwcrhyfyjm0000gn/T/tmp.QW8ElweRcd $(mktemp -d)

xpfjyxwx727_zrtwcrhyfyjm0000gn/T/tmp.gkJlk9hM$SRC_DIR/.venv/bin/coverage run --rcfile=$SRC_DIR/pyproject.toml $SRC_DIR/test_demo.py
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

xpfjyxwx727_zrtwcrhyfyjm0000gn/T/tmp.gkJlk9hM$SRC_DIR/.venv/bin/coverage lcov --rcfile=$SRC_DIR/pyproject.toml --debug=pathmap  # no luck:
Aliases (relative=True):
 Rule: '/**/demo' -> 'demo/' using regex '[/\\\\](.*[/\\\\])?demo[/\\\\]'
Rule '/**/demo' changed '/private/var/folders/_t/xpfjyxwx727_zrtwcrhyfyjm0000gn/T/tmp.QW8ElweR/demo/__init__.py' to 'demo/__init__.py' which doesn't exist, continuing
No rules match, path '/private/var/folders/_t/xpfjyxwx727_zrtwcrhyfyjm0000gn/T/tmp.QW8ElweR/demo/__init__.py' is unchanged
Wrote LCOV report to coverage.lcov

xpfjyxwx727_zrtwcrhyfyjm0000gn/T/tmp.gkJlk9hMvim $SRC_DIR/pyproject.toml  # try to fix by making the first path in "paths" absolute, so coverage.py can resolve it independently of $PWD:

xpfjyxwx727_zrtwcrhyfyjm0000gn/T/tmp.gkJlk9hMcat $SRC_DIR/pyproject.toml
[tool.coverage.run]
source_pkgs = ["demo"]
relative_files = true

[tool.coverage.paths]
source = [
  # Make this first path absolute:
  "${SRC_DIR-.}/demo",
  "/**/demo",
]

xpfjyxwx727_zrtwcrhyfyjm0000gn/T/tmp.gkJlk9hM$SRC_DIR/.venv/bin/coverage lcov --rcfile=$SRC_DIR/pyproject.toml --debug=pathmap  # Coverage now resolves the path and applies the transformation, but the transformation now includes the absolute path, so we haven't gotten anywhere:
Aliases (relative=True):
 Rule: '/**/demo' -> '/var/folders/_t/xpfjyxwx727_zrtwcrhyfyjm0000gn/T/tmp.QW8ElweR/demo/' using regex '[/\\\\](.*[/\\\\])?demo[/\\\\]'
Matched path '/private/var/folders/_t/xpfjyxwx727_zrtwcrhyfyjm0000gn/T/tmp.QW8ElweR/demo/__init__.py' to rule '/**/demo' -> '/var/folders/_t/xpfjyxwx727_zrtwcrhyfyjm0000gn/T/tmp.QW8ElweR/demo/', producing '/var/folders/_t/xpfjyxwx727_zrtwcrhyfyjm0000gn/T/tmp.QW8ElweR/demo/__init__.py'
Wrote LCOV report to coverage.lcov

xpfjyxwx727_zrtwcrhyfyjm0000gn/T/tmp.gkJlk9hMcat coverage.lcov  #  paths are absolute:
TN:
SF:/var/folders/_t/xpfjyxwx727_zrtwcrhyfyjm0000gn/T/tmp.QW8ElweR/demo/__init__.py
DA:1,1,8W6LTH60TaV5ZrrD7DkxQQ
DA:2,1,KjVIXJCtSHJ6Jnfwt67A8Q
LF:2
LH:2
end_of_record

Perhaps some new setting like relative_to could complement relative_paths = true to enable this use case?

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestwontfixThis will not be worked on

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions