ducktools-pythonfinder 0.7.0__tar.gz → 0.7.1__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 (41) hide show
  1. {ducktools_pythonfinder-0.7.0/src/ducktools_pythonfinder.egg-info → ducktools_pythonfinder-0.7.1}/PKG-INFO +1 -1
  2. ducktools_pythonfinder-0.7.1/src/ducktools/pythonfinder/_version.py +2 -0
  3. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/src/ducktools/pythonfinder/venv.py +91 -22
  4. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1/src/ducktools_pythonfinder.egg-info}/PKG-INFO +1 -1
  5. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/tests/test_venv_finder.py +7 -0
  6. ducktools_pythonfinder-0.7.0/src/ducktools/pythonfinder/_version.py +0 -2
  7. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/.gitignore +0 -0
  8. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/LICENSE +0 -0
  9. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/MANIFEST.in +0 -0
  10. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/README.md +0 -0
  11. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/pyproject.toml +0 -0
  12. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/scripts/build_zipapp.py +0 -0
  13. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/scripts/detail_this_python.py +0 -0
  14. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/scripts/print_python_versions.py +0 -0
  15. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/setup.cfg +0 -0
  16. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/src/ducktools/pythonfinder/__init__.py +0 -0
  17. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/src/ducktools/pythonfinder/__main__.py +0 -0
  18. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/src/ducktools/pythonfinder/darwin/__init__.py +0 -0
  19. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/src/ducktools/pythonfinder/details_script.py +0 -0
  20. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/src/ducktools/pythonfinder/linux/__init__.py +0 -0
  21. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/src/ducktools/pythonfinder/linux/pyenv_search.py +0 -0
  22. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/src/ducktools/pythonfinder/pythonorg_search.py +0 -0
  23. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/src/ducktools/pythonfinder/shared.py +0 -0
  24. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/src/ducktools/pythonfinder/win32/__init__.py +0 -0
  25. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/src/ducktools/pythonfinder/win32/pyenv_search.py +0 -0
  26. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/src/ducktools/pythonfinder/win32/registry_search.py +0 -0
  27. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/src/ducktools_pythonfinder.egg-info/SOURCES.txt +0 -0
  28. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/src/ducktools_pythonfinder.egg-info/dependency_links.txt +0 -0
  29. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/src/ducktools_pythonfinder.egg-info/entry_points.txt +0 -0
  30. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/src/ducktools_pythonfinder.egg-info/requires.txt +0 -0
  31. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/src/ducktools_pythonfinder.egg-info/top_level.txt +0 -0
  32. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/tests/conftest.py +0 -0
  33. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/tests/sources/python_versions.txt +0 -0
  34. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/tests/sources/release.json +0 -0
  35. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/tests/sources/release_file.json +0 -0
  36. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/tests/test_details_script.py +0 -0
  37. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/tests/test_foldersearch.py +0 -0
  38. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/tests/test_orgsearch.py +0 -0
  39. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/tests/test_pyenv.py +0 -0
  40. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/tests/test_shared.py +0 -0
  41. {ducktools_pythonfinder-0.7.0 → ducktools_pythonfinder-0.7.1}/tests/test_uv_finder.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: ducktools-pythonfinder
3
- Version: 0.7.0
3
+ Version: 0.7.1
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
@@ -0,0 +1,2 @@
1
+ __version__ = "0.7.1"
2
+ __version_tuple__ = (0, 7, 1)
@@ -55,6 +55,10 @@ VIRTUALENV_PY_VER_RE = (
55
55
  )
56
56
 
57
57
 
58
+ class InvalidVEnvError(Exception):
59
+ pass
60
+
61
+
58
62
  class PythonPackage(Prefab):
59
63
  name: str
60
64
  version: str
@@ -133,20 +137,18 @@ class PythonVEnv(Prefab):
133
137
 
134
138
  return packages
135
139
 
140
+ @classmethod
141
+ def from_cfg(cls, cfg_path):
142
+ """
143
+ Get a PythonVEnv instance from the path to a config file
136
144
 
137
- def get_python_venvs(base_dir=None, recursive=True):
138
- base_dir = os.getcwd() if base_dir is None else base_dir
139
-
140
- if recursive:
141
- glob_call = _laz.Path(base_dir).glob(f"**/{VENV_CONFIG_NAME}")
142
- else:
143
- glob_call = _laz.Path(base_dir).glob(f"*/{VENV_CONFIG_NAME}")
144
-
145
- for conf in glob_call:
145
+ :param cfg_path:
146
+ :return:
147
+ """
146
148
  parent_path, version_str = None, None
147
- venv_base = conf.parent
149
+ venv_base = cfg_path.parent
148
150
 
149
- with conf.open() as f:
151
+ with cfg_path.open() as f:
150
152
  for line in f:
151
153
  key, value = (item.strip() for item in line.split("="))
152
154
 
@@ -160,14 +162,13 @@ def get_python_venvs(base_dir=None, recursive=True):
160
162
  break
161
163
  else:
162
164
  # Not a valid venv, ignore
163
- continue
165
+ raise InvalidVEnvError(f"Path and version not defined in {cfg_path}")
164
166
 
165
167
  if sys.platform == "win32":
166
168
  venv_exe = os.path.join(venv_base, "Scripts", "python.exe")
167
169
  else:
168
170
  venv_exe = os.path.join(venv_base, "bin", "python")
169
171
 
170
- version_tuple = None
171
172
  try:
172
173
  version_tuple = version_str_to_tuple(version_str)
173
174
  except ValueError: # pragma: no cover
@@ -182,15 +183,83 @@ def get_python_venvs(base_dir=None, recursive=True):
182
183
  releaselevel,
183
184
  int(serial if serial != "" else 0),
184
185
  )
186
+ else:
187
+ raise InvalidVEnvError(
188
+ f"Could not determine version from venv version string {version_str}"
189
+ )
185
190
 
186
- if version_tuple is not None:
187
- yield PythonVEnv(
188
- folder=venv_base,
189
- executable=venv_exe,
190
- version=version_tuple,
191
- parent_path=parent_path
192
- )
191
+ return cls(
192
+ folder=venv_base,
193
+ executable=venv_exe,
194
+ version=version_tuple,
195
+ parent_path=parent_path
196
+ )
197
+
198
+
199
+ def get_python_venvs(base_dir=None, recursive=False, search_parent_folders=False):
200
+ """
201
+ Yield discoverable python virtual environment information
193
202
 
203
+ If recursive=True then search_parent_folders is ignored.
194
204
 
195
- def list_python_venvs(base_dir=None, recursive=True) -> list[PythonVEnv]:
196
- return list(get_python_venvs(base_dir=base_dir, recursive=recursive))
205
+ If you're in a project directory and are looking for a potential venv
206
+ search_parent_folders=True will search parents and yield installs discovered.
207
+
208
+ If you're in a folder of source trees and want to find venvs inside any subfolders
209
+ then use recursive=True.
210
+
211
+ :param base_dir: Base directory to search venvs
212
+ :param recursive: Also check subfolders of the base directory
213
+ :param search_parent_folders: Also search parent folders
214
+ :yield: PythonVEnv details.
215
+ """
216
+ base_dir = _laz.Path.cwd() if base_dir is None else _laz.Path(base_dir)
217
+
218
+ search_folders = [base_dir]
219
+
220
+ # Recursive searches don't also search parent folders.
221
+ if recursive:
222
+ pattern = f"**/{VENV_CONFIG_NAME}"
223
+ else:
224
+ pattern = f"*/{VENV_CONFIG_NAME}"
225
+ if search_parent_folders:
226
+ search_folders.extend(base_dir.parents)
227
+
228
+ for f in search_folders:
229
+ try:
230
+ for conf in f.glob(pattern):
231
+ try:
232
+ env = PythonVEnv.from_cfg(conf)
233
+ except InvalidVEnvError:
234
+ continue
235
+
236
+ yield env
237
+ except OSError as e:
238
+ # MacOS can error on searching up folders with an invalid argument
239
+ # On Python 3.11 or earlier.
240
+ if e.errno == 22:
241
+ continue
242
+
243
+
244
+ def list_python_venvs(
245
+ base_dir=None,
246
+ recursive=False,
247
+ search_parent_folders=False,
248
+ ) -> list[PythonVEnv]:
249
+ """
250
+ Get a list of discoverable python virtual environment information
251
+
252
+ If recursive=True then search_parent_folders is ignored.
253
+
254
+ :param base_dir: Base directory to search venvs
255
+ :param recursive: Also check subfolders of the base directory
256
+ :param search_parent_folders: Also search parent folders
257
+ :returns: List of Python VEnv details.
258
+ """
259
+ return list(
260
+ get_python_venvs(
261
+ base_dir=base_dir,
262
+ recursive=recursive,
263
+ search_parent_folders=search_parent_folders,
264
+ )
265
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: ducktools-pythonfinder
3
- Version: 0.7.0
3
+ Version: 0.7.1
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
@@ -76,6 +76,13 @@ def test_local_found(with_venvs):
76
76
  assert os.path.samefile(venvs[0].folder, os.path.join(with_venvs, ".venv"))
77
77
 
78
78
 
79
+ def test_found_in_parent(with_venvs):
80
+ venvs = list_python_venvs(base_dir=os.path.join(with_venvs, "subfolder"), search_parent_folders=True)
81
+
82
+ assert os.path.samefile(venvs[0].folder, os.path.join(with_venvs, "subfolder/.venv"))
83
+ assert os.path.samefile(venvs[1].folder, os.path.join(with_venvs, ".venv"))
84
+
85
+
79
86
  def test_all_found(with_venvs):
80
87
  venvs = sorted(
81
88
  list_python_venvs(base_dir=with_venvs, recursive=True),
@@ -1,2 +0,0 @@
1
- __version__ = "0.7.0"
2
- __version_tuple__ = (0, 7, 0)