Skip to content

Commit abeda08

Browse files
committed
fix: some file path settings didn't do tilde expansion
1 parent b0dcf84 commit abeda08

File tree

4 files changed

+47
-31
lines changed

4 files changed

+47
-31
lines changed

CHANGES.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ Unreleased
3939
":ref:`config_report_exclude_also`" setting for adding line exclusion
4040
patterns.
4141

42+
- A few file path configuration settings didn't allow for tilde expansion:
43+
:ref:`config_json_output`, :ref:`config_lcov_output` and
44+
:ref:`config_run_debug_file`. This is now fixed.
45+
46+
4247
.. _issue 310: https://github.com/nedbat/coveragepy/issues/310
4348
.. _issue 312: https://github.com/nedbat/coveragepy/issues/312
4449
.. _issue 831: https://github.com/nedbat/coveragepy/issues/831

coverage/config.py

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,11 @@ def get(self, section: str, option: str, *args: Any, **kwargs: Any) -> str: # ty
104104
v = substitute_variables(v, os.environ)
105105
return v
106106

107+
def getfile(self, section: str, option: str) -> str:
108+
"""Fix up a file path setting."""
109+
path = self.get(section, option)
110+
return process_file_value(path)
111+
107112
def getlist(self, section: str, option: str) -> list[str]:
108113
"""Read a list of strings.
109114
@@ -376,9 +381,9 @@ def copy(self) -> CoverageConfig:
376381
("context", "run:context"),
377382
("core", "run:core"),
378383
("cover_pylib", "run:cover_pylib", "boolean"),
379-
("data_file", "run:data_file"),
384+
("data_file", "run:data_file", "file"),
380385
("debug", "run:debug", "list"),
381-
("debug_file", "run:debug_file"),
386+
("debug_file", "run:debug_file", "file"),
382387
("disable_warnings", "run:disable_warnings", "list"),
383388
("dynamic_context", "run:dynamic_context"),
384389
("parallel", "run:parallel", "boolean"),
@@ -415,23 +420,23 @@ def copy(self) -> CoverageConfig:
415420

416421
# [html]
417422
("extra_css", "html:extra_css"),
418-
("html_dir", "html:directory"),
423+
("html_dir", "html:directory", "file"),
419424
("html_skip_covered", "html:skip_covered", "boolean"),
420425
("html_skip_empty", "html:skip_empty", "boolean"),
421426
("html_title", "html:title"),
422427
("show_contexts", "html:show_contexts", "boolean"),
423428

424429
# [xml]
425-
("xml_output", "xml:output"),
430+
("xml_output", "xml:output", "file"),
426431
("xml_package_depth", "xml:package_depth", "int"),
427432

428433
# [json]
429-
("json_output", "json:output"),
434+
("json_output", "json:output", "file"),
430435
("json_pretty_print", "json:pretty_print", "boolean"),
431436
("json_show_contexts", "json:show_contexts", "boolean"),
432437

433438
# [lcov]
434-
("lcov_output", "lcov:output"),
439+
("lcov_output", "lcov:output", "file"),
435440
("lcov_line_checksums", "lcov:line_checksums", "boolean")
436441
]
437442

@@ -523,17 +528,10 @@ def get_option(self, option_name: str) -> TConfigValueOut | None:
523528
# If we get here, we didn't find the option.
524529
raise ConfigError(f"No such option: {option_name!r}")
525530

526-
def post_process_file(self, path: str) -> str:
527-
"""Make final adjustments to a file path to make it usable."""
528-
return os.path.expanduser(path)
529-
530531
def post_process(self) -> None:
531532
"""Make final adjustments to settings to make them usable."""
532-
self.data_file = self.post_process_file(self.data_file)
533-
self.html_dir = self.post_process_file(self.html_dir)
534-
self.xml_output = self.post_process_file(self.xml_output)
535533
self.paths = {
536-
k: [self.post_process_file(f) for f in v]
534+
k: [process_file_value(f) for f in v]
537535
for k, v in self.paths.items()
538536
}
539537
self.exclude_list += self.exclude_also
@@ -546,6 +544,11 @@ def debug_info(self) -> list[tuple[str, Any]]:
546544
)
547545

548546

547+
def process_file_value(path: str) -> str:
548+
"""Make adjustments to a file path to make it usable."""
549+
return os.path.expanduser(path)
550+
551+
549552
def process_regexlist(name: str, option: str, values: list[str]) -> list[str]:
550553
"""Check the values in a regex list and keep the non-blank ones."""
551554
value_list = []

coverage/tomlconfig.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,10 @@ def getboolean(self, section: str, option: str) -> bool:
178178
bool_strings = {"true": True, "false": False}
179179
return self._check_type(name, option, value, bool, bool_strings.__getitem__, "a boolean")
180180

181+
def getfile(self, section: str, option: str) -> str:
182+
_, value = self._get_single(section, option)
183+
return config.process_file_value(value)
184+
181185
def _get_list(self, section: str, option: str) -> tuple[str, list[str]]:
182186
"""Get a list of strings, substituting environment variables in the elements."""
183187
name, values = self._get(section, option)

tests/test_config.py

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,12 @@ def test_tilde_in_config(self) -> None:
307307
[html]
308308
directory = ~joe/html_dir
309309
310+
[json]
311+
output = ~/json/output.json
312+
313+
[lcov]
314+
output = ~/lcov/~foo.lcov
315+
310316
[xml]
311317
output = ~/somewhere/xml.out
312318
@@ -321,23 +327,8 @@ def test_tilde_in_config(self) -> None:
321327
~/src
322328
~joe/source
323329
""")
324-
def expanduser(s: str) -> str:
325-
"""Fake tilde expansion"""
326-
s = s.replace("~/", "/Users/me/")
327-
s = s.replace("~joe/", "/Users/joe/")
328-
return s
329330

330-
with mock.patch.object(
331-
coverage.config.os.path, # type: ignore[attr-defined]
332-
'expanduser',
333-
new=expanduser
334-
):
335-
cov = coverage.Coverage()
336-
assert cov.config.data_file == "/Users/me/data.file"
337-
assert cov.config.html_dir == "/Users/joe/html_dir"
338-
assert cov.config.xml_output == "/Users/me/somewhere/xml.out"
339-
assert cov.config.exclude_list == ["~/data.file", "~joe/html_dir"]
340-
assert cov.config.paths == {'mapping': ['/Users/me/src', '/Users/joe/source']}
331+
self.assert_tilde_results()
341332

342333
def test_tilde_in_toml_config(self) -> None:
343334
# Config entries that are file paths can be tilde-expanded.
@@ -348,6 +339,12 @@ def test_tilde_in_toml_config(self) -> None:
348339
[tool.coverage.html]
349340
directory = "~joe/html_dir"
350341
342+
[tool.coverage.json]
343+
output = "~/json/output.json"
344+
345+
[tool.coverage.lcov]
346+
output = "~/lcov/~foo.lcov"
347+
351348
[tool.coverage.xml]
352349
output = "~/somewhere/xml.out"
353350
@@ -364,20 +361,27 @@ def test_tilde_in_toml_config(self) -> None:
364361
"~joe/source",
365362
]
366363
""")
364+
365+
self.assert_tilde_results()
366+
367+
def assert_tilde_results(self) -> None:
368+
"""Common assertions for two tilde tests."""
367369
def expanduser(s: str) -> str:
368370
"""Fake tilde expansion"""
369371
s = s.replace("~/", "/Users/me/")
370372
s = s.replace("~joe/", "/Users/joe/")
371373
return s
372374

373375
with mock.patch.object(
374-
coverage.config.os.path, # type: ignore[attr-defined]
376+
coverage.config.os.path, # type: ignore[attr-defined]
375377
'expanduser',
376378
new=expanduser
377379
):
378380
cov = coverage.Coverage()
379381
assert cov.config.data_file == "/Users/me/data.file"
380382
assert cov.config.html_dir == "/Users/joe/html_dir"
383+
assert cov.config.json_output == "/Users/me/json/output.json"
384+
assert cov.config.lcov_output == "/Users/me/lcov/~foo.lcov"
381385
assert cov.config.xml_output == "/Users/me/somewhere/xml.out"
382386
assert cov.config.exclude_list == ["~/data.file", "~joe/html_dir"]
383387
assert cov.config.paths == {'mapping': ['/Users/me/src', '/Users/joe/source']}

0 commit comments

Comments
 (0)