Skip to content

Commit f80405e

Browse files
worksbyfridayclaudepre-commit-ci[bot]
authored
Fix deps with ~= version specifier treated as local paths (#3726)
## Summary - Fix `deps = pre-commit ~= 4` being treated as a local path instead of an invalid requirement - Root cause: `packaging.Requirement()` rejects `~=` with single-segment versions (PEP 440 requires 2+ segments), then tox's fallback treats the `~` as a tilde home directory path - Fix: detect PEP 440 version specifier operators before path detection, keep the string as-is for pip to give a clear error Closes #3447 ## Test plan - [x] Added parametrized test for `pre-commit ~= 4` and `pre-commit~=4` - [x] All 104 req file tests pass - [x] All 49 pip install tests pass 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent e84002a commit f80405e

3 files changed

Lines changed: 12 additions & 0 deletions

File tree

docs/changelog/3447.bugfix.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix ``deps`` entries with ``~=`` version specifier being incorrectly treated as local paths instead of being passed
2+
through to pip - by :user:`Fridayai700`.

src/tox/tox_env/python/pip/req/file.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
# https://www.python.org/dev/peps/pep-0508/#extras
2828
_EXTRA_PATH = re.compile(r"(.*)\[([-._,\sa-zA-Z0-9]*)]")
2929
_EXTRA_ELEMENT = re.compile(r"[a-zA-Z0-9]*[-._a-zA-Z0-9]")
30+
_VERSION_SPECIFIER = re.compile(r"[><=!~]=|===?|[><]")
3031
ReqFileLines = Iterator[tuple[int, str]]
3132

3233
DEFAULT_INDEX_URL = "https://pypi.org/simple"
@@ -40,6 +41,8 @@ def __init__(self, req: str, options: dict[str, Any], from_file: str, lineno: in
4041
except InvalidRequirement:
4142
if is_url(req) or any(req.startswith(f"{v}+") and is_url(req[len(v) + 1 :]) for v in VCS):
4243
self._requirement = req
44+
elif _VERSION_SPECIFIER.search(req):
45+
self._requirement = req # invalid requirement with version specifier — let pip report the error
4346
else:
4447
root = Path(from_file).parent
4548
extras: list[str] = []

tests/tox_env/python/pip/req/test_file.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,13 @@ def test_requirement_via_file_protocol_na(tmp_path: Path) -> None:
550550
assert req_file.options
551551

552552

553+
@pytest.mark.parametrize("req", ["pre-commit ~= 4", "pre-commit~=4"])
554+
def test_requirement_with_tilde_equals_not_treated_as_path(tmp_path: Path, req: str) -> None:
555+
"""Deps with ``~=`` version specifier should not be treated as local paths (#3447)."""
556+
parsed = ParsedRequirement(req, {}, str(tmp_path / "tox.ini"), 1)
557+
assert str(parsed.requirement) == req
558+
559+
553560
def test_requirement_to_path_one_level_up(tmp_path: Path) -> None:
554561
other_req = tmp_path / "other.txt"
555562
other_req.write_text("-e ..")

0 commit comments

Comments
 (0)