python-discovery 1.4.3__py3-none-any.whl → 1.5.0__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.
@@ -104,7 +104,7 @@ class DiskContentStore:
104
104
 
105
105
  @contextmanager
106
106
  def locked(self) -> Generator[None]:
107
- from filelock import FileLock # noqa: PLC0415
107
+ from filelock import FileLock # ruff:ignore[import-outside-top-level]
108
108
 
109
109
  lock_path = self._folder / f"{self._key}.lock"
110
110
  lock_path.parent.mkdir(parents=True, exist_ok=True)
@@ -148,10 +148,10 @@ class DiskCache:
148
148
  class NoOpContentStore(ContentStore):
149
149
  """Content store that does nothing -- implements ContentStore protocol."""
150
150
 
151
- def exists(self) -> bool: # noqa: PLR6301
151
+ def exists(self) -> bool: # ruff:ignore[no-self-use]
152
152
  return False
153
153
 
154
- def read(self) -> dict | None: # noqa: PLR6301
154
+ def read(self) -> dict | None: # ruff:ignore[no-self-use]
155
155
  return None
156
156
 
157
157
  def write(self, content: dict) -> None:
@@ -161,14 +161,14 @@ class NoOpContentStore(ContentStore):
161
161
  pass
162
162
 
163
163
  @contextmanager
164
- def locked(self) -> Generator[None]: # noqa: PLR6301
164
+ def locked(self) -> Generator[None]: # ruff:ignore[no-self-use]
165
165
  yield
166
166
 
167
167
 
168
168
  class NoOpCache(PyInfoCache):
169
169
  """Cache that does nothing -- implements PyInfoCache protocol."""
170
170
 
171
- def py_info(self, path: Path) -> NoOpContentStore: # noqa: ARG002, PLR6301
171
+ def py_info(self, path: Path) -> NoOpContentStore: # ruff:ignore[unused-method-argument, no-self-use]
172
172
  return NoOpContentStore()
173
173
 
174
174
  def py_info_clear(self) -> None:
@@ -8,14 +8,14 @@ import logging
8
8
  import os
9
9
  import pkgutil
10
10
  import secrets
11
- import subprocess # noqa: S404
11
+ import subprocess # ruff:ignore[suspicious-subprocess-import]
12
12
  import sys
13
13
  import tempfile
14
14
  from collections import OrderedDict
15
15
  from contextlib import contextmanager
16
16
  from pathlib import Path
17
17
  from shlex import quote
18
- from subprocess import Popen, TimeoutExpired # noqa: S404
18
+ from subprocess import Popen, TimeoutExpired # ruff:ignore[suspicious-subprocess-import]
19
19
  from typing import TYPE_CHECKING, Final
20
20
 
21
21
  from ._cache import NoOpCache
@@ -32,7 +32,7 @@ _CACHE[Path(sys.executable)] = PythonInfo()
32
32
  _LOGGER: Final[logging.Logger] = logging.getLogger(__name__)
33
33
 
34
34
 
35
- def from_exe( # noqa: PLR0913
35
+ def from_exe( # ruff:ignore[too-many-arguments]
36
36
  cls: type[PythonInfo],
37
37
  cache: PyInfoCache | None,
38
38
  exe: str,
@@ -197,7 +197,7 @@ def _run_subprocess(
197
197
  env["PYTHONUTF8"] = "1"
198
198
  _LOGGER.debug("get interpreter info via cmd: %s", LogCmd(cmd))
199
199
  try:
200
- process = Popen( # noqa: S603
200
+ process = Popen( # ruff:ignore[subprocess-without-shell-equals-true]
201
201
  cmd,
202
202
  universal_newlines=True,
203
203
  stdin=subprocess.PIPE,
@@ -89,7 +89,7 @@ def iter_interpreters(
89
89
  yield from _iter_for_spec(spec_str, first_with, cache, env_map, predicate, seen)
90
90
 
91
91
 
92
- def _iter_for_spec( # noqa: PLR0913, PLR0917
92
+ def _iter_for_spec( # ruff:ignore[too-many-arguments, too-many-positional-arguments]
93
93
  spec_str: str | None,
94
94
  try_first_with: tuple[str, ...],
95
95
  cache: PyInfoCache | None,
@@ -231,7 +231,7 @@ def _propose_current_and_windows(
231
231
  yield current_python, True
232
232
 
233
233
  if IS_WIN: # pragma: win32 cover
234
- from ._windows import propose_interpreters as win_propose # noqa: PLC0415
234
+ from ._windows import propose_interpreters as win_propose # ruff:ignore[import-outside-top-level]
235
235
 
236
236
  for interpreter in win_propose(spec, cache, env):
237
237
  if _is_new_exe(str(interpreter.executable), tested_exes):
@@ -49,7 +49,7 @@ _CONF_VAR_RE: Final[re.Pattern[str]] = re.compile(
49
49
  )
50
50
 
51
51
 
52
- class PythonInfo: # noqa: PLR0904
52
+ class PythonInfo: # ruff:ignore[too-many-public-methods]
53
53
  """Contains information for a Python interpreter."""
54
54
 
55
55
  def __init__(self) -> None:
@@ -161,7 +161,7 @@ class PythonInfo: # noqa: PLR0904
161
161
  """Detect the tcl and tk libraries using tkinter."""
162
162
  tcl_lib, tk_lib = None, None
163
163
  try:
164
- import tkinter as tk # noqa: PLC0415
164
+ import tkinter as tk # ruff:ignore[import-outside-top-level]
165
165
  except ImportError:
166
166
  pass
167
167
  else:
@@ -177,7 +177,7 @@ class PythonInfo: # noqa: PLR0904
177
177
  @staticmethod
178
178
  def _query_tk_library(tcl: tk.Tk) -> str | None: # pragma: no cover
179
179
  """Try to get the TK library path directly from Tcl."""
180
- import tkinter as tk # noqa: PLC0415
180
+ import tkinter as tk # ruff:ignore[import-outside-top-level]
181
181
 
182
182
  try:
183
183
  if (tk_lib := tcl.eval("set tk_library")) and os.path.isdir(tk_lib):
@@ -303,8 +303,11 @@ class PythonInfo: # noqa: PLR0904
303
303
  with warnings.catch_warnings(): # disable warning for PEP-632
304
304
  warnings.simplefilter("ignore")
305
305
  try:
306
- from distutils import dist # noqa: PLC0415 # ty: ignore[unresolved-import]
307
- from distutils.command.install import SCHEME_KEYS # noqa: PLC0415 # ty: ignore[unresolved-import]
306
+ # ruff:ignore[import-outside-top-level]
307
+ from distutils import dist # ty: ignore[unresolved-import]
308
+
309
+ # ruff:ignore[import-outside-top-level]
310
+ from distutils.command.install import SCHEME_KEYS # ty: ignore[unresolved-import]
308
311
  except ImportError: # pragma: no cover # if removed or not installed ignore
309
312
  return {}
310
313
 
@@ -312,7 +315,7 @@ class PythonInfo: # noqa: PLR0904
312
315
  "script_args": "--no-user-cfg",
313
316
  }) # conf files not parsed so they do not hijack paths
314
317
  if hasattr(sys, "_framework"): # pragma: no cover # macOS framework builds only
315
- sys._framework = None # noqa: SLF001 # disable macOS static paths for framework
318
+ sys._framework = None # ruff:ignore[private-member-access] # disable macOS static paths for framework
316
319
 
317
320
  with warnings.catch_warnings(): # disable warning for PEP-632
318
321
  warnings.simplefilter("ignore")
@@ -459,12 +462,12 @@ class PythonInfo: # noqa: PLR0904
459
462
 
460
463
  :param cache: the cache store to clear.
461
464
  """
462
- from ._cached_py_info import clear # noqa: PLC0415
465
+ from ._cached_py_info import clear # ruff:ignore[import-outside-top-level]
463
466
 
464
467
  clear(cache)
465
468
  cls._cache_exe_discovery.clear()
466
469
 
467
- def satisfies(self, spec: PythonSpec, *, impl_must_match: bool) -> bool: # noqa: PLR0911
470
+ def satisfies(self, spec: PythonSpec, *, impl_must_match: bool) -> bool: # ruff:ignore[too-many-return-statements]
468
471
  """
469
472
  Check if a given specification can be satisfied by this python interpreter instance.
470
473
 
@@ -481,6 +484,8 @@ class PythonInfo: # noqa: PLR0904
481
484
  return False
482
485
  if spec.free_threaded is not None and spec.free_threaded != self.free_threaded:
483
486
  return False
487
+ if spec.debug is not None and spec.debug != self.debug_build:
488
+ return False
484
489
  if spec.version_specifier is not None and not self._satisfies_version_specifier(spec):
485
490
  return False
486
491
  return all(
@@ -508,7 +513,7 @@ class PythonInfo: # noqa: PLR0904
508
513
  return True
509
514
  version_info = self.version_info
510
515
  for specifier in spec.version_specifier:
511
- assert specifier.version is not None # noqa: S101
516
+ assert specifier.version is not None # ruff:ignore[assert]
512
517
  numeric_version = specifier.version_str
513
518
  for prefix in ("rc", "b", "a"):
514
519
  if prefix in numeric_version:
@@ -518,7 +523,7 @@ class PythonInfo: # noqa: PLR0904
518
523
  release = ".".join(str(c) for c in [version_info.major, version_info.minor, version_info.micro][:precision])
519
524
  if (
520
525
  version_info.releaselevel != "final"
521
- and (precision == 3 or specifier.version.pre_type is not None) # noqa: PLR2004
526
+ and (precision == 3 or specifier.version.pre_type is not None) # ruff:ignore[magic-value-comparison]
522
527
  and (suffix := {"alpha": "a", "beta": "b", "candidate": "rc"}.get(version_info.releaselevel))
523
528
  ):
524
529
  release = f"{release}{suffix}{version_info.serial}"
@@ -571,7 +576,7 @@ class PythonInfo: # noqa: PLR0904
571
576
  return data
572
577
 
573
578
  @classmethod
574
- def from_exe( # noqa: PLR0913
579
+ def from_exe( # ruff:ignore[too-many-arguments]
575
580
  cls,
576
581
  exe: str,
577
582
  cache: PyInfoCache | None = None,
@@ -591,7 +596,7 @@ class PythonInfo: # noqa: PLR0904
591
596
  :param resolve_to_host: resolve through virtualenv layers to the system interpreter.
592
597
  :param env: environment mapping; defaults to :data:`os.environ`.
593
598
  """
594
- from ._cached_py_info import from_exe # noqa: PLC0415
599
+ from ._cached_py_info import from_exe # ruff:ignore[import-outside-top-level]
595
600
 
596
601
  env = os.environ if env is None else env
597
602
  proposed = from_exe(cls, cache, exe, env=env, raise_on_error=raise_on_error, ignore_cache=ignore_cache)
@@ -701,7 +706,7 @@ class PythonInfo: # noqa: PLR0904
701
706
  msg = "failed to detect {} in {}".format("|".join(possible_names), os.pathsep.join(possible_folders))
702
707
  raise RuntimeError(msg)
703
708
 
704
- def _check_exe( # noqa: PLR0913
709
+ def _check_exe( # ruff:ignore[too-many-arguments]
705
710
  self,
706
711
  cache: PyInfoCache | None,
707
712
  folder: str,
@@ -717,7 +722,7 @@ class PythonInfo: # noqa: PLR0904
717
722
  info = self.from_exe(exe_path, cache, resolve_to_host=False, raise_on_error=False, env=env)
718
723
  if info is None: # ignore if for some reason we can't query
719
724
  return None
720
- for item in ["implementation", "architecture", "machine", "version_info"]:
725
+ for item in ["implementation", "architecture", "machine", "version_info", "free_threaded", "debug_build"]:
721
726
  found = getattr(info, item)
722
727
  searched = getattr(self, item)
723
728
  if found != searched:
@@ -772,7 +777,9 @@ class PythonInfo: # noqa: PLR0904
772
777
  def _find_possible_exe_names(self) -> list[str]:
773
778
  name_candidate = OrderedDict()
774
779
  mods = ["", "t"] if self.free_threaded else [""]
775
- debug_suffixes = ["_d", ""] if self.debug_build else [""]
780
+ # _d (Windows python_d.exe), -dbg (Debian's own name), d (the abiflags form used by
781
+ # upstream/Fedora and also shipped by Debian, so pythonX.Yd / pythonX.Ytd resolve too)
782
+ debug_suffixes = ["_d", "-dbg", "d", ""] if self.debug_build else [""]
776
783
  archs = [f"-{self.architecture}", ""]
777
784
  for name in self._possible_base():
778
785
  for at in (3, 2, 1, 0):
@@ -793,7 +800,7 @@ class PythonInfo: # noqa: PLR0904
793
800
  for base in possible_base:
794
801
  lower = base.lower()
795
802
  yield lower
796
- from ._compat import fs_is_case_sensitive # noqa: PLC0415
803
+ from ._compat import fs_is_case_sensitive # ruff:ignore[import-outside-top-level]
797
804
 
798
805
  if fs_is_case_sensitive(): # pragma: no branch
799
806
  if base != lower:
@@ -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
  $
@@ -54,7 +55,7 @@ def _parse_version_parts(version: str) -> tuple[int | None, int | None, int | No
54
55
  raise ValueError(msg)
55
56
  if len(versions) == _MAX_VERSION_PARTS:
56
57
  return versions[0], versions[1], versions[2]
57
- if len(versions) == 2: # noqa: PLR2004
58
+ if len(versions) == 2: # ruff:ignore[magic-value-comparison]
58
59
  return versions[0], versions[1], None
59
60
  version_data = versions[0]
60
61
  major = int(str(version_data)[0])
@@ -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(string_spec, impl, major, minor, micro, arch, None, free_threaded=threaded, machine=machine)
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,12 +121,13 @@ 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``.
123
128
  """
124
129
 
125
- def __init__( # noqa: PLR0913, PLR0917
130
+ def __init__( # ruff:ignore[too-many-arguments, too-many-positional-arguments]
126
131
  self,
127
132
  str_spec: str,
128
133
  implementation: str | 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
 
@@ -225,7 +233,7 @@ class PythonSpec:
225
233
  return len(item.version.release)
226
234
  return None
227
235
 
228
- def satisfies(self, spec: PythonSpec) -> bool: # noqa: PLR0911
236
+ def satisfies(self, spec: PythonSpec) -> bool: # ruff:ignore[too-many-return-statements]
229
237
  """
230
238
  Check if this spec is compatible with the given *spec* (e.g. PEP-514 on Windows).
231
239
 
@@ -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)})"
@@ -92,7 +92,7 @@ class SimpleVersion:
92
92
  def __hash__(self) -> int:
93
93
  return hash((self.release, self.pre_type, self.pre_num))
94
94
 
95
- def __lt__(self, other: object) -> bool: # noqa: PLR0911
95
+ def __lt__(self, other: object) -> bool: # ruff:ignore[too-many-return-statements]
96
96
  if not isinstance(other, SimpleVersion):
97
97
  return NotImplemented
98
98
  if self.release != other.release:
@@ -225,7 +225,7 @@ class SimpleSpecifier:
225
225
  return False
226
226
  if candidate < self.version:
227
227
  return False
228
- if len(self.version.release) >= 2: # noqa: PLR2004 # pragma: no branch # SimpleVersion always has 3-part release
228
+ if len(self.version.release) >= 2: # ruff:ignore[magic-value-comparison] # pragma: no branch # SimpleVersion always has 3-part release
229
229
  upper_parts = list(self.version.release[:-1])
230
230
  upper_parts[-1] += 1
231
231
  upper = SimpleVersion.from_string(".".join(str(p) for p in upper_parts))
@@ -47,7 +47,7 @@ _THREADED_TAG_RE: Final[re.Pattern[str]] = re.compile(
47
47
  )
48
48
 
49
49
 
50
- def enum_keys(key: Any) -> Generator[str, None, None]: # noqa: ANN401
50
+ def enum_keys(key: Any) -> Generator[str, None, None]: # ruff:ignore[any-type]
51
51
  at = 0
52
52
  while True:
53
53
  try:
@@ -57,7 +57,7 @@ def enum_keys(key: Any) -> Generator[str, None, None]: # noqa: ANN401
57
57
  at += 1
58
58
 
59
59
 
60
- def get_value(key: Any, value_name: str | None) -> Any: # noqa: ANN401
60
+ def get_value(key: Any, value_name: str | None) -> Any: # ruff:ignore[any-type]
61
61
  try:
62
62
  return winreg.QueryValueEx(key, value_name)[0] # ty: ignore[unresolved-attribute]
63
63
  except OSError:
@@ -93,7 +93,7 @@ def process_set(
93
93
  def process_company(
94
94
  hive_name: str,
95
95
  company: str,
96
- root_key: Any, # noqa: ANN401
96
+ root_key: Any, # ruff:ignore[any-type]
97
97
  default_arch: int,
98
98
  ) -> Generator[_RegistrySpec, None, None]:
99
99
  with winreg.OpenKeyEx(root_key, company) as company_key: # ty: ignore[unresolved-attribute]
@@ -103,7 +103,7 @@ def process_company(
103
103
  yield spec
104
104
 
105
105
 
106
- def process_tag(hive_name: str, company: str, company_key: Any, tag: str, default_arch: int) -> _RegistrySpec | None: # noqa: ANN401
106
+ def process_tag(hive_name: str, company: str, company_key: Any, tag: str, default_arch: int) -> _RegistrySpec | None: # ruff:ignore[any-type]
107
107
  with winreg.OpenKeyEx(company_key, tag) as tag_key: # ty: ignore[unresolved-attribute]
108
108
  version = load_version_data(hive_name, company, tag, tag_key)
109
109
  if version is not None: # if failed to get version bail
@@ -120,7 +120,7 @@ def process_tag(hive_name: str, company: str, company_key: Any, tag: str, defaul
120
120
  return None
121
121
 
122
122
 
123
- def load_exe(hive_name: str, company: str, company_key: Any, tag: str) -> tuple[str, str | None] | None: # noqa: ANN401
123
+ def load_exe(hive_name: str, company: str, company_key: Any, tag: str) -> tuple[str, str | None] | None: # ruff:ignore[any-type]
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]
@@ -132,7 +132,7 @@ def load_exe(hive_name: str, company: str, company_key: Any, tag: str) -> tuple[
132
132
  return None
133
133
 
134
134
 
135
- def _resolve_exe(ip_key: Any, key_path: str) -> str | None: # noqa: ANN401
135
+ def _resolve_exe(ip_key: Any, key_path: str) -> str | None: # ruff:ignore[any-type]
136
136
  if (exe := get_value(ip_key, "ExecutablePath")) is not None:
137
137
  return exe
138
138
  if (ip := get_value(ip_key, None)) is None:
@@ -141,7 +141,7 @@ def _resolve_exe(ip_key: Any, key_path: str) -> str | None: # noqa: ANN401
141
141
  return os.path.join(ip, "python.exe")
142
142
 
143
143
 
144
- def load_arch_data(hive_name: str, company: str, tag: str, tag_key: Any, default_arch: int) -> int | None: # noqa: ANN401
144
+ def load_arch_data(hive_name: str, company: str, tag: str, tag_key: Any, default_arch: int) -> int | None: # ruff:ignore[any-type]
145
145
  arch_str = get_value(tag_key, "SysArchitecture")
146
146
  if arch_str is not None:
147
147
  key_path = f"{hive_name}/{company}/{tag}/SysArchitecture"
@@ -152,7 +152,7 @@ def load_arch_data(hive_name: str, company: str, tag: str, tag_key: Any, default
152
152
  return default_arch
153
153
 
154
154
 
155
- def parse_arch(arch_str: Any) -> int: # noqa: ANN401
155
+ def parse_arch(arch_str: Any) -> int: # ruff:ignore[any-type]
156
156
  if isinstance(arch_str, str):
157
157
  if match := _ARCH_RE.match(arch_str):
158
158
  return int(next(iter(match.groups())))
@@ -166,7 +166,7 @@ def load_version_data(
166
166
  hive_name: str,
167
167
  company: str,
168
168
  tag: str,
169
- tag_key: Any, # noqa: ANN401
169
+ tag_key: Any, # ruff:ignore[any-type]
170
170
  ) -> tuple[int | None, int | None, int | None] | None:
171
171
  for candidate, key_path in [
172
172
  (get_value(tag_key, "SysVersion"), f"{hive_name}/{company}/{tag}/SysVersion"),
@@ -180,7 +180,7 @@ def load_version_data(
180
180
  return None
181
181
 
182
182
 
183
- def parse_version(version_str: Any) -> tuple[int | None, int | None, int | None]: # noqa: ANN401
183
+ def parse_version(version_str: Any) -> tuple[int | None, int | None, int | None]: # ruff:ignore[any-type]
184
184
  if isinstance(version_str, str):
185
185
  if match := _VERSION_RE.match(version_str):
186
186
  g1, g2, g3 = match.groups()
@@ -195,7 +195,7 @@ def parse_version(version_str: Any) -> tuple[int | None, int | None, int | None]
195
195
  raise ValueError(error)
196
196
 
197
197
 
198
- def load_threaded(hive_name: str, company: str, tag: str, tag_key: Any) -> bool: # noqa: ANN401
198
+ def load_threaded(hive_name: str, company: str, tag: str, tag_key: Any) -> bool: # ruff:ignore[any-type]
199
199
  display_name = get_value(tag_key, "DisplayName")
200
200
  if display_name is not None:
201
201
  if isinstance(display_name, str):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-discovery
3
- Version: 1.4.3
3
+ Version: 1.5.0
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
@@ -42,25 +42,13 @@ Classifier: Programming Language :: Python :: 3.11
42
42
  Classifier: Programming Language :: Python :: 3.12
43
43
  Classifier: Programming Language :: Python :: 3.13
44
44
  Classifier: Programming Language :: Python :: 3.14
45
+ Classifier: Programming Language :: Python :: 3.15
45
46
  Classifier: Programming Language :: Python :: Implementation :: CPython
46
47
  Classifier: Topic :: Software Development :: Libraries
47
48
  Classifier: Topic :: Utilities
48
49
  Requires-Python: >=3.8
49
50
  Requires-Dist: filelock>=3.15.4
50
51
  Requires-Dist: platformdirs<5,>=4.3.6
51
- Provides-Extra: docs
52
- Requires-Dist: furo>=2025.12.19; extra == 'docs'
53
- Requires-Dist: sphinx-autodoc-typehints>=3.6.3; extra == 'docs'
54
- Requires-Dist: sphinx>=9.1; extra == 'docs'
55
- Requires-Dist: sphinxcontrib-mermaid>=2; extra == 'docs'
56
- Requires-Dist: sphinxcontrib-towncrier>=0.4; extra == 'docs'
57
- Requires-Dist: towncrier>=25.8; extra == 'docs'
58
- Provides-Extra: testing
59
- Requires-Dist: covdefaults>=2.3; extra == 'testing'
60
- Requires-Dist: coverage>=7.5.4; extra == 'testing'
61
- Requires-Dist: pytest-mock>=3.14; extra == 'testing'
62
- Requires-Dist: pytest>=8.3.5; extra == 'testing'
63
- Requires-Dist: setuptools>=75.1; extra == 'testing'
64
52
  Description-Content-Type: text/markdown
65
53
 
66
54
  # [`python-discovery`](https://python-discovery.readthedocs.io/en/latest/)
@@ -0,0 +1,16 @@
1
+ python_discovery/__init__.py,sha256=Hi4j3pMvC8Fo8KoNL-wOHsPSCUHwY4c0qvv5sjnFzME,808
2
+ python_discovery/_cache.py,sha256=svmG2GzUQzd2xTjAbm2eX1CKxxGAglfv4kxaPuxwPIA,5147
3
+ python_discovery/_cached_py_info.py,sha256=l9Y9UJGjKjp4M7iR9HfXYzW3hWdQ3KyNOD5IbSEphLw,8659
4
+ python_discovery/_compat.py,sha256=iqxGuhK9rPaiui80G1H1KrweoMJ16gyLYEmUrSXrp5c,709
5
+ python_discovery/_discovery.py,sha256=HF_TIuH6WLpvHU1KQiK9_yWfJ-np1eB9OSJwI1xRgdc,16894
6
+ python_discovery/_py_info.py,sha256=sXsOX4epGDRhrLvEcpDThh3r1yKMf5KZVaF2ZegoSOA,37311
7
+ python_discovery/_py_spec.py,sha256=O1RMC9JQ7m5SRzF6cEELxnIWZpadXYWO4bWDD7TemUs,10965
8
+ python_discovery/_specifier.py,sha256=Mu9Wp0tgY3WhXNffsG8Je8K3uc-vxqadEdvvQzWIanA,10416
9
+ python_discovery/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ python_discovery/_windows/__init__.py,sha256=qthcyGh36b3WkeSbZ4ECRRYLbCOCaQu5X_DRZ98tXfk,315
11
+ python_discovery/_windows/_pep514.py,sha256=1TqLTdofSCPu3VFE7D4u2fXweyaqhKJR7b48VmJ9AQQ,7787
12
+ python_discovery/_windows/_propose.py,sha256=iMJf6l0TU4SvuV8UsCHHINAe9L5dUT949xyeyMVdong,1565
13
+ python_discovery-1.5.0.dist-info/METADATA,sha256=aDIMeMkRg8kO2fVDwL41lD5DgT_u9Iu8tokFsq-0UPY,5018
14
+ python_discovery-1.5.0.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
15
+ python_discovery-1.5.0.dist-info/licenses/LICENSE,sha256=kOJeH68qSq6o2V7o5_VziwUqGswMnFgGgQH5Mahr1Yg,1023
16
+ python_discovery-1.5.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.30.1
2
+ Generator: hatchling 1.31.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,16 +0,0 @@
1
- python_discovery/__init__.py,sha256=Hi4j3pMvC8Fo8KoNL-wOHsPSCUHwY4c0qvv5sjnFzME,808
2
- python_discovery/_cache.py,sha256=bG4fD-h2pdPmb4v9BldbXmJ5Tlt1boiH1Qrz8kp9Dds,5063
3
- python_discovery/_cached_py_info.py,sha256=1AFEoc_-OaCi1PXmaFz9Iy_bEuqOhJdFSDFHljzD-EE,8540
4
- python_discovery/_compat.py,sha256=iqxGuhK9rPaiui80G1H1KrweoMJ16gyLYEmUrSXrp5c,709
5
- python_discovery/_discovery.py,sha256=cdo6xlktNMEwsQn2R8pn4T1VDE4LD2EJPUyCT7vlHnM,16830
6
- python_discovery/_py_info.py,sha256=7e4Ei810L2uS-JuqNdiCHvgyb-R4gj_5VrtN3yNtIAg,36647
7
- python_discovery/_py_spec.py,sha256=Kky0LXi8BU_ojfMdvvRhtDk6I4WPjovwXOu6E2QYHTo,10421
8
- python_discovery/_specifier.py,sha256=PTh6suNTC0hMnFSxFYF_vggdciFLnwxoh1YflSbgCXA,10368
9
- python_discovery/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- python_discovery/_windows/__init__.py,sha256=qthcyGh36b3WkeSbZ4ECRRYLbCOCaQu5X_DRZ98tXfk,315
11
- python_discovery/_windows/_pep514.py,sha256=zXxje07QIXfrCB3eYbZHOBgXcvAQQpZ-ZWun3_zrapA,7688
12
- python_discovery/_windows/_propose.py,sha256=iMJf6l0TU4SvuV8UsCHHINAe9L5dUT949xyeyMVdong,1565
13
- python_discovery-1.4.3.dist-info/METADATA,sha256=_9Cm3smElV4lgueLcYMDdHjFzo66tjO1tqH-K6KwkG0,5592
14
- python_discovery-1.4.3.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
15
- python_discovery-1.4.3.dist-info/licenses/LICENSE,sha256=kOJeH68qSq6o2V7o5_VziwUqGswMnFgGgQH5Mahr1Yg,1023
16
- python_discovery-1.4.3.dist-info/RECORD,,