Make SimplePath path parameters positional-only - #543
Conversation
pathlib.Path does not satisfy SimplePath under ty or pyrefly. The protocol names the joinpath and __truediv__ parameter `other`, while typeshed names Path's `key` and `*other`. Neither side is positional-only, so the typing spec requires the names to match, and they do not. Marking the parameters positional-only reflects how both members are called and lets Path satisfy the protocol. mypy passes before and after. Closes python#542
Check 3.10, the minimum supported version, rather than 3.14. ty prunes `sys.version_info` branches statically, so 3.14 left the `< (3, 11)` compat paths in _builder, env and _compat/tomllib unchecked. Exclude tests/packages, as the mypy config did. Without it ty checks the fixture packages and fails on legacy/setup.py; CI only passed because its type job pins 3.10, whose venv still seeds setuptools. Enable every rule as an error to recover some of what `strict = true` and `disallow_any_explicit` enforced. ty has no strict mode, so this is the closest available. `missing-override-decorator` stays off: `@override` needs typing_extensions before 3.12, and build takes no runtime deps for typing. Restore `__spec__.parent` in _ctx: `__package__` is deprecated and stops being set in 3.15, and the other five modules already use `__spec__.parent`. Point every suppression and workaround at its upstream issue: - astral-sh/ty#4017 for `__spec__` typed `ModuleSpec | None` - astral-sh/ty#3962 for the `warnings.showwarning` assignment - astral-sh/ty#3699 for the recursive TOMLValue alias degrading to Divergent - python/importlib_metadata#542 for `Path` failing the SimplePath protocol The SimplePath rejection is not a ty bug: pyrefly rejects it too, and the protocol's parameters are not positional-only. python/importlib_metadata#543 fixes it upstream. Add the changelog fragment the PR template requires.
|
FWIW, I wanted to do this several years ago, when I first found this bug ( |
|
@jaraco would you have a minute to take a look at this pull request? Thank you. |
|
Looks good to me. My only hesitation - should this be considered a bugfix, feature/general change, or breaking change? On one hand, it is technically backward incompatible and does change behavior. On the other hand, the original intention was that this parameter should always have been positional (and all uses of it in tests are positional). Hyrum's law says someone somewhere is passing the value by keyword. Still, I think those cases are unlikely given this is mainly a protocol meant to constrain the interface of pathlib.Path (not expand it). Bugfix is the right choice. |
|
I'd imagine it would break anyway at runtime, no? If they use that? |
Fixes #542.
SimplePathnames thejoinpathand__truediv__parameterotherwithout marking it positional-only, so the typing spec requires an implementation to acceptother=.... Typeshed names the matchingpathlib.Pathparameterskeyand*other, soPathfails the protocol under checkers that compare parameter names:ty 0.0.59 explains it:
pyrefly 1.1.1 reports the same. mypy 2.1.0 accepts it, which is why the docstring's claim that
SimplePathis "A minimal subset of pathlib.Path" has held up until now.Adding
/to both members letsPathsatisfy the protocol under ty and pyrefly, and leaves mypy passing. I verified all three against importlib_metadata 9.0.0 with the parameters patched, and checked each protocol member againstPathin isolation:joinpathand__truediv__were the only failures.ruff format,ruff checkandmypypass.tox -e pyfails to collect on this branch, but it fails the same way on a pristinemaincheckout, so that is unrelated.pypa/build ran into this while moving off mypy (pypa/build#1135).