Python match (PEP634) "or wildcard" pattern marked as missing branch · Issue #1421 · nedbat/coveragepy (original) (raw)
Describe the bug
Two equivalent function using the match/case construct with two different ways to use wildcard pattern are reported differently, with the "or wildcard" case marked as missing branch.
To Reproduce
Here is a minimal reproducible example, note testing is done pairwise to verify the functions as equivalent.
import os
import pytest
def wildcard_alone() -> str:
match os.getenv("SOME_VAR", "default"):
case "dev":
return "development value"
case "test" | "prod":
return "production value"
case "default":
return "default value"
case _:
return "default value"
def wildcard_or() -> str:
match os.getenv("SOME_VAR", "default"):
case "dev":
return "development value"
case "test" | "prod":
return "production value"
case "default" | _: # marked as missing
return "default value"
@pytest.mark.parametrize("f", [wildcard_or, wildcard_alone]) def test_match_env_dev(f, monkeypatch): monkeypatch.setenv("SOME_VAR", "dev") assert f() == "development value"
@pytest.mark.parametrize("f", [wildcard_or, wildcard_alone]) def test_match_env_test(f, monkeypatch): monkeypatch.setenv("SOME_VAR", "test") assert f() == "production value"
@pytest.mark.parametrize("f", [wildcard_or, wildcard_alone]) def test_match_env_prod(f, monkeypatch): monkeypatch.setenv("SOME_VAR", "prod") assert f() == "production value"
@pytest.mark.parametrize("f", [wildcard_or, wildcard_alone]) def test_match_env_unset(f, monkeypatch): monkeypatch.delenv("SOME_VAR", raising=False) assert f() == "default value"
@pytest.mark.parametrize("f", [wildcard_or, wildcard_alone]) def test_match_env_other(f, monkeypatch): monkeypatch.setenv("SOME_VAR", "unmatched") assert f() == "default value"
Here is the coverage.
% coverage run --branch -m pytest && coverage report -m
========================================================================================= test session starts =========================================================================================
platform darwin -- Python 3.10.5, pytest-7.1.2, pluggy-1.0.0
rootdir: /private/tmp/mre-coverage-case
collected 10 items
test_mre.py .......... [100%]
========================================================================================= 10 passed in 0.05s ==========================================================================================
Name Stmts Miss Branch BrPart Cover Missing
---------------------------------------------------------
test_mre.py 40 0 12 1 98% 26->exit
---------------------------------------------------------
TOTAL 40 0 12 1 98%
And finally, the output of coverage debug sys
.
-- sys -------------------------------------------------------
coverage_version: 6.4.2
coverage_module: /private/tmp/mre-coverage-case/.venv/lib/python3.10/site-packages/coverage/__init__.py
tracer: -none-
CTracer: available
plugins.file_tracers: -none-
plugins.configurers: -none-
plugins.context_switchers: -none-
configs_attempted: .coveragerc
setup.cfg
tox.ini
pyproject.toml
configs_read: -none-
config_file: None
config_contents: -none-
data_file: -none-
python: 3.10.5 (main, Jul 6 2022, 15🔞41) [Clang 13.1.6 (clang-1316.0.21.2.3)]
platform: macOS-12.2.1-x86_64-i386-64bit
implementation: CPython
executable: /private/tmp/mre-coverage-case/.venv/bin/python
def_encoding: utf-8
fs_encoding: utf-8
pid: 44594
cwd: /private/tmp/mre-coverage-case
path: /private/tmp/mre-coverage-case/.venv/bin
/Users/redacted/.pyenv/versions/3.10.5/lib/python310.zip
/Users/redacted/.pyenv/versions/3.10.5/lib/python3.10
/Users/redacted/.pyenv/versions/3.10.5/lib/python3.10/lib-dynload
/private/tmp/mre-coverage-case/.venv/lib/python3.10/site-packages
environment: HOME = /Users/redacted
PYENV_DIR = /tmp/mre-coverage-case
PYENV_HOOK_PATH = /Users/redacted/.pyenv/pyenv.d:/usr/local/Cellar/pyenv/2.3.2/pyenv.d:/usr/local/etc/pyenv.d:/etc/pyenv.d:/usr/lib/pyenv/hooks
PYENV_ROOT = /Users/redacted/.pyenv
PYENV_SHELL = zsh
PYENV_VERSION = 3.10.5
command_line: /private/tmp/mre-coverage-case/.venv/bin/coverage debug sys
sqlite3_version: 2.6.0
sqlite3_sqlite_version: 3.36.0
sqlite3_temp_store: 0
sqlite3_compile_options: BUG_COMPATIBLE_20160819, COMPILER=clang-13.0.0, DEFAULT_CACHE_SIZE=2000,
DEFAULT_CKPTFULLFSYNC, DEFAULT_JOURNAL_SIZE_LIMIT=32768,
DEFAULT_MEMSTATUS=0, DEFAULT_PAGE_SIZE=4096, DEFAULT_SYNCHRONOUS=2,
DEFAULT_WAL_SYNCHRONOUS=1, ENABLE_API_ARMOR, ENABLE_BYTECODE_VTAB,
ENABLE_COLUMN_METADATA, ENABLE_DBSTAT_VTAB, ENABLE_FTS3,
ENABLE_FTS3_PARENTHESIS, ENABLE_FTS3_TOKENIZER, ENABLE_FTS4, ENABLE_FTS5,
ENABLE_JSON1, ENABLE_LOCKING_STYLE=1, ENABLE_NORMALIZE,
ENABLE_PREUPDATE_HOOK, ENABLE_RTREE, ENABLE_SESSION, ENABLE_SNAPSHOT,
ENABLE_SQLLOG, ENABLE_STMT_SCANSTATUS, ENABLE_UNKNOWN_SQL_FUNCTION,
ENABLE_UPDATE_DELETE_LIMIT, HAS_CODEC_RESTRICTED, HAVE_ISNAN,
MAX_LENGTH=2147483645, MAX_MMAP_SIZE=1073741824,
MAX_VARIABLE_NUMBER=500000, OMIT_AUTORESET, OMIT_LOAD_EXTENSION,
STMTJRNL_SPILL=131072, SYSTEM_MALLOC, THREADSAFE=2, USE_URI
Expected behavior
All branches are covered and both function should be at 100% coverage.
Additional context
NA