Skip to content

Commit 1577d61

Browse files
rs-bh-nRobPasMue
andauthored
Conda Venv (#67)
Co-authored-by: Roberto Pastor Muela <[email protected]>
1 parent 461a604 commit 1577d61

File tree

3 files changed

+133
-95
lines changed

3 files changed

+133
-95
lines changed

src/ansys/tools/installer/constants.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929

3030
PYTHON_VERSION_SELECTION_FOR_VENV = """Choose the version of Python to use for your virtual environment.
3131
32-
Please select the Python version from the table below to create its respective virtual environment. Currently Conda Forge Versions are not supported."""
32+
Please select the Python version from the table below to create its respective virtual environment."""
3333

3434
NAME_FOR_VENV = f"""Provide the name for your virtual environment.<br><br>Virtual environments are created under user directory <i>{ANSYS_VENVS}</i>. If the name provided already exists for another virtual environment, it will not be created. Users will receive a warning informing of the situation."""
3535

src/ansys/tools/installer/create_virtual_environment.py

Lines changed: 18 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
)
1616
from ansys.tools.installer.installed_table import DataTable
1717

18-
ALLOWED_FOCUS_EVENTS = [QtCore.QEvent.WindowActivate, QtCore.QEvent.Show]
18+
ALLOWED_FOCUS_EVENTS = [QtCore.QEvent.Type.WindowActivate, QtCore.QEvent.Type.Show]
1919

2020
LOG = logging.getLogger(__name__)
2121
LOG.setLevel("DEBUG")
@@ -86,22 +86,20 @@ def __init__(self, parent):
8686

8787
def create_venv(self):
8888
"""Create virtual environment at selected directory."""
89-
user_directory = os.path.expanduser("~")
90-
venv_dir = ANSYS_VENVS
91-
user_venv_dir = f"{user_directory}/{venv_dir}/{self.venv_name.text()}"
92-
isExist = os.path.exists(user_venv_dir)
89+
venv_dir = Path(Path.home(), ANSYS_VENVS, self.venv_name.text())
9390

9491
if self.venv_name.text() == "":
9592
self.failed_to_create_dialog(case_1=True)
96-
elif isExist:
93+
elif venv_dir.exists():
9794
self.failed_to_create_dialog(case_2=True)
9895
else:
99-
Path(f"{user_directory}/{venv_dir}/{self.venv_name.text()}").mkdir(
100-
parents=True, exist_ok=True
101-
)
102-
user_venv_dir = f"{user_directory}/{venv_dir}/{self.venv_name.text()}"
103-
cmd = f'python -m venv "{user_venv_dir}"'
104-
self.launch_cmd(cmd, minimized_window=True, exit_cmd="^& exit")
96+
venv_dir.mkdir(parents=True, exist_ok=True)
97+
98+
try:
99+
self.cmd_create_venv(venv_dir)
100+
except:
101+
self.failed_to_create_dialog()
102+
105103
self.update_table()
106104
self.venv_success_dialog()
107105

@@ -160,42 +158,25 @@ def eventFilter(self, source, event):
160158
self.table.setFocus()
161159
return super().eventFilter(source, event)
162160

163-
def launch_cmd(self, extra="", minimized_window=False, exit_cmd=""):
164-
"""Run a command in a new command prompt.
161+
def cmd_create_venv(self, venv_dir):
162+
"""Create a virtual environment in a new command prompt.
165163
166164
Parameters
167165
----------
168-
extra : str, default: ""
169-
Any additional command(s).
170-
minimized_window : bool, default: False
171-
Whether the window should run minimized or not.
166+
venv_dir : str
167+
The location for the virtual environment.
172168
"""
173169
py_path = self.table.active_path
174-
user_profile = os.path.expanduser("~")
175-
min_win = "/w /min" if minimized_window else ""
170+
176171
if "Python" in self.table.active_version:
177172
scripts_path = os.path.join(py_path, "Scripts")
178173
new_path = f"{py_path};{scripts_path};%PATH%"
179-
180-
if extra:
181-
cmd = f"& {extra} {exit_cmd}"
182-
else:
183-
cmd = f"& echo Python set to {py_path}"
184-
185174
subprocess.call(
186-
f'start {min_win} cmd /K "set PATH={new_path};{cmd}"{exit_cmd}',
175+
f'start /w /min cmd /K "set PATH={new_path} && python -m venv {venv_dir} && exit"',
187176
shell=True,
188-
cwd=user_profile,
189177
)
190-
else: # probably conda
191-
if extra:
192-
# Replace the pip install command for conda
193-
extra = extra.replace("pip", "conda")
194-
cmd = f"& {extra} {exit_cmd}"
195-
else:
196-
cmd = f"& echo Activating conda forge at path {py_path}"
178+
else: # conda
197179
subprocess.call(
198-
f'start {min_win} cmd /K "{py_path}\\Scripts\\activate.bat {py_path}&cd %userprofile%{cmd}"{exit_cmd}',
180+
f'start /w /min cmd /K "{py_path}\\Scripts\\activate.bat && conda create --prefix {venv_dir} -y && exit"',
199181
shell=True,
200-
cwd=user_profile,
201182
)

src/ansys/tools/installer/installed_table.py

Lines changed: 114 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
get_all_python_venv,
1818
)
1919

20-
ALLOWED_FOCUS_EVENTS = [QtCore.QEvent.WindowActivate, QtCore.QEvent.Show]
20+
ALLOWED_FOCUS_EVENTS = [QtCore.QEvent.Type.WindowActivate, QtCore.QEvent.Type.Show]
2121
LOG = logging.getLogger(__name__)
2222
LOG.setLevel("DEBUG")
2323

@@ -147,10 +147,10 @@ def __init__(self, parent=None):
147147
self.setLayout(layout)
148148

149149
# Group 1: Available Virtual Environments
150-
available_venv_box = QtWidgets.QGroupBox("Available virtual environments")
150+
self.available_venv_box = QtWidgets.QGroupBox("Available virtual environments")
151151
available_venv_box_layout = QtWidgets.QVBoxLayout()
152152
available_venv_box_layout.setContentsMargins(10, 20, 10, 20)
153-
available_venv_box.setLayout(available_venv_box_layout)
153+
self.available_venv_box.setLayout(available_venv_box_layout)
154154

155155
# --> Add text for available virtual environments
156156
available_venv_box_text = QtWidgets.QLabel()
@@ -164,7 +164,35 @@ def __init__(self, parent=None):
164164
self.venv_table.setSelectionMode(QtWidgets.QTableWidget.SingleSelection)
165165

166166
available_venv_box_layout.addWidget(self.venv_table)
167-
layout.addWidget(available_venv_box)
167+
layout.addWidget(self.available_venv_box)
168+
169+
# EXTRA Group: Available Python installation
170+
self.available_python_install_box = QtWidgets.QGroupBox(
171+
"Available base Python versions"
172+
)
173+
available_python_install_box_layout = QtWidgets.QVBoxLayout()
174+
available_python_install_box_layout.setContentsMargins(10, 20, 10, 20)
175+
self.available_python_install_box.setLayout(available_python_install_box_layout)
176+
177+
# Python Version, Forge Version Table
178+
self.table = DataTable(installed_python=True, installed_forge=True)
179+
self.table.setSelectionMode(QtWidgets.QTableWidget.SingleSelection)
180+
available_python_install_box_layout.addWidget(self.table)
181+
layout.addWidget(self.available_python_install_box)
182+
183+
# Hide it at first
184+
self.available_python_install_box.hide()
185+
186+
# EXTRA: Use general Python installations for the above actions
187+
self.check_box_opt = QtWidgets.QCheckBox(
188+
"NOT RECOMMENDED: Use base Python versions instead of virtual environments."
189+
)
190+
self.check_box_opt.setCheckState(QtCore.Qt.CheckState.Unchecked)
191+
layout.addWidget(self.check_box_opt)
192+
self.check_box_opt.stateChanged.connect(self.set_chk_box_focus)
193+
self.check_box_opt.stateChanged.connect(self.display_ctrl)
194+
195+
####### Launching and package management options #######
168196

169197
# Group 2: Launching Options
170198
launching_options_box = QtWidgets.QGroupBox("Launching options")
@@ -234,6 +262,7 @@ def __init__(self, parent=None):
234262

235263
self.package_pip_dict = {
236264
"PyAnsys-Metapackage": "pyansys",
265+
"PyAnsys-Math": "ansys-math-core",
237266
"PyAEDT": "pyaedt",
238267
"PyDPF-Core": "ansys-dpf-core",
239268
"PyDPF-Post": "ansys-dpf-post",
@@ -243,9 +272,12 @@ def __init__(self, parent=None):
243272
"PyFluent-Visualization": "ansys-fluent-visualization",
244273
"PyMAPDL": "ansys-mapdl-core",
245274
"PyMAPDL Reader": "ansys-mapdl-reader",
275+
"PyMechanical": "ansys-mechanical-core",
276+
"PyMotorCAD": "ansys-motorcad-core",
246277
"PyPIM": "ansys-platform-instancemanagement",
247278
"PyPrimeMesh": "ansys-meshing-prime",
248279
"PySeascape": "ansys-seascape",
280+
"PySystem Coupling": "ansys-systemcoupling-core",
249281
"PyTwin": "pytwin",
250282
"Granta MI BoM Analytics": "ansys-grantami-bomanalytics",
251283
"Shared Components": "ansys-openapi-common",
@@ -272,34 +304,6 @@ def __init__(self, parent=None):
272304
hbox_install_pyansys.addWidget(self.button_launch_cmd)
273305
layout.addWidget(pyansys_pkg_manage_box)
274306

275-
# EXTRA: Use general Python installations for the above actions
276-
self.check_box_opt = QtWidgets.QCheckBox(
277-
"NOT RECOMMENDED: Use base Python environments for the above actions."
278-
)
279-
self.check_box_opt.setCheckState(QtCore.Qt.CheckState.Unchecked)
280-
layout.addWidget(self.check_box_opt)
281-
self.check_box_opt.stateChanged.connect(self.set_chk_box_focus)
282-
self.check_box_opt.stateChanged.connect(self.display_ctrl)
283-
284-
# Group 5: Available Python installation
285-
self.available_python_install_box = QtWidgets.QGroupBox(
286-
"Available Python installations"
287-
)
288-
self.available_python_install_box_layout = QtWidgets.QVBoxLayout()
289-
self.available_python_install_box_layout.setContentsMargins(10, 20, 10, 20)
290-
self.available_python_install_box.setLayout(
291-
self.available_python_install_box_layout
292-
)
293-
294-
# Python Version, Forge Version Table
295-
self.table = DataTable(installed_python=True, installed_forge=True)
296-
self.table.setSelectionMode(QtWidgets.QTableWidget.SingleSelection)
297-
self.available_python_install_box_layout.addWidget(self.table)
298-
layout.addWidget(self.available_python_install_box)
299-
300-
self.isHidden = True
301-
self.available_python_install_box.hide()
302-
303307
# ensure the table is always in focus
304308
self.installEventFilter(self)
305309

@@ -310,12 +314,12 @@ def update_table(self):
310314

311315
def display_ctrl(self):
312316
"""Set the focus accordingly depending on check box."""
313-
if self.isHidden:
317+
if self.is_chk_box_active():
314318
self.available_python_install_box.show()
315-
self.isHidden = False
319+
self.available_venv_box.hide()
316320
else:
317321
self.available_python_install_box.hide()
318-
self.isHidden = True
322+
self.available_venv_box.show()
319323

320324
def set_chk_box_focus(self, state):
321325
"""Set the focus accordingly depending on check box."""
@@ -381,13 +385,18 @@ def list_packages(self):
381385
self.launch_cmd("pip list")
382386

383387
def _update_pck_mnger(self):
384-
"""Update package manager if needed."""
385-
cmd = ""
386-
if "Python" in self.table.active_version:
387-
cmd = "python -m pip install -U pip & exit"
388-
elif "Conda" in self.table.active_version:
389-
cmd = "conda update conda & exit"
390-
self.launch_cmd(cmd, True)
388+
"""Update package manager if needed.
389+
390+
Notes
391+
-----
392+
Only working on base Python installations, for now.
393+
"""
394+
if self.is_chk_box_active():
395+
if "Python" in self.table.active_version:
396+
cmd = "python -m pip install -U pip && exit"
397+
else: # Otherwise, conda
398+
cmd = "conda update conda --yes && exit"
399+
self.launch_cmd(cmd, minimized_window=True)
391400

392401
def launch_cmd(self, extra="", minimized_window=False):
393402
"""Run a command in a new command prompt.
@@ -399,43 +408,91 @@ def launch_cmd(self, extra="", minimized_window=False):
399408
minimized_window : bool, default: False
400409
Whether the window should run minimized or not.
401410
"""
411+
min_win = "/w /min" if minimized_window else ""
412+
413+
# is_venv - True : virtual environment , False: base python installation
414+
# is_vanilla_python - True : Vanilla Python , False : Miniforge/Conda
415+
402416
if self.is_chk_box_active():
417+
is_venv = False
418+
# when base python installation is chosen in the table
403419
py_path = self.table.active_path
420+
py_version = self.table.active_version
421+
# chosen_general_base_is_python = True if "Python" in py_version else False
422+
423+
miniforge_path = "" if "Python" in py_version else py_path
424+
is_vanilla_python = True if miniforge_path == "" else False
404425
else:
426+
is_venv = True
427+
# when virtual environment is chosen in the table
405428
py_path = self.venv_table.active_path
406-
407-
min_win = "/w /min" if minimized_window else ""
408-
if "Python" in self.table.active_version and self.is_chk_box_active():
429+
parent_path = os.path.dirname(py_path) # No Scripts Folder
430+
# If py_path has a folder called conda-meta . then it is a conda environment
431+
is_vanilla_python = (
432+
False if "conda-meta" in os.listdir(parent_path) else True
433+
)
434+
if is_vanilla_python:
435+
miniforge_path = ""
436+
else:
437+
py_path = os.path.dirname(py_path) # No Scripts Folder
438+
# If it is a conda environment, then we need to get the path to the miniforge installation
439+
with open(os.path.join(py_path, "conda-meta", "history"), "r") as f:
440+
for line in f:
441+
if line.startswith("# cmd:"):
442+
line = line.lstrip("# cmd: ")
443+
path = line.strip().split("create --prefix")[0]
444+
miniforge_path = (
445+
path.strip().split("Scripts")[0].rstrip("\\")
446+
)
447+
break
448+
is_vanilla_python = True if miniforge_path == "" else False
449+
450+
if is_vanilla_python and not is_venv:
409451
scripts_path = os.path.join(py_path, "Scripts")
410452
new_path = f"{py_path};{scripts_path};%PATH%"
411453

412454
if extra:
413-
cmd = f"& {extra}"
455+
cmd = f"&& {extra}"
414456
else:
415-
cmd = f"& echo Python set to {py_path}"
457+
cmd = f"&& echo Python set to {py_path}"
416458

417459
subprocess.call(
418-
f'start {min_win} cmd /K "set PATH={new_path}&cd %userprofile%{cmd}"',
460+
f'start {min_win} cmd /K "set PATH={new_path} && cd %userprofile% {cmd}"',
461+
shell=True,
462+
)
463+
elif is_vanilla_python and is_venv:
464+
# Launch with active python virtual environment
465+
if extra:
466+
cmd = f"&& {extra}"
467+
else:
468+
cmd = f"&& echo Python set to {py_path}"
469+
subprocess.call(
470+
f'start {min_win} cmd /K "{py_path}\\activate.bat {py_path} && cd %userprofile% {cmd}"',
419471
shell=True,
420472
)
421-
elif not self.is_chk_box_active():
422-
# Launch with active virtual environment
473+
elif not is_vanilla_python and is_venv:
474+
# Launch with active conda virtual environment
423475
if extra:
424-
cmd = f"& {extra}"
476+
# Replace the pip install command for conda
477+
extra = extra.replace("pip", "conda")
478+
extra = extra.replace("conda install", "conda install --yes")
479+
cmd = f"&& {extra}"
425480
else:
426-
cmd = f"& echo Python set to {py_path}"
481+
cmd = f"&& echo Activating conda forge at path {py_path}"
427482
subprocess.call(
428-
f'start {min_win} cmd /K "{py_path}\\activate.bat {py_path}&cd %userprofile%{cmd}"',
483+
f'start {min_win} cmd /K "{miniforge_path}\\Scripts\\activate.bat && conda activate {py_path} && cd %userprofile% {cmd}"',
429484
shell=True,
430485
)
431-
else: # probably conda
486+
else:
487+
# not is_vanilla_python and not is_venv
432488
if extra:
433489
# Replace the pip install command for conda
434490
extra = extra.replace("pip", "conda")
435-
cmd = f"& {extra}"
491+
extra = extra.replace("conda install", "conda install --yes")
492+
cmd = f"&& {extra}"
436493
else:
437-
cmd = f"& echo Activating conda forge at path {py_path}"
494+
cmd = f"&& echo Activating conda forge at path {py_path}"
438495
subprocess.call(
439-
f'start {min_win} cmd /K "{py_path}\\Scripts\\activate.bat {py_path}&cd %userprofile%{cmd}"',
496+
f'start {min_win} cmd /K "{miniforge_path}\\Scripts\\activate.bat && conda activate {py_path} && cd %userprofile% {cmd}"',
440497
shell=True,
441498
)

0 commit comments

Comments
 (0)