ducktools-pythonfinder 0.7.5__tar.gz → 0.7.7__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 (43) hide show
  1. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/LICENSE +1 -1
  2. {ducktools_pythonfinder-0.7.5/src/ducktools_pythonfinder.egg-info → ducktools_pythonfinder-0.7.7}/PKG-INFO +1 -1
  3. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/src/ducktools/pythonfinder/__init__.py +1 -1
  4. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/src/ducktools/pythonfinder/__main__.py +1 -1
  5. ducktools_pythonfinder-0.7.7/src/ducktools/pythonfinder/_version.py +2 -0
  6. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/src/ducktools/pythonfinder/darwin/__init__.py +1 -1
  7. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/src/ducktools/pythonfinder/details_script.py +1 -1
  8. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/src/ducktools/pythonfinder/linux/__init__.py +1 -1
  9. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/src/ducktools/pythonfinder/linux/pyenv_search.py +1 -1
  10. ducktools_pythonfinder-0.7.7/src/ducktools/pythonfinder/package_list_script.py +55 -0
  11. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/src/ducktools/pythonfinder/pythonorg_search.py +1 -1
  12. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/src/ducktools/pythonfinder/shared.py +1 -1
  13. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/src/ducktools/pythonfinder/venv.py +31 -38
  14. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/src/ducktools/pythonfinder/win32/__init__.py +1 -1
  15. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/src/ducktools/pythonfinder/win32/pyenv_search.py +1 -1
  16. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/src/ducktools/pythonfinder/win32/registry_search.py +1 -1
  17. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7/src/ducktools_pythonfinder.egg-info}/PKG-INFO +1 -1
  18. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/src/ducktools_pythonfinder.egg-info/SOURCES.txt +1 -0
  19. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/tests/test_venv_finder.py +3 -2
  20. ducktools_pythonfinder-0.7.5/src/ducktools/pythonfinder/_version.py +0 -2
  21. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/.gitignore +0 -0
  22. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/MANIFEST.in +0 -0
  23. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/README.md +0 -0
  24. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/pyproject.toml +0 -0
  25. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/scripts/build_zipapp.py +0 -0
  26. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/scripts/detail_this_python.py +0 -0
  27. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/scripts/list_python_venvs.py +0 -0
  28. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/scripts/print_python_versions.py +0 -0
  29. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/setup.cfg +0 -0
  30. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/src/ducktools_pythonfinder.egg-info/dependency_links.txt +0 -0
  31. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/src/ducktools_pythonfinder.egg-info/entry_points.txt +0 -0
  32. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/src/ducktools_pythonfinder.egg-info/requires.txt +0 -0
  33. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/src/ducktools_pythonfinder.egg-info/top_level.txt +0 -0
  34. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/tests/conftest.py +0 -0
  35. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/tests/sources/python_versions.txt +0 -0
  36. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/tests/sources/release.json +0 -0
  37. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/tests/sources/release_file.json +0 -0
  38. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/tests/test_details_script.py +0 -0
  39. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/tests/test_foldersearch.py +0 -0
  40. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/tests/test_orgsearch.py +0 -0
  41. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/tests/test_pyenv.py +0 -0
  42. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/tests/test_shared.py +0 -0
  43. {ducktools_pythonfinder-0.7.5 → ducktools_pythonfinder-0.7.7}/tests/test_uv_finder.py +0 -0
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2023-2024 David C Ellis
3
+ Copyright (c) 2023-2025 David C Ellis
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: ducktools-pythonfinder
3
- Version: 0.7.5
3
+ Version: 0.7.7
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,7 +1,7 @@
1
1
  # ducktools-pythonfinder
2
2
  # MIT License
3
3
  #
4
- # Copyright (c) 2023-2024 David C Ellis
4
+ # Copyright (c) 2023-2025 David C Ellis
5
5
  #
6
6
  # Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
  # of this software and associated documentation files (the "Software"), to deal
@@ -1,7 +1,7 @@
1
1
  # ducktools-pythonfinder
2
2
  # MIT License
3
3
  #
4
- # Copyright (c) 2023-2024 David C Ellis
4
+ # Copyright (c) 2023-2025 David C Ellis
5
5
  #
6
6
  # Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
  # of this software and associated documentation files (the "Software"), to deal
@@ -0,0 +1,2 @@
1
+ __version__ = "0.7.7"
2
+ __version_tuple__ = (0, 7, 7)
@@ -1,7 +1,7 @@
1
1
  # ducktools-pythonfinder
2
2
  # MIT License
3
3
  #
4
- # Copyright (c) 2023-2024 David C Ellis
4
+ # Copyright (c) 2023-2025 David C Ellis
5
5
  #
6
6
  # Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
  # of this software and associated documentation files (the "Software"), to deal
@@ -1,7 +1,7 @@
1
1
  # ducktools-pythonfinder
2
2
  # MIT License
3
3
  #
4
- # Copyright (c) 2023-2024 David C Ellis
4
+ # Copyright (c) 2023-2025 David C Ellis
5
5
  #
6
6
  # Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
  # of this software and associated documentation files (the "Software"), to deal
@@ -1,7 +1,7 @@
1
1
  # ducktools-pythonfinder
2
2
  # MIT License
3
3
  #
4
- # Copyright (c) 2023-2024 David C Ellis
4
+ # Copyright (c) 2023-2025 David C Ellis
5
5
  #
6
6
  # Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
  # of this software and associated documentation files (the "Software"), to deal
@@ -1,7 +1,7 @@
1
1
  # ducktools-pythonfinder
2
2
  # MIT License
3
3
  #
4
- # Copyright (c) 2023-2024 David C Ellis
4
+ # Copyright (c) 2023-2025 David C Ellis
5
5
  #
6
6
  # Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
  # of this software and associated documentation files (the "Software"), to deal
@@ -0,0 +1,55 @@
1
+ # Dependency listing code modified from python-readiness
2
+ # https://github.com/hauntsaninja/python_readiness/
3
+ # MIT License
4
+ #
5
+ # Copyright (c) 2024 hauntsaninja
6
+
7
+ # Part of ducktools-pythonfinder
8
+ # MIT License
9
+ #
10
+ # Copyright (c) 2025 David C Ellis
11
+ #
12
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ # of this software and associated documentation files (the "Software"), to deal
14
+ # in the Software without restriction, including without limitation the rights
15
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ # copies of the Software, and to permit persons to whom the Software is
17
+ # furnished to do so, subject to the following conditions:
18
+ #
19
+ # The above copyright notice and this permission notice shall be included in all
20
+ # copies or substantial portions of the Software.
21
+ #
22
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
+ # SOFTWARE.
29
+ #
30
+ import importlib.metadata
31
+ import sys
32
+ import sysconfig
33
+
34
+ from pathlib import Path
35
+
36
+
37
+ def get_dependencies():
38
+ purelib = Path(sysconfig.get_paths()["purelib"])
39
+ safe_path = sys.path if getattr(sys.flags, "safe_path", False) else sys.path[1:]
40
+ context = importlib.metadata.DistributionFinder.Context(path=safe_path)
41
+ for dist in importlib.metadata.distributions(context=context):
42
+ if (
43
+ isinstance(dist, importlib.metadata.PathDistribution)
44
+ and (dist_path := getattr(dist, '_path', None))
45
+ and isinstance(dist_path, Path)
46
+ and not dist_path.is_relative_to(purelib)
47
+ ):
48
+ continue
49
+ metadata = dist.metadata
50
+ name, version = metadata["Name"], metadata["Version"]
51
+ print(f"{name}=={version}")
52
+
53
+
54
+ if __name__ == "__main__":
55
+ get_dependencies()
@@ -1,7 +1,7 @@
1
1
  # ducktools-pythonfinder
2
2
  # MIT License
3
3
  #
4
- # Copyright (c) 2013-2014 David C Ellis
4
+ # Copyright (c) 2023-2025 David C Ellis
5
5
  #
6
6
  # Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
  # of this software and associated documentation files (the "Software"), to deal
@@ -1,7 +1,7 @@
1
1
  # ducktools-pythonfinder
2
2
  # MIT License
3
3
  #
4
- # Copyright (c) 2023-2024 David C Ellis
4
+ # Copyright (c) 2023-2025 David C Ellis
5
5
  #
6
6
  # Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
  # of this software and associated documentation files (the "Software"), to deal
@@ -1,7 +1,7 @@
1
1
  # ducktools-pythonfinder
2
2
  # MIT License
3
3
  #
4
- # Copyright (c) 2013-2014 David C Ellis
4
+ # Copyright (c) 2023-2025 David C Ellis
5
5
  #
6
6
  # Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
  # of this software and associated documentation files (the "Software"), to deal
@@ -49,7 +49,9 @@ _laz = LazyImporter(
49
49
  ModuleImport("subprocess"),
50
50
  FromImport("pathlib", "Path"),
51
51
  FromImport("subprocess", "run"),
52
- ]
52
+ FromImport(".", "package_list_script"),
53
+ ],
54
+ globs=globals()
53
55
  )
54
56
 
55
57
  VENV_CONFIG_NAME = "pyvenv.cfg"
@@ -85,18 +87,24 @@ class PythonVEnv(Prefab):
85
87
  def parent_executable(self) -> str:
86
88
  if self._parent_executable is None:
87
89
  # Guess the parent executable file
90
+ parent_exe = None
88
91
  if sys.platform == "win32":
89
92
  parent_exe = os.path.join(self.parent_path, "python.exe")
90
93
  else:
91
- parent_exe = os.path.join(self.parent_path, "python")
92
-
93
- if not os.path.exists(parent_exe):
94
+ # try with additional numbers in order eg: python313, python3, python
95
+ for i in reversed(range(2)):
96
+ version_part = "".join(str(v) for v in self.version[:i])
97
+ parent_exe = os.path.join(self.parent_path, f"python{version_part}")
98
+ if os.path.exists(parent_exe):
99
+ break
100
+
101
+ if not (parent_exe and os.path.exists(parent_exe)):
94
102
  try:
95
103
  pyout = _laz.run(
96
104
  [
97
105
  self.executable,
98
106
  "-c",
99
- "from sysconfig import get_config_var; print(get_config_var('EXENAME'))",
107
+ "import sys; sys.stdout.write(getattr(sys, '_base_executable', ''))",
100
108
  ],
101
109
  capture_output=True,
102
110
  text=True,
@@ -105,7 +113,7 @@ class PythonVEnv(Prefab):
105
113
  except (_laz.subprocess.CalledProcessError, FileNotFoundError):
106
114
  pass
107
115
  else:
108
- if out_exe := pyout.stdout.strip():
116
+ if out_exe := pyout.stdout:
109
117
  parent_exe = os.path.join(self.parent_path, os.path.basename(out_exe))
110
118
 
111
119
  self._parent_executable = parent_exe
@@ -140,30 +148,21 @@ class PythonVEnv(Prefab):
140
148
  f"Parent Python at \"{self.parent_executable}\" does not exist."
141
149
  )
142
150
 
143
- # Should probably use sys.executable and have pip as a dependency
144
- # We would need to look at possibly changing how ducktools-env works for that however.
151
+ package_list_script = _laz.package_list_script.__file__
145
152
 
146
153
  data = _laz.run(
147
- [
148
- self.parent_executable,
149
- "-m", "pip",
150
- "--python", self.executable,
151
- "list",
152
- "--format", "json"
153
- ],
154
+ [self.executable, package_list_script],
154
155
  capture_output=True,
155
156
  text=True,
156
157
  check=True,
157
158
  )
158
159
 
159
- raw_packages = _laz.json.loads(data.stdout)
160
+ raw_packages = data.stdout.split("\n")
160
161
 
161
162
  packages = [
162
- PythonPackage(
163
- name=p["name"],
164
- version=p["version"],
165
- )
163
+ PythonPackage(*p.split("=="))
166
164
  for p in raw_packages
165
+ if p
167
166
  ]
168
167
 
169
168
  return packages
@@ -176,27 +175,21 @@ class PythonVEnv(Prefab):
176
175
  :param cfg_path: Path to a virtualenv config file
177
176
  :return: PythonVEnv with details relative to that config file
178
177
  """
179
- parent_path, version_str, parent_exe = None, None, None
180
178
  venv_base = os.path.dirname(cfg_path)
181
179
 
182
180
  with open(cfg_path, 'r') as f:
181
+ conf = {}
183
182
  for line in f:
184
- key, value = (item.strip() for item in line.split("="))
183
+ key, _, value = [item.strip() for item in line.partition("=")]
184
+ conf[key] = value
185
185
 
186
- if key == "home":
187
- parent_path = value
188
- elif key in {"version", "version_info"}:
189
- # venv and uv use different key names :)
190
- version_str = value
191
- elif key == "executable":
192
- parent_exe = value
193
-
194
- if parent_path and version_str and parent_exe:
195
- break
186
+ parent_path = conf.get("home")
187
+ version_str = conf.get("version", conf.get("version_info"))
188
+ parent_exe = conf.get("executable", conf.get("base-executable"))
196
189
 
197
- if parent_path is None or version_str is None:
198
- # Not a valid venv
199
- raise InvalidVEnvError(f"Path or version not defined in {cfg_path}")
190
+ if parent_path is None or version_str is None:
191
+ # Not a valid venv
192
+ raise InvalidVEnvError(f"Path or version not defined in {cfg_path}")
200
193
 
201
194
  if sys.platform == "win32":
202
195
  venv_exe = os.path.join(venv_base, "Scripts", "python.exe")
@@ -281,8 +274,8 @@ def get_python_venvs(
281
274
  except OSError as e:
282
275
  # MacOS can error on searching up folders with an invalid argument
283
276
  # On Python 3.11 or earlier.
284
- if e.errno == 22:
285
- continue
277
+ if e.errno != 22:
278
+ raise
286
279
 
287
280
 
288
281
  def list_python_venvs(
@@ -1,7 +1,7 @@
1
1
  # ducktools-pythonfinder
2
2
  # MIT License
3
3
  #
4
- # Copyright (c) 2023-2024 David C Ellis
4
+ # Copyright (c) 2023-2025 David C Ellis
5
5
  #
6
6
  # Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
  # of this software and associated documentation files (the "Software"), to deal
@@ -1,7 +1,7 @@
1
1
  # ducktools-pythonfinder
2
2
  # MIT License
3
3
  #
4
- # Copyright (c) 2023-2024 David C Ellis
4
+ # Copyright (c) 2023-2025 David C Ellis
5
5
  #
6
6
  # Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
  # of this software and associated documentation files (the "Software"), to deal
@@ -1,7 +1,7 @@
1
1
  # ducktools-pythonfinder
2
2
  # MIT License
3
3
  #
4
- # Copyright (c) 2023-2024 David C Ellis
4
+ # Copyright (c) 2023-2025 David C Ellis
5
5
  #
6
6
  # Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
  # of this software and associated documentation files (the "Software"), to deal
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: ducktools-pythonfinder
3
- Version: 0.7.5
3
+ Version: 0.7.7
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
@@ -11,6 +11,7 @@ src/ducktools/pythonfinder/__init__.py
11
11
  src/ducktools/pythonfinder/__main__.py
12
12
  src/ducktools/pythonfinder/_version.py
13
13
  src/ducktools/pythonfinder/details_script.py
14
+ src/ducktools/pythonfinder/package_list_script.py
14
15
  src/ducktools/pythonfinder/pythonorg_search.py
15
16
  src/ducktools/pythonfinder/shared.py
16
17
  src/ducktools/pythonfinder/venv.py
@@ -127,8 +127,9 @@ def test_found_parent(with_venvs, this_python, this_venv):
127
127
  parent = venv_ex.get_parent_install()
128
128
  assert os.path.dirname(parent.executable) == os.path.dirname(this_python.executable)
129
129
 
130
- # venv version str works same as parent
131
- assert venv_ex.version_str == parent.version_str
130
+ # venvs created by the venv module don't record prerelease details in the version
131
+ # That's not my fault that's venv!
132
+ assert venv_ex.version[:3] == parent.version[:3]
132
133
 
133
134
 
134
135
  def test_found_parent_cache(with_venvs, this_python):
@@ -1,2 +0,0 @@
1
- __version__ = "0.7.5"
2
- __version_tuple__ = (0, 7, 5)