fix(tests): parallel runner reports a known flag's missing value as usage, not per file
The bare-flag check asked pytest's parser which tokens it does not know, but wrapped parse_known_args in the same blanket except that guards parser construction. A known flag with a missing value (`--tb` alone) raises pytest.UsageError there, which the except turned into "nothing unknown", so discovery ran and every per-file pytest died with "argument --tb: expected one argument". Keep the fallback around building the parser only; let parse_known_args run outside it and surface UsageError (and the unknown-token list) as this runner's own usage error before discovery. One invariant test.
This commit is contained in:
@@ -56,7 +56,7 @@ import threading
|
||||
import time
|
||||
from concurrent.futures import ThreadPoolExecutor, Future
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Tuple
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
|
||||
|
||||
# Default test discovery roots.
|
||||
@@ -824,8 +824,8 @@ def _make_stdio_glyph_safe() -> None:
|
||||
pass
|
||||
|
||||
|
||||
def _unknown_pytest_flags(tokens: List[str]) -> List[str]:
|
||||
"""Return the bare passthrough tokens pytest itself would reject.
|
||||
def _pytest_flag_error(tokens: List[str]) -> Optional[str]:
|
||||
"""Return pytest's own complaint about the bare passthrough tokens, if any.
|
||||
|
||||
A mistyped flag (``--jbs``) that is not one of OUR options used to be
|
||||
forwarded to every per-file pytest, so the run discovered the whole suite
|
||||
@@ -833,19 +833,27 @@ def _unknown_pytest_flags(tokens: List[str]) -> List[str]:
|
||||
learn about a typo. Ask pytest's own argparse parser (with the installed
|
||||
plugins loaded, so ``-n``/``--timeout`` count) which tokens it does not
|
||||
know; argparse handles the attached-value (``-rA``), combined-flag
|
||||
(``-xvs``) and ``-k expr`` forms for us. If the parser cannot be built
|
||||
the check is skipped and tokens are forwarded as before.
|
||||
(``-xvs``) and ``-k expr`` forms for us. A known flag with a bad or
|
||||
missing value (``--tb`` alone) makes that parser raise ``UsageError``;
|
||||
it is reported the same way instead of once per discovered file. Only
|
||||
if the parser cannot be built is the check skipped and tokens forwarded
|
||||
as before.
|
||||
"""
|
||||
try:
|
||||
from _pytest.config import get_config
|
||||
from _pytest.config import UsageError, get_config
|
||||
|
||||
config = get_config()
|
||||
config.pluginmanager.load_setuptools_entrypoints("pytest11")
|
||||
parser = config._parser.optparser
|
||||
_, unknown = parser.parse_known_args(tokens)
|
||||
except Exception:
|
||||
return []
|
||||
return [tok for tok in unknown if tok.startswith("-")]
|
||||
return None
|
||||
try:
|
||||
_, unknown = parser.parse_known_args(tokens)
|
||||
except UsageError as exc:
|
||||
# "usage: ...\n<prog>: error: argument --tb: expected one argument"
|
||||
return str(exc).rsplit("error: ", 1)[-1].strip()
|
||||
unknown = [tok for tok in unknown if tok.startswith("-")]
|
||||
return f"unrecognized arguments: {' '.join(unknown)}" if unknown else None
|
||||
|
||||
|
||||
def main() -> int:
|
||||
@@ -1008,9 +1016,9 @@ def main() -> int:
|
||||
# here with usage instead of once per discovered file. Anything after a
|
||||
# literal ``--`` is the caller's explicit choice and is forwarded as-is.
|
||||
if bare_passthrough:
|
||||
unknown = _unknown_pytest_flags(bare_passthrough)
|
||||
if unknown:
|
||||
parser.error(f"unrecognized arguments: {' '.join(unknown)}")
|
||||
flag_error = _pytest_flag_error(bare_passthrough)
|
||||
if flag_error:
|
||||
parser.error(flag_error)
|
||||
|
||||
# ── Node-id selectors → file + ``-k`` filter ────────────────────────────
|
||||
# This runner is FILE-granular: it spawns one ``pytest <file>`` per test
|
||||
|
||||
@@ -312,6 +312,20 @@ def test_unknown_bare_flag_errors_with_usage_instead_of_sweeping(tmp_path: Path)
|
||||
assert "2✓" in proc.stdout or "2 passed" in proc.stdout, proc.stdout
|
||||
|
||||
|
||||
def test_known_flag_missing_value_errors_with_usage_instead_of_sweeping(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""``--tb`` with no value is a pytest UsageError, not a per-file sweep.
|
||||
|
||||
The flag itself is known, so an unknown-token check alone lets it through;
|
||||
pytest's own parser must be allowed to reject it up front.
|
||||
"""
|
||||
probe_dir = _make_probe_dir(tmp_path)
|
||||
|
||||
proc = _run_runner(probe_dir, "--tb")
|
||||
assert proc.returncode == 2, proc.stdout
|
||||
assert "usage:" in proc.stdout and "--tb: expected one argument" in proc.stdout
|
||||
assert "Discovered" not in proc.stdout
|
||||
|
||||
|
||||
def test_bare_value_flag_keeps_its_value(tmp_path: Path) -> None:
|
||||
|
||||
Reference in New Issue
Block a user