diff --git a/CHANGELOG.md b/CHANGELOG.md index e85c30c90..f16280684 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Set the unit-test hermetic HOME at conftest import time so a single xdist worker on the `windows-2025-vs2026` runner can no longer race fixture setup and re-trigger the 53 `Path.home()` failures the session-scoped autouse fixture was supposed to prevent. (#1271) - Override `Path.home()` itself in the root test conftest so the 46 remaining Windows `RuntimeError: Could not determine home directory` failures on xdist worker `gw2` cannot recur regardless of which conftest the worker imports first; per-test `monkeypatch.setenv("HOME", ...)` continues to work because the override consults env vars before falling back to the hermetic tmp dir. (#1272) - Retry the `apm mcp search` and `apm mcp show` integration tests on the documented "Could not reach MCP registry" transient (with backoff and a final skip) so a brief `api.mcp.github.com` outage no longer red-marks the Windows integration job. (#1274) +- Also wrap `Path.expanduser()` in the root test conftest so the `windows-2025-vs2026` runner cannot raise `RuntimeError("Could not determine home directory.")` from `ntpath.expanduser` when production code (e.g. `install.package_resolution.user_scope_rejection_reason`) calls `Path("~/pkg").expanduser()`. Falls back to the hermetic tmp dir; assertions about `~/pkg` being absolute still hold. (#1276) ## [0.13.0] - 2026-05-11 diff --git a/tests/conftest.py b/tests/conftest.py index 1f498da66..df22375b3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -70,6 +70,29 @@ def _hermetic_home(_cls=Path) -> Path: Path.home = classmethod(_hermetic_home) # type: ignore[method-assign] +# Same problem, different code path: Path("~/pkg").expanduser() goes through +# ntpath.expanduser, which raises RuntimeError("Could not determine home +# directory.") on the windows-2025-vs2026 runner when USERPROFILE and +# HOMEPATH are both absent. Production code (e.g. install.package_resolution +# user_scope_rejection_reason) relies on expanduser to detect that ~/pkg is +# absolute. Wrap Path.expanduser so the RuntimeError can never surface. +_ORIGINAL_EXPANDUSER = Path.expanduser + + +def _hermetic_expanduser(self): + try: + return _ORIGINAL_EXPANDUSER(self) + except RuntimeError: + parts = self.parts + if not parts or not parts[0].startswith("~"): + return self + remainder = parts[1:] + return _TMP_HOME.joinpath(*remainder) if remainder else _TMP_HOME + + +Path.expanduser = _hermetic_expanduser # type: ignore[method-assign] + + @pytest.fixture(autouse=True, scope="session") def _validate_primitive_coverage(): """Fail fast if KNOWN_TARGETS has primitives without dispatch handlers.""" diff --git a/tests/unit/test_path_home_override.py b/tests/unit/test_path_home_override.py index 5e07bd897..dcc910d5d 100644 --- a/tests/unit/test_path_home_override.py +++ b/tests/unit/test_path_home_override.py @@ -31,3 +31,20 @@ def test_path_home_honors_per_test_home_setenv(monkeypatch, tmp_path): monkeypatch.setenv("HOME", str(target)) assert Path.home() == target + + +def test_path_expanduser_does_not_raise_with_cleared_env(monkeypatch): + """Path('~/pkg').expanduser() must not raise on the windows-2025-vs2026 runner. + + ntpath.expanduser raises RuntimeError when USERPROFILE and HOMEPATH are + both absent. Production code in install.package_resolution depends on + expanduser to detect that `~/pkg` is absolute. The root conftest wraps + Path.expanduser so this case falls back to the hermetic tmp dir. + """ + for key in ("HOME", "USERPROFILE", "HOMEDRIVE", "HOMEPATH"): + monkeypatch.delenv(key, raising=False) + + expanded = Path("~/pkg").expanduser() + + assert expanded.is_absolute() + assert expanded.name == "pkg"