Skip to content

Commit

Permalink
Install custom toolkit
Browse files Browse the repository at this point in the history
  • Loading branch information
Samuelopez-ansys committed Apr 30, 2024
1 parent dcb6826 commit 9f3b822
Show file tree
Hide file tree
Showing 13 changed files with 313 additions and 320 deletions.
293 changes: 0 additions & 293 deletions pyaedt/desktop.py
Original file line number Diff line number Diff line change
Expand Up @@ -1685,299 +1685,6 @@ def get_available_toolkits(self):

return list(available_toolkits.keys())

@pyaedt_function_handler()
def add_custom_toolkit(self, toolkit_name, wheel_toolkit=None, install=True): # pragma: no cover
"""Add toolkit to AEDT Automation Tab.
Parameters
----------
toolkit_name : str
Name of toolkit to add.
wheel_toolkit : str
Wheelhouse path.
install : bool, optional
Whether to install the toolkit.
Returns
-------
bool
"""
from pyaedt.misc.install_extra_toolkits import available_toolkits

toolkit = available_toolkits[toolkit_name]

# Set Python version based on AEDT version
python_version = "3.10" if self.aedt_version_id > "2023.1" else "3.7"

if is_windows:
base_venv = os.path.normpath(
os.path.join(
self.install_path,
"commonfiles",
"CPython",
python_version.replace(".", "_"),
"winx64",
"Release",
"python",
"python.exe",
)
)
else:
base_venv = os.path.normpath(
os.path.join(
self.install_path,
"commonfiles",
"CPython",
python_version.replace(".", "_"),
"linx64",
"Release",
"python",
"runpython",
)
)

self.logger.info(base_venv)

def run_command(command):
try:
if is_linux: # pragma: no cover
process = subprocess.Popen(
command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
) # nosec
else:
process = subprocess.Popen(
command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
) # nosec
_, stderr = process.communicate()
ret_code = process.returncode
if ret_code != 0:
print("Error occurred:", stderr.decode("utf-8"))
return ret_code
except Exception as e:
print("Exception occurred:", str(e))
return 1 # Return non-zero exit code for indicating an error

version = self.odesktop.GetVersion()[2:6].replace(".", "")

if is_windows:
venv_dir = os.path.join(os.environ["APPDATA"], "pyaedt_env_ide", "toolkits_v{}".format(version))
python_exe = os.path.join(venv_dir, "Scripts", "python.exe")
pip_exe = os.path.join(venv_dir, "Scripts", "pip.exe")
package_dir = os.path.join(venv_dir, "Lib")
else:
venv_dir = os.path.join(os.environ["HOME"], "pyaedt_env_ide", "toolkits_v{}".format(version))
python_exe = os.path.join(venv_dir, "bin", "python")
pip_exe = os.path.join(venv_dir, "bin", "pip")
package_dir = os.path.join(venv_dir, "lib")
edt_root = os.path.normpath(self.odesktop.GetExeDir())
os.environ["ANSYSEM_ROOT{}".format(version)] = edt_root
ld_library_path_dirs_to_add = [
"{}/commonfiles/CPython/{}/linx64/Release/python/lib".format(
edt_root, python_version.replace(".", "_")
),
"{}/common/mono/Linux64/lib64".format(edt_root),
"{}".format(edt_root),
]
if version < "232":
ld_library_path_dirs_to_add.append("{}/Delcross".format(edt_root))
os.environ["LD_LIBRARY_PATH"] = (
":".join(ld_library_path_dirs_to_add) + ":" + os.getenv("LD_LIBRARY_PATH", "")
)

# Create virtual environment

if not os.path.exists(venv_dir):
self.logger.info("Creating virtual environment")
run_command('"{}" -m venv "{}" --system-site-packages'.format(base_venv, venv_dir))
self.logger.info("Virtual environment created.")

is_installed = False
script_file = None
if os.path.isdir(os.path.normpath(os.path.join(package_dir, toolkit["toolkit_script"]))):
script_file = os.path.normpath(os.path.join(package_dir, toolkit["toolkit_script"]))
else:
for dirpath, dirnames, _ in os.walk(package_dir):
if "site-packages" in dirnames:
script_file = os.path.normpath(os.path.join(dirpath, "site-packages", toolkit["toolkit_script"]))
break
if os.path.isfile(script_file):
is_installed = True
if wheel_toolkit:
wheel_toolkit = os.path.normpath(wheel_toolkit)
self.logger.info("Installing dependencies")
if install and wheel_toolkit and os.path.exists(wheel_toolkit):
self.logger.info("Starting offline installation")
if is_installed:
run_command('"{}" uninstall --yes {}'.format(pip_exe, toolkit["pip"]))
import zipfile

unzipped_path = os.path.join(
os.path.dirname(wheel_toolkit), os.path.splitext(os.path.basename(wheel_toolkit))[0]
)
if os.path.exists(unzipped_path):
shutil.rmtree(unzipped_path, ignore_errors=True)
with zipfile.ZipFile(wheel_toolkit, "r") as zip_ref:
zip_ref.extractall(unzipped_path)

package_name = available_toolkits[toolkit_name]["package_name"]
run_command(
'"{}" install --no-cache-dir --no-index --find-links={} {}'.format(pip_exe, unzipped_path, package_name)
)
elif install and not is_installed:
# Install the specified package
run_command('"{}" --default-timeout=1000 install {}'.format(pip_exe, toolkit["pip"]))
elif not install and is_installed:
# Uninstall toolkit
run_command('"{}" --default-timeout=1000 uninstall -y {}'.format(pip_exe, toolkit["package_name"]))
elif install and is_installed:
# Update toolkit
run_command('"{}" --default-timeout=1000 install {} -U'.format(pip_exe, toolkit["pip"]))
else:
self.logger.info("Incorrect input")
return
toolkit_dir = os.path.join(self.personallib, "Toolkits")
tool_dir = os.path.join(toolkit_dir, toolkit["installation_path"], toolkit_name)

script_image = os.path.join(os.path.dirname(__file__), "misc", "images", "large", toolkit["image"])

if install:
if not os.path.exists(tool_dir):
# Install toolkit inside AEDT
self.add_script_to_menu(
toolkit_name=toolkit_name,
script_path=script_file,
script_image=script_image,
product=toolkit["installation_path"],
copy_to_personal_lib=False,
executable_interpreter=python_exe,
)
else:
toolkit_dir = os.path.join(self.personallib, "Toolkits")
tool_dir = os.path.join(toolkit_dir, toolkit["installation_path"], toolkit_name)

if os.path.exists(tool_dir):
# Install toolkit inside AEDT
self.remove_script_from_menu(
toolkit_name=toolkit_name,
product=toolkit["installation_path"],
)

@pyaedt_function_handler()
def add_script_to_menu(
self,
toolkit_name,
script_path,
script_image=None,
product="Project",
copy_to_personal_lib=True,
executable_interpreter=None,
):
"""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``.
executable_interpreter : str, optional
Executable python path. The default is the one current interpreter.
Returns
-------
bool
"""
if not os.path.exists(script_path):
self.logger.error("Script does not exists.")
return False
if not executable_interpreter:
executable_version_agnostic = sys.executable
else:
executable_version_agnostic = executable_interpreter

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"]

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("{} 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
Binary file removed pyaedt/misc/images/gallery/console.png
Binary file not shown.
Binary file removed pyaedt/misc/images/gallery/jupyter.png
Binary file not shown.
Binary file removed pyaedt/misc/images/gallery/run_script.png
Binary file not shown.
Binary file removed pyaedt/misc/images/gallery/toolkit_manager.png
Binary file not shown.
Binary file removed pyaedt/misc/images/large/pyansys.png
Binary file not shown.
Loading

0 comments on commit 9f3b822

Please sign in to comment.