ducktools-pythonfinder 0.5.4__tar.gz → 0.5.5__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.
Files changed (38) hide show
  1. {ducktools_pythonfinder-0.5.4/src/ducktools_pythonfinder.egg-info → ducktools_pythonfinder-0.5.5}/PKG-INFO +1 -1
  2. {ducktools_pythonfinder-0.5.4 → ducktools_pythonfinder-0.5.5}/src/ducktools/pythonfinder/__main__.py +39 -17
  3. ducktools_pythonfinder-0.5.5/src/ducktools/pythonfinder/_version.py +2 -0
  4. {ducktools_pythonfinder-0.5.4 → ducktools_pythonfinder-0.5.5}/src/ducktools/pythonfinder/details_script.py +40 -1
  5. {ducktools_pythonfinder-0.5.4 → ducktools_pythonfinder-0.5.5}/src/ducktools/pythonfinder/shared.py +20 -4
  6. {ducktools_pythonfinder-0.5.4 → ducktools_pythonfinder-0.5.5/src/ducktools_pythonfinder.egg-info}/PKG-INFO +1 -1
  7. ducktools_pythonfinder-0.5.4/src/ducktools/pythonfinder/_version.py +0 -2
  8. {ducktools_pythonfinder-0.5.4 → ducktools_pythonfinder-0.5.5}/.gitignore +0 -0
  9. {ducktools_pythonfinder-0.5.4 → ducktools_pythonfinder-0.5.5}/LICENSE +0 -0
  10. {ducktools_pythonfinder-0.5.4 → ducktools_pythonfinder-0.5.5}/MANIFEST.in +0 -0
  11. {ducktools_pythonfinder-0.5.4 → ducktools_pythonfinder-0.5.5}/README.md +0 -0
  12. {ducktools_pythonfinder-0.5.4 → ducktools_pythonfinder-0.5.5}/pyproject.toml +0 -0
  13. {ducktools_pythonfinder-0.5.4 → ducktools_pythonfinder-0.5.5}/scripts/build_zipapp.py +0 -0
  14. {ducktools_pythonfinder-0.5.4 → ducktools_pythonfinder-0.5.5}/scripts/detail_this_python.py +0 -0
  15. {ducktools_pythonfinder-0.5.4 → ducktools_pythonfinder-0.5.5}/scripts/print_python_versions.py +0 -0
  16. {ducktools_pythonfinder-0.5.4 → ducktools_pythonfinder-0.5.5}/setup.cfg +0 -0
  17. {ducktools_pythonfinder-0.5.4 → ducktools_pythonfinder-0.5.5}/src/ducktools/pythonfinder/__init__.py +0 -0
  18. {ducktools_pythonfinder-0.5.4 → ducktools_pythonfinder-0.5.5}/src/ducktools/pythonfinder/darwin/__init__.py +0 -0
  19. {ducktools_pythonfinder-0.5.4 → ducktools_pythonfinder-0.5.5}/src/ducktools/pythonfinder/linux/__init__.py +0 -0
  20. {ducktools_pythonfinder-0.5.4 → ducktools_pythonfinder-0.5.5}/src/ducktools/pythonfinder/linux/pyenv_search.py +0 -0
  21. {ducktools_pythonfinder-0.5.4 → ducktools_pythonfinder-0.5.5}/src/ducktools/pythonfinder/pythonorg_search.py +0 -0
  22. {ducktools_pythonfinder-0.5.4 → ducktools_pythonfinder-0.5.5}/src/ducktools/pythonfinder/win32/__init__.py +0 -0
  23. {ducktools_pythonfinder-0.5.4 → ducktools_pythonfinder-0.5.5}/src/ducktools/pythonfinder/win32/pyenv_search.py +0 -0
  24. {ducktools_pythonfinder-0.5.4 → ducktools_pythonfinder-0.5.5}/src/ducktools/pythonfinder/win32/registry_search.py +0 -0
  25. {ducktools_pythonfinder-0.5.4 → ducktools_pythonfinder-0.5.5}/src/ducktools_pythonfinder.egg-info/SOURCES.txt +0 -0
  26. {ducktools_pythonfinder-0.5.4 → ducktools_pythonfinder-0.5.5}/src/ducktools_pythonfinder.egg-info/dependency_links.txt +0 -0
  27. {ducktools_pythonfinder-0.5.4 → ducktools_pythonfinder-0.5.5}/src/ducktools_pythonfinder.egg-info/entry_points.txt +0 -0
  28. {ducktools_pythonfinder-0.5.4 → ducktools_pythonfinder-0.5.5}/src/ducktools_pythonfinder.egg-info/requires.txt +0 -0
  29. {ducktools_pythonfinder-0.5.4 → ducktools_pythonfinder-0.5.5}/src/ducktools_pythonfinder.egg-info/top_level.txt +0 -0
  30. {ducktools_pythonfinder-0.5.4 → ducktools_pythonfinder-0.5.5}/tests/conftest.py +0 -0
  31. {ducktools_pythonfinder-0.5.4 → ducktools_pythonfinder-0.5.5}/tests/sources/python_versions.txt +0 -0
  32. {ducktools_pythonfinder-0.5.4 → ducktools_pythonfinder-0.5.5}/tests/sources/release.json +0 -0
  33. {ducktools_pythonfinder-0.5.4 → ducktools_pythonfinder-0.5.5}/tests/sources/release_file.json +0 -0
  34. {ducktools_pythonfinder-0.5.4 → ducktools_pythonfinder-0.5.5}/tests/test_details_script.py +0 -0
  35. {ducktools_pythonfinder-0.5.4 → ducktools_pythonfinder-0.5.5}/tests/test_foldersearch.py +0 -0
  36. {ducktools_pythonfinder-0.5.4 → ducktools_pythonfinder-0.5.5}/tests/test_orgsearch.py +0 -0
  37. {ducktools_pythonfinder-0.5.4 → ducktools_pythonfinder-0.5.5}/tests/test_pyenv.py +0 -0
  38. {ducktools_pythonfinder-0.5.4 → ducktools_pythonfinder-0.5.5}/tests/test_shared.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ducktools-pythonfinder
3
- Version: 0.5.4
3
+ Version: 0.5.5
4
4
  Summary: Cross platform tool to find available python installations
5
5
  Author: David C Ellis
6
6
  Project-URL: Homepage, https://github.com/davidcellis/ducktools-pythonfinder
@@ -20,6 +20,7 @@
20
20
  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
21
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
22
  # SOFTWARE.
23
+ from __future__ import annotations
23
24
 
24
25
 
25
26
  import sys
@@ -140,23 +141,16 @@ def display_local_installs(
140
141
  compatible = tuple(int(i) for i in compatible.split("."))
141
142
 
142
143
  installs = list_python_installs(query_executables=query_executables)
143
- headings = ["Python Version", "Executable Location"]
144
- max_executable_len = max(
145
- len(headings[1]), max(len(inst.executable) for inst in installs)
146
- )
147
- headings_str = f"| {headings[0]} | {headings[1]:<{max_executable_len}s} |"
148
144
 
149
- print("Discoverable Python Installs")
150
- if sys.platform == "win32":
151
- print("+ - Listed in the Windows Registry ")
152
- print("^ - This is a 32-bit Python install")
153
- if sys.platform != "win32":
154
- print("[] - This Python install is shadowed by another on Path")
155
- print("* - This is the active Python executable used to call this module")
156
- print("** - This is the parent Python executable of the venv used to call this module")
157
- print()
158
- print(headings_str)
159
- print(f"| {'-' * len(headings[0])} | {'-' * max_executable_len} |")
145
+ headings = ["Version", "Executable Location"]
146
+
147
+ install_collection: list[tuple[str, str]] = []
148
+ max_version_len = len(headings[0])
149
+ max_executable_len = len(headings[1])
150
+
151
+ alternate_implementations = False
152
+
153
+ # First collect the strings
160
154
  for install in installs:
161
155
  if min_ver and install.version < min_ver:
162
156
  continue
@@ -190,7 +184,35 @@ def display_local_installs(
190
184
  if install.shadowed:
191
185
  version_str = f"[{version_str}]"
192
186
 
193
- print(f"| {version_str:>14s} | {install.executable:<{max_executable_len}s} |")
187
+ if install.implementation != "cpython":
188
+ alternate_implementations = True
189
+ version_str = f"({install.implementation_version_str}) {version_str}"
190
+
191
+ max_version_len = max(max_version_len, len(version_str))
192
+ max_executable_len = max(max_executable_len, len(install.executable))
193
+
194
+ install_collection.append((version_str, install.executable))
195
+
196
+ print("Discoverable Python Installs")
197
+ print()
198
+ if alternate_implementations:
199
+ print("Alternate implementation versions are listed in parentheses")
200
+
201
+ if sys.platform == "win32":
202
+ print("+ - Listed in the Windows Registry ")
203
+ print("^ - This is a 32-bit Python install")
204
+ if sys.platform != "win32":
205
+ print("[] - This Python install is shadowed by another on Path")
206
+ print("* - This is the active Python executable used to call this module")
207
+ print("** - This is the parent Python executable of the venv used to call this module")
208
+ print()
209
+
210
+ headings_str = f"| {headings[0]:<{max_version_len}s} | {headings[1]:<{max_executable_len}s} |"
211
+ print(headings_str)
212
+ print(f"| {'-' * max_version_len} | {'-' * max_executable_len} |")
213
+
214
+ for version_str, executable in install_collection:
215
+ print(f"| {version_str:>{max_version_len}s} | {executable:<{max_executable_len}s} |")
194
216
 
195
217
 
196
218
  def display_remote_binaries(
@@ -0,0 +1,2 @@
1
+ __version__ = "0.5.5"
2
+ __version_tuple__ = (0, 5, 5)
@@ -26,6 +26,35 @@ Get the details from a python install as JSON
26
26
  """
27
27
  import sys
28
28
 
29
+ FULL_PY_VER_RE = r"(?P<major>\d+)\.(?P<minor>\d+)\.?(?P<micro>\d*)-?(?P<releaselevel>[a-zA-Z]*)(?P<serial>\d*)"
30
+
31
+
32
+ def version_str_to_tuple(version):
33
+ # Needed to parse GraalPy versions only available as strings
34
+ import re
35
+
36
+ parsed_version = re.fullmatch(FULL_PY_VER_RE, version)
37
+
38
+ major, minor, micro, releaselevel, serial = parsed_version.groups()
39
+
40
+ if releaselevel in {"a", "dev"}:
41
+ releaselevel = "alpha"
42
+ elif releaselevel == "b":
43
+ releaselevel = "beta"
44
+ elif releaselevel == "rc":
45
+ releaselevel = "candidate"
46
+ else:
47
+ releaselevel = "final"
48
+
49
+ version_tuple = (
50
+ int(major),
51
+ int(minor),
52
+ int(micro) if micro else 0,
53
+ releaselevel,
54
+ int(serial if serial != "" else 0),
55
+ )
56
+ return version_tuple
57
+
29
58
 
30
59
  def get_details():
31
60
  try:
@@ -37,7 +66,17 @@ def get_details():
37
66
  implementation = platform.python_implementation().lower()
38
67
  metadata = {}
39
68
  else:
40
- if implementation != "cpython": # pragma: no cover
69
+ if implementation == "graalpy":
70
+ # Special case GraalPy as it erroneously reports the CPython target
71
+ # instead of the Graal versiion
72
+ try:
73
+ ver = __graalpython__.get_graalvm_version()
74
+ metadata = {
75
+ "graalpy_version": version_str_to_tuple(ver)
76
+ }
77
+ except NameError:
78
+ metadata = {"{}_version".format(implementation): sys.implementation.version}
79
+ elif implementation != "cpython": # pragma: no cover
41
80
  metadata = {"{}_version".format(implementation): sys.implementation.version}
42
81
  else:
43
82
  metadata = {}
@@ -46,7 +46,7 @@ _laz = LazyImporter(
46
46
  )
47
47
 
48
48
 
49
- FULL_PY_VER_RE = r"(?P<major>\d+)\.(?P<minor>\d+)\.?(?P<micro>\d*)(?P<releaselevel>[a-zA-Z]*)(?P<serial>\d*)"
49
+ FULL_PY_VER_RE = r"(?P<major>\d+)\.(?P<minor>\d+)\.?(?P<micro>\d*)-?(?P<releaselevel>[a-zA-Z]*)(?P<serial>\d*)"
50
50
 
51
51
 
52
52
  def version_str_to_tuple(version):
@@ -57,7 +57,7 @@ def version_str_to_tuple(version):
57
57
 
58
58
  major, minor, micro, releaselevel, serial = parsed_version.groups()
59
59
 
60
- if releaselevel == "a":
60
+ if releaselevel in {"a", "dev"}:
61
61
  releaselevel = "alpha"
62
62
  elif releaselevel == "b":
63
63
  releaselevel = "beta"
@@ -131,6 +131,7 @@ class PythonInstall(Prefab):
131
131
  implementation: str = "cpython"
132
132
  metadata: dict = attribute(default_factory=dict)
133
133
  shadowed: bool = False
134
+ _implementation_version: tuple[int, int, int, str, int] | None = attribute(default=None, private=True)
134
135
 
135
136
  def __prefab_post_init__(
136
137
  self,
@@ -147,6 +148,22 @@ class PythonInstall(Prefab):
147
148
  def version_str(self) -> str:
148
149
  return version_tuple_to_str(self.version)
149
150
 
151
+ @property
152
+ def implementation_version(self) -> tuple[int, int, int, str, int] | None:
153
+ if self._implementation_version is None:
154
+ if self.implementation == "cpython":
155
+ self._implementation_version = self.version
156
+ elif implementation_ver := self.metadata.get(f"{self.implementation}_version"):
157
+ if len(implementation_ver) == 3:
158
+ self._implementation_version = tuple([*implementation_ver, "final", 0]) # type: ignore
159
+ else:
160
+ self._implementation_version = implementation_ver
161
+ return self._implementation_version
162
+
163
+ @property
164
+ def implementation_version_str(self) -> str:
165
+ return version_tuple_to_str(self.implementation_version)
166
+
150
167
  @classmethod
151
168
  def from_str(
152
169
  cls,
@@ -242,7 +259,6 @@ def get_install_details(executable: str) -> PythonInstall | None:
242
259
  try:
243
260
  output = _laz.json.loads(detail_output)
244
261
  except _laz.json.JSONDecodeError as e:
245
- print(e)
246
262
  return None
247
263
 
248
264
  return PythonInstall.from_json(**output)
@@ -302,7 +318,7 @@ def _implementation_from_uv_dir(
302
318
  if query_executables:
303
319
  install = get_install_details(python_path)
304
320
  else:
305
- if implementation in {"cpython", "pypy"}:
321
+ if implementation in {"cpython"}:
306
322
  install = PythonInstall.from_str(
307
323
  version=version,
308
324
  executable=python_path,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ducktools-pythonfinder
3
- Version: 0.5.4
3
+ Version: 0.5.5
4
4
  Summary: Cross platform tool to find available python installations
5
5
  Author: David C Ellis
6
6
  Project-URL: Homepage, https://github.com/davidcellis/ducktools-pythonfinder
@@ -1,2 +0,0 @@
1
- __version__ = "0.5.4"
2
- __version_tuple__ = (0, 5, 4)