Skip to content

Commit 3173540

Browse files
authored
Merge pull request #51 from DavidCEllis/ignore_venv_folders
Ignore venv folders inside the pyenv/versions
2 parents bcac8f8 + 7470d6b commit 3173540

File tree

3 files changed

+72
-3
lines changed

3 files changed

+72
-3
lines changed

src/ducktools/pythonfinder/linux/pyenv_search.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,11 @@ def get_pyenv_pythons(
9191

9292
with finder:
9393
for p in sorted(os.scandir(versions_folder), key=lambda x: x.path):
94+
# Don't include folders that are venvs
95+
venv_indicator = os.path.join(p.path, "pyvenv.cfg")
96+
if os.path.exists(venv_indicator):
97+
continue
98+
9499
executable = os.path.join(p.path, "bin/python")
95100
if os.path.exists(executable):
96101
install = finder.get_install_details(executable, managed_by="pyenv")

src/ducktools/pythonfinder/win32/pyenv_search.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,13 @@ def get_pyenv_pythons(
5353

5454
with finder:
5555
for p in os.scandir(versions_folder):
56+
# On windows, venv folders usually have the python.exe in \Scripts\
57+
# while runtimes have it in the base folder so venvs shouldn't be disovered
58+
# but exclude them early anyway
59+
venv_indicator = os.path.join(p.path, "pyvenv.cfg")
60+
if os.path.exists(venv_indicator):
61+
continue
62+
5663
path_base = os.path.basename(p.path)
5764

5865
if path_base.startswith("pypy"):

tests/test_pyenv.py

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,16 @@ def test_mock_versions_folder(temp_finder):
9494
patch("os.scandir") as scandir_mock, \
9595
patch.object(DetailFinder, "get_install_details") as details_mock:
9696

97+
assert os.path.exists is exists_mock
98+
9799
return_val = PythonInstall.from_str(version=out_ver, executable=out_executable, managed_by="pyenv")
98100
details_mock.return_value = return_val
99-
exists_mock.return_value = True
101+
102+
def false_on_venv_check(pth):
103+
# Need to return False on the venv check, but True on all other exists checks
104+
return False if pth.endswith("pyvenv.cfg") else True
105+
106+
exists_mock.side_effect = false_on_venv_check
100107
scandir_mock.return_value = iter([mock_dir_entry])
101108

102109
python_versions = list(get_pyenv_pythons(versions_folder=versions_folder, finder=temp_finder))
@@ -106,6 +113,40 @@ def test_mock_versions_folder(temp_finder):
106113
assert python_versions == [return_val]
107114

108115

116+
def test_mock_folder_skip_venv(temp_finder):
117+
# If the venv check returns true, make sure the folder is skipped
118+
mock_dir_entry = Mock(os.DirEntry)
119+
120+
out_ver = "3.12.1"
121+
if sys.platform == "win32":
122+
versions_folder = os.path.join("c:", "fake", "versions")
123+
out_executable = os.path.join(versions_folder, out_ver, "python.exe")
124+
else:
125+
versions_folder = "~/fake/versions"
126+
out_executable = os.path.join(versions_folder, out_ver, "bin/python")
127+
128+
mock_dir_entry.name = out_ver
129+
mock_dir_entry.path = os.path.join(versions_folder, out_ver)
130+
131+
with patch("os.path.exists") as exists_mock, \
132+
patch("os.scandir") as scandir_mock, \
133+
patch.object(DetailFinder, "get_install_details") as details_mock:
134+
135+
assert os.path.exists is exists_mock
136+
137+
return_val = PythonInstall.from_str(version=out_ver, executable=out_executable, managed_by="pyenv")
138+
details_mock.return_value = return_val
139+
140+
exists_mock.return_value = True
141+
scandir_mock.return_value = iter([mock_dir_entry])
142+
143+
python_versions = list(get_pyenv_pythons(versions_folder=versions_folder, finder=temp_finder))
144+
145+
details_mock.assert_not_called()
146+
147+
assert python_versions == []
148+
149+
109150
@pytest.mark.skipif(sys.platform != "win32", reason="Test for Windows only")
110151
def test_fs_versions_win(fs, temp_finder):
111152
# Test with folders in fake file system
@@ -125,7 +166,15 @@ def test_fs_versions_win(fs, temp_finder):
125166

126167
details_mock.assert_called_once_with(py_exe, managed_by="pyenv")
127168

128-
assert versions == [PythonInstall.from_str(version="3.12.1", executable=py_exe, managed_by="pyenv")]
169+
assert versions == [return_val]
170+
171+
# Check presence of a 'pyvenv.cfg' ignores the folder
172+
details_mock.reset_mock()
173+
fs.create_file(os.path.join(py_folder, "pyvenv.cfg"))
174+
175+
versions = list(get_pyenv_pythons(tmpdir, finder=temp_finder))
176+
details_mock.assert_not_called()
177+
assert versions == []
129178

130179

131180
@pytest.mark.skipif(sys.platform == "win32", reason="Test for non-Windows only")
@@ -148,7 +197,15 @@ def test_fs_versions_nix(fs, temp_finder):
148197
versions = list(get_pyenv_pythons(tmpdir, finder=temp_finder))
149198
details_mock.assert_called_once_with(py_exe, managed_by="pyenv")
150199

151-
assert versions == [PythonInstall.from_str(version="3.12.1", executable=py_exe, managed_by="pyenv")]
200+
assert versions == [return_val]
201+
202+
details_mock.reset_mock()
203+
204+
# Test the same folder is ignored if a 'pyvenv.cfg' file is discovered
205+
fs.create_file(os.path.join(py_folder, "pyvenv.cfg"))
206+
versions = list(get_pyenv_pythons(tmpdir, finder=temp_finder))
207+
details_mock.assert_not_called()
208+
assert versions == []
152209

153210

154211
@pytest.mark.skipif(sys.platform == "win32", reason="Test for non-Windows only")

0 commit comments

Comments
 (0)