ducktools-pythonfinder 0.6.8__tar.gz → 0.7.0__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.
- {ducktools_pythonfinder-0.6.8/src/ducktools_pythonfinder.egg-info → ducktools_pythonfinder-0.7.0}/PKG-INFO +12 -1
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/README.md +11 -0
- ducktools_pythonfinder-0.7.0/src/ducktools/pythonfinder/_version.py +2 -0
- ducktools_pythonfinder-0.7.0/src/ducktools/pythonfinder/venv.py +196 -0
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0/src/ducktools_pythonfinder.egg-info}/PKG-INFO +12 -1
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/src/ducktools_pythonfinder.egg-info/SOURCES.txt +2 -0
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/tests/conftest.py +23 -0
- ducktools_pythonfinder-0.7.0/tests/test_venv_finder.py +118 -0
- ducktools_pythonfinder-0.6.8/src/ducktools/pythonfinder/_version.py +0 -2
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/.gitignore +0 -0
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/LICENSE +0 -0
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/MANIFEST.in +0 -0
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/pyproject.toml +0 -0
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/scripts/build_zipapp.py +0 -0
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/scripts/detail_this_python.py +0 -0
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/scripts/print_python_versions.py +0 -0
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/setup.cfg +0 -0
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/src/ducktools/pythonfinder/__init__.py +0 -0
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/src/ducktools/pythonfinder/__main__.py +0 -0
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/src/ducktools/pythonfinder/darwin/__init__.py +0 -0
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/src/ducktools/pythonfinder/details_script.py +0 -0
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/src/ducktools/pythonfinder/linux/__init__.py +0 -0
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/src/ducktools/pythonfinder/linux/pyenv_search.py +0 -0
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/src/ducktools/pythonfinder/pythonorg_search.py +0 -0
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/src/ducktools/pythonfinder/shared.py +0 -0
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/src/ducktools/pythonfinder/win32/__init__.py +0 -0
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/src/ducktools/pythonfinder/win32/pyenv_search.py +0 -0
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/src/ducktools/pythonfinder/win32/registry_search.py +0 -0
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/src/ducktools_pythonfinder.egg-info/dependency_links.txt +0 -0
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/src/ducktools_pythonfinder.egg-info/entry_points.txt +0 -0
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/src/ducktools_pythonfinder.egg-info/requires.txt +0 -0
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/src/ducktools_pythonfinder.egg-info/top_level.txt +0 -0
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/tests/sources/python_versions.txt +0 -0
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/tests/sources/release.json +0 -0
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/tests/sources/release_file.json +0 -0
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/tests/test_details_script.py +0 -0
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/tests/test_foldersearch.py +0 -0
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/tests/test_orgsearch.py +0 -0
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/tests/test_pyenv.py +0 -0
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/tests/test_shared.py +0 -0
- {ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/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.
|
|
3
|
+
Version: 0.7.0
|
|
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
|
|
@@ -124,6 +124,17 @@ PythonInstall(version=(3, 8, 10, 'final', 0), executable='~\\.pyenv\\pyenv-win\\
|
|
|
124
124
|
PythonInstall(version=(3, 13, 0, 'candidate', 1), executable='~\\.pyenv\\pyenv-win\\versions\\3.13.0rc1\\python.exe', architecture='64bit', implementation='cpython', metadata={}, shadowed=False)```
|
|
125
125
|
```
|
|
126
126
|
|
|
127
|
+
### Finding venvs ###
|
|
128
|
+
|
|
129
|
+
There is now a submodule to search for virtual environments.
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
from ducktools.pythonfinder.venv import list_python_venvs
|
|
133
|
+
|
|
134
|
+
for venv in list_python_venvs():
|
|
135
|
+
print(venv.executable)
|
|
136
|
+
```
|
|
137
|
+
|
|
127
138
|
### Python.org search ###
|
|
128
139
|
|
|
129
140
|
Python.org searches are handled by the `ducktools.pythonfinder.pythonorg_search` module.
|
|
@@ -96,6 +96,17 @@ PythonInstall(version=(3, 8, 10, 'final', 0), executable='~\\.pyenv\\pyenv-win\\
|
|
|
96
96
|
PythonInstall(version=(3, 13, 0, 'candidate', 1), executable='~\\.pyenv\\pyenv-win\\versions\\3.13.0rc1\\python.exe', architecture='64bit', implementation='cpython', metadata={}, shadowed=False)```
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
+
### Finding venvs ###
|
|
100
|
+
|
|
101
|
+
There is now a submodule to search for virtual environments.
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
from ducktools.pythonfinder.venv import list_python_venvs
|
|
105
|
+
|
|
106
|
+
for venv in list_python_venvs():
|
|
107
|
+
print(venv.executable)
|
|
108
|
+
```
|
|
109
|
+
|
|
99
110
|
### Python.org search ###
|
|
100
111
|
|
|
101
112
|
Python.org searches are handled by the `ducktools.pythonfinder.pythonorg_search` module.
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# ducktools-pythonfinder
|
|
2
|
+
# MIT License
|
|
3
|
+
#
|
|
4
|
+
# Copyright (c) 2013-2014 David C Ellis
|
|
5
|
+
#
|
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
# in the Software without restriction, including without limitation the rights
|
|
9
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
# furnished to do so, subject to the following conditions:
|
|
12
|
+
#
|
|
13
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
# copies or substantial portions of the Software.
|
|
15
|
+
#
|
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
# SOFTWARE.
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import os
|
|
26
|
+
import sys
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
from ducktools.classbuilder.prefab import Prefab
|
|
30
|
+
from ducktools.lazyimporter import LazyImporter, FromImport, ModuleImport
|
|
31
|
+
|
|
32
|
+
from .shared import (
|
|
33
|
+
PythonInstall,
|
|
34
|
+
get_install_details,
|
|
35
|
+
version_str_to_tuple,
|
|
36
|
+
version_tuple_to_str,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
_laz = LazyImporter(
|
|
41
|
+
[
|
|
42
|
+
ModuleImport("re"),
|
|
43
|
+
ModuleImport("json"),
|
|
44
|
+
FromImport("pathlib", "Path"),
|
|
45
|
+
FromImport("subprocess", "run"),
|
|
46
|
+
]
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
VENV_CONFIG_NAME = "pyvenv.cfg"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# VIRTUALENV can make some invalid regexes that are just the tuple with dots.
|
|
53
|
+
VIRTUALENV_PY_VER_RE = (
|
|
54
|
+
r"(?P<major>\d+)\.(?P<minor>\d+)\.?(?P<micro>\d*)\.(?P<releaselevel>.+)\.(?P<serial>\d*)?"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class PythonPackage(Prefab):
|
|
59
|
+
name: str
|
|
60
|
+
version: str
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class PythonVEnv(Prefab):
|
|
64
|
+
folder: str
|
|
65
|
+
executable: str
|
|
66
|
+
version: tuple[int, int, int, str, int]
|
|
67
|
+
parent_path: str
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def version_str(self) -> str:
|
|
71
|
+
return version_tuple_to_str(self.version)
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def parent_executable(self) -> str:
|
|
75
|
+
if sys.platform == "win32":
|
|
76
|
+
return os.path.join(self.parent_path, "python.exe")
|
|
77
|
+
else:
|
|
78
|
+
return os.path.join(self.parent_path, "python")
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def parent_exists(self) -> bool:
|
|
82
|
+
return os.path.exists(self.parent_executable)
|
|
83
|
+
|
|
84
|
+
def get_parent_install(self, cache: list[PythonInstall] | None = None) -> PythonInstall | None:
|
|
85
|
+
install = None
|
|
86
|
+
cache = [] if cache is None else cache
|
|
87
|
+
|
|
88
|
+
if self.parent_exists:
|
|
89
|
+
exe = self.parent_executable
|
|
90
|
+
|
|
91
|
+
# Python installs may be cached, can skip querying exe.
|
|
92
|
+
for inst in cache:
|
|
93
|
+
if os.path.samefile(inst.executable, exe):
|
|
94
|
+
install = inst
|
|
95
|
+
break
|
|
96
|
+
|
|
97
|
+
if install is None:
|
|
98
|
+
install = get_install_details(exe)
|
|
99
|
+
|
|
100
|
+
return install
|
|
101
|
+
|
|
102
|
+
def list_packages(self):
|
|
103
|
+
if not self.parent_exists:
|
|
104
|
+
raise FileNotFoundError(
|
|
105
|
+
f"Parent Python at \"{self.parent_executable}\" does not exist."
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# Should probably use sys.executable and have pip as a dependency
|
|
109
|
+
# We would need to look at possibly changing how ducktools-env works for that however.
|
|
110
|
+
|
|
111
|
+
data = _laz.run(
|
|
112
|
+
[
|
|
113
|
+
self.parent_executable,
|
|
114
|
+
"-m", "pip",
|
|
115
|
+
"--python", self.executable,
|
|
116
|
+
"list",
|
|
117
|
+
"--format", "json"
|
|
118
|
+
],
|
|
119
|
+
capture_output=True,
|
|
120
|
+
text=True,
|
|
121
|
+
check=True,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
raw_packages = _laz.json.loads(data.stdout)
|
|
125
|
+
|
|
126
|
+
packages = [
|
|
127
|
+
PythonPackage(
|
|
128
|
+
name=p["name"],
|
|
129
|
+
version=p["version"],
|
|
130
|
+
)
|
|
131
|
+
for p in raw_packages
|
|
132
|
+
]
|
|
133
|
+
|
|
134
|
+
return packages
|
|
135
|
+
|
|
136
|
+
|
|
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:
|
|
146
|
+
parent_path, version_str = None, None
|
|
147
|
+
venv_base = conf.parent
|
|
148
|
+
|
|
149
|
+
with conf.open() as f:
|
|
150
|
+
for line in f:
|
|
151
|
+
key, value = (item.strip() for item in line.split("="))
|
|
152
|
+
|
|
153
|
+
if key == "home":
|
|
154
|
+
parent_path = value
|
|
155
|
+
elif key in {"version", "version_info"}:
|
|
156
|
+
# venv and uv use different key names :)
|
|
157
|
+
version_str = value
|
|
158
|
+
|
|
159
|
+
if parent_path and version_str:
|
|
160
|
+
break
|
|
161
|
+
else:
|
|
162
|
+
# Not a valid venv, ignore
|
|
163
|
+
continue
|
|
164
|
+
|
|
165
|
+
if sys.platform == "win32":
|
|
166
|
+
venv_exe = os.path.join(venv_base, "Scripts", "python.exe")
|
|
167
|
+
else:
|
|
168
|
+
venv_exe = os.path.join(venv_base, "bin", "python")
|
|
169
|
+
|
|
170
|
+
version_tuple = None
|
|
171
|
+
try:
|
|
172
|
+
version_tuple = version_str_to_tuple(version_str)
|
|
173
|
+
except ValueError: # pragma: no cover
|
|
174
|
+
# Might be virtualenv putting in incorrect versions
|
|
175
|
+
parsed_version = _laz.re.fullmatch(VIRTUALENV_PY_VER_RE, version_str)
|
|
176
|
+
if parsed_version:
|
|
177
|
+
major, minor, micro, releaselevel, serial = parsed_version.groups()
|
|
178
|
+
version_tuple = (
|
|
179
|
+
int(major),
|
|
180
|
+
int(minor),
|
|
181
|
+
int(micro) if micro else 0,
|
|
182
|
+
releaselevel,
|
|
183
|
+
int(serial if serial != "" else 0),
|
|
184
|
+
)
|
|
185
|
+
|
|
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
|
+
)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def list_python_venvs(base_dir=None, recursive=True) -> list[PythonVEnv]:
|
|
196
|
+
return list(get_python_venvs(base_dir=base_dir, recursive=recursive))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: ducktools-pythonfinder
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
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
|
|
@@ -124,6 +124,17 @@ PythonInstall(version=(3, 8, 10, 'final', 0), executable='~\\.pyenv\\pyenv-win\\
|
|
|
124
124
|
PythonInstall(version=(3, 13, 0, 'candidate', 1), executable='~\\.pyenv\\pyenv-win\\versions\\3.13.0rc1\\python.exe', architecture='64bit', implementation='cpython', metadata={}, shadowed=False)```
|
|
125
125
|
```
|
|
126
126
|
|
|
127
|
+
### Finding venvs ###
|
|
128
|
+
|
|
129
|
+
There is now a submodule to search for virtual environments.
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
from ducktools.pythonfinder.venv import list_python_venvs
|
|
133
|
+
|
|
134
|
+
for venv in list_python_venvs():
|
|
135
|
+
print(venv.executable)
|
|
136
|
+
```
|
|
137
|
+
|
|
127
138
|
### Python.org search ###
|
|
128
139
|
|
|
129
140
|
Python.org searches are handled by the `ducktools.pythonfinder.pythonorg_search` module.
|
|
@@ -12,6 +12,7 @@ src/ducktools/pythonfinder/_version.py
|
|
|
12
12
|
src/ducktools/pythonfinder/details_script.py
|
|
13
13
|
src/ducktools/pythonfinder/pythonorg_search.py
|
|
14
14
|
src/ducktools/pythonfinder/shared.py
|
|
15
|
+
src/ducktools/pythonfinder/venv.py
|
|
15
16
|
src/ducktools/pythonfinder/darwin/__init__.py
|
|
16
17
|
src/ducktools/pythonfinder/linux/__init__.py
|
|
17
18
|
src/ducktools/pythonfinder/linux/pyenv_search.py
|
|
@@ -31,6 +32,7 @@ tests/test_orgsearch.py
|
|
|
31
32
|
tests/test_pyenv.py
|
|
32
33
|
tests/test_shared.py
|
|
33
34
|
tests/test_uv_finder.py
|
|
35
|
+
tests/test_venv_finder.py
|
|
34
36
|
tests/sources/python_versions.txt
|
|
35
37
|
tests/sources/release.json
|
|
36
38
|
tests/sources/release_file.json
|
|
@@ -20,11 +20,14 @@
|
|
|
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
|
+
import sys
|
|
24
|
+
|
|
23
25
|
from pathlib import Path
|
|
24
26
|
|
|
25
27
|
import pytest
|
|
26
28
|
|
|
27
29
|
from ducktools.pythonfinder import details_script
|
|
30
|
+
from ducktools.pythonfinder.shared import get_install_details
|
|
28
31
|
|
|
29
32
|
|
|
30
33
|
@pytest.fixture(scope="session")
|
|
@@ -37,6 +40,26 @@ def uses_details_script(fs):
|
|
|
37
40
|
fs.add_real_file(details_script.__file__)
|
|
38
41
|
|
|
39
42
|
|
|
43
|
+
@pytest.fixture(scope="session")
|
|
44
|
+
def this_python():
|
|
45
|
+
if sys.platform == "win32":
|
|
46
|
+
py_exe = Path(sys.base_prefix) / "python.exe"
|
|
47
|
+
else:
|
|
48
|
+
py_exe = Path(sys.base_prefix) / "bin" / "python"
|
|
49
|
+
|
|
50
|
+
return get_install_details(str(py_exe))
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@pytest.fixture(scope="session")
|
|
54
|
+
def this_venv():
|
|
55
|
+
if sys.platform == "win32":
|
|
56
|
+
exe = sys.executable
|
|
57
|
+
else:
|
|
58
|
+
exe = str(Path(sys.executable).with_name("python"))
|
|
59
|
+
venv = get_install_details(exe)
|
|
60
|
+
return venv
|
|
61
|
+
|
|
62
|
+
|
|
40
63
|
def pytest_addoption(parser):
|
|
41
64
|
parser.addoption(
|
|
42
65
|
"--run-uv-python",
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# ducktools-pythonfinder
|
|
2
|
+
# MIT License
|
|
3
|
+
#
|
|
4
|
+
# Copyright (c) 2013-2014 David C Ellis
|
|
5
|
+
#
|
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
# in the Software without restriction, including without limitation the rights
|
|
9
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
# furnished to do so, subject to the following conditions:
|
|
12
|
+
#
|
|
13
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
# copies or substantial portions of the Software.
|
|
15
|
+
#
|
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
# SOFTWARE.
|
|
23
|
+
import os
|
|
24
|
+
import subprocess
|
|
25
|
+
import sys
|
|
26
|
+
import tempfile
|
|
27
|
+
|
|
28
|
+
from ducktools.pythonfinder.venv import list_python_venvs
|
|
29
|
+
|
|
30
|
+
import pytest
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@pytest.fixture(scope="module")
|
|
34
|
+
def with_venvs():
|
|
35
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
36
|
+
# We can't actually use venv directly here as
|
|
37
|
+
# Older python on linux makes invalid venvs
|
|
38
|
+
if sys.platform == "win32":
|
|
39
|
+
python_exe = os.path.join(sys.base_prefix, "python.exe")
|
|
40
|
+
else:
|
|
41
|
+
python_exe = os.path.join(sys.base_prefix, "bin", "python")
|
|
42
|
+
|
|
43
|
+
def make_venv(pth):
|
|
44
|
+
subprocess.run(
|
|
45
|
+
[
|
|
46
|
+
python_exe,
|
|
47
|
+
"-m", "venv",
|
|
48
|
+
"--without-pip",
|
|
49
|
+
os.path.join(tmpdir, pth),
|
|
50
|
+
],
|
|
51
|
+
check=True,
|
|
52
|
+
capture_output=True
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
make_venv(".venv")
|
|
56
|
+
make_venv("subfolder/.venv")
|
|
57
|
+
make_venv("subfolder/subsubfolder/env")
|
|
58
|
+
|
|
59
|
+
assert os.path.exists(os.path.join(tmpdir, ".venv"))
|
|
60
|
+
|
|
61
|
+
yield tmpdir
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_no_venvs():
|
|
65
|
+
# Don't use the venv directory here
|
|
66
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
67
|
+
venvs = list_python_venvs(base_dir=tmpdir)
|
|
68
|
+
|
|
69
|
+
assert len(venvs) == 0
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def test_local_found(with_venvs):
|
|
73
|
+
venvs = list_python_venvs(base_dir=with_venvs, recursive=False)
|
|
74
|
+
|
|
75
|
+
assert len(venvs) == 1
|
|
76
|
+
assert os.path.samefile(venvs[0].folder, os.path.join(with_venvs, ".venv"))
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def test_all_found(with_venvs):
|
|
80
|
+
venvs = sorted(
|
|
81
|
+
list_python_venvs(base_dir=with_venvs, recursive=True),
|
|
82
|
+
key=lambda x: x.folder
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
assert len(venvs) == 3
|
|
86
|
+
assert os.path.samefile(venvs[0].folder, os.path.join(with_venvs, ".venv"))
|
|
87
|
+
assert os.path.samefile(venvs[1].folder, os.path.join(with_venvs, "subfolder/.venv"))
|
|
88
|
+
assert os.path.samefile(venvs[2].folder, os.path.join(with_venvs, "subfolder/subsubfolder/env"))
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def test_found_parent(with_venvs, this_python, this_venv):
|
|
92
|
+
venv_ex = list_python_venvs(base_dir=with_venvs, recursive=False)[0]
|
|
93
|
+
|
|
94
|
+
assert os.path.samefile(this_python.executable, venv_ex.parent_executable)
|
|
95
|
+
|
|
96
|
+
# We found the base env that created this python, all details match
|
|
97
|
+
parent = venv_ex.get_parent_install()
|
|
98
|
+
assert parent == this_python
|
|
99
|
+
|
|
100
|
+
# venv version str works same as parent
|
|
101
|
+
assert venv_ex.version_str == parent.version_str
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def test_found_parent_cache(with_venvs, this_python):
|
|
105
|
+
venv_ex = list_python_venvs(base_dir=with_venvs, recursive=False)[0]
|
|
106
|
+
|
|
107
|
+
parent = venv_ex.get_parent_install(cache=[this_python])
|
|
108
|
+
assert parent == this_python
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def test_empty_packages(with_venvs):
|
|
112
|
+
venv_ex = list_python_venvs(base_dir=with_venvs, recursive=False)[0]
|
|
113
|
+
|
|
114
|
+
assert os.path.exists(venv_ex.parent_executable)
|
|
115
|
+
assert os.path.exists(venv_ex.executable)
|
|
116
|
+
|
|
117
|
+
packages = venv_ex.list_packages()
|
|
118
|
+
assert packages == []
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/scripts/print_python_versions.py
RENAMED
|
File without changes
|
|
File without changes
|
{ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/src/ducktools/pythonfinder/__init__.py
RENAMED
|
File without changes
|
{ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/src/ducktools/pythonfinder/__main__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/src/ducktools/pythonfinder/shared.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/tests/sources/python_versions.txt
RENAMED
|
File without changes
|
|
File without changes
|
{ducktools_pythonfinder-0.6.8 → ducktools_pythonfinder-0.7.0}/tests/sources/release_file.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|