Skip to content

Commit 9b588c0

Browse files
committed
🔧 build(ty): type check against both 3.8 and 3.14
The codebase needs to be validated against both the oldest supported Python (3.8) and the newest (3.14) because some constructs like distutils imports are only valid on one version. Split the single `type` tox env into `type-3.8` and `type-3.14` with explicit `--python-version` flags. Use per-file ty overrides instead of globally ignoring unused-ignore-comment, so only the two files with version-conditional ignores (distutils imports in _py_info.py, type: ignore on tkinter fallback in test_py_info_extra.py) are exempted. Suppress ruff B905 (zip-without-explicit-strict) since the `strict` parameter does not exist in Python 3.8.
1 parent 02b4cd2 commit 9b588c0

8 files changed

Lines changed: 35 additions & 25 deletions

File tree

‎.github/workflows/check.yaml‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ jobs:
2424
- "3.12"
2525
- "3.11"
2626
- "3.10"
27-
- type
27+
- type-3.8
28+
- type-3.14
2829
- dev
2930
- pkg_meta
3031
steps:

‎.pre-commit-config.yaml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ repos:
1313
rev: v2.4.1
1414
hooks:
1515
- id: codespell
16-
additional_dependencies: ["tomli>=2.2.1"]
16+
additional_dependencies: ["tomli>=2.4"]
1717
- repo: https://github.com/tox-dev/pyproject-fmt
1818
rev: "v2.11.1"
1919
hooks:
@@ -29,7 +29,7 @@ repos:
2929
hooks:
3030
- id: prettier
3131
additional_dependencies:
32-
- prettier@3.6.2
32+
- prettier@3.8.1
3333
- "@prettier/plugin-xml@3.4.2"
3434
- repo: meta
3535
hooks:

‎pyproject.toml‎

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
build-backend = "hatchling.build"
33
requires = [
44
"hatch-vcs>=0.5",
5-
"hatchling>=1.27",
5+
"hatchling>=1.28",
66
]
77

88
[project]
@@ -40,21 +40,21 @@ dynamic = [
4040
"version",
4141
]
4242
dependencies = [
43-
"filelock>=3.16.1",
44-
"platformdirs>=3.9.1,<5",
43+
"filelock>=3.24.2",
44+
"platformdirs<5,>=4.9.2",
4545
]
4646
optional-dependencies.docs = [
47-
"furo>=2024.8.6",
48-
"sphinx>=8.1.3",
49-
"sphinx-autodoc-typehints>=2.5",
50-
"sphinxcontrib-mermaid>=1",
47+
"furo>=2025.12.19",
48+
"sphinx>=9.1",
49+
"sphinx-autodoc-typehints>=3.6.3",
50+
"sphinxcontrib-mermaid>=2",
5151
]
5252
optional-dependencies.testing = [
5353
"covdefaults>=2.3",
54-
"coverage>=7.10.7",
55-
"pytest>=8.4.2",
54+
"coverage>=7.13.4",
55+
"pytest>=9.0.2",
5656
"pytest-mock>=3.15.1",
57-
"setuptools>=68",
57+
"setuptools>=82",
5858
]
5959
urls.Changelog = "https://github.com/tox-dev/py-discovery/releases"
6060
urls.Documentation = "https://py-discovery.readthedocs.io"
@@ -84,6 +84,7 @@ lint.ignore = [
8484
"ANN401", # Dynamically typed expressions (typing.Any)
8585
"ARG001", # Unused function argument
8686
"ARG005", # Unused lambda argument
87+
"B905", # zip-without-explicit-strict (conflicts with ty on 3.8)
8788
"C901", # Too complex
8889
"COM812", # Conflict with formatter
8990
"CPY", # No copyright statements
@@ -170,5 +171,8 @@ paths.source = [
170171
]
171172

172173
[tool.ty]
173-
environment.python-version = "3.14"
174174
src.exclude = [ "tests/windows/winreg_mock_values.py" ]
175+
176+
[[tool.ty.overrides]]
177+
include = [ "src/py_discovery/_py_info.py", "tests/test_py_info_extra.py" ]
178+
rules.unused-ignore-comment = "ignore"

‎src/py_discovery/_py_info.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -506,7 +506,7 @@ def satisfies(self, spec: PythonSpec, impl_must_match: bool) -> bool:
506506
if not spec.version_specifier.contains(release):
507507
return False
508508

509-
for our, req in zip(self.version_info[0:3], (spec.major, spec.minor, spec.micro), strict=False):
509+
for our, req in zip(self.version_info[0:3], (spec.major, spec.minor, spec.micro)):
510510
if req is not None and our is not None and our != req:
511511
return False
512512
return True

‎src/py_discovery/_py_spec.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ def satisfies(self, spec: PythonSpec) -> bool:
209209
if spec.version_specifier is not None and not self._check_version_specifier(spec):
210210
return False
211211

212-
for our, req in zip((self.major, self.minor, self.micro), (spec.major, spec.minor, spec.micro), strict=False):
212+
for our, req in zip((self.major, self.minor, self.micro), (spec.major, spec.minor, spec.micro)):
213213
if req is not None and our is not None and our != req:
214214
return False
215215
return True

‎tests/py_info/test_py_info.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@ def test_select_most_likely_prefers_machine_match(
437437
target = copy.deepcopy(CURRENT)
438438
target.sysconfig_platform = target_platform
439439
discovered = [copy.deepcopy(CURRENT) for _ in discovered_platforms]
440-
for d, plat in zip(discovered, discovered_platforms, strict=False):
440+
for d, plat in zip(discovered, discovered_platforms):
441441
d.sysconfig_platform = plat
442442
result = PythonInfo._select_most_likely(discovered, target)
443443
assert result.sysconfig_platform == discovered_platforms[expected_idx]

‎tests/test_py_info_extra.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def test_get_tcl_tk_libs_returns_tuple() -> None:
5454
@pytest.mark.skipif(tk is None, reason="tkinter not available")
5555
def test_get_tcl_tk_libs_tcl_error(mocker) -> None: # pragma: no cover
5656
mock_tcl = MagicMock()
57-
mock_tcl.eval.side_effect = tk.TclError("fail") # type: ignore[union-attr]
57+
mock_tcl.eval.side_effect = tk.TclError("fail")
5858
mocker.patch("tkinter.Tcl", return_value=mock_tcl)
5959

6060
tcl, _tk = PythonInfo._get_tcl_tk_libs()

‎tox.toml‎

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
requires = ["tox>=4.30.3", "tox-uv>=1.28"]
2-
env_list = ["fix", "3.14", "3.13", "3.12", "3.11", "3.10", "type", "docs", "pkg_meta"]
1+
requires = ["tox>=4.38", "tox-uv>=1.29"]
2+
env_list = ["fix", "3.14", "3.13", "3.12", "3.11", "3.10", "type-3.8", "type-3.14", "docs", "pkg_meta"]
33
skip_missing_interpreters = true
44

55
[env_run_base]
@@ -30,14 +30,19 @@ commands = [
3030
[env.fix]
3131
description = "run static analysis and style check using flake8"
3232
skip_install = true
33-
deps = ["pre-commit-uv>=4.1.5"]
33+
deps = ["pre-commit-uv>=4.2.1"]
3434
pass_env = ["HOMEPATH", "PROGRAMDATA"]
3535
commands = [["pre-commit", "run", "--all-files", "--show-diff-on-failure"]]
3636

37-
[env.type]
38-
description = "run type check on code base"
37+
[env."type-3.8"]
38+
description = "run type check on code base (3.8)"
3939
deps = ["ty==0.0.17"]
40-
commands = [["ty", "check", "--output-format", "concise", "--error-on-warning", "."]]
40+
commands = [["ty", "check", "--output-format", "concise", "--error-on-warning", "--python-version", "3.8", "."]]
41+
42+
[env."type-3.14"]
43+
description = "run type check on code base (3.14)"
44+
deps = ["ty==0.0.17"]
45+
commands = [["ty", "check", "--output-format", "concise", "--error-on-warning", "--python-version", "3.14", "."]]
4146

4247
[env.docs]
4348
description = "build documentation"
@@ -59,7 +64,7 @@ commands = [
5964
[env.pkg_meta]
6065
description = "check that the long description is valid"
6166
skip_install = true
62-
deps = ["check-wheel-contents>=0.6.3", "twine>=6.2", "uv>=0.8.22"]
67+
deps = ["check-wheel-contents>=0.6.3", "twine>=6.2", "uv>=0.10.4"]
6368
commands = [
6469
["uv", "build", "--sdist", "--wheel", "--out-dir", "{env_tmp_dir}", "."],
6570
["twine", "check", "{env_tmp_dir}{/}*"],

0 commit comments

Comments
 (0)