python-discovery 1.4.3__tar.gz → 1.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. python_discovery-1.5.0/.github/FUNDING.yaml +2 -0
  2. {python_discovery-1.4.3 → python_discovery-1.5.0}/.github/workflows/check.yaml +3 -4
  3. {python_discovery-1.4.3 → python_discovery-1.5.0}/.github/workflows/prepare-release.yaml +1 -10
  4. {python_discovery-1.4.3 → python_discovery-1.5.0}/.github/workflows/release.yaml +1 -5
  5. {python_discovery-1.4.3 → python_discovery-1.5.0}/.pre-commit-config.yaml +17 -10
  6. {python_discovery-1.4.3 → python_discovery-1.5.0}/PKG-INFO +2 -14
  7. {python_discovery-1.4.3 → python_discovery-1.5.0}/docs/changelog.rst +19 -0
  8. {python_discovery-1.4.3 → python_discovery-1.5.0}/docs/conf.py +1 -1
  9. {python_discovery-1.4.3 → python_discovery-1.5.0}/docs/explanation.rst +16 -2
  10. {python_discovery-1.4.3 → python_discovery-1.5.0}/docs/how-to/standalone-usage.rst +21 -0
  11. {python_discovery-1.4.3 → python_discovery-1.5.0}/docs/tutorial/getting-started.rst +11 -5
  12. {python_discovery-1.4.3 → python_discovery-1.5.0}/pyproject.toml +17 -16
  13. {python_discovery-1.4.3 → python_discovery-1.5.0}/src/python_discovery/_cache.py +5 -5
  14. {python_discovery-1.4.3 → python_discovery-1.5.0}/src/python_discovery/_cached_py_info.py +4 -4
  15. {python_discovery-1.4.3 → python_discovery-1.5.0}/src/python_discovery/_discovery.py +2 -2
  16. {python_discovery-1.4.3 → python_discovery-1.5.0}/src/python_discovery/_py_info.py +23 -16
  17. {python_discovery-1.4.3 → python_discovery-1.5.0}/src/python_discovery/_py_spec.py +14 -5
  18. {python_discovery-1.4.3 → python_discovery-1.5.0}/src/python_discovery/_specifier.py +2 -2
  19. {python_discovery-1.4.3 → python_discovery-1.5.0}/src/python_discovery/_windows/_pep514.py +11 -11
  20. {python_discovery-1.4.3 → python_discovery-1.5.0}/tasks/release.py +35 -35
  21. {python_discovery-1.4.3 → python_discovery-1.5.0}/tests/py_info/test_py_info.py +40 -0
  22. {python_discovery-1.4.3 → python_discovery-1.5.0}/tests/test_discovery.py +2 -1
  23. {python_discovery-1.4.3 → python_discovery-1.5.0}/tests/test_py_info_extra.py +4 -1
  24. {python_discovery-1.4.3 → python_discovery-1.5.0}/tests/test_py_spec.py +26 -0
  25. {python_discovery-1.4.3 → python_discovery-1.5.0}/tests/test_py_spec_extra.py +9 -0
  26. {python_discovery-1.4.3 → python_discovery-1.5.0}/tests/test_specifier.py +5 -5
  27. {python_discovery-1.4.3 → python_discovery-1.5.0}/tests/windows/conftest.py +1 -1
  28. {python_discovery-1.4.3 → python_discovery-1.5.0}/tox.toml +4 -4
  29. python_discovery-1.4.3/.github/FUNDING.yaml +0 -1
  30. {python_discovery-1.4.3 → python_discovery-1.5.0}/.github/SECURITY.md +0 -0
  31. {python_discovery-1.4.3 → python_discovery-1.5.0}/.github/dependabot.yaml +0 -0
  32. {python_discovery-1.4.3 → python_discovery-1.5.0}/.github/release.yaml +0 -0
  33. {python_discovery-1.4.3 → python_discovery-1.5.0}/.gitignore +0 -0
  34. {python_discovery-1.4.3 → python_discovery-1.5.0}/.readthedocs.yaml +0 -0
  35. {python_discovery-1.4.3 → python_discovery-1.5.0}/.readthedocs.yml +0 -0
  36. {python_discovery-1.4.3 → python_discovery-1.5.0}/CODE_OF_CONDUCT.md +0 -0
  37. {python_discovery-1.4.3 → python_discovery-1.5.0}/LICENSE +0 -0
  38. {python_discovery-1.4.3 → python_discovery-1.5.0}/README.md +0 -0
  39. {python_discovery-1.4.3 → python_discovery-1.5.0}/docs/_static/custom.css +0 -0
  40. {python_discovery-1.4.3 → python_discovery-1.5.0}/docs/_static/logo.svg +0 -0
  41. {python_discovery-1.4.3 → python_discovery-1.5.0}/docs/changelog/template.jinja2 +0 -0
  42. {python_discovery-1.4.3 → python_discovery-1.5.0}/docs/index.rst +0 -0
  43. {python_discovery-1.4.3 → python_discovery-1.5.0}/docs/reference/api.rst +0 -0
  44. {python_discovery-1.4.3 → python_discovery-1.5.0}/docs/reference/environment-variables.rst +0 -0
  45. {python_discovery-1.4.3 → python_discovery-1.5.0}/src/python_discovery/__init__.py +0 -0
  46. {python_discovery-1.4.3 → python_discovery-1.5.0}/src/python_discovery/_compat.py +0 -0
  47. {python_discovery-1.4.3 → python_discovery-1.5.0}/src/python_discovery/_windows/__init__.py +0 -0
  48. {python_discovery-1.4.3 → python_discovery-1.5.0}/src/python_discovery/_windows/_propose.py +0 -0
  49. {python_discovery-1.4.3 → python_discovery-1.5.0}/src/python_discovery/py.typed +0 -0
  50. {python_discovery-1.4.3 → python_discovery-1.5.0}/tests/conftest.py +0 -0
  51. {python_discovery-1.4.3 → python_discovery-1.5.0}/tests/py_info/test_py_info_exe_based_of.py +0 -0
  52. {python_discovery-1.4.3 → python_discovery-1.5.0}/tests/test_cache.py +0 -0
  53. {python_discovery-1.4.3 → python_discovery-1.5.0}/tests/test_cached_py_info.py +0 -0
  54. {python_discovery-1.4.3 → python_discovery-1.5.0}/tests/test_discovery_extra.py +0 -0
  55. {python_discovery-1.4.3 → python_discovery-1.5.0}/tests/test_iter_interpreters.py +0 -0
  56. {python_discovery-1.4.3 → python_discovery-1.5.0}/tests/windows/test_windows.py +0 -0
  57. {python_discovery-1.4.3 → python_discovery-1.5.0}/tests/windows/test_windows_pep514.py +0 -0
  58. {python_discovery-1.4.3 → python_discovery-1.5.0}/tests/windows/winreg_mock_values.py +0 -0
@@ -0,0 +1,2 @@
1
+ github: gaborbernat
2
+ thanks_dev: u/gh/gaborbernat
@@ -7,14 +7,11 @@ on:
7
7
  pull_request:
8
8
  schedule:
9
9
  - cron: "0 8 * * *"
10
-
11
10
  concurrency:
12
11
  group: check-${{ github.ref }}
13
12
  cancel-in-progress: true
14
-
15
13
  permissions:
16
14
  contents: read
17
-
18
15
  jobs:
19
16
  test:
20
17
  runs-on: ubuntu-latest
@@ -22,6 +19,8 @@ jobs:
22
19
  fail-fast: false
23
20
  matrix:
24
21
  env:
22
+ - "3.15"
23
+ - "3.15t"
25
24
  - "3.14"
26
25
  - "3.13"
27
26
  - "3.12"
@@ -39,7 +38,7 @@ jobs:
39
38
  fetch-depth: 0
40
39
  persist-credentials: false
41
40
  - name: Install the latest version of uv
42
- uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
41
+ uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
43
42
  with:
44
43
  enable-cache: false
45
44
  cache-dependency-glob: "pyproject.toml"
@@ -12,13 +12,10 @@ on:
12
12
  - minor
13
13
  - patch
14
14
  default: auto
15
-
16
15
  permissions:
17
16
  contents: read
18
-
19
17
  env:
20
18
  FORCE_COLOR: 1
21
-
22
19
  jobs:
23
20
  prepare-release:
24
21
  runs-on: ubuntu-24.04
@@ -31,19 +28,16 @@ jobs:
31
28
  fetch-depth: 0
32
29
  token: ${{ secrets.RELEASE_PAT }}
33
30
  persist-credentials: true
34
-
35
31
  - name: Install the latest version of uv
36
- uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
32
+ uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
37
33
  with:
38
34
  enable-cache: true
39
35
  cache-dependency-glob: "pyproject.toml"
40
36
  github-token: ${{ secrets.GITHUB_TOKEN }}
41
-
42
37
  - name: Configure git
43
38
  run: |
44
39
  git config user.name "${GITHUB_ACTOR}"
45
40
  git config user.email "${GITHUB_ACTOR}@users.noreply.github.com"
46
-
47
41
  - name: Calculate next version
48
42
  id: version
49
43
  env:
@@ -84,16 +78,13 @@ jobs:
84
78
 
85
79
  echo "Next version: $next"
86
80
  echo "version=$next" >> $GITHUB_OUTPUT
87
-
88
81
  - name: Install tox
89
82
  run: uv tool install --python-preference only-managed --python 3.14 tox@latest --with tox-uv
90
-
91
83
  - name: Run release process
92
84
  run: tox run -e release -- ${STEPS_VERSION_OUTPUTS_VERSION}
93
85
  env:
94
86
  GH_TOKEN: ${{ secrets.RELEASE_PAT }}
95
87
  STEPS_VERSION_OUTPUTS_VERSION: ${{ steps.version.outputs.version }}
96
-
97
88
  - name: Display completion message
98
89
  run: echo "Release ${STEPS_VERSION_OUTPUTS_VERSION} prepared and pushed successfully!"
99
90
  env:
@@ -2,13 +2,10 @@ name: Release to PyPI
2
2
  on:
3
3
  push:
4
4
  tags: ["*"]
5
-
6
5
  permissions:
7
6
  contents: read
8
-
9
7
  env:
10
8
  dists-artifact-name: python-package-distributions
11
-
12
9
  jobs:
13
10
  build:
14
11
  runs-on: ubuntu-latest
@@ -18,7 +15,7 @@ jobs:
18
15
  fetch-depth: 0
19
16
  persist-credentials: false
20
17
  - name: Install the latest version of uv
21
- uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
18
+ uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
22
19
  with:
23
20
  enable-cache: false
24
21
  cache-dependency-glob: "pyproject.toml"
@@ -30,7 +27,6 @@ jobs:
30
27
  with:
31
28
  name: ${{ env.dists-artifact-name }}
32
29
  path: dist/*
33
-
34
30
  release:
35
31
  needs:
36
32
  - build
@@ -5,34 +5,41 @@ repos:
5
5
  - id: end-of-file-fixer
6
6
  - id: trailing-whitespace
7
7
  - repo: https://github.com/python-jsonschema/check-jsonschema
8
- rev: 0.37.3
8
+ rev: 0.37.4
9
9
  hooks:
10
10
  - id: check-github-workflows
11
11
  args: ["--verbose"]
12
12
  - repo: https://github.com/codespell-project/codespell
13
- rev: v2.4.2
13
+ rev: v2.4.3
14
14
  hooks:
15
15
  - id: codespell
16
16
  additional_dependencies: ["tomli>=2.4"]
17
17
  - repo: https://github.com/tox-dev/pyproject-fmt
18
- rev: "v2.25.1"
18
+ rev: "v2.25.3"
19
19
  hooks:
20
20
  - id: pyproject-fmt
21
21
  - repo: https://github.com/astral-sh/ruff-pre-commit
22
- rev: "v0.15.20"
22
+ rev: "v0.15.22"
23
23
  hooks:
24
24
  - id: ruff-format
25
25
  - id: ruff
26
26
  args: ["--fix", "--unsafe-fixes", "--exit-non-zero-on-fix"]
27
- - repo: https://github.com/rbubley/mirrors-prettier
28
- rev: "v3.9.1"
27
+ - repo: https://github.com/google/yamlfmt
28
+ rev: "v0.21.0"
29
29
  hooks:
30
- - id: prettier
30
+ - id: yamlfmt
31
+ - repo: local
32
+ hooks:
33
+ - id: mdformat
34
+ name: mdformat
35
+ entry: mdformat --wrap 120
36
+ language: python
37
+ types: [markdown]
31
38
  additional_dependencies:
32
- - prettier@3.8.1
33
- - "@prettier/plugin-xml@3.4.2"
39
+ - mdformat-gfm>=1
40
+ - mdformat-ruff>=0.1.3
34
41
  - repo: https://github.com/zizmorcore/zizmor-pre-commit
35
- rev: v1.26.1
42
+ rev: v1.27.0
36
43
  hooks:
37
44
  - id: zizmor
38
45
  - repo: meta
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-discovery
3
- Version: 1.4.3
3
+ Version: 1.5.0
4
4
  Summary: Python interpreter discovery
5
5
  Project-URL: Changelog, https://github.com/tox-dev/python-discovery/releases
6
6
  Project-URL: Documentation, https://python-discovery.readthedocs.io
@@ -42,25 +42,13 @@ Classifier: Programming Language :: Python :: 3.11
42
42
  Classifier: Programming Language :: Python :: 3.12
43
43
  Classifier: Programming Language :: Python :: 3.13
44
44
  Classifier: Programming Language :: Python :: 3.14
45
+ Classifier: Programming Language :: Python :: 3.15
45
46
  Classifier: Programming Language :: Python :: Implementation :: CPython
46
47
  Classifier: Topic :: Software Development :: Libraries
47
48
  Classifier: Topic :: Utilities
48
49
  Requires-Python: >=3.8
49
50
  Requires-Dist: filelock>=3.15.4
50
51
  Requires-Dist: platformdirs<5,>=4.3.6
51
- Provides-Extra: docs
52
- Requires-Dist: furo>=2025.12.19; extra == 'docs'
53
- Requires-Dist: sphinx-autodoc-typehints>=3.6.3; extra == 'docs'
54
- Requires-Dist: sphinx>=9.1; extra == 'docs'
55
- Requires-Dist: sphinxcontrib-mermaid>=2; extra == 'docs'
56
- Requires-Dist: sphinxcontrib-towncrier>=0.4; extra == 'docs'
57
- Requires-Dist: towncrier>=25.8; extra == 'docs'
58
- Provides-Extra: testing
59
- Requires-Dist: covdefaults>=2.3; extra == 'testing'
60
- Requires-Dist: coverage>=7.5.4; extra == 'testing'
61
- Requires-Dist: pytest-mock>=3.14; extra == 'testing'
62
- Requires-Dist: pytest>=8.3.5; extra == 'testing'
63
- Requires-Dist: setuptools>=75.1; extra == 'testing'
64
52
  Description-Content-Type: text/markdown
65
53
 
66
54
  # [`python-discovery`](https://python-discovery.readthedocs.io/en/latest/)
@@ -6,6 +6,25 @@
6
6
 
7
7
  .. towncrier release notes start
8
8
 
9
+ ********************
10
+ v1.5.0 (2026-07-21)
11
+ ********************
12
+
13
+ Packaging updates and notes for downstreams - 1.5.0
14
+ ===================================================
15
+ - Declare support for Python 3.15 and run the test suite against it, currently in beta. (:issue:`104`)
16
+
17
+ ********************
18
+ v1.4.4 (2026-07-08)
19
+ ********************
20
+
21
+ Bug fixes - 1.4.4
22
+ =================
23
+ - Parse the debug build flag in interpreter specs - ``python3.13d`` and Debian's ``python3.13-dbg`` /
24
+ ``python3.13-debug`` now select a ``Py_DEBUG`` interpreter instead of being misread as an ISA named ``dbg``.
25
+ Resolving a virtualenv to its base interpreter also checks the free-threaded and debug ABI flags, so a debug or
26
+ free-threaded environment no longer resolves to a release build of the same version - by :user:`gaborbernat`. (:issue:`96`)
27
+
9
28
  ********************
10
29
  v1.4.3 (2026-07-03)
11
30
  ********************
@@ -11,7 +11,7 @@ company = "tox-dev"
11
11
  name = "python-discovery"
12
12
  version = ".".join(__version__.split(".")[:2])
13
13
  release = __version__
14
- copyright = f"2026-{datetime.now(tz=timezone.utc).year}, {company}" # noqa: A001
14
+ copyright = f"2026-{datetime.now(tz=timezone.utc).year}, {company}" # ruff:ignore[builtin-variable-shadowing]
15
15
 
16
16
  extensions = [
17
17
  "sphinx.ext.autodoc",
@@ -213,7 +213,7 @@ when encountering problematic interpreters.
213
213
  Spec format reference
214
214
  -----------------------
215
215
 
216
- A spec string follows the pattern ``[impl][version][t][-arch][-machine]``. Every part is optional.
216
+ A spec string follows the pattern ``[impl][version][t][d][-arch][-machine]``. Every part is optional.
217
217
 
218
218
  .. mermaid::
219
219
 
@@ -221,12 +221,14 @@ A spec string follows the pattern ``[impl][version][t][-arch][-machine]``. Every
221
221
  Spec["Spec string"] --> Impl["impl<br>(optional)"]
222
222
  Impl --> Version["version<br>(optional)"]
223
223
  Version --> T["t<br>(optional)"]
224
- T --> Arch["-arch<br>(optional)"]
224
+ T --> D["d / -dbg / -debug<br>(optional)"]
225
+ D --> Arch["-arch<br>(optional)"]
225
226
  Arch --> Machine["-machine<br>(optional)"]
226
227
 
227
228
  style Impl fill:#4a90d9,stroke:#2a5f8f,color:#fff
228
229
  style Version fill:#4a9f4a,stroke:#2a6f2a,color:#fff
229
230
  style T fill:#d9904a,stroke:#8f5f2a,color:#fff
231
+ style D fill:#4ad9c4,stroke:#2a8f7f,color:#fff
230
232
  style Arch fill:#d94a4a,stroke:#8f2a2a,color:#fff
231
233
  style Machine fill:#904ad9,stroke:#5f2a8f,color:#fff
232
234
 
@@ -237,9 +239,17 @@ A spec string follows the pattern ``[impl][version][t][-arch][-machine]``. Every
237
239
  - **version** -- dotted version number (``3``, ``3.12``, or ``3.12.1``). You can also write
238
240
  ``312`` as shorthand for ``3.12``.
239
241
  - **t** -- appended directly after the version. Matches free-threaded (no-GIL) builds only.
242
+ - **d** -- require a debug (``Py_DEBUG``) build. Written as ``d`` right after the version (the ABI flag
243
+ form, as in ``python3.13d``) or as Debian's ``-dbg`` / ``-debug`` suffix (``python3.13-dbg``). When
244
+ omitted the build type is unconstrained.
240
245
  - **-arch** -- ``-32`` or ``-64`` for 32-bit or 64-bit interpreters.
241
246
  - **-machine** -- the CPU instruction set: ``-arm64``, ``-x86_64``, ``-aarch64``, ``-riscv64``, etc.
242
247
 
248
+ Order matters: the parts appear in the sequence shown above. Combine the ABI flags as ``python3.13td``
249
+ (free-threaded then debug, the order CPython uses in ``sys.abiflags``), and keep ``-arch`` before
250
+ ``-machine`` as in ``python3.13d-64-arm64``. Mixing ``d`` with ``-dbg`` in one spec, or swapping the arch
251
+ and machine segments, leaves the string unrecognized, so it falls back to a literal executable-name match.
252
+
243
253
  **Full examples:**
244
254
 
245
255
  .. list-table::
@@ -258,6 +268,10 @@ A spec string follows the pattern ``[impl][version][t][-arch][-machine]``. Every
258
268
  - PyPy 3.9
259
269
  * - ``python3.13t``
260
270
  - Free-threaded (no-GIL) CPython 3.13
271
+ * - ``python3.13-dbg``
272
+ - Debug (``Py_DEBUG``) CPython 3.13 (``python3.13d`` also works)
273
+ * - ``python3.13td``
274
+ - Free-threaded debug CPython 3.13
261
275
  * - ``python3.12-64``
262
276
  - 64-bit CPython 3.12
263
277
  * - ``python3.12-64-arm64``
@@ -16,6 +16,25 @@ standard locations.
16
16
  if info is not None:
17
17
  print(info.executable)
18
18
 
19
+ Select a debug build
20
+ ----------------------
21
+
22
+ To require a debug (``Py_DEBUG``) interpreter, add the debug marker to the spec. Both the ABI-flag form
23
+ ``python3.13d`` and the Debian package name ``python3.13-dbg`` work, and each resolves to the same
24
+ interpreter (Debian ships ``/usr/bin/python3.13d`` and ``/usr/bin/python3.13-dbg`` side by side). A spec with no
25
+ marker keeps matching either build, so reach for the marker only when you need the debug build.
26
+
27
+ .. code-block:: python
28
+
29
+ from python_discovery import get_interpreter
30
+
31
+ info = get_interpreter("python3.13-dbg")
32
+ if info is not None:
33
+ assert info.debug_build
34
+
35
+ Combine the marker with the free-threaded ``t`` flag to select a free-threaded debug build, written
36
+ ``python3.13td`` (ABI-flag order is ``t`` then ``d``) or ``python3.13t-dbg``.
37
+
19
38
  Restrict the search environment
20
39
  ---------------------------------
21
40
 
@@ -135,6 +154,7 @@ Once you have a :class:`~python_discovery.PythonInfo`, you can inspect everythin
135
154
  +sysconfig_paths: dict
136
155
  +machine: str
137
156
  +free_threaded: bool
157
+ +debug_build: bool
138
158
  }
139
159
 
140
160
  .. code-block:: python
@@ -154,6 +174,7 @@ Once you have a :class:`~python_discovery.PythonInfo`, you can inspect everythin
154
174
  info.platform # sys.platform value ("linux", "darwin", "win32").
155
175
  info.machine # ISA: "arm64", "x86_64", etc.
156
176
  info.free_threaded # True if this is a no-GIL build.
177
+ info.debug_build # True if this is a Py_DEBUG build.
157
178
  info.sysconfig_vars # All sysconfig.get_config_vars() values.
158
179
  info.sysconfig_paths # All sysconfig.get_paths() values.
159
180
 
@@ -142,12 +142,14 @@ You can add more constraints to narrow the search.
142
142
  Spec["Spec string"] --> Impl["impl<br>(optional)"]
143
143
  Impl --> Version["version<br>(optional)"]
144
144
  Version --> T["t<br>(optional)"]
145
- T --> Arch["-arch<br>(optional)"]
145
+ T --> D["d / -dbg / -debug<br>(optional)"]
146
+ D --> Arch["-arch<br>(optional)"]
146
147
  Arch --> Machine["-machine<br>(optional)"]
147
148
 
148
149
  style Impl fill:#4a90d9,stroke:#2a5f8f,color:#fff
149
150
  style Version fill:#4a9f4a,stroke:#2a6f2a,color:#fff
150
151
  style T fill:#d9904a,stroke:#8f5f2a,color:#fff
152
+ style D fill:#4ad9c4,stroke:#2a8f7f,color:#fff
151
153
  style Arch fill:#d94a4a,stroke:#8f2a2a,color:#fff
152
154
  style Machine fill:#904ad9,stroke:#5f2a8f,color:#fff
153
155
 
@@ -167,6 +169,8 @@ Common examples:
167
169
  - PyPy 3.9
168
170
  * - ``python3.13t``
169
171
  - Free-threaded (no-GIL) CPython 3.13
172
+ * - ``python3.13-dbg``
173
+ - Debug (``Py_DEBUG``) CPython 3.13 (``python3.13d`` also works)
170
174
  * - ``python3.12-64``
171
175
  - 64-bit CPython 3.12
172
176
  * - ``python3.12-64-arm64``
@@ -188,11 +192,12 @@ inspecting what a spec means or for building tools on top of python-discovery.
188
192
  .. mermaid::
189
193
 
190
194
  flowchart TD
191
- Input["cpython3.12t-64-arm64"] --> Parse["PythonSpec.from_string_spec()"]
195
+ Input["cpython3.13td-64-arm64"] --> Parse["PythonSpec.from_string_spec()"]
192
196
  Parse --> Spec["PythonSpec"]
193
197
  Spec --> impl["implementation: cpython"]
194
- Spec --> ver["major: 3, minor: 12"]
198
+ Spec --> ver["major: 3, minor: 13"]
195
199
  Spec --> ft["free_threaded: True"]
200
+ Spec --> dbg["debug: True"]
196
201
  Spec --> arch["architecture: 64"]
197
202
  Spec --> mach["machine: arm64"]
198
203
 
@@ -203,11 +208,12 @@ inspecting what a spec means or for building tools on top of python-discovery.
203
208
 
204
209
  from python_discovery import PythonSpec
205
210
 
206
- spec = PythonSpec.from_string_spec("cpython3.12t-64-arm64")
211
+ spec = PythonSpec.from_string_spec("cpython3.13td-64-arm64")
207
212
  spec.implementation # "cpython"
208
213
  spec.major # 3
209
- spec.minor # 12
214
+ spec.minor # 13
210
215
  spec.free_threaded # True
216
+ spec.debug # True
211
217
  spec.architecture # 64
212
218
  spec.machine # "arm64"
213
219
 
@@ -35,6 +35,7 @@ classifiers = [
35
35
  "Programming Language :: Python :: 3.12",
36
36
  "Programming Language :: Python :: 3.13",
37
37
  "Programming Language :: Python :: 3.14",
38
+ "Programming Language :: Python :: 3.15",
38
39
  "Programming Language :: Python :: Implementation :: CPython",
39
40
  "Topic :: Software Development :: Libraries",
40
41
  "Topic :: Utilities",
@@ -46,21 +47,6 @@ dependencies = [
46
47
  "filelock>=3.15.4",
47
48
  "platformdirs<5,>=4.3.6",
48
49
  ]
49
- optional-dependencies.docs = [
50
- "furo>=2025.12.19",
51
- "sphinx>=9.1",
52
- "sphinx-autodoc-typehints>=3.6.3",
53
- "sphinxcontrib-mermaid>=2",
54
- "sphinxcontrib-towncrier>=0.4",
55
- "towncrier>=25.8",
56
- ]
57
- optional-dependencies.testing = [
58
- "covdefaults>=2.3",
59
- "coverage>=7.5.4",
60
- "pytest>=8.3.5",
61
- "pytest-mock>=3.14",
62
- "setuptools>=75.1",
63
- ]
64
50
  urls.Changelog = "https://github.com/tox-dev/python-discovery/releases"
65
51
  urls.Documentation = "https://python-discovery.readthedocs.io"
66
52
  urls.Homepage = "https://github.com/tox-dev/python-discovery"
@@ -68,6 +54,21 @@ urls.Source = "https://github.com/tox-dev/python-discovery"
68
54
  urls.Tracker = "https://github.com/tox-dev/python-discovery/issues"
69
55
 
70
56
  [dependency-groups]
57
+ test = [
58
+ "covdefaults>=2.3",
59
+ "coverage>=7.5.4",
60
+ "pytest>=8.3.5",
61
+ "pytest-mock>=3.14",
62
+ "setuptools>=75.1",
63
+ ]
64
+ docs = [
65
+ "furo>=2025.12.19",
66
+ "sphinx>=9.1",
67
+ "sphinx-autodoc-typehints>=3.6.3",
68
+ "sphinxcontrib-mermaid>=2",
69
+ "sphinxcontrib-towncrier>=0.4",
70
+ "towncrier>=25.8",
71
+ ]
71
72
  release = [
72
73
  "gitpython>=3.1.46",
73
74
  "packaging>=26",
@@ -145,7 +146,7 @@ write-changes = true
145
146
  count = true
146
147
 
147
148
  [tool.pyproject-fmt]
148
- max_supported_python = "3.14"
149
+ max_supported_python = "3.15"
149
150
 
150
151
  [tool.ty]
151
152
  src.exclude = [ "tests/windows/winreg_mock_values.py" ]
@@ -104,7 +104,7 @@ class DiskContentStore:
104
104
 
105
105
  @contextmanager
106
106
  def locked(self) -> Generator[None]:
107
- from filelock import FileLock # noqa: PLC0415
107
+ from filelock import FileLock # ruff:ignore[import-outside-top-level]
108
108
 
109
109
  lock_path = self._folder / f"{self._key}.lock"
110
110
  lock_path.parent.mkdir(parents=True, exist_ok=True)
@@ -148,10 +148,10 @@ class DiskCache:
148
148
  class NoOpContentStore(ContentStore):
149
149
  """Content store that does nothing -- implements ContentStore protocol."""
150
150
 
151
- def exists(self) -> bool: # noqa: PLR6301
151
+ def exists(self) -> bool: # ruff:ignore[no-self-use]
152
152
  return False
153
153
 
154
- def read(self) -> dict | None: # noqa: PLR6301
154
+ def read(self) -> dict | None: # ruff:ignore[no-self-use]
155
155
  return None
156
156
 
157
157
  def write(self, content: dict) -> None:
@@ -161,14 +161,14 @@ class NoOpContentStore(ContentStore):
161
161
  pass
162
162
 
163
163
  @contextmanager
164
- def locked(self) -> Generator[None]: # noqa: PLR6301
164
+ def locked(self) -> Generator[None]: # ruff:ignore[no-self-use]
165
165
  yield
166
166
 
167
167
 
168
168
  class NoOpCache(PyInfoCache):
169
169
  """Cache that does nothing -- implements PyInfoCache protocol."""
170
170
 
171
- def py_info(self, path: Path) -> NoOpContentStore: # noqa: ARG002, PLR6301
171
+ def py_info(self, path: Path) -> NoOpContentStore: # ruff:ignore[unused-method-argument, no-self-use]
172
172
  return NoOpContentStore()
173
173
 
174
174
  def py_info_clear(self) -> None:
@@ -8,14 +8,14 @@ import logging
8
8
  import os
9
9
  import pkgutil
10
10
  import secrets
11
- import subprocess # noqa: S404
11
+ import subprocess # ruff:ignore[suspicious-subprocess-import]
12
12
  import sys
13
13
  import tempfile
14
14
  from collections import OrderedDict
15
15
  from contextlib import contextmanager
16
16
  from pathlib import Path
17
17
  from shlex import quote
18
- from subprocess import Popen, TimeoutExpired # noqa: S404
18
+ from subprocess import Popen, TimeoutExpired # ruff:ignore[suspicious-subprocess-import]
19
19
  from typing import TYPE_CHECKING, Final
20
20
 
21
21
  from ._cache import NoOpCache
@@ -32,7 +32,7 @@ _CACHE[Path(sys.executable)] = PythonInfo()
32
32
  _LOGGER: Final[logging.Logger] = logging.getLogger(__name__)
33
33
 
34
34
 
35
- def from_exe( # noqa: PLR0913
35
+ def from_exe( # ruff:ignore[too-many-arguments]
36
36
  cls: type[PythonInfo],
37
37
  cache: PyInfoCache | None,
38
38
  exe: str,
@@ -197,7 +197,7 @@ def _run_subprocess(
197
197
  env["PYTHONUTF8"] = "1"
198
198
  _LOGGER.debug("get interpreter info via cmd: %s", LogCmd(cmd))
199
199
  try:
200
- process = Popen( # noqa: S603
200
+ process = Popen( # ruff:ignore[subprocess-without-shell-equals-true]
201
201
  cmd,
202
202
  universal_newlines=True,
203
203
  stdin=subprocess.PIPE,
@@ -89,7 +89,7 @@ def iter_interpreters(
89
89
  yield from _iter_for_spec(spec_str, first_with, cache, env_map, predicate, seen)
90
90
 
91
91
 
92
- def _iter_for_spec( # noqa: PLR0913, PLR0917
92
+ def _iter_for_spec( # ruff:ignore[too-many-arguments, too-many-positional-arguments]
93
93
  spec_str: str | None,
94
94
  try_first_with: tuple[str, ...],
95
95
  cache: PyInfoCache | None,
@@ -231,7 +231,7 @@ def _propose_current_and_windows(
231
231
  yield current_python, True
232
232
 
233
233
  if IS_WIN: # pragma: win32 cover
234
- from ._windows import propose_interpreters as win_propose # noqa: PLC0415
234
+ from ._windows import propose_interpreters as win_propose # ruff:ignore[import-outside-top-level]
235
235
 
236
236
  for interpreter in win_propose(spec, cache, env):
237
237
  if _is_new_exe(str(interpreter.executable), tested_exes):