python-discovery 1.4.0__py3-none-any.whl → 1.4.2__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.
@@ -204,7 +204,7 @@ class PythonInfo: # noqa: PLR0904
204
204
  # if we're not in a virtual environment, this is already a system python, so return the original executable
205
205
  # note we must choose the original and not the pure executable as shim scripts might throw us off
206
206
  if not (self.real_prefix or (self.base_prefix is not None and self.base_prefix != self.prefix)):
207
- return self.original_executable
207
+ return self._resolve_executable_symlink(self.original_executable)
208
208
 
209
209
  # if this is NOT a virtual environment, can't determine easily, bail out
210
210
  if self.real_prefix is not None:
@@ -220,11 +220,48 @@ class PythonInfo: # noqa: PLR0904
220
220
 
221
221
  # We're not in a venv and base_executable exists; use it directly
222
222
  if os.path.exists(base_executable): # pragma: >=3.11 cover
223
- return base_executable
223
+ return self._resolve_executable_symlink(base_executable)
224
224
 
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, *, framework: bool | None = None) -> 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. 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.
236
+ """
237
+ result = os.path.abspath(path)
238
+ if self.os != "posix": # CPython only does this where HAVE_READLINK
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
244
+ real_path = os.path.realpath(result)
245
+ if not os.path.exists(real_path): # symlink loop or broken symlink
246
+ return result
247
+ while os.path.islink(result):
248
+ if self._stdlib_landmark_exists(os.path.dirname(result)):
249
+ return result
250
+ link = os.readlink(result)
251
+ candidate = link if os.path.isabs(link) else os.path.normpath(os.path.join(os.path.dirname(result), link))
252
+ # normpath through a symlinked directory may point at a different file - stop resolving there
253
+ if not (os.path.exists(candidate) and os.path.samefile(real_path, candidate)):
254
+ return result
255
+ result = candidate
256
+ return result
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
+
228
265
  def _try_posix_fallback_executable(self, base_executable: str) -> str | None:
229
266
  """Find a versioned Python binary as fallback for POSIX virtual environments."""
230
267
  major, minor = self.version_info.major, self.version_info.minor
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-discovery
3
- Version: 1.4.0
3
+ Version: 1.4.2
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=XZGBI0hVIG54tjgVGX-5ZwNUuDv_RoIhEj0mBsT1R9I,34467
6
+ python_discovery/_py_info.py,sha256=7RAPxig94jjhEU_tqR7rWIgVe4c4sy517qcWylr-nCA,36654
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.4.0.dist-info/METADATA,sha256=VtlySRmCB_wZLirKeBUcSOMxKPufldx-ULtqt90Rd5k,5592
14
- python_discovery-1.4.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
15
- python_discovery-1.4.0.dist-info/licenses/LICENSE,sha256=kOJeH68qSq6o2V7o5_VziwUqGswMnFgGgQH5Mahr1Yg,1023
16
- python_discovery-1.4.0.dist-info/RECORD,,
13
+ python_discovery-1.4.2.dist-info/METADATA,sha256=Okvw-dTP972zFGoyhkWappE0MFoWJrExjbGjI-Wok_I,5592
14
+ python_discovery-1.4.2.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
15
+ python_discovery-1.4.2.dist-info/licenses/LICENSE,sha256=kOJeH68qSq6o2V7o5_VziwUqGswMnFgGgQH5Mahr1Yg,1023
16
+ python_discovery-1.4.2.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