Skip to content

Commit ed1a2ac

Browse files
authored
Merge pull request #420 from devchat-ai/update_windows_env
feat: Enhance PyEnvManager with improved environment handling
2 parents 97f7d40 + 3f74ca9 commit ed1a2ac

File tree

1 file changed

+87
-42
lines changed

1 file changed

+87
-42
lines changed

devchat/workflow/env_manager.py

Lines changed: 87 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import hashlib
22
import os
3+
import shutil
34
import subprocess
45
import sys
56
from typing import Dict, Optional, Tuple
@@ -82,6 +83,7 @@ def ensure(
8283
py = self.get_py(env_name)
8384

8485
should_remove_old = False
86+
should_install_deps = False
8587

8688
if py:
8789
# check the version of the python executable
@@ -91,60 +93,61 @@ def ensure(
9193
should_remove_old = True
9294

9395
if reqirements_file and self.should_reinstall(env_name, reqirements_file):
94-
should_remove_old = True
96+
should_install_deps = True
9597

96-
if not should_remove_old:
98+
if not should_remove_old and not should_install_deps:
9799
return py
98100

99101
log_file = get_logging_file()
100102
print("\n```Step\n# Setting up workflow environment\n", flush=True)
101103
if should_remove_old:
102-
print(f"- Dependencies of {env_name} have been changed.", flush=True)
104+
print(f"- Python version of {env_name} needs to be updated.", flush=True)
103105
print(f"- Removing the old {env_name}...", flush=True)
104-
self.remove(env_name)
106+
self.remove(env_name, py_version)
107+
108+
# create the environment if it doesn't exist or needs to be recreated
109+
if should_remove_old or not py:
110+
if py_version:
111+
print(f"- Creating {env_name} with {py_version}...", flush=True)
112+
create_ok, msg = self.create(env_name, py_version)
113+
else:
114+
print(f"- Creating {env_name} with current Python version...", flush=True)
115+
create_ok, msg = self.create_with_virtualenv(env_name)
116+
117+
if not create_ok:
118+
print(f"- Failed to create {env_name}.", flush=True)
119+
print(f"\nFor more details, check {log_file}.", flush=True)
120+
print("\n```", flush=True)
121+
print(
122+
f"\n\nFailed to create {env_name}, the workflow will not run.",
123+
flush=True,
124+
)
125+
logger.warning(f"Failed to create {env_name}: {msg}")
126+
sys.exit(0)
105127

106-
# create the environment
107-
if py_version:
108-
print(f"- Creating {env_name} with {py_version}...", flush=True)
109-
create_ok, msg = self.create(env_name, py_version)
110-
else:
111-
print(f"- Creating {env_name} with current Python version...", flush=True)
112-
create_ok, msg = self.create_with_virtualenv(env_name)
113-
114-
if not create_ok:
115-
print(f"- Failed to create {env_name}.", flush=True)
116-
print(f"\nFor more details, check {log_file}.", flush=True)
117-
print("\n```", flush=True)
118-
print(
119-
f"\n\nFailed to create {env_name}, the workflow will not run.",
120-
flush=True,
121-
)
122-
logger.warning(f"Failed to create {env_name}: {msg}")
123-
sys.exit(0)
124-
# return None
125-
126-
# install the requirements
128+
# install or update the requirements
127129
if reqirements_file:
128130
filename = os.path.basename(reqirements_file)
129-
print(f"- Installing dependencies from {filename}...", flush=True)
131+
action = "Updating" if should_install_deps else "Installing"
132+
print(f"- {action} dependencies from {filename}...", flush=True)
130133
install_ok, msg = self.install(env_name, reqirements_file)
131134
if not install_ok:
132-
print(f"- Failed to install dependencies from {filename}.", flush=True)
135+
print(f"- Failed to {action.lower()} dependencies from {filename}.", flush=True)
133136
print(f"\nFor more details, check {log_file}.", flush=True)
134137
print("\n```", flush=True)
135138
print(
136-
"\n\nFailed to install dependencies, the workflow will not run.",
139+
f"\n\nFailed to {action.lower()} dependencies, "
140+
"the workflow may not run correctly.",
137141
flush=True,
138142
)
139-
logger.warning(f"Failed to install dependencies: {msg}")
143+
logger.warning(f"Failed to {action.lower()} dependencies: {msg}")
140144
sys.exit(0)
141-
# return None
142-
143-
# save the hash of the requirements file content
144-
dep_hash = self.get_dep_hash(reqirements_file)
145-
cache_file = os.path.join(ENV_CACHE_DIR, f"{env_name}")
146-
with open(cache_file, "w", encoding="utf-8") as f:
147-
f.write(dep_hash)
145+
else:
146+
# save the hash of the requirements file content
147+
dep_hash = self.get_dep_hash(reqirements_file)
148+
cache_file = os.path.join(ENV_CACHE_DIR, f"{env_name}")
149+
with open(cache_file, "w", encoding="utf-8") as f:
150+
f.write(dep_hash)
148151

149152
print("\n```", flush=True)
150153
return self.get_py(env_name)
@@ -160,16 +163,31 @@ def create_with_virtualenv(self, env_name: str) -> Tuple[bool, str]:
160163
try:
161164
# Use virtualenv.cli_run to create a virtual environment
162165
virtualenv.cli_run([env_path, "--python", sys.executable])
166+
167+
# Create sitecustomize.py in the lib/site-packages directory
168+
site_packages_dir = os.path.join(env_path, "Lib", "site-packages")
169+
if os.path.exists(site_packages_dir):
170+
sitecustomize_path = os.path.join(site_packages_dir, "sitecustomize.py")
171+
with open(sitecustomize_path, "w") as f:
172+
f.write("import sys\n")
173+
f.write('sys.path = [path for path in sys.path if path.find("conda") == -1]')
174+
163175
return True, ""
164176
except Exception as e:
165177
return False, str(e)
166178

167179
def install(self, env_name: str, requirements_file: str) -> Tuple[bool, str]:
168180
"""
169-
Install requirements into the python environment.
181+
Install or update requirements in the python environment.
170182
171183
Args:
172-
requirements: the absolute path to the requirements file.
184+
env_name: the name of the python environment
185+
requirements_file: the absolute path to the requirements file.
186+
187+
Returns:
188+
A tuple (success, message), where success is a boolean indicating
189+
whether the installation was successful, and message is a string
190+
containing output or error information.
173191
"""
174192
py = self.get_py(env_name)
175193
if not py:
@@ -178,6 +196,7 @@ def install(self, env_name: str, requirements_file: str) -> Tuple[bool, str]:
178196
if not os.path.exists(requirements_file):
179197
return False, "Dependencies file not found."
180198

199+
# Base command
181200
cmd = [
182201
py,
183202
"-m",
@@ -189,17 +208,24 @@ def install(self, env_name: str, requirements_file: str) -> Tuple[bool, str]:
189208
PYPI_TUNA,
190209
"--no-warn-script-location",
191210
]
211+
212+
# Check if this is an update or a fresh install
213+
cache_file = os.path.join(ENV_CACHE_DIR, f"{env_name}")
214+
if os.path.exists(cache_file):
215+
# This is an update, add --upgrade flag
216+
cmd.append("--upgrade")
217+
192218
env = os.environ.copy()
193-
env.pop("PYTHONPATH")
219+
env.pop("PYTHONPATH", None)
194220
with subprocess.Popen(
195221
cmd, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, env=env
196222
) as proc:
197223
_, err = proc.communicate()
198224

199225
if proc.returncode != 0:
200-
return False, err.decode("utf-8")
226+
return False, f"Installation failed: {err.decode('utf-8')}"
201227

202-
return True, ""
228+
return True, "Installation successful"
203229

204230
def should_reinstall(self, env_name: str, requirements_file: str) -> bool:
205231
"""
@@ -247,7 +273,26 @@ def create(self, env_name: str, py_version: str) -> Tuple[bool, str]:
247273
return False, msg
248274
return True, ""
249275

250-
def remove(self, env_name: str) -> bool:
276+
def remove(self, env_name: str, py_version: Optional[str] = None) -> bool:
277+
if py_version:
278+
return self.remove_by_mamba(env_name)
279+
return self.remove_by_del(env_name)
280+
281+
def remove_by_del(self, env_name: str) -> bool:
282+
"""
283+
Remove the python environment.
284+
"""
285+
env_path = os.path.join(MAMBA_PY_ENVS, env_name)
286+
try:
287+
# Remove the environment directory
288+
if os.path.exists(env_path):
289+
shutil.rmtree(env_path)
290+
return True
291+
except Exception as e:
292+
print(f"Failed to remove environment {env_name}: {e}")
293+
return False
294+
295+
def remove_by_mamba(self, env_name: str) -> bool:
251296
"""
252297
Remove the python environment.
253298
"""

0 commit comments

Comments
 (0)