python-discovery 1.3.1__py3-none-any.whl → 1.3.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.
@@ -314,13 +314,7 @@ class LazyPathDump:
314
314
  content += " with =>"
315
315
  for file_path in self.path.iterdir():
316
316
  try:
317
- if file_path.is_dir():
318
- continue
319
- if IS_WIN: # pragma: win32 cover
320
- pathext = self.env.get("PATHEXT", ".COM;.EXE;.BAT;.CMD").split(";")
321
- if not any(file_path.name.upper().endswith(ext) for ext in pathext):
322
- continue
323
- elif not (file_path.stat().st_mode & os.X_OK):
317
+ if not self._is_executable(file_path):
324
318
  continue
325
319
  except OSError:
326
320
  pass
@@ -328,6 +322,14 @@ class LazyPathDump:
328
322
  content += file_path.name
329
323
  return content
330
324
 
325
+ def _is_executable(self, file_path: Path) -> bool:
326
+ if file_path.is_dir():
327
+ return False
328
+ if IS_WIN: # pragma: win32 cover
329
+ pathext = self.env.get("PATHEXT", ".COM;.EXE;.BAT;.CMD").split(";")
330
+ return any(file_path.name.upper().endswith(ext) for ext in pathext)
331
+ return bool(file_path.stat().st_mode & os.X_OK)
332
+
331
333
 
332
334
  def path_exe_finder(
333
335
  spec: PythonSpec, *, all_implementations: bool = False
@@ -16,6 +16,7 @@ from string import digits
16
16
  from typing import TYPE_CHECKING, ClassVar, Final, NamedTuple
17
17
 
18
18
  if TYPE_CHECKING:
19
+ import tkinter as tk
19
20
  from collections.abc import Generator, Mapping
20
21
 
21
22
  from ._cache import PyInfoCache
@@ -165,42 +166,37 @@ class PythonInfo: # noqa: PLR0904
165
166
  try:
166
167
  tcl = tk.Tcl()
167
168
  tcl_lib = tcl.eval("info library")
168
-
169
- # Try to get TK library path directly first
170
- try:
171
- tk_lib = tcl.eval("set tk_library")
172
- if tk_lib and os.path.isdir(tk_lib):
173
- pass # We found it directly
174
- else:
175
- tk_lib = None # Reset if invalid
176
- except tk.TclError:
177
- tk_lib = None
178
-
179
- # If direct query failed, try constructing the path
180
- if tk_lib is None:
181
- tk_version = tcl.eval("package require Tk")
182
- tcl_parent = os.path.dirname(tcl_lib)
183
-
184
- # Try different version formats
185
- version_variants = [
186
- tk_version, # Full version like "8.6.12"
187
- ".".join(tk_version.split(".")[:2]), # Major.minor like "8.6"
188
- tk_version.split(".")[0], # Just major like "8"
189
- ]
190
-
191
- for version in version_variants:
192
- tk_lib_path = os.path.join(tcl_parent, f"tk{version}")
193
- if not os.path.isdir(tk_lib_path):
194
- continue
195
- if os.path.exists(os.path.join(tk_lib_path, "tk.tcl")):
196
- tk_lib = tk_lib_path
197
- break
198
-
169
+ tk_lib = PythonInfo._resolve_tk_lib(tcl, tcl_lib)
199
170
  except tk.TclError:
200
171
  pass
201
172
 
202
173
  return tcl_lib, tk_lib
203
174
 
175
+ @staticmethod
176
+ def _query_tk_library(tcl: tk.Tk) -> str | None: # pragma: no cover
177
+ """Try to get the TK library path directly from Tcl."""
178
+ import tkinter as tk # noqa: PLC0415
179
+
180
+ try:
181
+ if (tk_lib := tcl.eval("set tk_library")) and os.path.isdir(tk_lib):
182
+ return tk_lib
183
+ except tk.TclError:
184
+ pass
185
+ return None
186
+
187
+ @staticmethod
188
+ def _resolve_tk_lib(tcl: tk.Tk, tcl_lib: str) -> str | None: # pragma: no cover
189
+ """Resolve the TK library path by direct query or path construction."""
190
+ if (tk_lib := PythonInfo._query_tk_library(tcl)) is not None:
191
+ return tk_lib
192
+ tk_version = tcl.eval("package require Tk")
193
+ tcl_parent = os.path.dirname(tcl_lib)
194
+ for version in (tk_version, ".".join(tk_version.split(".")[:2]), tk_version.split(".")[0]):
195
+ tk_lib_path = os.path.join(tcl_parent, f"tk{version}")
196
+ if os.path.isdir(tk_lib_path) and os.path.exists(os.path.join(tk_lib_path, "tk.tcl")):
197
+ return tk_lib_path
198
+ return None
199
+
204
200
  def _fast_get_system_executable(self) -> str | None:
205
201
  """Try to get the system executable by just looking at properties."""
206
202
  # if we're not in a virtual environment, this is already a system python, so return the original executable
@@ -17,7 +17,7 @@ PATTERN = re.compile(
17
17
  (?P<version>[0-9.]+)? # version (e.g. 3.12, 3.12.1)
18
18
  (?P<threaded>t)? # free-threaded flag
19
19
  (?:-(?P<arch>32|64))? # architecture bitness
20
- (?:-(?P<machine>[a-zA-Z0-9_]+))? # ISA (e.g. arm64, x86_64)
20
+ (?:-(?P<machine>[a-zA-Z0-9_.]+))? # ISA (e.g. arm64, x86_64, i86pc.64bit)
21
21
  $
22
22
  """,
23
23
  re.VERBOSE,
@@ -124,23 +124,23 @@ def load_exe(hive_name: str, company: str, company_key: Any, tag: str) -> tuple[
124
124
  key_path = f"{hive_name}/{company}/{tag}"
125
125
  try:
126
126
  with winreg.OpenKeyEx(company_key, rf"{tag}\InstallPath") as ip_key, ip_key: # ty: ignore[unresolved-attribute]
127
- exe = get_value(ip_key, "ExecutablePath")
128
- if exe is None:
129
- ip = get_value(ip_key, None)
130
- if ip is None:
131
- msg(key_path, "no ExecutablePath or default for it")
132
-
133
- else:
134
- exe = os.path.join(ip, "python.exe")
135
- if exe is not None and os.path.exists(exe):
136
- args = get_value(ip_key, "ExecutableArguments")
137
- return exe, args
127
+ if (exe := _resolve_exe(ip_key, key_path)) is not None and os.path.exists(exe):
128
+ return exe, get_value(ip_key, "ExecutableArguments")
138
129
  msg(key_path, f"could not load exe with value {exe}")
139
130
  except OSError:
140
131
  msg(f"{key_path}/InstallPath", "missing")
141
132
  return None
142
133
 
143
134
 
135
+ def _resolve_exe(ip_key: Any, key_path: str) -> str | None: # noqa: ANN401
136
+ if (exe := get_value(ip_key, "ExecutablePath")) is not None:
137
+ return exe
138
+ if (ip := get_value(ip_key, None)) is None:
139
+ msg(key_path, "no ExecutablePath or default for it")
140
+ return None
141
+ return os.path.join(ip, "python.exe")
142
+
143
+
144
144
  def load_arch_data(hive_name: str, company: str, tag: str, tag_key: Any, default_arch: int) -> int | None: # noqa: ANN401
145
145
  arch_str = get_value(tag_key, "SysArchitecture")
146
146
  if arch_str is not None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-discovery
3
- Version: 1.3.1
3
+ Version: 1.3.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
@@ -2,15 +2,15 @@ python_discovery/__init__.py,sha256=Hi4j3pMvC8Fo8KoNL-wOHsPSCUHwY4c0qvv5sjnFzME,
2
2
  python_discovery/_cache.py,sha256=bG4fD-h2pdPmb4v9BldbXmJ5Tlt1boiH1Qrz8kp9Dds,5063
3
3
  python_discovery/_cached_py_info.py,sha256=1AFEoc_-OaCi1PXmaFz9Iy_bEuqOhJdFSDFHljzD-EE,8540
4
4
  python_discovery/_compat.py,sha256=iqxGuhK9rPaiui80G1H1KrweoMJ16gyLYEmUrSXrp5c,709
5
- python_discovery/_discovery.py,sha256=tlcmGDsOL8zitSSQSws9cXYt6MKT6ENiVtfAjQBzBtw,16820
6
- python_discovery/_py_info.py,sha256=Z6wFPjiQTxiAWMxguWKw-iP7oTurGcT3pKDpL8I3imc,34535
7
- python_discovery/_py_spec.py,sha256=apFN9Eux4BPMLE2LWd9WBCS3XbM7zu7H9V1-iIBpa6I,10407
5
+ python_discovery/_discovery.py,sha256=cdo6xlktNMEwsQn2R8pn4T1VDE4LD2EJPUyCT7vlHnM,16830
6
+ python_discovery/_py_info.py,sha256=ls4ZOe93RCsfjN2uEz1r4l3K8m-rtm3ZKRp59xJDJHg,34354
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
- python_discovery/_windows/_pep514.py,sha256=JYSQ5tOYbSMOKtVVfOEkyYskBhEoTsImNr3DJcv7XNE,7658
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.1.dist-info/METADATA,sha256=1NNrmgsOvtUf7FM3R7Al-ByD1bo4s3WQ5UtkWeYv-lw,5592
14
- python_discovery-1.3.1.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
15
- python_discovery-1.3.1.dist-info/licenses/LICENSE,sha256=kOJeH68qSq6o2V7o5_VziwUqGswMnFgGgQH5Mahr1Yg,1023
16
- python_discovery-1.3.1.dist-info/RECORD,,
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,,