[Bugfix] Modify _resolve_pytest_target to support glob patterns and return multiple paths (#1843)

Signed-off-by: yenuo26 <410167048@qq.com>
This commit is contained in:
wangyu authored and GitHub committed 2026-03-12 09:29:08 +00:00
1 parent 4dbaa74f2b
commit 1c7f963a2d
1 file changed
+21 -11
+21 -11
View File
@@ -147,21 +147,31 @@ def _parse_collect_only_stdout(stdout: str, *, raise_on_empty: bool = True, stde
return out
def _resolve_pytest_target(target: str, repo_root: Path, extra: list[str], raw_line: str) -> tuple[str, str, int]:
def _resolve_pytest_target(target: str, repo_root: Path, extra: list[str], raw_line: str) -> tuple[list[str], str, int]:
"""
Resolve target + repo_root + extra to (path_arg, fallback_path, timeout).
Resolve target + repo_root + extra to (path_args, fallback_path, timeout).
Raises FileNotFoundError/RuntimeError on invalid config.
"""
if target.endswith(".py"):
path = (repo_root / target).resolve()
# Target extracted from buildkite yaml may include glob patterns like
# tests/e2e/online_serving/test_*_expansion.py. Pytest does not reliably
# expand globs across platforms, so expand them here.
rel = Path(target)
if any(ch in target for ch in ["*", "?", "["]):
matches = sorted(repo_root.glob(rel.as_posix()))
if not matches:
raise FileNotFoundError(f"Pytest target glob did not match any files: {repo_root / target}")
return [str(p.resolve()) for p in matches], target.replace("\\", "/"), 90
path = (repo_root / rel).resolve()
if not path.exists():
raise FileNotFoundError(f"Pytest target path does not exist: {path}")
return str(path), target.replace("\\", "/"), 60
return [str(path)], target.replace("\\", "/"), 60
if not extra:
raise RuntimeError(f"Failed to parse -m/--run-level from pytest line: {raw_line!r}")
if not (repo_root / "tests").exists():
raise FileNotFoundError("tests/ directory not found under repo root")
return "tests/", "tests/", 120
return ["tests/"], "tests/", 120
def collect_test_names(target: str, repo_root: Path, raw_line: str) -> list[str]:
@@ -170,9 +180,9 @@ def collect_test_names(target: str, repo_root: Path, raw_line: str) -> list[str]
target: path like tests/foo.py or marker string (e.g. "-m expr"). raw_line used for -m/--run-level.
"""
extra = _parse_extra_args_from_line(raw_line)
path_arg, _fallback, timeout_quiet = _resolve_pytest_target(target, repo_root, extra, raw_line)
path_args, _fallback, timeout_quiet = _resolve_pytest_target(target, repo_root, extra, raw_line)
cmd_quiet = [sys.executable, "-m", "pytest", path_arg, "--collect-only", "-q"]
cmd_quiet = [sys.executable, "-m", "pytest", *path_args, "--collect-only", "-q"]
cmd_quiet.extend(extra)
result = subprocess.run(
cmd_quiet,
@@ -182,8 +192,8 @@ def collect_test_names(target: str, repo_root: Path, raw_line: str) -> list[str]
timeout=timeout_quiet,
)
if result.returncode != 0:
raise RuntimeError(f"pytest --collect-only failed for {path_arg} with args {extra}: {result.stderr.strip()}")
print(f"pytest --collect-only success for {path_arg} with args {extra}: {result.stdout.strip()}")
raise RuntimeError(f"pytest --collect-only failed for {path_args} with args {extra}: {result.stderr.strip()}")
print(f"pytest --collect-only success for {path_args} with args {extra}: {result.stdout.strip()}")
return _parse_collect_only_stdout(result.stdout or "", stderr=result.stderr or "")
@@ -214,12 +224,12 @@ def get_skip_status(target: str, raw_line: str, repo_root: Path) -> set[str]:
"""
extra = _parse_extra_args_from_line(raw_line)
try:
path_arg, _, timeout_quiet = _resolve_pytest_target(target, repo_root, extra, raw_line)
path_args, _, timeout_quiet = _resolve_pytest_target(target, repo_root, extra, raw_line)
except (FileNotFoundError, RuntimeError):
return set()
extra_skip = _extra_with_skip_marker(extra)
cmd = [sys.executable, "-m", "pytest", path_arg, "--collect-only", "-q"]
cmd = [sys.executable, "-m", "pytest", *path_args, "--collect-only", "-q"]
cmd.extend(extra_skip)
try:
result = subprocess.run(