python-discovery 1.3.2__py3-none-any.whl → 1.4.1__py3-none-any.whl

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.
@@ -12,6 +12,7 @@ import sys
12
12
  import sysconfig
13
13
  import warnings
14
14
  from collections import OrderedDict
15
+ from itertools import product
15
16
  from string import digits
16
17
  from typing import TYPE_CHECKING, ClassVar, Final, NamedTuple
17
18
 
@@ -73,6 +74,7 @@ class PythonInfo: # noqa: PLR0904
73
74
  self.version = sys.version
74
75
  self.os = os.name
75
76
  self.free_threaded = sysconfig.get_config_var("Py_GIL_DISABLED") == 1
77
+ self.debug_build = bool(sysconfig.get_config_var("Py_DEBUG"))
76
78
 
77
79
  def _init_prefixes(self) -> None:
78
80
  def abs_path(value: str | None) -> str | None:
@@ -202,7 +204,7 @@ class PythonInfo: # noqa: PLR0904
202
204
  # if we're not in a virtual environment, this is already a system python, so return the original executable
203
205
  # note we must choose the original and not the pure executable as shim scripts might throw us off
204
206
  if not (self.real_prefix or (self.base_prefix is not None and self.base_prefix != self.prefix)):
205
- return self.original_executable
207
+ return self._resolve_executable_symlink(self.original_executable)
206
208
 
207
209
  # if this is NOT a virtual environment, can't determine easily, bail out
208
210
  if self.real_prefix is not None:
@@ -218,11 +220,34 @@ class PythonInfo: # noqa: PLR0904
218
220
 
219
221
  # We're not in a venv and base_executable exists; use it directly
220
222
  if os.path.exists(base_executable): # pragma: >=3.11 cover
221
- return base_executable
223
+ return self._resolve_executable_symlink(base_executable)
222
224
 
223
225
  # Try fallback for POSIX virtual environments
224
226
  return self._try_posix_fallback_executable(base_executable) # pragma: >=3.11 cover
225
227
 
228
+ def _resolve_executable_symlink(self, path: str) -> str:
229
+ """
230
+ Resolve symlinks of the executable itself, but never of its parent directories.
231
+
232
+ Mirrors CPython's ``getpath.realpath`` (and ``venv`` in python/cpython#115237): an executable-only symlink
233
+ resolves to the real interpreter so its home can be located, while a fully symlinked interpreter tree is
234
+ kept as-is.
235
+ """
236
+ result = os.path.abspath(path)
237
+ if self.os != "posix": # CPython only does this where HAVE_READLINK
238
+ return result
239
+ real_path = os.path.realpath(result)
240
+ if not os.path.exists(real_path): # symlink loop or broken symlink
241
+ return result
242
+ while os.path.islink(result):
243
+ link = os.readlink(result)
244
+ candidate = link if os.path.isabs(link) else os.path.normpath(os.path.join(os.path.dirname(result), link))
245
+ # normpath through a symlinked directory may point at a different file - stop resolving there
246
+ if not (os.path.exists(candidate) and os.path.samefile(real_path, candidate)):
247
+ return result
248
+ result = candidate
249
+ return result
250
+
226
251
  def _try_posix_fallback_executable(self, base_executable: str) -> str | None:
227
252
  """Find a versioned Python binary as fallback for POSIX virtual environments."""
228
253
  major, minor = self.version_info.major, self.version_info.minor
@@ -404,10 +429,11 @@ class PythonInfo: # noqa: PLR0904
404
429
  @property
405
430
  def spec(self) -> str:
406
431
  """A specification string identifying this interpreter (e.g. ``CPython3.13.2-64-arm64``)."""
407
- return "{}{}{}-{}-{}".format(
432
+ return "{}{}{}{}-{}-{}".format(
408
433
  self.implementation,
409
434
  ".".join(str(i) for i in self.version_info),
410
435
  "t" if self.free_threaded else "",
436
+ "d" if self.debug_build else "",
411
437
  self.architecture,
412
438
  self.machine,
413
439
  )
@@ -731,17 +757,14 @@ class PythonInfo: # noqa: PLR0904
731
757
 
732
758
  def _find_possible_exe_names(self) -> list[str]:
733
759
  name_candidate = OrderedDict()
760
+ mods = ["", "t"] if self.free_threaded else [""]
761
+ debug_suffixes = ["_d", ""] if self.debug_build else [""]
762
+ archs = [f"-{self.architecture}", ""]
734
763
  for name in self._possible_base():
735
764
  for at in (3, 2, 1, 0):
736
765
  version = ".".join(str(i) for i in self.version_info[:at])
737
- mods = [""]
738
- if self.free_threaded:
739
- mods.append("t")
740
- for mod in mods:
741
- for arch in [f"-{self.architecture}", ""]:
742
- for ext in EXTENSIONS:
743
- candidate = f"{name}{version}{mod}{arch}{ext}"
744
- name_candidate[candidate] = None
766
+ for mod, debug, arch, ext in product(mods, debug_suffixes, archs, EXTENSIONS):
767
+ name_candidate[f"{name}{version}{mod}{debug}{arch}{ext}"] = None
745
768
  return list(name_candidate.keys())
746
769
 
747
770
  def _possible_base(self) -> Generator[str, None, None]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-discovery
3
- Version: 1.3.2
3
+ Version: 1.4.1
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
@@ -3,14 +3,14 @@ python_discovery/_cache.py,sha256=bG4fD-h2pdPmb4v9BldbXmJ5Tlt1boiH1Qrz8kp9Dds,50
3
3
  python_discovery/_cached_py_info.py,sha256=1AFEoc_-OaCi1PXmaFz9Iy_bEuqOhJdFSDFHljzD-EE,8540
4
4
  python_discovery/_compat.py,sha256=iqxGuhK9rPaiui80G1H1KrweoMJ16gyLYEmUrSXrp5c,709
5
5
  python_discovery/_discovery.py,sha256=cdo6xlktNMEwsQn2R8pn4T1VDE4LD2EJPUyCT7vlHnM,16830
6
- python_discovery/_py_info.py,sha256=ls4ZOe93RCsfjN2uEz1r4l3K8m-rtm3ZKRp59xJDJHg,34354
6
+ python_discovery/_py_info.py,sha256=Pd-rZhulxTWCoEa2SaEH2MLm8oeNzjWzpgAFmZ5oIic,35723
7
7
  python_discovery/_py_spec.py,sha256=Kky0LXi8BU_ojfMdvvRhtDk6I4WPjovwXOu6E2QYHTo,10421
8
8
  python_discovery/_specifier.py,sha256=PTh6suNTC0hMnFSxFYF_vggdciFLnwxoh1YflSbgCXA,10368
9
9
  python_discovery/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  python_discovery/_windows/__init__.py,sha256=qthcyGh36b3WkeSbZ4ECRRYLbCOCaQu5X_DRZ98tXfk,315
11
11
  python_discovery/_windows/_pep514.py,sha256=zXxje07QIXfrCB3eYbZHOBgXcvAQQpZ-ZWun3_zrapA,7688
12
12
  python_discovery/_windows/_propose.py,sha256=iMJf6l0TU4SvuV8UsCHHINAe9L5dUT949xyeyMVdong,1565
13
- python_discovery-1.3.2.dist-info/METADATA,sha256=nmMtjtmWU2DTdKudFjFa6Nrtlwk3OEDbB_BkeRYaw5A,5592
14
- python_discovery-1.3.2.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
15
- python_discovery-1.3.2.dist-info/licenses/LICENSE,sha256=kOJeH68qSq6o2V7o5_VziwUqGswMnFgGgQH5Mahr1Yg,1023
16
- python_discovery-1.3.2.dist-info/RECORD,,
13
+ python_discovery-1.4.1.dist-info/METADATA,sha256=g-gwW8yAGaOFpbnCZoxY4fTQ3KBrGzYYadpCvbZjRcc,5592
14
+ python_discovery-1.4.1.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
15
+ python_discovery-1.4.1.dist-info/licenses/LICENSE,sha256=kOJeH68qSq6o2V7o5_VziwUqGswMnFgGgQH5Mahr1Yg,1023
16
+ python_discovery-1.4.1.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.29.0
2
+ Generator: hatchling 1.30.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any