python-discovery 1.4.1__tar.gz → 1.4.3__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.1 → python_discovery-1.4.3}/.github/workflows/check.yaml +1 -1
- {python_discovery-1.4.1 → python_discovery-1.4.3}/.github/workflows/prepare-release.yaml +1 -1
- {python_discovery-1.4.1 → python_discovery-1.4.3}/.github/workflows/release.yaml +1 -1
- {python_discovery-1.4.1 → python_discovery-1.4.3}/.pre-commit-config.yaml +5 -5
- {python_discovery-1.4.1 → python_discovery-1.4.3}/PKG-INFO +1 -1
- {python_discovery-1.4.1 → python_discovery-1.4.3}/docs/changelog.rst +20 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/pyproject.toml +2 -1
- {python_discovery-1.4.1 → python_discovery-1.4.3}/src/python_discovery/_py_info.py +17 -3
- {python_discovery-1.4.1 → python_discovery-1.4.3}/tests/test_py_info_extra.py +28 -3
- {python_discovery-1.4.1 → python_discovery-1.4.3}/.github/FUNDING.yaml +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/.github/SECURITY.md +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/.github/dependabot.yaml +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/.github/release.yaml +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/.gitignore +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/.readthedocs.yaml +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/.readthedocs.yml +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/CODE_OF_CONDUCT.md +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/LICENSE +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/README.md +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/docs/_static/custom.css +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/docs/_static/logo.svg +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/docs/changelog/template.jinja2 +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/docs/conf.py +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/docs/explanation.rst +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/docs/how-to/standalone-usage.rst +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/docs/index.rst +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/docs/reference/api.rst +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/docs/reference/environment-variables.rst +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/docs/tutorial/getting-started.rst +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/src/python_discovery/__init__.py +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/src/python_discovery/_cache.py +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/src/python_discovery/_cached_py_info.py +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/src/python_discovery/_compat.py +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/src/python_discovery/_discovery.py +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/src/python_discovery/_py_spec.py +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/src/python_discovery/_specifier.py +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/src/python_discovery/_windows/__init__.py +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/src/python_discovery/_windows/_pep514.py +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/src/python_discovery/_windows/_propose.py +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/src/python_discovery/py.typed +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/tasks/release.py +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/tests/conftest.py +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/tests/py_info/test_py_info.py +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/tests/py_info/test_py_info_exe_based_of.py +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/tests/test_cache.py +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/tests/test_cached_py_info.py +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/tests/test_discovery.py +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/tests/test_discovery_extra.py +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/tests/test_iter_interpreters.py +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/tests/test_py_spec.py +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/tests/test_py_spec_extra.py +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/tests/test_specifier.py +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/tests/windows/conftest.py +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/tests/windows/test_windows.py +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/tests/windows/test_windows_pep514.py +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/tests/windows/winreg_mock_values.py +0 -0
- {python_discovery-1.4.1 → python_discovery-1.4.3}/tox.toml +0 -0
|
@@ -26,7 +26,7 @@ jobs:
|
|
|
26
26
|
permissions:
|
|
27
27
|
contents: write
|
|
28
28
|
steps:
|
|
29
|
-
- uses: actions/checkout@
|
|
29
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
30
30
|
with:
|
|
31
31
|
fetch-depth: 0
|
|
32
32
|
token: ${{ secrets.RELEASE_PAT }}
|
|
@@ -13,7 +13,7 @@ jobs:
|
|
|
13
13
|
build:
|
|
14
14
|
runs-on: ubuntu-latest
|
|
15
15
|
steps:
|
|
16
|
-
- uses: actions/checkout@
|
|
16
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
17
17
|
with:
|
|
18
18
|
fetch-depth: 0
|
|
19
19
|
persist-credentials: false
|
|
@@ -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.3
|
|
9
9
|
hooks:
|
|
10
10
|
- id: check-github-workflows
|
|
11
11
|
args: ["--verbose"]
|
|
@@ -15,24 +15,24 @@ repos:
|
|
|
15
15
|
- id: codespell
|
|
16
16
|
additional_dependencies: ["tomli>=2.4"]
|
|
17
17
|
- repo: https://github.com/tox-dev/pyproject-fmt
|
|
18
|
-
rev: "v2.
|
|
18
|
+
rev: "v2.25.1"
|
|
19
19
|
hooks:
|
|
20
20
|
- id: pyproject-fmt
|
|
21
21
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
22
|
-
rev: "v0.15.
|
|
22
|
+
rev: "v0.15.20"
|
|
23
23
|
hooks:
|
|
24
24
|
- id: ruff-format
|
|
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.
|
|
28
|
+
rev: "v3.9.1"
|
|
29
29
|
hooks:
|
|
30
30
|
- id: prettier
|
|
31
31
|
additional_dependencies:
|
|
32
32
|
- prettier@3.8.1
|
|
33
33
|
- "@prettier/plugin-xml@3.4.2"
|
|
34
34
|
- repo: https://github.com/zizmorcore/zizmor-pre-commit
|
|
35
|
-
rev: v1.
|
|
35
|
+
rev: v1.26.1
|
|
36
36
|
hooks:
|
|
37
37
|
- id: zizmor
|
|
38
38
|
- repo: meta
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-discovery
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.3
|
|
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,26 @@
|
|
|
6
6
|
|
|
7
7
|
.. towncrier release notes start
|
|
8
8
|
|
|
9
|
+
********************
|
|
10
|
+
v1.4.3 (2026-07-03)
|
|
11
|
+
********************
|
|
12
|
+
|
|
13
|
+
Packaging updates and notes for downstreams - 1.4.3
|
|
14
|
+
===================================================
|
|
15
|
+
- Constrain the ``hatchling`` build requirement per Python version so sdist builds resolve a compatible backend on the
|
|
16
|
+
declared ``>=3.8`` floor - ``hatchling>=1.28`` dropped Python 3.9, leaving 3.8/3.9 unbuildable from source - by
|
|
17
|
+
:user:`gaborbernat`. (:issue:`92`)
|
|
18
|
+
|
|
19
|
+
********************
|
|
20
|
+
v1.4.2 (2026-06-11)
|
|
21
|
+
********************
|
|
22
|
+
|
|
23
|
+
Bug fixes - 1.4.2
|
|
24
|
+
=================
|
|
25
|
+
- Stop executable symlink resolution once the stdlib landmark is reachable and keep macOS framework builds untouched,
|
|
26
|
+
matching ``getpath`` - Homebrew interpreters no longer get version-pinned ``Cellar`` paths recorded and stable
|
|
27
|
+
aliases such as Debian's ``/usr/bin/python3`` are preserved - by :user:`gaborbernat`. (:issue:`86`)
|
|
28
|
+
|
|
9
29
|
********************
|
|
10
30
|
v1.4.1 (2026-06-11)
|
|
11
31
|
********************
|
|
@@ -225,21 +225,28 @@ class PythonInfo: # noqa: PLR0904
|
|
|
225
225
|
# Try fallback for POSIX virtual environments
|
|
226
226
|
return self._try_posix_fallback_executable(base_executable) # pragma: >=3.11 cover
|
|
227
227
|
|
|
228
|
-
def _resolve_executable_symlink(self, path: str) -> str:
|
|
228
|
+
def _resolve_executable_symlink(self, path: str, *, framework: bool | None = None) -> str:
|
|
229
229
|
"""
|
|
230
230
|
Resolve symlinks of the executable itself, but never of its parent directories.
|
|
231
231
|
|
|
232
232
|
Mirrors CPython's ``getpath.realpath`` (and ``venv`` in python/cpython#115237): an executable-only symlink
|
|
233
233
|
resolves to the real interpreter so its home can be located, while a fully symlinked interpreter tree is
|
|
234
|
-
kept as-is.
|
|
234
|
+
kept as-is. Like ``getpath``, resolution stops as soon as the stdlib landmark is reachable from the current
|
|
235
|
+
directory - an alias such as Debian's ``/usr/bin/python3`` is a usable home and stays untouched.
|
|
235
236
|
"""
|
|
236
237
|
result = os.path.abspath(path)
|
|
237
238
|
if self.os != "posix": # CPython only does this where HAVE_READLINK
|
|
238
239
|
return result
|
|
240
|
+
if framework is None:
|
|
241
|
+
framework = bool(sysconfig.get_config_var("PYTHONFRAMEWORK"))
|
|
242
|
+
if framework: # macOS framework builds self-locate via dyld from the real binary; e.g. for Homebrew
|
|
243
|
+
return result # resolving would pin the versioned Cellar path into the recorded home
|
|
239
244
|
real_path = os.path.realpath(result)
|
|
240
245
|
if not os.path.exists(real_path): # symlink loop or broken symlink
|
|
241
246
|
return result
|
|
242
247
|
while os.path.islink(result):
|
|
248
|
+
if self._stdlib_landmark_exists(os.path.dirname(result)):
|
|
249
|
+
return result
|
|
243
250
|
link = os.readlink(result)
|
|
244
251
|
candidate = link if os.path.isabs(link) else os.path.normpath(os.path.join(os.path.dirname(result), link))
|
|
245
252
|
# normpath through a symlinked directory may point at a different file - stop resolving there
|
|
@@ -248,6 +255,13 @@ class PythonInfo: # noqa: PLR0904
|
|
|
248
255
|
result = candidate
|
|
249
256
|
return result
|
|
250
257
|
|
|
258
|
+
@staticmethod
|
|
259
|
+
def _stdlib_landmark_exists(dir_path: str) -> bool:
|
|
260
|
+
lib_name = os.path.basename(os.path.dirname(os.__file__))
|
|
261
|
+
return any(
|
|
262
|
+
os.path.exists(os.path.join(dir_path, os.pardir, lib, lib_name, "os.py")) for lib in ("lib", "lib64")
|
|
263
|
+
)
|
|
264
|
+
|
|
251
265
|
def _try_posix_fallback_executable(self, base_executable: str) -> str | None:
|
|
252
266
|
"""Find a versioned Python binary as fallback for POSIX virtual environments."""
|
|
253
267
|
major, minor = self.version_info.major, self.version_info.minor
|
|
@@ -415,7 +429,7 @@ class PythonInfo: # noqa: PLR0904
|
|
|
415
429
|
|
|
416
430
|
@property
|
|
417
431
|
def machine(self) -> str:
|
|
418
|
-
"""
|
|
432
|
+
"""The instruction set architecture (ISA) derived from :func:`sysconfig.get_platform`."""
|
|
419
433
|
plat = self.sysconfig_platform
|
|
420
434
|
if plat is None:
|
|
421
435
|
return "unknown"
|
|
@@ -183,11 +183,36 @@ def test_resolve_executable_symlink(
|
|
|
183
183
|
layout: Callable[[Path], tuple[Path, Path]],
|
|
184
184
|
) -> None:
|
|
185
185
|
path, expected = layout(tmp_path)
|
|
186
|
-
assert posix_info._resolve_executable_symlink(str(path)) == str(expected)
|
|
186
|
+
assert posix_info._resolve_executable_symlink(str(path), framework=False) == str(expected)
|
|
187
187
|
|
|
188
188
|
|
|
189
189
|
@pytest.mark.skipif(sys.platform == "win32", reason="POSIX only")
|
|
190
|
-
def
|
|
190
|
+
def test_resolve_executable_symlink_framework_kept(tmp_path: Path, posix_info: PythonInfo) -> None:
|
|
191
|
+
link, _exe = _layout_absolute_symlink(tmp_path)
|
|
192
|
+
assert posix_info._resolve_executable_symlink(str(link), framework=True) == str(link)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
@pytest.mark.skipif(sys.platform == "win32", reason="POSIX only")
|
|
196
|
+
def test_resolve_executable_symlink_stdlib_landmark_kept(tmp_path: Path, posix_info: PythonInfo) -> None:
|
|
197
|
+
exe = tmp_path / "install" / "bin" / "python3.12"
|
|
198
|
+
exe.parent.mkdir(parents=True)
|
|
199
|
+
exe.touch()
|
|
200
|
+
alias_bin = tmp_path / "alias" / "bin"
|
|
201
|
+
alias_bin.mkdir(parents=True)
|
|
202
|
+
landmark = tmp_path / "alias" / "lib" / Path(os.__file__).parent.name / "os.py"
|
|
203
|
+
landmark.parent.mkdir(parents=True)
|
|
204
|
+
landmark.touch()
|
|
205
|
+
link = alias_bin / "python3"
|
|
206
|
+
link.symlink_to(exe)
|
|
207
|
+
assert posix_info._resolve_executable_symlink(str(link), framework=False) == str(link)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
@pytest.mark.skipif(sys.platform == "win32", reason="POSIX only")
|
|
211
|
+
@pytest.mark.skipif(bool(CURRENT.sysconfig_vars.get("PYTHONFRAMEWORK")), reason="framework builds keep recorded path")
|
|
212
|
+
def test_from_exe_resolves_executable_only_symlink( # pragma: no cover # skipped on framework interpreter hosts
|
|
213
|
+
tmp_path: Path,
|
|
214
|
+
session_cache: DiskCache,
|
|
215
|
+
) -> None:
|
|
191
216
|
system_exe = CURRENT.system_executable
|
|
192
217
|
assert system_exe is not None
|
|
193
218
|
link = tmp_path / "python3"
|
|
@@ -196,7 +221,7 @@ def test_from_exe_resolves_executable_only_symlink(tmp_path: Path, session_cache
|
|
|
196
221
|
assert info is not None
|
|
197
222
|
assert info.system_executable is not None
|
|
198
223
|
assert Path(info.system_executable).samefile(system_exe)
|
|
199
|
-
assert
|
|
224
|
+
assert Path(info.system_executable).parent != tmp_path
|
|
200
225
|
|
|
201
226
|
|
|
202
227
|
def test_try_posix_fallback_not_posix() -> None:
|
|
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
|
|
File without changes
|
|
File without changes
|
{python_discovery-1.4.1 → python_discovery-1.4.3}/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
|
|
File without changes
|