python-discovery 1.4.3__tar.gz → 1.4.4__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.
- python_discovery-1.4.4/.github/FUNDING.yaml +2 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/.pre-commit-config.yaml +2 -2
- {python_discovery-1.4.3 → python_discovery-1.4.4}/PKG-INFO +1 -1
- {python_discovery-1.4.3 → python_discovery-1.4.4}/docs/changelog.rst +11 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/docs/explanation.rst +16 -2
- {python_discovery-1.4.3 → python_discovery-1.4.4}/docs/how-to/standalone-usage.rst +21 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/docs/tutorial/getting-started.rst +11 -5
- {python_discovery-1.4.3 → python_discovery-1.4.4}/src/python_discovery/_py_info.py +6 -2
- {python_discovery-1.4.3 → python_discovery-1.4.4}/src/python_discovery/_py_spec.py +11 -2
- {python_discovery-1.4.3 → python_discovery-1.4.4}/tests/py_info/test_py_info.py +40 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/tests/test_py_spec.py +26 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/tests/test_py_spec_extra.py +9 -0
- python_discovery-1.4.3/.github/FUNDING.yaml +0 -1
- {python_discovery-1.4.3 → python_discovery-1.4.4}/.github/SECURITY.md +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/.github/dependabot.yaml +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/.github/release.yaml +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/.github/workflows/check.yaml +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/.github/workflows/prepare-release.yaml +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/.github/workflows/release.yaml +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/.gitignore +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/.readthedocs.yaml +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/.readthedocs.yml +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/CODE_OF_CONDUCT.md +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/LICENSE +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/README.md +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/docs/_static/custom.css +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/docs/_static/logo.svg +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/docs/changelog/template.jinja2 +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/docs/conf.py +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/docs/index.rst +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/docs/reference/api.rst +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/docs/reference/environment-variables.rst +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/pyproject.toml +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/src/python_discovery/__init__.py +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/src/python_discovery/_cache.py +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/src/python_discovery/_cached_py_info.py +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/src/python_discovery/_compat.py +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/src/python_discovery/_discovery.py +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/src/python_discovery/_specifier.py +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/src/python_discovery/_windows/__init__.py +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/src/python_discovery/_windows/_pep514.py +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/src/python_discovery/_windows/_propose.py +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/src/python_discovery/py.typed +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/tasks/release.py +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/tests/conftest.py +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/tests/py_info/test_py_info_exe_based_of.py +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/tests/test_cache.py +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/tests/test_cached_py_info.py +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/tests/test_discovery.py +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/tests/test_discovery_extra.py +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/tests/test_iter_interpreters.py +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/tests/test_py_info_extra.py +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/tests/test_specifier.py +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/tests/windows/conftest.py +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/tests/windows/test_windows.py +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/tests/windows/test_windows_pep514.py +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/tests/windows/winreg_mock_values.py +0 -0
- {python_discovery-1.4.3 → python_discovery-1.4.4}/tox.toml +0 -0
|
@@ -5,7 +5,7 @@ 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.
|
|
8
|
+
rev: 0.37.4
|
|
9
9
|
hooks:
|
|
10
10
|
- id: check-github-workflows
|
|
11
11
|
args: ["--verbose"]
|
|
@@ -25,7 +25,7 @@ repos:
|
|
|
25
25
|
- id: ruff
|
|
26
26
|
args: ["--fix", "--unsafe-fixes", "--exit-non-zero-on-fix"]
|
|
27
27
|
- repo: https://github.com/rbubley/mirrors-prettier
|
|
28
|
-
rev: "v3.9.
|
|
28
|
+
rev: "v3.9.4"
|
|
29
29
|
hooks:
|
|
30
30
|
- id: prettier
|
|
31
31
|
additional_dependencies:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-discovery
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.4
|
|
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
|
|
@@ -6,6 +6,17 @@
|
|
|
6
6
|
|
|
7
7
|
.. towncrier release notes start
|
|
8
8
|
|
|
9
|
+
********************
|
|
10
|
+
v1.4.4 (2026-07-08)
|
|
11
|
+
********************
|
|
12
|
+
|
|
13
|
+
Bug fixes - 1.4.4
|
|
14
|
+
=================
|
|
15
|
+
- Parse the debug build flag in interpreter specs - ``python3.13d`` and Debian's ``python3.13-dbg`` /
|
|
16
|
+
``python3.13-debug`` now select a ``Py_DEBUG`` interpreter instead of being misread as an ISA named ``dbg``.
|
|
17
|
+
Resolving a virtualenv to its base interpreter also checks the free-threaded and debug ABI flags, so a debug or
|
|
18
|
+
free-threaded environment no longer resolves to a release build of the same version - by :user:`gaborbernat`. (:issue:`96`)
|
|
19
|
+
|
|
9
20
|
********************
|
|
10
21
|
v1.4.3 (2026-07-03)
|
|
11
22
|
********************
|
|
@@ -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 -->
|
|
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 -->
|
|
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.
|
|
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:
|
|
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.
|
|
211
|
+
spec = PythonSpec.from_string_spec("cpython3.13td-64-arm64")
|
|
207
212
|
spec.implementation # "cpython"
|
|
208
213
|
spec.major # 3
|
|
209
|
-
spec.minor #
|
|
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
|
|
|
@@ -481,6 +481,8 @@ class PythonInfo: # noqa: PLR0904
|
|
|
481
481
|
return False
|
|
482
482
|
if spec.free_threaded is not None and spec.free_threaded != self.free_threaded:
|
|
483
483
|
return False
|
|
484
|
+
if spec.debug is not None and spec.debug != self.debug_build:
|
|
485
|
+
return False
|
|
484
486
|
if spec.version_specifier is not None and not self._satisfies_version_specifier(spec):
|
|
485
487
|
return False
|
|
486
488
|
return all(
|
|
@@ -717,7 +719,7 @@ class PythonInfo: # noqa: PLR0904
|
|
|
717
719
|
info = self.from_exe(exe_path, cache, resolve_to_host=False, raise_on_error=False, env=env)
|
|
718
720
|
if info is None: # ignore if for some reason we can't query
|
|
719
721
|
return None
|
|
720
|
-
for item in ["implementation", "architecture", "machine", "version_info"]:
|
|
722
|
+
for item in ["implementation", "architecture", "machine", "version_info", "free_threaded", "debug_build"]:
|
|
721
723
|
found = getattr(info, item)
|
|
722
724
|
searched = getattr(self, item)
|
|
723
725
|
if found != searched:
|
|
@@ -772,7 +774,9 @@ class PythonInfo: # noqa: PLR0904
|
|
|
772
774
|
def _find_possible_exe_names(self) -> list[str]:
|
|
773
775
|
name_candidate = OrderedDict()
|
|
774
776
|
mods = ["", "t"] if self.free_threaded else [""]
|
|
775
|
-
|
|
777
|
+
# _d (Windows python_d.exe), -dbg (Debian's own name), d (the abiflags form used by
|
|
778
|
+
# upstream/Fedora and also shipped by Debian, so pythonX.Yd / pythonX.Ytd resolve too)
|
|
779
|
+
debug_suffixes = ["_d", "-dbg", "d", ""] if self.debug_build else [""]
|
|
776
780
|
archs = [f"-{self.architecture}", ""]
|
|
777
781
|
for name in self._possible_base():
|
|
778
782
|
for at in (3, 2, 1, 0):
|
|
@@ -16,6 +16,7 @@ PATTERN = re.compile(
|
|
|
16
16
|
(?P<impl>[a-zA-Z]+)? # implementation (e.g. cpython, pypy)
|
|
17
17
|
(?P<version>[0-9.]+)? # version (e.g. 3.12, 3.12.1)
|
|
18
18
|
(?P<threaded>t)? # free-threaded flag
|
|
19
|
+
(?P<debug>d|(?:-dbg|-debug)(?=-|$))? # debug build flag (d, -dbg or -debug)
|
|
19
20
|
(?:-(?P<arch>32|64))? # architecture bitness
|
|
20
21
|
(?:-(?P<machine>[a-zA-Z0-9_.]+))? # ISA (e.g. arm64, x86_64, i86pc.64bit)
|
|
21
22
|
$
|
|
@@ -68,6 +69,7 @@ def _parse_spec_pattern(string_spec: str) -> PythonSpec | None:
|
|
|
68
69
|
groups = match.groupdict()
|
|
69
70
|
version = groups["version"]
|
|
70
71
|
major, minor, micro, threaded = None, None, None, None
|
|
72
|
+
debug = True if groups["debug"] else None # unconstrained unless an explicit d/-dbg/-debug marker is present
|
|
71
73
|
if version is not None:
|
|
72
74
|
try:
|
|
73
75
|
major, minor, micro = _parse_version_parts(version)
|
|
@@ -83,7 +85,9 @@ def _parse_spec_pattern(string_spec: str) -> PythonSpec | None:
|
|
|
83
85
|
machine = groups.get("machine")
|
|
84
86
|
if machine is not None:
|
|
85
87
|
machine = normalize_isa(machine)
|
|
86
|
-
return PythonSpec(
|
|
88
|
+
return PythonSpec(
|
|
89
|
+
string_spec, impl, major, minor, micro, arch, None, free_threaded=threaded, machine=machine, debug=debug
|
|
90
|
+
)
|
|
87
91
|
|
|
88
92
|
|
|
89
93
|
def _parse_specifier(string_spec: str) -> PythonSpec | None:
|
|
@@ -117,6 +121,7 @@ class PythonSpec:
|
|
|
117
121
|
:param path: filesystem path to a specific interpreter, or ``None``.
|
|
118
122
|
:param free_threaded: whether a free-threaded build is required, or ``None`` for any.
|
|
119
123
|
:param machine: required ISA (e.g. ``"arm64"``), or ``None`` for any.
|
|
124
|
+
:param debug: whether a debug (``Py_DEBUG``) build is required, or ``None`` for any.
|
|
120
125
|
:param version_specifier:
|
|
121
126
|
`version specifier <https://packaging.python.org/en/latest/specifications/version-specifiers/>`_
|
|
122
127
|
constraints, or ``None``.
|
|
@@ -134,6 +139,7 @@ class PythonSpec:
|
|
|
134
139
|
*,
|
|
135
140
|
free_threaded: bool | None = None,
|
|
136
141
|
machine: str | None = None,
|
|
142
|
+
debug: bool | None = None,
|
|
137
143
|
version_specifier: SpecifierSet | None = None,
|
|
138
144
|
) -> None:
|
|
139
145
|
self.str_spec = str_spec
|
|
@@ -144,6 +150,7 @@ class PythonSpec:
|
|
|
144
150
|
self.free_threaded = free_threaded
|
|
145
151
|
self.architecture = architecture
|
|
146
152
|
self.machine = machine
|
|
153
|
+
self.debug = debug
|
|
147
154
|
self.path = path
|
|
148
155
|
self.version_specifier = version_specifier
|
|
149
156
|
|
|
@@ -181,10 +188,11 @@ class PythonSpec:
|
|
|
181
188
|
else:
|
|
182
189
|
impl = "python"
|
|
183
190
|
mod = "t?" if self.free_threaded else ""
|
|
191
|
+
dbg = "(?:d|-dbg|-debug)?" if self.debug else ""
|
|
184
192
|
suffix = r"\.exe" if windows else ""
|
|
185
193
|
version_conditional = "?" if windows or self.major is None else ""
|
|
186
194
|
return re.compile(
|
|
187
|
-
rf"(?P<impl>{impl})(?P<v>{version}{mod}){version_conditional}{suffix}$",
|
|
195
|
+
rf"(?P<impl>{impl})(?P<v>{version}{mod}){version_conditional}{dbg}{suffix}$",
|
|
188
196
|
flags=re.IGNORECASE,
|
|
189
197
|
)
|
|
190
198
|
|
|
@@ -263,6 +271,7 @@ class PythonSpec:
|
|
|
263
271
|
"machine",
|
|
264
272
|
"path",
|
|
265
273
|
"free_threaded",
|
|
274
|
+
"debug",
|
|
266
275
|
"version_specifier",
|
|
267
276
|
)
|
|
268
277
|
return f"{name}({', '.join(f'{k}={getattr(self, k)}' for k in params if getattr(self, k) is not None)})"
|
|
@@ -259,6 +259,31 @@ def test_system_executable_no_exact_match(
|
|
|
259
259
|
assert warn_similar.msg.startswith("no exact match found, chosen most similar")
|
|
260
260
|
|
|
261
261
|
|
|
262
|
+
@pytest.mark.parametrize("attr", ["free_threaded", "debug_build"])
|
|
263
|
+
def test_discover_exe_validates_abi_flag(
|
|
264
|
+
attr: str, tmp_path: Path, mocker: MockerFixture, session_cache: DiskCache
|
|
265
|
+
) -> None:
|
|
266
|
+
def candidate(*, flag: bool, name: str) -> PythonInfo:
|
|
267
|
+
info = copy.deepcopy(CURRENT)
|
|
268
|
+
setattr(info, attr, flag)
|
|
269
|
+
exe = tmp_path / name
|
|
270
|
+
exe.write_text("", encoding="utf-8")
|
|
271
|
+
info.executable = str(exe)
|
|
272
|
+
return info
|
|
273
|
+
|
|
274
|
+
target = candidate(flag=True, name="target")
|
|
275
|
+
mismatch = candidate(flag=False, name="release")
|
|
276
|
+
match = candidate(flag=True, name="debug")
|
|
277
|
+
by_path = {info.executable: info for info in (mismatch, match)}
|
|
278
|
+
|
|
279
|
+
mocker.patch.object(target, "_find_possible_exe_names", return_value=["release", "debug"])
|
|
280
|
+
mocker.patch.object(target, "_find_possible_folders", return_value=[str(tmp_path)])
|
|
281
|
+
mocker.patch.object(target, "from_exe", side_effect=lambda exe, *_a, **_k: by_path[exe])
|
|
282
|
+
|
|
283
|
+
found = target.discover_exe(session_cache, prefix=str(tmp_path), exact=True)
|
|
284
|
+
assert found is match # the release build is refused even though version, impl and arch match
|
|
285
|
+
|
|
286
|
+
|
|
262
287
|
def test_py_info_ignores_distutils_config(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
|
|
263
288
|
raw = f"""
|
|
264
289
|
[install]
|
|
@@ -287,6 +312,18 @@ def test_discover_exe_on_path_non_spec_name_match(mocker: MockerFixture) -> None
|
|
|
287
312
|
assert CURRENT.satisfies(spec, impl_must_match=True) is True
|
|
288
313
|
|
|
289
314
|
|
|
315
|
+
def test_satisfies_debug_spec_requires_debug_build(mocker: MockerFixture) -> None:
|
|
316
|
+
spec = PythonSpec.from_string_spec(f"python{CURRENT.version_info.major}.{CURRENT.version_info.minor}-dbg")
|
|
317
|
+
assert spec.debug is True
|
|
318
|
+
mocker.patch.object(CURRENT, "free_threaded", spec.free_threaded)
|
|
319
|
+
|
|
320
|
+
mocker.patch.object(CURRENT, "debug_build", False)
|
|
321
|
+
assert CURRENT.satisfies(spec, impl_must_match=True) is False
|
|
322
|
+
|
|
323
|
+
mocker.patch.object(CURRENT, "debug_build", True)
|
|
324
|
+
assert CURRENT.satisfies(spec, impl_must_match=True) is True
|
|
325
|
+
|
|
326
|
+
|
|
290
327
|
def test_discover_exe_on_path_non_spec_name_not_match(mocker: MockerFixture) -> None:
|
|
291
328
|
suffixed_name = f"python{CURRENT.version_info.major}.{CURRENT.version_info.minor}m"
|
|
292
329
|
if sys.platform == "win32": # pragma: win32 cover
|
|
@@ -431,7 +468,10 @@ def test_py_info_debug_build_exe_names(*, debug: bool) -> None:
|
|
|
431
468
|
info = copy.deepcopy(CURRENT)
|
|
432
469
|
info.debug_build = debug
|
|
433
470
|
names = info._find_possible_exe_names()
|
|
471
|
+
abiflag_name = f"python{info.version_info.major}.{info.version_info.minor}d"
|
|
434
472
|
assert any("_d" in n for n in names) is debug
|
|
473
|
+
assert any(n.endswith("-dbg") for n in names) is debug
|
|
474
|
+
assert (abiflag_name in names) is debug
|
|
435
475
|
|
|
436
476
|
|
|
437
477
|
def test_py_info_debug_build_json_round_trip() -> None:
|
|
@@ -186,6 +186,7 @@ def test_specifier_satisfies_with_partial_information() -> None:
|
|
|
186
186
|
def test_spec_parse_machine(spec_str: str, expected_machine: str | None) -> None:
|
|
187
187
|
spec = PythonSpec.from_string_spec(spec_str)
|
|
188
188
|
assert spec.machine == expected_machine
|
|
189
|
+
assert spec.debug is None # the debug marker sits before -arch/-machine and must not swallow the ISA
|
|
189
190
|
|
|
190
191
|
|
|
191
192
|
@pytest.mark.parametrize(
|
|
@@ -202,6 +203,31 @@ def test_spec_parse_arch_and_machine_together(spec_str: str, expected_arch: int,
|
|
|
202
203
|
assert spec.machine == expected_machine
|
|
203
204
|
|
|
204
205
|
|
|
206
|
+
@pytest.mark.parametrize(
|
|
207
|
+
"spec_str",
|
|
208
|
+
[
|
|
209
|
+
pytest.param("python3.13-dbg", id="debian-dbg"),
|
|
210
|
+
pytest.param("python3.13-debug", id="debian-debug"),
|
|
211
|
+
pytest.param("python3.13d", id="abi-flag"),
|
|
212
|
+
pytest.param("cpython3.13d", id="impl-abi-flag"),
|
|
213
|
+
pytest.param("python3.13td", id="free-threaded-debug"),
|
|
214
|
+
],
|
|
215
|
+
)
|
|
216
|
+
def test_spec_parse_debug(spec_str: str) -> None:
|
|
217
|
+
spec = PythonSpec.from_string_spec(spec_str)
|
|
218
|
+
|
|
219
|
+
assert spec.debug is True
|
|
220
|
+
assert spec.major == 3
|
|
221
|
+
assert spec.minor == 13
|
|
222
|
+
assert spec.machine is None
|
|
223
|
+
assert spec.path is None
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
@pytest.mark.parametrize("spec_str", ["python3.13", "python"])
|
|
227
|
+
def test_spec_parse_debug_unconstrained_without_marker(spec_str: str) -> None:
|
|
228
|
+
assert PythonSpec.from_string_spec(spec_str).debug is None
|
|
229
|
+
|
|
230
|
+
|
|
205
231
|
@pytest.mark.parametrize(
|
|
206
232
|
("left", "right", "expected"),
|
|
207
233
|
[
|
|
@@ -2,6 +2,8 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from unittest.mock import MagicMock
|
|
4
4
|
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
5
7
|
from python_discovery import PythonSpec
|
|
6
8
|
|
|
7
9
|
|
|
@@ -86,6 +88,13 @@ def test_generate_re_with_threaded() -> None:
|
|
|
86
88
|
assert pat.fullmatch("python3.12t") is not None
|
|
87
89
|
|
|
88
90
|
|
|
91
|
+
@pytest.mark.parametrize("name", ["python3.12-dbg", "python3.12d", "python3.12"])
|
|
92
|
+
def test_generate_re_with_debug(name: str) -> None:
|
|
93
|
+
spec = PythonSpec.from_string_spec("python3.12-dbg")
|
|
94
|
+
pat = spec.generate_re(windows=False)
|
|
95
|
+
assert pat.fullmatch(name) is not None
|
|
96
|
+
|
|
97
|
+
|
|
89
98
|
def test_single_digit_version() -> None:
|
|
90
99
|
spec = PythonSpec.from_string_spec("python3")
|
|
91
100
|
assert spec.major == 3
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
github: gaborbernat
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_discovery-1.4.3 → python_discovery-1.4.4}/tests/py_info/test_py_info_exe_based_of.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|