Skip to content

Commit

Permalink
FEAT: Toolkit installer (#4593)
Browse files Browse the repository at this point in the history
Co-authored-by: Kathy Pippert <[email protected]>
Co-authored-by: maxcapodi78 <Shark78>
  • Loading branch information
Samuelopez-ansys and PipKat committed May 3, 2024
1 parent eca2ef6 commit 22a1fb5
Show file tree
Hide file tree
Showing 54 changed files with 1,924 additions and 733 deletions.
12 changes: 7 additions & 5 deletions _unittest/test_01_Design.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from pyaedt.application.design_solutions import model_names
from pyaedt.generic.general_methods import is_linux
from pyaedt.generic.general_methods import settings
from pyaedt.workflows import customize_automation_tab

test_subfolder = "T01"
if config["desktopVersion"] > "2022.2":
Expand Down Expand Up @@ -398,17 +399,18 @@ def test_36_test_load(self, add_app):
assert True

def test_37_add_custom_toolkit(self, desktop):
assert desktop.get_available_toolkits()
assert customize_automation_tab.available_toolkits

def test_38_toolkit(self, desktop):
file = os.path.join(self.local_scratch.path, "test.py")
with open(file, "w") as f:
f.write("import pyaedt\n")
assert desktop.add_script_to_menu(
"test_toolkit",
file,
assert customize_automation_tab.add_script_to_menu(
desktop_object=self.aedtapp.desktop_class, name="test_toolkit", script_file=file
)
assert customize_automation_tab.remove_script_from_menu(
desktop_object=self.aedtapp.desktop_class, name="test_toolkit"
)
assert desktop.remove_script_from_menu("test_toolkit")

def test_39_load_project(self, desktop):
new_project = os.path.join(self.local_scratch.path, "new.aedt")
Expand Down
34 changes: 18 additions & 16 deletions _unittest/test_01_toolkit_icons.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import os
import xml.etree.ElementTree as ET

import defusedxml.ElementTree as ET
import defusedxml.minidom

defusedxml.defuse_stdlib()


import pytest

from pyaedt.misc.aedtlib_personalib_install import write_tab_config
from pyaedt.workflows.customize_automation_tab import add_automation_tab


@pytest.fixture(scope="module", autouse=True)
Expand All @@ -17,8 +22,7 @@ def init(self, local_scratch):
self.local_scratch = local_scratch

def test_00_write_new_xml(self):
file_path = os.path.join(self.local_scratch.path, "TabConfig.xml")
write_tab_config(os.path.dirname(file_path), self.local_scratch.path)
file_path = add_automation_tab(name="Test", lib_dir=self.local_scratch.path)
root = self.validate_file_exists_and_pyaedt_tabs_added(file_path)
panels = root.findall("./panel")
panel_names = [panel.attrib["label"] for panel in panels]
Expand All @@ -29,7 +33,7 @@ def test_01_add_pyaedt_config_to_existing_existing_xml(self):
First write a dummy XML with a different Panel and then add PyAEDT's tabs
:return:
"""
file_path = os.path.join(self.local_scratch.path, "TabConfig.xml")
file_path = os.path.join(self.local_scratch.path, "Project", "TabConfig.xml")
with open(file_path, "w") as fid:
fid.write(
"""<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
Expand All @@ -47,15 +51,15 @@ def test_01_add_pyaedt_config_to_existing_existing_xml(self):
"""
)

write_tab_config(os.path.dirname(file_path), self.local_scratch.path)
file_path = add_automation_tab(name="Test", lib_dir=self.local_scratch.path)
root = self.validate_file_exists_and_pyaedt_tabs_added(file_path)
panels = root.findall("./panel")
panel_names = [panel.attrib["label"] for panel in panels]
assert len(panel_names) == 2
assert "Panel_1" in panel_names

def test_03_overwrite_existing_pyaedt_config(self):
file_path = os.path.join(self.local_scratch.path, "TabConfig.xml")
file_path = os.path.join(self.local_scratch.path, "Project", "TabConfig.xml")
with open(file_path, "w") as fid:
fid.write(
"""<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
Expand All @@ -72,14 +76,14 @@ def test_03_overwrite_existing_pyaedt_config(self):
</TabConfig>
"""
)
write_tab_config(os.path.dirname(file_path), self.local_scratch.path)
file_path = add_automation_tab(name="Test", lib_dir=self.local_scratch.path)
root = self.validate_file_exists_and_pyaedt_tabs_added(file_path)
panels = root.findall("./panel")
panel_names = [panel.attrib["label"] for panel in panels]
assert len(panel_names) == 1
assert len(panel_names) == 2

def test_04_write_to_existing_file_but_no_panels(self):
file_path = os.path.join(self.local_scratch.path, "TabConfig.xml")
file_path = os.path.join(self.local_scratch.path, "Project", "TabConfig.xml")
with open(file_path, "w") as fid:
fid.write(
"""<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
Expand All @@ -88,7 +92,7 @@ def test_04_write_to_existing_file_but_no_panels(self):
</TabConfig>
"""
)
write_tab_config(os.path.dirname(file_path), self.local_scratch.path)
file_path = add_automation_tab(name="Test", lib_dir=self.local_scratch.path)
root = self.validate_file_exists_and_pyaedt_tabs_added(file_path)
junks = root.findall("./junk")
junk_names = [junk.attrib["label"] for junk in junks]
Expand All @@ -98,15 +102,13 @@ def test_04_write_to_existing_file_but_no_panels(self):
panel_names = [panel.attrib["label"] for panel in panels]
assert len(panel_names) == 1

def validate_file_exists_and_pyaedt_tabs_added(self, file_path):
@staticmethod
def validate_file_exists_and_pyaedt_tabs_added(file_path):
assert os.path.isfile(file_path) is True
assert ET.parse(file_path) is not None
tree = ET.parse(file_path)
root = tree.getroot()
panels = root.findall("./panel")
panel_names = [panel.attrib["label"] for panel in panels]
assert "Panel_PyAEDT" in panel_names
files_to_verify = ["images/large/pyansys.png", "images/gallery/PyAEDT.png"]
for file_name in files_to_verify:
assert os.path.isfile(os.path.join(os.path.dirname(file_path), file_name))
assert "Panel_PyAEDT_Toolkits" in panel_names
return root
38 changes: 15 additions & 23 deletions doc/source/Resources/PyAEDTInstallerFromDesktop.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@ def run_pyinstaller_from_c_python(oDesktop):
# enable in debu mode
# f.write("import sys\n")
# f.write('sys.path.insert(0, r"c:\\ansysdev\\git\\repos\\pyaedt")\n')
f.write("from pyaedt.misc.aedtlib_personalib_install import add_pyaedt_to_aedt\n")
f.write("from pyaedt.workflows.installer.pyaedt_installer import add_pyaedt_to_aedt\n")
f.write(
'add_pyaedt_to_aedt(aedt_version="{}", is_student_version={}, use_sys_lib=False, new_desktop_session=False, pers_dir=r"{}")\n'.format(
oDesktop.GetVersion()[:6], is_student_version(oDesktop), oDesktop.GetPersonalLibDirectory()))
'add_pyaedt_to_aedt(aedt_version="{}", student_version={}, new_desktop_session=False)\n'.format(
oDesktop.GetVersion()[:6], is_student_version(oDesktop)))

command = r'"{}" "{}"'.format(python_exe, python_script)
oDesktop.AddMessage("", "", 0, command)
Expand Down Expand Up @@ -119,6 +119,14 @@ def install_pyaedt():
if args.version < "232":
ld_library_path_dirs_to_add.append("{}/Delcross".format(args.edt_root))
os.environ["LD_LIBRARY_PATH"] = ":".join(ld_library_path_dirs_to_add) + ":" + os.getenv("LD_LIBRARY_PATH", "")
os.environ["TK_LIBRARY"] = ("{}/commonfiles/CPython/{}/linx64/Release/python/lib/tk8.5".
format(args.edt_root,
args.python_version.replace(
".", "_")))
os.environ["TCL_LIBRARY"] = ("{}/commonfiles/CPython/{}/linx64/Release/python/lib/tcl8.5".
format(args.edt_root,
args.python_version.replace(
".", "_")))

if not os.path.exists(venv_dir):

Expand All @@ -139,22 +147,20 @@ def install_pyaedt():
zip_ref.extractall(unzipped_path)

run_command(
'"{}" install --no-cache-dir --no-index --find-links={} pyaedt[all,dotnet]'.format(pip_exe, unzipped_path))
'"{}" install --no-cache-dir --no-index --find-links={} pyaedt[all,dotnet]'.format(pip_exe,
unzipped_path))
run_command(
'"{}" install --no-cache-dir --no-index --find-links={} jupyterlab'.format(pip_exe, unzipped_path))

else:
run_command('"{}" -m pip install --upgrade pip'.format(python_exe))
run_command('"{}" --default-timeout=1000 install wheel'.format(pip_exe))
run_command('"{}" --default-timeout=1000 install pyaedt[all]'.format(pip_exe))
# run_command('"{}" --default-timeout=1000 install git+https://github.com/ansys/pyaedt.git@main'.format(pip_exe))
# run_command(
# '"{}" --default-timeout=1000 install git+https://github.com/ansys/pyaedt.git@main'.format(pip_exe))
run_command('"{}" --default-timeout=1000 install jupyterlab'.format(pip_exe))
run_command('"{}" --default-timeout=1000 install ipython -U'.format(pip_exe))
run_command('"{}" --default-timeout=1000 install ipyvtklink'.format(pip_exe))
# User can uncomment these lines to install Pyside6 modules
# run_command('"{}" --default-timeout=1000 install pyside6==6.4.0'.format(pip_exe))
# run_command('"{}" --default-timeout=1000 install pyqtgraph'.format(pip_exe))
# run_command('"{}" --default-timeout=1000 install qdarkstyle'.format(pip_exe))

if args.version == "231":
run_command('"{}" uninstall -y pywin32'.format(pip_exe))
Expand All @@ -176,20 +182,6 @@ def install_pyaedt():
run_command('"{}" install --no-cache-dir --no-index --find-links={} pyaedt'.format(pip_exe, unzipped_path))
else:
run_command('"{}" --default-timeout=1000 install pyaedt[all]'.format(pip_exe))

# if is_windows:
# pyaedt_setup_script = "{}/Lib/site-packages/pyaedt/misc/aedtlib_personalib_install.py".format(venv_dir)
# else:
# pyaedt_setup_script = "{}/lib/python{}/site-packages/pyaedt/misc/aedtlib_personalib_install.py".format(
# venv_dir, args.python_version)
#
# if not os.path.isfile(pyaedt_setup_script):
# sys.exit("[ERROR] PyAEDT was not setup properly since {} file does not exist.".format(pyaedt_setup_script))
#
# command = '"{}" "{}" --version={}'.format(python_exe, pyaedt_setup_script, args.version)
# if args.student:
# command += " --student"
# run_command(command)
sys.exit(0)


Expand Down
169 changes: 2 additions & 167 deletions pyaedt/desktop.py
Original file line number Diff line number Diff line change
Expand Up @@ -1461,7 +1461,8 @@ def _exception(self, ex_value, tb_data):
tblist = tb_trace[0].split("\n")
self.logger.error(str(ex_value))
for el in tblist:
self.logger.error(el)
if el:
self.logger.error(el)

return str(ex_value)

Expand Down Expand Up @@ -1744,172 +1745,6 @@ def get_available_toolkits(self):

return list(available_toolkits.keys())

@pyaedt_function_handler()
def add_custom_toolkit(self, toolkit_name): # pragma: no cover
"""Add toolkit to AEDT Automation Tab.
Parameters
----------
toolkit_name : str
Name of toolkit to add.
Returns
-------
bool
"""
from pyaedt.misc.install_extra_toolkits import available_toolkits

toolkit = available_toolkits[toolkit_name]
toolkit_name = toolkit_name.replace("_", "")

def install(package_path, package_name=None):
executable = '"{}"'.format(sys.executable) if is_windows else sys.executable

commands = []
if package_path.startswith("git") and package_name:
commands.append([executable, "-m", "pip", "uninstall", "--yes", package_name])

commands.append([executable, "-m", "pip", "install", "--upgrade", package_path])

if self.aedt_version_id == "2023.1" and is_windows and "AnsysEM" in sys.base_prefix:
commands.append([executable, "-m", "pip", "uninstall", "--yes", "pywin32"])

for command in commands:
if is_linux:
p = subprocess.Popen(command)
else:
p = subprocess.Popen(" ".join(command))
p.wait()

install(toolkit["pip"], toolkit.get("package_name", None))
import site

packages = site.getsitepackages()
full_path = None
for pkg in packages:
if os.path.exists(os.path.join(pkg, toolkit["toolkit_script"])):
full_path = os.path.join(pkg, toolkit["toolkit_script"])
break
if not full_path:
raise FileNotFoundError("Error finding the package.")
self.add_script_to_menu(
toolkit_name=toolkit_name,
script_path=full_path,
script_image=toolkit,
product=toolkit["installation_path"],
copy_to_personal_lib=False,
add_pyaedt_desktop_init=False,
)

@pyaedt_function_handler()
def add_script_to_menu(
self,
toolkit_name,
script_path,
script_image=None,
product="Project",
copy_to_personal_lib=True,
add_pyaedt_desktop_init=True,
):
"""Add a script to the ribbon menu.
.. note::
This method is available in AEDT 2023 R2 and later. PyAEDT must be installed
in AEDT to allow this method to run. For more information, see `Installation
<https://aedt.docs.pyansys.com/version/stable/Getting_started/Installation.html>`_.
Parameters
----------
toolkit_name : str
Name of the toolkit to appear in AEDT.
script_path : str
Full path to the script file. The script will be moved to Personal Lib.
script_image : str, optional
Full path to the image logo (a 30x30 pixel PNG file) to add to the UI.
The default is ``None``.
product : str, optional
Product to which the toolkit applies. The default is ``"Project"``, in which case
it applies to all designs. You can also specify a product, such as ``"HFSS"``.
copy_to_personal_lib : bool, optional
Whether to copy the script to Personal Lib or link the original script. Default is ``True``.
Returns
-------
bool
"""
if not os.path.exists(script_path):
self.logger.error("Script does not exists.")
return False
from pyaedt.misc.install_extra_toolkits import write_toolkit_config

toolkit_dir = os.path.join(self.personallib, "Toolkits")
aedt_version = self.aedt_version_id
tool_dir = os.path.join(toolkit_dir, product, toolkit_name)
lib_dir = os.path.join(tool_dir, "Lib")
toolkit_rel_lib_dir = os.path.relpath(lib_dir, tool_dir)
if is_linux and aedt_version <= "2023.1":
toolkit_rel_lib_dir = os.path.join("Lib", toolkit_name)
lib_dir = os.path.join(toolkit_dir, toolkit_rel_lib_dir)
toolkit_rel_lib_dir = "../../" + toolkit_rel_lib_dir
os.makedirs(lib_dir, exist_ok=True)
os.makedirs(tool_dir, exist_ok=True)
dest_script_path = script_path
if copy_to_personal_lib:
dest_script_path = os.path.join(lib_dir, os.path.split(script_path)[-1])
shutil.copy2(script_path, dest_script_path)
files_to_copy = ["Run_PyAEDT_Toolkit_Script"]
executable_version_agnostic = sys.executable
for file_name in files_to_copy:
src = os.path.join(pathname, "misc", file_name + ".py_build")
dst = os.path.join(tool_dir, file_name.replace("_", " ") + ".py")
if not os.path.isfile(src):
raise FileNotFoundError("File not found: {}".format(src))
with open_file(src, "r") as build_file:
with open_file(dst, "w") as out_file:
self.logger.info("Building to " + dst)
build_file_data = build_file.read()
build_file_data = (
build_file_data.replace("##TOOLKIT_REL_LIB_DIR##", toolkit_rel_lib_dir)
.replace("##PYTHON_EXE##", executable_version_agnostic)
.replace("##PYTHON_SCRIPT##", dest_script_path)
)
build_file_data = build_file_data.replace(" % version", "")
out_file.write(build_file_data)
if aedt_version >= "2023.2":
if not script_image:
script_image = os.path.join(os.path.dirname(__file__), "misc", "images", "large", "pyansys.png")
write_toolkit_config(os.path.join(toolkit_dir, product), lib_dir, toolkit_name, toolkit=script_image)
self.logger.info("{} toolkit installed.".format(toolkit_name))
return True

@pyaedt_function_handler()
def remove_script_from_menu(self, toolkit_name, product="Project"):
"""Remove a toolkit script from the menu.
Parameters
----------
toolkit_name : str
Name of the toolkit to remove.
product : str, optional
Product to which the toolkit applies. The default is ``"Project"``, in which case
it applies to all designs. You can also specify a product, such as ``"HFSS"``.
Returns
-------
bool
"""
from pyaedt.misc.install_extra_toolkits import remove_toolkit_config

toolkit_dir = os.path.join(self.personallib, "Toolkits")
aedt_version = self.aedt_version_id
tool_dir = os.path.join(toolkit_dir, product, toolkit_name)
shutil.rmtree(tool_dir, ignore_errors=True)
if aedt_version >= "2023.2":
remove_toolkit_config(os.path.join(toolkit_dir, product), toolkit_name)
self.logger.info("{} toolkit removed successfully.".format(toolkit_name))
return True

@pyaedt_function_handler()
def submit_job(
self,
Expand Down
Loading

0 comments on commit 22a1fb5

Please sign in to comment.