Skip to content

Commit cff5d61

Browse files
committed
Fix for missing pip in codespaces with venv
1 parent d613776 commit cff5d61

File tree

5 files changed

+87
-98
lines changed

5 files changed

+87
-98
lines changed

pythonFiles/create_microvenv.py

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import pathlib
77
import subprocess
88
import sys
9-
import urllib.request as url_lib
109
from typing import Optional, Sequence
1110

1211
VENV_NAME = ".venv"
@@ -29,13 +28,6 @@ def run_process(args: Sequence[str], error_message: str) -> None:
2928
def parse_args(argv: Sequence[str]) -> argparse.Namespace:
3029
parser = argparse.ArgumentParser()
3130

32-
parser.add_argument(
33-
"--install-pip",
34-
action="store_true",
35-
default=False,
36-
help="Install pip into the virtual environment.",
37-
)
38-
3931
parser.add_argument(
4032
"--name",
4133
default=VENV_NAME,
@@ -54,31 +46,6 @@ def create_microvenv(name: str):
5446
)
5547

5648

57-
def download_pip_pyz(name: str):
58-
url = "https://bootstrap.pypa.io/pip/pip.pyz"
59-
print("CREATE_MICROVENV.DOWNLOADING_PIP")
60-
61-
try:
62-
with url_lib.urlopen(url) as response:
63-
pip_pyz_path = os.fspath(CWD / name / "pip.pyz")
64-
with open(pip_pyz_path, "wb") as out_file:
65-
data = response.read()
66-
out_file.write(data)
67-
out_file.flush()
68-
except Exception:
69-
raise MicroVenvError("CREATE_MICROVENV.DOWNLOAD_PIP_FAILED")
70-
71-
72-
def install_pip(name: str):
73-
pip_pyz_path = os.fspath(CWD / name / "pip.pyz")
74-
executable = os.fspath(CWD / name / "bin" / "python")
75-
print("CREATE_MICROVENV.INSTALLING_PIP")
76-
run_process(
77-
[executable, pip_pyz_path, "install", "pip"],
78-
"CREATE_MICROVENV.INSTALL_PIP_FAILED",
79-
)
80-
81-
8249
def main(argv: Optional[Sequence[str]] = None) -> None:
8350
if argv is None:
8451
argv = []
@@ -88,10 +55,6 @@ def main(argv: Optional[Sequence[str]] = None) -> None:
8855
create_microvenv(args.name)
8956
print("CREATE_MICROVENV.CREATED_MICROVENV")
9057

91-
if args.install_pip:
92-
download_pip_pyz(args.name)
93-
install_pip(args.name)
94-
9558

9659
if __name__ == "__main__":
9760
main(sys.argv[1:])

pythonFiles/create_venv.py

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import pathlib
88
import subprocess
99
import sys
10+
import urllib.request as url_lib
1011
from typing import List, Optional, Sequence, Union
1112

1213
VENV_NAME = ".venv"
@@ -126,51 +127,91 @@ def add_gitignore(name: str) -> None:
126127
f.write("*")
127128

128129

130+
def download_pip_pyz(name: str):
131+
url = "https://bootstrap.pypa.io/pip/pip.pyz"
132+
print("CREATE_VENV.DOWNLOADING_PIP")
133+
134+
try:
135+
with url_lib.urlopen(url) as response:
136+
pip_pyz_path = os.fspath(CWD / name / "pip.pyz")
137+
with open(pip_pyz_path, "wb") as out_file:
138+
data = response.read()
139+
out_file.write(data)
140+
out_file.flush()
141+
except Exception:
142+
raise VenvError("CREATE_VENV.DOWNLOAD_PIP_FAILED")
143+
144+
145+
def install_pip(name: str):
146+
pip_pyz_path = os.fspath(CWD / name / "pip.pyz")
147+
executable = get_venv_path(name)
148+
print("CREATE_VENV.INSTALLING_PIP")
149+
run_process(
150+
[executable, pip_pyz_path, "install", "pip"],
151+
"CREATE_VENV.INSTALL_PIP_FAILED",
152+
)
153+
154+
129155
def main(argv: Optional[Sequence[str]] = None) -> None:
130156
if argv is None:
131157
argv = []
132158
args = parse_args(argv)
133159

134160
use_micro_venv = False
135-
if not is_installed("venv"):
161+
venv_installed = is_installed("venv")
162+
pip_installed = is_installed("pip")
163+
164+
if not venv_installed:
136165
if sys.platform == "win32":
137166
raise VenvError("CREATE_VENV.VENV_NOT_FOUND")
138167
else:
139168
use_micro_venv = True
140169

141-
pip_installed = is_installed("pip")
142-
deps_needed = args.requirements or args.extras or args.toml
143-
if deps_needed and not pip_installed and not use_micro_venv:
144-
raise VenvError("CREATE_VENV.PIP_NOT_FOUND")
145-
146170
if venv_exists(args.name):
171+
# A virtual environment with same name exists.
172+
# We will use the existing virtual environment.
147173
venv_path = get_venv_path(args.name)
148174
print(f"EXISTING_VENV:{venv_path}")
149175
else:
150176
if use_micro_venv:
177+
# `venv` was not found but on this platform we can use `microvenv`
151178
run_process(
152179
[
153180
sys.executable,
154181
os.fspath(MICROVENV_SCRIPT_PATH),
155-
"--install-pip",
156182
"--name",
157183
args.name,
158184
],
159185
"CREATE_VENV.MICROVENV_FAILED_CREATION",
160186
)
161-
pip_installed = True
187+
elif not pip_installed:
188+
# `venv` was found but `pip` was not found. We create a venv without
189+
# `pip` in it. We will later install `pip`
190+
run_process(
191+
[sys.executable, "-m", "venv", "--without-pip", args.name],
192+
"CREATE_VENV.VENV_FAILED_CREATION",
193+
)
162194
else:
195+
# Both `venv` and `pip` were found. So create a .venv normally
163196
run_process(
164197
[sys.executable, "-m", "venv", args.name],
165198
"CREATE_VENV.VENV_FAILED_CREATION",
166199
)
200+
167201
venv_path = get_venv_path(args.name)
168202
print(f"CREATED_VENV:{venv_path}")
203+
169204
if args.git_ignore:
170205
add_gitignore(args.name)
171206

207+
# At this point we have a .venv. Now we handle installing `pip`.
172208
if pip_installed:
209+
# We upgrade pip if it is already installed.
173210
upgrade_pip(venv_path)
211+
else:
212+
# `pip` was not found, so we download it and install it.
213+
download_pip_pyz(args.name)
214+
install_pip(args.name)
174215

175216
if args.toml:
176217
print(f"VENV_INSTALLING_PYPROJECT: {args.toml}")

pythonFiles/tests/test_create_microvenv.py

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -27,34 +27,3 @@ def run_process(args, error_message):
2727

2828
create_microvenv.main()
2929
assert run_process_called == True
30-
31-
32-
def test_create_microvenv_with_pip():
33-
importlib.reload(create_microvenv)
34-
35-
download_pip_pyz_called = False
36-
37-
def download_pip_pyz(name):
38-
nonlocal download_pip_pyz_called
39-
download_pip_pyz_called = True
40-
assert name == create_microvenv.VENV_NAME
41-
42-
create_microvenv.download_pip_pyz = download_pip_pyz
43-
44-
run_process_called = False
45-
46-
def run_process(args, error_message):
47-
if "install" in args and "pip" in args:
48-
nonlocal run_process_called
49-
run_process_called = True
50-
pip_pyz_path = os.fspath(
51-
create_microvenv.CWD / create_microvenv.VENV_NAME / "pip.pyz"
52-
)
53-
executable = os.fspath(
54-
create_microvenv.CWD / create_microvenv.VENV_NAME / "bin" / "python"
55-
)
56-
assert args == [executable, pip_pyz_path, "install", "pip"]
57-
assert error_message == "CREATE_MICROVENV.INSTALL_PIP_FAILED"
58-
59-
create_microvenv.run_process = run_process
60-
create_microvenv.main(["--install-pip"])

pythonFiles/tests/test_create_venv.py

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ def test_venv_not_installed_unix():
1919

2020
def run_process(args, error_message):
2121
nonlocal run_process_called
22-
if "--install-pip" in args:
22+
microvenv_path = os.fspath(create_venv.MICROVENV_SCRIPT_PATH)
23+
if microvenv_path in args:
2324
run_process_called = True
2425
assert args == [
2526
sys.executable,
26-
os.fspath(create_venv.MICROVENV_SCRIPT_PATH),
27-
"--install-pip",
27+
microvenv_path,
2828
"--name",
2929
".test_venv",
3030
]
@@ -49,20 +49,6 @@ def test_venv_not_installed_windows():
4949
assert str(e.value) == "CREATE_VENV.VENV_NOT_FOUND"
5050

5151

52-
@pytest.mark.parametrize("install", ["requirements", "toml"])
53-
def test_pip_not_installed(install):
54-
importlib.reload(create_venv)
55-
create_venv.venv_exists = lambda _n: True
56-
create_venv.is_installed = lambda module: module != "pip"
57-
create_venv.run_process = lambda _args, _error_message: None
58-
with pytest.raises(create_venv.VenvError) as e:
59-
if install == "requirements":
60-
create_venv.main(["--requirements", "requirements-for-test.txt"])
61-
elif install == "toml":
62-
create_venv.main(["--toml", "pyproject.toml", "--extras", "test"])
63-
assert str(e.value) == "CREATE_VENV.PIP_NOT_FOUND"
64-
65-
6652
@pytest.mark.parametrize("env_exists", ["hasEnv", "noEnv"])
6753
@pytest.mark.parametrize("git_ignore", ["useGitIgnore", "skipGitIgnore"])
6854
@pytest.mark.parametrize("install", ["requirements", "toml", "skipInstall"])
@@ -207,3 +193,33 @@ def run_process(args, error_message):
207193
create_venv.install_requirements(sys.executable, extras)
208194

209195
assert actual == expected
196+
197+
198+
def test_create_venv_missing_pip():
199+
importlib.reload(create_venv)
200+
create_venv.venv_exists = lambda _n: True
201+
create_venv.is_installed = lambda module: module != "pip"
202+
203+
download_pip_pyz_called = False
204+
205+
def download_pip_pyz(name):
206+
nonlocal download_pip_pyz_called
207+
download_pip_pyz_called = True
208+
assert name == create_venv.VENV_NAME
209+
210+
create_venv.download_pip_pyz = download_pip_pyz
211+
212+
run_process_called = False
213+
214+
def run_process(args, error_message):
215+
if "install" in args and "pip" in args:
216+
nonlocal run_process_called
217+
run_process_called = True
218+
pip_pyz_path = os.fspath(
219+
create_venv.CWD / create_venv.VENV_NAME / "pip.pyz"
220+
)
221+
assert args[1:] == [pip_pyz_path, "install", "pip"]
222+
assert error_message == "CREATE_VENV.INSTALL_PIP_FAILED"
223+
224+
create_venv.run_process = run_process
225+
create_venv.main([])

src/client/pythonEnvironments/creation/provider/venvProgressAndTelemetry.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ const CREATING_MICROVENV_MARKER = 'CREATE_MICROVENV.CREATING_MICROVENV';
2525
const CREATE_MICROVENV_FAILED_MARKER = 'CREATE_VENV.MICROVENV_FAILED_CREATION';
2626
const CREATE_MICROVENV_FAILED_MARKER2 = 'CREATE_MICROVENV.MICROVENV_FAILED_CREATION';
2727
const MICROVENV_CREATED_MARKER = 'CREATE_MICROVENV.CREATED_MICROVENV';
28-
const INSTALLING_PIP_MARKER = 'CREATE_MICROVENV.INSTALLING_PIP';
29-
const INSTALL_PIP_FAILED_MARKER = 'CREATE_MICROVENV.INSTALL_PIP_FAILED';
30-
const DOWNLOADING_PIP_MARKER = 'CREATE_MICROVENV.DOWNLOADING_PIP';
31-
const DOWNLOAD_PIP_FAILED_MARKER = 'CREATE_MICROVENV.DOWNLOAD_PIP_FAILED';
28+
const INSTALLING_PIP_MARKER = 'CREATE_VENV.INSTALLING_PIP';
29+
const INSTALL_PIP_FAILED_MARKER = 'CREATE_VENV.INSTALL_PIP_FAILED';
30+
const DOWNLOADING_PIP_MARKER = 'CREATE_VENV.DOWNLOADING_PIP';
31+
const DOWNLOAD_PIP_FAILED_MARKER = 'CREATE_VENV.DOWNLOAD_PIP_FAILED';
3232

3333
export class VenvProgressAndTelemetry {
3434
private readonly processed = new Set<string>();
@@ -266,7 +266,7 @@ export class VenvProgressAndTelemetry {
266266
(progress: CreateEnvironmentProgress) => {
267267
progress.report({ message: CreateEnv.Venv.upgradingPip });
268268
sendTelemetryEvent(EventName.ENVIRONMENT_INSTALLING_PACKAGES, undefined, {
269-
environmentType: 'microvenv',
269+
environmentType: 'venv',
270270
using: 'pipUpgrade',
271271
});
272272
return undefined;

0 commit comments

Comments
 (0)