From db6cb11b77007f88013e3790876d76b59b263619 Mon Sep 17 00:00:00 2001 From: klmcadams <58492561+klmcadams@users.noreply.github.com> Date: Tue, 20 Aug 2024 16:16:59 -0400 Subject: [PATCH 01/35] launch gui on save for specified file --- src/ansys/mechanical/core/embedding/app.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ansys/mechanical/core/embedding/app.py b/src/ansys/mechanical/core/embedding/app.py index e182fb163..5286e83c1 100644 --- a/src/ansys/mechanical/core/embedding/app.py +++ b/src/ansys/mechanical/core/embedding/app.py @@ -23,6 +23,8 @@ """Main application class for embedded Mechanical.""" import atexit import os +import subprocess +from tempfile import NamedTemporaryFile import typing import warnings @@ -184,6 +186,10 @@ def save(self, path=None): """Save the project.""" if path is not None: self.DataModel.Project.Save(path) + mechdat_file = NamedTemporaryFile() # delete=False + subprocess.Popen( + ["ansys-mechanical", "--project-file", mechdat_file.name, "--graphical"] + ) else: self.DataModel.Project.Save() From d5d5b01af57497b4ac924fbf114c3b05bcfe34ee Mon Sep 17 00:00:00 2001 From: klmcadams <58492561+klmcadams@users.noreply.github.com> Date: Wed, 21 Aug 2024 16:13:15 -0400 Subject: [PATCH 02/35] comments on how to implement this --- src/ansys/mechanical/core/embedding/app.py | 25 ++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/ansys/mechanical/core/embedding/app.py b/src/ansys/mechanical/core/embedding/app.py index 5286e83c1..d5bf9edc4 100644 --- a/src/ansys/mechanical/core/embedding/app.py +++ b/src/ansys/mechanical/core/embedding/app.py @@ -23,8 +23,10 @@ """Main application class for embedded Mechanical.""" import atexit import os -import subprocess -from tempfile import NamedTemporaryFile + +# import shutil +# import subprocess +# from tempfile import NamedTemporaryFile import typing import warnings @@ -35,6 +37,8 @@ from ansys.mechanical.core.embedding.poster import Poster from ansys.mechanical.core.embedding.warnings import connect_warnings, disconnect_warnings +# from ansys.mechanical.core.run import _run + try: import ansys.tools.visualization_interface # noqa: F401 @@ -185,14 +189,23 @@ def open(self, db_file): def save(self, path=None): """Save the project.""" if path is not None: + # Save the file to the path self.DataModel.Project.Save(path) - mechdat_file = NamedTemporaryFile() # delete=False - subprocess.Popen( - ["ansys-mechanical", "--project-file", mechdat_file.name, "--graphical"] - ) else: self.DataModel.Project.Save() + # get loc of current project + # call save as + # copy all files + # this will be an expensive function + # make launch gui function + # save copy function? + + # Copy mechdat file to NamedTemporaryFile + # mechdat_file = NamedTemporaryFile() # delete=False + # shutil.copyfile(path, mechdat_file.name) + # subprocess.Popen(["ansys-mechanical", "--project-file", mechdat_file.name, "--graphical"]) + def save_as(self, path): """Save the project as.""" self.DataModel.Project.SaveAs(path) From ca191226aaa097229aadafaa3b08290e6f663126 Mon Sep 17 00:00:00 2001 From: klmcadams <58492561+klmcadams@users.noreply.github.com> Date: Mon, 26 Aug 2024 13:31:52 -0400 Subject: [PATCH 03/35] first draft of launching the gui --- src/ansys/mechanical/core/embedding/app.py | 39 +++++++++++++++------- tests/embedding/test_app.py | 14 ++++++++ 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/src/ansys/mechanical/core/embedding/app.py b/src/ansys/mechanical/core/embedding/app.py index d5bf9edc4..59f3df40c 100644 --- a/src/ansys/mechanical/core/embedding/app.py +++ b/src/ansys/mechanical/core/embedding/app.py @@ -23,6 +23,8 @@ """Main application class for embedded Mechanical.""" import atexit import os +from subprocess import Popen +import tempfile # import shutil # import subprocess @@ -194,22 +196,35 @@ def save(self, path=None): else: self.DataModel.Project.Save() - # get loc of current project - # call save as - # copy all files - # this will be an expensive function - # make launch gui function - # save copy function? - - # Copy mechdat file to NamedTemporaryFile - # mechdat_file = NamedTemporaryFile() # delete=False - # shutil.copyfile(path, mechdat_file.name) - # subprocess.Popen(["ansys-mechanical", "--project-file", mechdat_file.name, "--graphical"]) - def save_as(self, path): """Save the project as.""" self.DataModel.Project.SaveAs(path) + def launch_gui(self): + """Launch the GUI.""" + # Get the project directory + project_directory = self.DataModel.Project.ProjectDirectory + # Create a temporary file + named_temp_file = tempfile.NamedTemporaryFile() + # Create the entire mechdb file path + temp_mechdb = os.path.join(project_directory, f"{named_temp_file.name}.mechdb") + # Call SaveAs - SaveAs copies the entire directory + self.DataModel.Project.SaveAs( + os.path.join(project_directory, f"{named_temp_file.name}.mechdb") + ) + # Launch the temporary mechdb file in GUI mode + Popen( + [ + "ansys-mechanical", + "--project-file", + temp_mechdb, + "--graphical", + "--revision", + str(self.version), + ] + ) + print(f"Done launching Ansys Mechanical {str(self.version)}...") + def new(self): """Clear to a new application.""" self.DataModel.Project.New() diff --git a/tests/embedding/test_app.py b/tests/embedding/test_app.py index 3cfda59bd..896c44fcd 100644 --- a/tests/embedding/test_app.py +++ b/tests/embedding/test_app.py @@ -312,3 +312,17 @@ def test_rm_lockfile(embedded_app, tmp_path: pytest.TempPathFactory): lockfile_path = os.path.join(embedded_app.DataModel.Project.ProjectDirectory, ".mech_lock") # Assert lock file path does not exist assert not os.path.exists(lockfile_path) + + +@pytest.mark.embedding +def test_launch_gui(embedded_app, tmp_path: pytest.TempPathFactory, capfd): + """Test lock file is removed on close of embedded application.""" + version = str(embedded_app.version) + + mechdat_path = os.path.join(tmp_path, "test.mechdat") + embedded_app.save(mechdat_path) + embedded_app.launch_gui() + embedded_app.close() + + out, err = capfd.readouterr() + assert f"Done launching Ansys Mechanical {version}" in out From 83536ec4e62bd01654aaa17f51a87fc1dbfe6f6e Mon Sep 17 00:00:00 2001 From: klmcadams <58492561+klmcadams@users.noreply.github.com> Date: Mon, 26 Aug 2024 15:51:16 -0400 Subject: [PATCH 04/35] remove comments --- src/ansys/mechanical/core/embedding/app.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/ansys/mechanical/core/embedding/app.py b/src/ansys/mechanical/core/embedding/app.py index 59f3df40c..263f48df9 100644 --- a/src/ansys/mechanical/core/embedding/app.py +++ b/src/ansys/mechanical/core/embedding/app.py @@ -25,10 +25,6 @@ import os from subprocess import Popen import tempfile - -# import shutil -# import subprocess -# from tempfile import NamedTemporaryFile import typing import warnings @@ -39,8 +35,6 @@ from ansys.mechanical.core.embedding.poster import Poster from ansys.mechanical.core.embedding.warnings import connect_warnings, disconnect_warnings -# from ansys.mechanical.core.run import _run - try: import ansys.tools.visualization_interface # noqa: F401 @@ -191,7 +185,6 @@ def open(self, db_file): def save(self, path=None): """Save the project.""" if path is not None: - # Save the file to the path self.DataModel.Project.Save(path) else: self.DataModel.Project.Save() From e7f37ecc16163db395d95469ad146062b255d4d0 Mon Sep 17 00:00:00 2001 From: pyansys-ci-bot <92810346+pyansys-ci-bot@users.noreply.github.com> Date: Mon, 26 Aug 2024 19:55:31 +0000 Subject: [PATCH 05/35] chore: adding changelog file 882.added.md --- doc/changelog.d/882.added.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 doc/changelog.d/882.added.md diff --git a/doc/changelog.d/882.added.md b/doc/changelog.d/882.added.md new file mode 100644 index 000000000..c8df9545e --- /dev/null +++ b/doc/changelog.d/882.added.md @@ -0,0 +1 @@ +launch_gui command \ No newline at end of file From 5ededcbd4d640173fac81eb308088012e5e8dc70 Mon Sep 17 00:00:00 2001 From: Mohamed Koubaa Date: Thu, 5 Sep 2024 09:53:41 -0500 Subject: [PATCH 06/35] wip --- src/ansys/mechanical/core/embedding/app.py | 26 +--- src/ansys/mechanical/core/embedding/ui.py | 133 +++++++++++++++++++++ 2 files changed, 135 insertions(+), 24 deletions(-) create mode 100644 src/ansys/mechanical/core/embedding/ui.py diff --git a/src/ansys/mechanical/core/embedding/app.py b/src/ansys/mechanical/core/embedding/app.py index 2ab1c5d47..e9a3da5f5 100644 --- a/src/ansys/mechanical/core/embedding/app.py +++ b/src/ansys/mechanical/core/embedding/app.py @@ -23,8 +23,6 @@ """Main application class for embedded Mechanical.""" import atexit import os -from subprocess import Popen -import tempfile import typing import warnings @@ -34,6 +32,7 @@ from ansys.mechanical.core.embedding.imports import global_entry_points, global_variables from ansys.mechanical.core.embedding.poster import Poster from ansys.mechanical.core.embedding.warnings import connect_warnings, disconnect_warnings +from ansys.mechanical.core.embedding.ui import launch_ui try: import ansys.tools.visualization_interface # noqa: F401 @@ -195,28 +194,7 @@ def save_as(self, path): def launch_gui(self): """Launch the GUI.""" - # Get the project directory - project_directory = self.DataModel.Project.ProjectDirectory - # Create a temporary file - named_temp_file = tempfile.NamedTemporaryFile() - # Create the entire mechdb file path - temp_mechdb = os.path.join(project_directory, f"{named_temp_file.name}.mechdb") - # Call SaveAs - SaveAs copies the entire directory - self.DataModel.Project.SaveAs( - os.path.join(project_directory, f"{named_temp_file.name}.mechdb") - ) - # Launch the temporary mechdb file in GUI mode - Popen( - [ - "ansys-mechanical", - "--project-file", - temp_mechdb, - "--graphical", - "--revision", - str(self.version), - ] - ) - print(f"Done launching Ansys Mechanical {str(self.version)}...") + launch_ui(self) def new(self): """Clear to a new application.""" diff --git a/src/ansys/mechanical/core/embedding/ui.py b/src/ansys/mechanical/core/embedding/ui.py new file mode 100644 index 000000000..e804a436f --- /dev/null +++ b/src/ansys/mechanical/core/embedding/ui.py @@ -0,0 +1,133 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +"""Run Mechanical UI from python.""" + +import os +from subprocess import Popen +import tempfile + +""" +If Mechanical has ever been saved, then we are allowed to use `app.save()`, +otherwise, we have to use `app.save_as()` and pass a temp file path which +will have to be cleaned up later... + +Mechanical can be in one of three states: +1. not-saved +2. saved|dirty +3. saved|not-dirty + +#1 +Example: + app = App() + ... + app.launch_gui() + +A save_as() to save1.mechdb +B save_as() to save2.mechdb +C open() the mechdb from A +D run ansys-mechanical -g on save2.mechdb + +** side-effect: not-saved => saved|not-dirty + +#2 +Example: + app.save_as("/path/to/file.mechdb") + app.project_file => + "/path/to/file.mechdb" + ... + app.launch_gui() + app.project_file => + "/path/to/save2.mechdb" + +A save() +B identify the mechdb of the saved session +C save_as() to a save.mechdb +D open() the mechdb from A +E run ansys-mechanical -g on save.mechdb from C + +** side-effect: saved|dirty => saved|not-dirty + +#3 +Example: + app.save_as("/path/to/some/file.mechdb") + app.launch_gui() + + +A save() +B identify the mechdb of the saved session +C save_as() into a save.mechdb +D open() the mechdb saved session (from A) +E run ansys-mechanical -g on that temp path (B) +** side-effect: no side effect + +side-effect of all: + If we are either in a dirty state or have never been saved, we went to a save|not-dirty state + +side-effect/bug: + There is now a mechdb in the temp folder that is leaked. + +""" + +def _is_saved(app: "ansys.mechanical.core.embedding.App"): + return True + +def launch_ui(app: "ansys.mechanical.core.embedding.App"): + """Launch the Mechanical UI. + + Precondition: Mechanical has to have already been saved + Side effect: If Mechanical has ever been saved, it overwrites that save. + """ + + if not _is_saved(app): + raise Exception("The App must have already been saved before using launch_ui!") + + """ + A save() + B identify the mechdb of the saved session + C save_as() to a save.mechdb + D open() the mechdb from A + E run ansys-mechanical -g on save.mechdb from C + """ + + # Get the project directory + project_directory = app.DataModel.Project.ProjectDirectory + # Create a temporary file + named_temp_file = tempfile.NamedTemporaryFile() + # Create the entire mechdb file path + temp_mechdb = os.path.join(project_directory, f"{named_temp_file.name}.mechdb") + # Call SaveAs - SaveAs copies the entire directory + app.DataModel.Project.SaveAs( + os.path.join(project_directory, f"{named_temp_file.name}.mechdb") + ) + # Launch the temporary mechdb file in GUI mode + Popen( + [ + "ansys-mechanical", + "--project-file", + temp_mechdb, + "--graphical", + "--revision", + str(app.version), + ] + ) + print(f"Done launching Ansys Mechanical {str(app.version)}...") \ No newline at end of file From 80520ca2286674af7cfbb4030aef25aeb1f07b3f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 14:53:59 +0000 Subject: [PATCH 07/35] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/ansys/mechanical/core/embedding/app.py | 2 +- src/ansys/mechanical/core/embedding/ui.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ansys/mechanical/core/embedding/app.py b/src/ansys/mechanical/core/embedding/app.py index e9a3da5f5..be9688157 100644 --- a/src/ansys/mechanical/core/embedding/app.py +++ b/src/ansys/mechanical/core/embedding/app.py @@ -31,8 +31,8 @@ from ansys.mechanical.core.embedding.appdata import UniqueUserProfile from ansys.mechanical.core.embedding.imports import global_entry_points, global_variables from ansys.mechanical.core.embedding.poster import Poster -from ansys.mechanical.core.embedding.warnings import connect_warnings, disconnect_warnings from ansys.mechanical.core.embedding.ui import launch_ui +from ansys.mechanical.core.embedding.warnings import connect_warnings, disconnect_warnings try: import ansys.tools.visualization_interface # noqa: F401 diff --git a/src/ansys/mechanical/core/embedding/ui.py b/src/ansys/mechanical/core/embedding/ui.py index e804a436f..26b554fb9 100644 --- a/src/ansys/mechanical/core/embedding/ui.py +++ b/src/ansys/mechanical/core/embedding/ui.py @@ -88,9 +88,11 @@ """ + def _is_saved(app: "ansys.mechanical.core.embedding.App"): return True + def launch_ui(app: "ansys.mechanical.core.embedding.App"): """Launch the Mechanical UI. @@ -116,9 +118,7 @@ def launch_ui(app: "ansys.mechanical.core.embedding.App"): # Create the entire mechdb file path temp_mechdb = os.path.join(project_directory, f"{named_temp_file.name}.mechdb") # Call SaveAs - SaveAs copies the entire directory - app.DataModel.Project.SaveAs( - os.path.join(project_directory, f"{named_temp_file.name}.mechdb") - ) + app.DataModel.Project.SaveAs(os.path.join(project_directory, f"{named_temp_file.name}.mechdb")) # Launch the temporary mechdb file in GUI mode Popen( [ @@ -130,4 +130,4 @@ def launch_ui(app: "ansys.mechanical.core.embedding.App"): str(app.version), ] ) - print(f"Done launching Ansys Mechanical {str(app.version)}...") \ No newline at end of file + print(f"Done launching Ansys Mechanical {str(app.version)}...") From 76e1a13cb61cb38c00d57f7a6e65ee36cdde8f92 Mon Sep 17 00:00:00 2001 From: klmcadams <58492561+klmcadams@users.noreply.github.com> Date: Tue, 10 Sep 2024 10:43:41 -0400 Subject: [PATCH 08/35] first draft of launch_ui function --- src/ansys/mechanical/core/embedding/ui.py | 78 +++++++++++++++-------- tests/embedding/test_app.py | 5 +- 2 files changed, 53 insertions(+), 30 deletions(-) diff --git a/src/ansys/mechanical/core/embedding/ui.py b/src/ansys/mechanical/core/embedding/ui.py index 26b554fb9..e27ccca87 100644 --- a/src/ansys/mechanical/core/embedding/ui.py +++ b/src/ansys/mechanical/core/embedding/ui.py @@ -22,7 +22,9 @@ """Run Mechanical UI from python.""" +import glob import os +import pathlib from subprocess import Popen import tempfile @@ -99,35 +101,55 @@ def launch_ui(app: "ansys.mechanical.core.embedding.App"): Precondition: Mechanical has to have already been saved Side effect: If Mechanical has ever been saved, it overwrites that save. """ - if not _is_saved(app): raise Exception("The App must have already been saved before using launch_ui!") + else: + # Save the app + app.save() + + # Identify the mechdb of the saved session + project_directory = app.DataModel.Project.ProjectDirectory + # print(project_directory) + + # list all files / find mechdb file in project_directory + # find a command that lists active mechdb file + level_above_projdir = pathlib.Path(project_directory).parent + mechdb_list = glob.glob(f"{level_above_projdir}/*.mechdb") + if len(mechdb_list) > 1: + print("multiple mechdb files found") + + mechdb_file = mechdb_list[0] + # print(f"mechdb file: {mechdb_file}") + + # Get the directory the mechdb_file is in and the name of the file + dirname, basename = os.path.split(mechdb_file) + # Create a named temporary file + temp_file = ( + tempfile.NamedTemporaryFile() + ) # (dir=dirname, suffix=".mechdb", delete=True) # mode='w', delete=False) + # Get the name of the temporary file + temp_file_basename = os.path.basename(temp_file.name) + # print(f"temp_file name: {temp_file_basename}") + + # Use the name of the temporary file to create a mechdb file + temp_file_name = os.path.join(dirname, f"{temp_file_basename}.mechdb") + + # Save app with name of temporary file + app.save_as(temp_file_name) + + # Open the mechdb of the saved session + app.open(mechdb_file) + + # Run ansys-mechanical on the save.mechdb in the temporary location + Popen( + [ + "ansys-mechanical", + "--project-file", + temp_file_name, + "--graphical", + "--revision", + str(app.version), + ] + ) - """ - A save() - B identify the mechdb of the saved session - C save_as() to a save.mechdb - D open() the mechdb from A - E run ansys-mechanical -g on save.mechdb from C - """ - - # Get the project directory - project_directory = app.DataModel.Project.ProjectDirectory - # Create a temporary file - named_temp_file = tempfile.NamedTemporaryFile() - # Create the entire mechdb file path - temp_mechdb = os.path.join(project_directory, f"{named_temp_file.name}.mechdb") - # Call SaveAs - SaveAs copies the entire directory - app.DataModel.Project.SaveAs(os.path.join(project_directory, f"{named_temp_file.name}.mechdb")) - # Launch the temporary mechdb file in GUI mode - Popen( - [ - "ansys-mechanical", - "--project-file", - temp_mechdb, - "--graphical", - "--revision", - str(app.version), - ] - ) print(f"Done launching Ansys Mechanical {str(app.version)}...") diff --git a/tests/embedding/test_app.py b/tests/embedding/test_app.py index 896c44fcd..1133bcacf 100644 --- a/tests/embedding/test_app.py +++ b/tests/embedding/test_app.py @@ -319,8 +319,9 @@ def test_launch_gui(embedded_app, tmp_path: pytest.TempPathFactory, capfd): """Test lock file is removed on close of embedded application.""" version = str(embedded_app.version) - mechdat_path = os.path.join(tmp_path, "test.mechdat") - embedded_app.save(mechdat_path) + project_directory = embedded_app.DataModel.Project.ProjectDirectory + mechdb_path = os.path.join(tmp_path, "test.mechdb") + embedded_app.save(mechdb_path) embedded_app.launch_gui() embedded_app.close() From 04f741f1a2014fa8445b1286e6283f79d39c11ae Mon Sep 17 00:00:00 2001 From: klmcadams <58492561+klmcadams@users.noreply.github.com> Date: Tue, 10 Sep 2024 11:00:54 -0400 Subject: [PATCH 09/35] use tempfile with name formatted --- src/ansys/mechanical/core/embedding/ui.py | 20 +++++++++++--------- tests/embedding/test_app.py | 1 + 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/ansys/mechanical/core/embedding/ui.py b/src/ansys/mechanical/core/embedding/ui.py index e27ccca87..6a339136a 100644 --- a/src/ansys/mechanical/core/embedding/ui.py +++ b/src/ansys/mechanical/core/embedding/ui.py @@ -114,6 +114,7 @@ def launch_ui(app: "ansys.mechanical.core.embedding.App"): # list all files / find mechdb file in project_directory # find a command that lists active mechdb file level_above_projdir = pathlib.Path(project_directory).parent + print(level_above_projdir) mechdb_list = glob.glob(f"{level_above_projdir}/*.mechdb") if len(mechdb_list) > 1: print("multiple mechdb files found") @@ -124,15 +125,16 @@ def launch_ui(app: "ansys.mechanical.core.embedding.App"): # Get the directory the mechdb_file is in and the name of the file dirname, basename = os.path.split(mechdb_file) # Create a named temporary file - temp_file = ( - tempfile.NamedTemporaryFile() - ) # (dir=dirname, suffix=".mechdb", delete=True) # mode='w', delete=False) - # Get the name of the temporary file - temp_file_basename = os.path.basename(temp_file.name) - # print(f"temp_file name: {temp_file_basename}") - - # Use the name of the temporary file to create a mechdb file - temp_file_name = os.path.join(dirname, f"{temp_file_basename}.mechdb") + # temp_file = tempfile.NamedTemporaryFile() + # # Get the name of the temporary file + # temp_file_basename = os.path.basename(temp_file.name) + # # print(f"temp_file name: {temp_file_basename}") + # # Use the name of the temporary file to create a mechdb file + # temp_file_name = os.path.join(dirname, f"{temp_file_basename}.mechdb") + + temp_file_name = tempfile.NamedTemporaryFile( + dir=dirname, suffix=".mechdb", delete=True + ).name # Save app with name of temporary file app.save_as(temp_file_name) diff --git a/tests/embedding/test_app.py b/tests/embedding/test_app.py index 1133bcacf..bef5d5ff6 100644 --- a/tests/embedding/test_app.py +++ b/tests/embedding/test_app.py @@ -326,4 +326,5 @@ def test_launch_gui(embedded_app, tmp_path: pytest.TempPathFactory, capfd): embedded_app.close() out, err = capfd.readouterr() + print(out) assert f"Done launching Ansys Mechanical {version}" in out From a0d64a92a37fe99b84f3254bdd0d1719ef82fab9 Mon Sep 17 00:00:00 2001 From: Mohamed Koubaa Date: Tue, 10 Sep 2024 10:17:18 -0500 Subject: [PATCH 10/35] save --- src/ansys/mechanical/core/embedding/ui.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ansys/mechanical/core/embedding/ui.py b/src/ansys/mechanical/core/embedding/ui.py index 6a339136a..307bbd49f 100644 --- a/src/ansys/mechanical/core/embedding/ui.py +++ b/src/ansys/mechanical/core/embedding/ui.py @@ -92,6 +92,10 @@ def _is_saved(app: "ansys.mechanical.core.embedding.App"): + try: + app.save() + except: + raise Exception("The App must have already been saved before using launch_ui!") return True From 14e1eb191711d8c4300584348b62d1fd246302c8 Mon Sep 17 00:00:00 2001 From: Mohamed Koubaa Date: Tue, 10 Sep 2024 10:33:48 -0500 Subject: [PATCH 11/35] suggestions --- src/ansys/mechanical/core/embedding/ui.py | 50 +++++++++++++++++++++-- src/ansys/mechanical/core/run.py | 2 +- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/src/ansys/mechanical/core/embedding/ui.py b/src/ansys/mechanical/core/embedding/ui.py index 307bbd49f..3675a4d5c 100644 --- a/src/ansys/mechanical/core/embedding/ui.py +++ b/src/ansys/mechanical/core/embedding/ui.py @@ -99,12 +99,36 @@ def _is_saved(app: "ansys.mechanical.core.embedding.App"): return True -def launch_ui(app: "ansys.mechanical.core.embedding.App"): +class UILauncher(): + def save(self, app): + app.save() + +class MockLauncher(): + def __init__(self): + self.ops = [] + + def save(self, app): + self.ops.append("save") + +def _launch_ui(app, launcher): + # all the implemation below goes here + pass + +def test_launch_ui(app): + # there are also python frameworks for fake/mock objects... + m = MockLauncher() + _launch_ui(app, m) + assert m.ops[0] == "save" + +def launch_ui(app: "ansys.mechanical.core.embedding.App", testing: bool = False) -> Popen: """Launch the Mechanical UI. Precondition: Mechanical has to have already been saved Side effect: If Mechanical has ever been saved, it overwrites that save. """ + # not testing + _launch_ui(UILauncher()) + if not _is_saved(app): raise Exception("The App must have already been saved before using launch_ui!") else: @@ -141,13 +165,20 @@ def launch_ui(app: "ansys.mechanical.core.embedding.App"): ).name # Save app with name of temporary file + # file a bug about adding an option overwrite=True to `save_as` app.save_as(temp_file_name) # Open the mechdb of the saved session app.open(mechdb_file) + # another option for testing: + # if testing: + # don't put --graphical in the args + # return the process object + # the test code can wait for the process to exit... + # maybe work around the command line parsing of ansys-mechanical # Run ansys-mechanical on the save.mechdb in the temporary location - Popen( + p = Popen( [ "ansys-mechanical", "--project-file", @@ -156,6 +187,19 @@ def launch_ui(app: "ansys.mechanical.core.embedding.App"): "--revision", str(app.version), ] + , env= os.environ.copy() # with some env var for DRY_RUN ) - print(f"Done launching Ansys Mechanical {str(app.version)}...") + # Problem: the mechdb started above will not automatically get cleaned up + # option 1 - its the users problem, just tell them with a print + print("Opened a new mechanical session based on ... {mechdb_file}. PyMechanical will not delete after use.") + # option 2 - try to delete it (maybe this can be an opt-in, an argument to launch_ui??) + # by starting a background process that waits until the process exits and automatically cleans it up. + # like `Popen(f"python /path/to/cleanup-gui.py {p.pid}")` + # option 3 - use a canonical name /tmp/some_name/file.mechdb and overwrite it each time launch_ui is run + # so that worst case only one extra mechdb is not cleaned up. The downside is that you can't use launch_ui + # in parallel but who would even do that? You can throw an exception if that mechdb file is open by another + # process at the beginning of this function... + print(f"Done launching Ansys Mechanical {str(app.version)}...") + return p + diff --git a/src/ansys/mechanical/core/run.py b/src/ansys/mechanical/core/run.py index bafce0442..b580b2048 100644 --- a/src/ansys/mechanical/core/run.py +++ b/src/ansys/mechanical/core/run.py @@ -35,7 +35,7 @@ from ansys.mechanical.core.embedding.appdata import UniqueUserProfile from ansys.mechanical.core.feature_flags import get_command_line_arguments, get_feature_flag_names -DRY_RUN = False +DRY_RUN = False # control this with an env var to opt into dry run? """Dry run constant.""" # TODO - add logging options (reuse env var based logging initialization) From 684ba901e2471adb56271c167c34a2926dcacef7 Mon Sep 17 00:00:00 2001 From: Mohamed Koubaa Date: Tue, 10 Sep 2024 10:35:35 -0500 Subject: [PATCH 12/35] suggestions --- src/ansys/mechanical/core/embedding/ui.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/ansys/mechanical/core/embedding/ui.py b/src/ansys/mechanical/core/embedding/ui.py index 3675a4d5c..6f65cf2f0 100644 --- a/src/ansys/mechanical/core/embedding/ui.py +++ b/src/ansys/mechanical/core/embedding/ui.py @@ -103,12 +103,7 @@ class UILauncher(): def save(self, app): app.save() -class MockLauncher(): - def __init__(self): - self.ops = [] - def save(self, app): - self.ops.append("save") def _launch_ui(app, launcher): # all the implemation below goes here @@ -116,6 +111,14 @@ def _launch_ui(app, launcher): def test_launch_ui(app): # there are also python frameworks for fake/mock objects... + # if its one test it can go in the test, if its multiple it can be defined in the test_---.py file + # or in a fixture in conftest + class MockLauncher(): + def __init__(self): + self.ops = [] + + def save(self, app): + self.ops.append("save") m = MockLauncher() _launch_ui(app, m) assert m.ops[0] == "save" From ac53baac85535b0a60a941f5dbbe667e735c663e Mon Sep 17 00:00:00 2001 From: klmcadams <58492561+klmcadams@users.noreply.github.com> Date: Tue, 10 Sep 2024 15:45:31 -0400 Subject: [PATCH 13/35] update UILauncher class --- src/ansys/mechanical/core/embedding/ui.py | 178 ++++++++++++---------- tests/embedding/test_app.py | 16 ++ 2 files changed, 115 insertions(+), 79 deletions(-) diff --git a/src/ansys/mechanical/core/embedding/ui.py b/src/ansys/mechanical/core/embedding/ui.py index 6f65cf2f0..32a1886aa 100644 --- a/src/ansys/mechanical/core/embedding/ui.py +++ b/src/ansys/mechanical/core/embedding/ui.py @@ -22,11 +22,11 @@ """Run Mechanical UI from python.""" -import glob import os -import pathlib +from pathlib import Path from subprocess import Popen import tempfile +import typing """ If Mechanical has ever been saved, then we are allowed to use `app.save()`, @@ -99,110 +99,130 @@ def _is_saved(app: "ansys.mechanical.core.embedding.App"): return True -class UILauncher(): - def save(self, app): - app.save() - - - -def _launch_ui(app, launcher): - # all the implemation below goes here - pass - -def test_launch_ui(app): - # there are also python frameworks for fake/mock objects... - # if its one test it can go in the test, if its multiple it can be defined in the test_---.py file - # or in a fixture in conftest - class MockLauncher(): - def __init__(self): - self.ops = [] - - def save(self, app): - self.ops.append("save") - m = MockLauncher() - _launch_ui(app, m) - assert m.ops[0] == "save" +class UILauncher: + """Launch the GUI on a temporary mechdb file.""" -def launch_ui(app: "ansys.mechanical.core.embedding.App", testing: bool = False) -> Popen: - """Launch the Mechanical UI. + def save_original(self, app) -> None: + """Save the active mechdb file. - Precondition: Mechanical has to have already been saved - Side effect: If Mechanical has ever been saved, it overwrites that save. - """ - # not testing - _launch_ui(UILauncher()) - - if not _is_saved(app): - raise Exception("The App must have already been saved before using launch_ui!") - else: - # Save the app + Parameters + ---------- + app: ansys.mechanical.core.embedding.app.App + A Mechanical embedding application. + """ app.save() - # Identify the mechdb of the saved session - project_directory = app.DataModel.Project.ProjectDirectory - # print(project_directory) - - # list all files / find mechdb file in project_directory - # find a command that lists active mechdb file - level_above_projdir = pathlib.Path(project_directory).parent - print(level_above_projdir) - mechdb_list = glob.glob(f"{level_above_projdir}/*.mechdb") - if len(mechdb_list) > 1: - print("multiple mechdb files found") - - mechdb_file = mechdb_list[0] - # print(f"mechdb file: {mechdb_file}") - - # Get the directory the mechdb_file is in and the name of the file - dirname, basename = os.path.split(mechdb_file) - # Create a named temporary file - # temp_file = tempfile.NamedTemporaryFile() - # # Get the name of the temporary file - # temp_file_basename = os.path.basename(temp_file.name) - # # print(f"temp_file name: {temp_file_basename}") - # # Use the name of the temporary file to create a mechdb file - # temp_file_name = os.path.join(dirname, f"{temp_file_basename}.mechdb") + def save_temp_copy(self, app) -> typing.Union[Path, Path]: + """Save a new mechdb file with a temporary name. + + Parameters + ---------- + app: ansys.mechanical.core.embedding.app.App + A Mechanical embedding application. + """ + # Identify the mechdb of the saved session from save_original() + project_directory = Path(app.DataModel.Project.ProjectDirectory) + project_directory_parent = project_directory.parent + mechdb_file = os.path.join( + project_directory_parent, f"{project_directory.parts[-1].split('_')[0]}.mechdb" + ) + # Get name of NamedTemporaryFile temp_file_name = tempfile.NamedTemporaryFile( - dir=dirname, suffix=".mechdb", delete=True + dir=project_directory_parent, suffix=".mechdb", delete=True ).name # Save app with name of temporary file - # file a bug about adding an option overwrite=True to `save_as` app.save_as(temp_file_name) - # Open the mechdb of the saved session + return mechdb_file, temp_file_name + + def open_original(self, app, mechdb_file) -> None: + """Open the original mechdb file from save_original(). + + Parameters + ---------- + app: ansys.mechanical.core.embedding.app.App + A Mechanical embedding application. + mechdb_file: pathlib.Path + The full path to the active mechdb file. + """ app.open(mechdb_file) - # another option for testing: - # if testing: - # don't put --graphical in the args - # return the process object - # the test code can wait for the process to exit... - # maybe work around the command line parsing of ansys-mechanical - # Run ansys-mechanical on the save.mechdb in the temporary location + def graphically_launch_temp(self, app, mechdb_file, temp_file) -> Popen: + """Launch the GUI for the mechdb file with a temporary name from save_temp_copy(). + + Parameters + ---------- + app: ansys.mechanical.core.embedding.app.App + A Mechanical embedding application. + mechdb_file: pathlib.Path + The full path to the active mechdb file. + temp_file: pathlib.Path + The full path to the temporary mechdb file. + + Returns + ------- + subprocess.Popen + The subprocess that launches the GUI for the temporary mechdb file. + """ p = Popen( [ "ansys-mechanical", "--project-file", - temp_file_name, + temp_file, "--graphical", "--revision", str(app.version), ] - , env= os.environ.copy() # with some env var for DRY_RUN + # , env= os.environ.copy() # with some env var for DRY_RUN ) # Problem: the mechdb started above will not automatically get cleaned up # option 1 - its the users problem, just tell them with a print - print("Opened a new mechanical session based on ... {mechdb_file}. PyMechanical will not delete after use.") + print( + f"""Opened a new mechanical session based on ... {mechdb_file}. \ + PyMechanical will not delete after use.""" + ) # option 2 - try to delete it (maybe this can be an opt-in, an argument to launch_ui??) - # by starting a background process that waits until the process exits and automatically cleans it up. - # like `Popen(f"python /path/to/cleanup-gui.py {p.pid}")` - # option 3 - use a canonical name /tmp/some_name/file.mechdb and overwrite it each time launch_ui is run - # so that worst case only one extra mechdb is not cleaned up. The downside is that you can't use launch_ui - # in parallel but who would even do that? You can throw an exception if that mechdb file is open by another + # by starting a background process that waits until the process exits and + # automatically cleans it up like + # `Popen(f"python /path/to/cleanup-gui.py {p.pid}")` + # option 3 - use a canonical name /tmp/some_name/file.mechdb and overwrite it each time + # launch_ui is run so that worst case only one extra mechdb is not cleaned up. + # The downside is that you can't use launch_ui in parallel but who would even + # do that? You can throw an exception if that mechdb file is open by another # process at the beginning of this function... print(f"Done launching Ansys Mechanical {str(app.version)}...") + return p + +# class MockLauncher: +# def __init__(self): +# self.ops = [] + +# def save(self, app): +# self.ops.append("save") + + +def _launch_ui(app, launcher): + # all the implemation below goes here + if not _is_saved(app): + raise Exception("The App must have already been saved before using launch_ui!") + else: + launcher.save_original(app) + mechdb_file, temp_file = launcher.save_temp_copy(app) + launcher.open_original(app, mechdb_file) + p = launcher.graphically_launch_temp(app, mechdb_file, temp_file) + return p + + +def launch_ui(app: "ansys.mechanical.core.embedding.App", testing: bool = False) -> Popen: + """Launch the Mechanical UI. + + Precondition: Mechanical has to have already been saved + Side effect: If Mechanical has ever been saved, it overwrites that save. + """ + if not testing: + _launch_ui(app, UILauncher()) diff --git a/tests/embedding/test_app.py b/tests/embedding/test_app.py index bef5d5ff6..a2675c237 100644 --- a/tests/embedding/test_app.py +++ b/tests/embedding/test_app.py @@ -328,3 +328,19 @@ def test_launch_gui(embedded_app, tmp_path: pytest.TempPathFactory, capfd): out, err = capfd.readouterr() print(out) assert f"Done launching Ansys Mechanical {version}" in out + + +# def test_launch_ui(app): +# # there are also python frameworks for fake/mock objects... +# # if its one test it can go in the test, +# # if its multiple it can be defined in the test_---.py file +# # or in a fixture in conftest +# class MockLauncher(): +# def __init__(self): +# self.ops = [] + +# def save(self, app): +# self.ops.append("save") +# m = MockLauncher() +# _launch_ui(app, m) +# assert m.ops[0] == "save" From 1e1cd53500f1a767f4b8c1848a116a574fdc9bbb Mon Sep 17 00:00:00 2001 From: klmcadams <58492561+klmcadams@users.noreply.github.com> Date: Wed, 11 Sep 2024 15:56:15 -0400 Subject: [PATCH 14/35] give option to delete temporary mechdb file & folder on close --- src/ansys/mechanical/core/embedding/app.py | 4 +- .../mechanical/core/embedding/cleanup_gui.py | 64 +++++++ src/ansys/mechanical/core/embedding/ui.py | 175 +++++++----------- tests/embedding/test_app.py | 78 +++++--- 4 files changed, 186 insertions(+), 135 deletions(-) create mode 100644 src/ansys/mechanical/core/embedding/cleanup_gui.py diff --git a/src/ansys/mechanical/core/embedding/app.py b/src/ansys/mechanical/core/embedding/app.py index be9688157..c7683ebb8 100644 --- a/src/ansys/mechanical/core/embedding/app.py +++ b/src/ansys/mechanical/core/embedding/app.py @@ -192,9 +192,9 @@ def save_as(self, path): """Save the project as.""" self.DataModel.Project.SaveAs(path) - def launch_gui(self): + def launch_gui(self, delete_tmp_on_close: bool = True): """Launch the GUI.""" - launch_ui(self) + launch_ui(self, delete_tmp_on_close) def new(self): """Clear to a new application.""" diff --git a/src/ansys/mechanical/core/embedding/cleanup_gui.py b/src/ansys/mechanical/core/embedding/cleanup_gui.py new file mode 100644 index 000000000..05e33639c --- /dev/null +++ b/src/ansys/mechanical/core/embedding/cleanup_gui.py @@ -0,0 +1,64 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Clean up temporary mechdb files after GUI is closed.""" + +from pathlib import Path, PurePath +import shutil +import sys +import time + +import psutil + + +def cleanup_gui(pid, temp_mechdb) -> None: + """Remove the temporary mechdb file after it is closed. + + Parameters + ---------- + pid: int + The process ID of the open temporary mechdb file. + temp_mechdb: Path + The path of the temporary mechdb file. + """ + # While the pid exists, sleep + while psutil.pid_exists(pid): + time.sleep(1) + + # Delete the temporary mechdb file once the GUI is closed + Path.unlink(temp_mechdb) + + # Delete the temporary mechdb Mech_Files folder + dirname = PurePath(temp_mechdb).parent + basename = PurePath(temp_mechdb).name + pd_index = basename.index(".") + temp_folder = f"{basename[0:pd_index]}_Mech_Files" + temp_folder_path = PurePath.joinpath(dirname, temp_folder) + shutil.rmtree(temp_folder_path) + + +if __name__ == "__main__": + # Convert the process id (pid) argument into an integer + pid = int(sys.argv[1]) + # Convert the temporary mechdb path into a Path + temp_mechdb_path = Path(sys.argv[2]) + # Remove the temporary mechdb file when the GUI is closed + cleanup_gui(pid, temp_mechdb_path) diff --git a/src/ansys/mechanical/core/embedding/ui.py b/src/ansys/mechanical/core/embedding/ui.py index 32a1886aa..3d5812000 100644 --- a/src/ansys/mechanical/core/embedding/ui.py +++ b/src/ansys/mechanical/core/embedding/ui.py @@ -20,89 +20,21 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -"""Run Mechanical UI from python.""" +"""Run Mechanical UI from Python.""" +import atexit import os from pathlib import Path from subprocess import Popen +import sys import tempfile import typing -""" -If Mechanical has ever been saved, then we are allowed to use `app.save()`, -otherwise, we have to use `app.save_as()` and pass a temp file path which -will have to be cleaned up later... - -Mechanical can be in one of three states: -1. not-saved -2. saved|dirty -3. saved|not-dirty - -#1 -Example: - app = App() - ... - app.launch_gui() - -A save_as() to save1.mechdb -B save_as() to save2.mechdb -C open() the mechdb from A -D run ansys-mechanical -g on save2.mechdb - -** side-effect: not-saved => saved|not-dirty - -#2 -Example: - app.save_as("/path/to/file.mechdb") - app.project_file => - "/path/to/file.mechdb" - ... - app.launch_gui() - app.project_file => - "/path/to/save2.mechdb" - -A save() -B identify the mechdb of the saved session -C save_as() to a save.mechdb -D open() the mechdb from A -E run ansys-mechanical -g on save.mechdb from C - -** side-effect: saved|dirty => saved|not-dirty - -#3 -Example: - app.save_as("/path/to/some/file.mechdb") - app.launch_gui() - - -A save() -B identify the mechdb of the saved session -C save_as() into a save.mechdb -D open() the mechdb saved session (from A) -E run ansys-mechanical -g on that temp path (B) -** side-effect: no side effect - -side-effect of all: - If we are either in a dirty state or have never been saved, we went to a save|not-dirty state - -side-effect/bug: - There is now a mechdb in the temp folder that is leaked. - -""" - - -def _is_saved(app: "ansys.mechanical.core.embedding.App"): - try: - app.save() - except: - raise Exception("The App must have already been saved before using launch_ui!") - return True - class UILauncher: - """Launch the GUI on a temporary mechdb file.""" + """Launch the GUI using a temporary mechdb file.""" - def save_original(self, app) -> None: + def save_original(self, app: "ansys.mechanical.core.embedding.App") -> None: """Save the active mechdb file. Parameters @@ -112,7 +44,9 @@ def save_original(self, app) -> None: """ app.save() - def save_temp_copy(self, app) -> typing.Union[Path, Path]: + def save_temp_copy( + self, app: "ansys.mechanical.core.embedding.App" + ) -> typing.Union[Path, Path]: """Save a new mechdb file with a temporary name. Parameters @@ -123,6 +57,7 @@ def save_temp_copy(self, app) -> typing.Union[Path, Path]: # Identify the mechdb of the saved session from save_original() project_directory = Path(app.DataModel.Project.ProjectDirectory) project_directory_parent = project_directory.parent + print(project_directory_parent) mechdb_file = os.path.join( project_directory_parent, f"{project_directory.parts[-1].split('_')[0]}.mechdb" ) @@ -137,7 +72,7 @@ def save_temp_copy(self, app) -> typing.Union[Path, Path]: return mechdb_file, temp_file_name - def open_original(self, app, mechdb_file) -> None: + def open_original(self, app: "ansys.mechanical.core.embedding.App", mechdb_file: Path) -> None: """Open the original mechdb file from save_original(). Parameters @@ -149,7 +84,9 @@ def open_original(self, app, mechdb_file) -> None: """ app.open(mechdb_file) - def graphically_launch_temp(self, app, mechdb_file, temp_file) -> Popen: + def graphically_launch_temp( + self, app: "ansys.mechanical.core.embedding.App", mechdb_file: Path, temp_file: Path + ) -> Popen: """Launch the GUI for the mechdb file with a temporary name from save_temp_copy(). Parameters @@ -174,55 +111,75 @@ def graphically_launch_temp(self, app, mechdb_file, temp_file) -> Popen: "--graphical", "--revision", str(app.version), - ] - # , env= os.environ.copy() # with some env var for DRY_RUN - ) - - # Problem: the mechdb started above will not automatically get cleaned up - # option 1 - its the users problem, just tell them with a print - print( - f"""Opened a new mechanical session based on ... {mechdb_file}. \ - PyMechanical will not delete after use.""" + ], ) - # option 2 - try to delete it (maybe this can be an opt-in, an argument to launch_ui??) - # by starting a background process that waits until the process exits and - # automatically cleans it up like - # `Popen(f"python /path/to/cleanup-gui.py {p.pid}")` - # option 3 - use a canonical name /tmp/some_name/file.mechdb and overwrite it each time - # launch_ui is run so that worst case only one extra mechdb is not cleaned up. - # The downside is that you can't use launch_ui in parallel but who would even - # do that? You can throw an exception if that mechdb file is open by another - # process at the beginning of this function... - print(f"Done launching Ansys Mechanical {str(app.version)}...") return p + def _cleanup_gui(self, process, temp_mechdb_path): + cleanup_script = os.path.join(os.path.dirname(os.path.abspath(__file__)), "cleanup_gui.py") + Popen([sys.executable, cleanup_script, str(process.pid), temp_mechdb_path]) -# class MockLauncher: -# def __init__(self): -# self.ops = [] -# def save(self, app): -# self.ops.append("save") +def _is_saved(app: "ansys.mechanical.core.embedding.App") -> bool: + """Check if the mechdb file has been saved and raise an exception if not. - -def _launch_ui(app, launcher): - # all the implemation below goes here - if not _is_saved(app): + Parameters + ---------- + app: ansys.mechanical.core.embedding.app.App + A Mechanical embedding application. + """ + try: + app.save() + except: raise Exception("The App must have already been saved before using launch_ui!") - else: + return True + + +def _launch_ui( + app: "ansys.mechanical.core.embedding.App", delete_tmp_on_close: bool, launcher: UILauncher +) -> None: + """Launch the Mechanical UI if the mechdb file has been saved. + + Parameters + ---------- + app: ansys.mechanical.core.embedding.app.App + A Mechanical embedding application. + delete_tmp_on_close: bool + Whether to delete the temporary mechdb file when the GUI is closed. + By default, this is ``True``. + launcher: UILauncher + Launch the GUI using a temporary mechdb file. + """ + if _is_saved(app): launcher.save_original(app) mechdb_file, temp_file = launcher.save_temp_copy(app) launcher.open_original(app, mechdb_file) p = launcher.graphically_launch_temp(app, mechdb_file, temp_file) - return p + # If the user wants the temporary file to be deleted + if delete_tmp_on_close: + atexit.register(launcher._cleanup_gui, p, temp_file) + else: + # Let the user know that the mechdb started above will not automatically get cleaned up + print( + f"""Opened a new mechanical session based on {mechdb_file} named {temp_file}. +PyMechanical will not delete it after use.""" + ) -def launch_ui(app: "ansys.mechanical.core.embedding.App", testing: bool = False) -> Popen: + +def launch_ui(app: "ansys.mechanical.core.embedding.App", delete_tmp_on_close: bool = True) -> None: """Launch the Mechanical UI. Precondition: Mechanical has to have already been saved Side effect: If Mechanical has ever been saved, it overwrites that save. + + Parameters + ---------- + app: ansys.mechanical.core.embedding.app.App + A Mechanical embedding application. + delete_tmp_on_close: bool + Whether to delete the temporary mechdb file when the GUI is closed. + By default, this is ``True``. """ - if not testing: - _launch_ui(app, UILauncher()) + _launch_ui(app, delete_tmp_on_close, UILauncher()) diff --git a/tests/embedding/test_app.py b/tests/embedding/test_app.py index a2675c237..75a153258 100644 --- a/tests/embedding/test_app.py +++ b/tests/embedding/test_app.py @@ -29,6 +29,7 @@ import pytest +from ansys.mechanical.core.embedding.ui import _launch_ui import ansys.mechanical.core.embedding.utils as utils @@ -315,32 +316,61 @@ def test_rm_lockfile(embedded_app, tmp_path: pytest.TempPathFactory): @pytest.mark.embedding -def test_launch_gui(embedded_app, tmp_path: pytest.TempPathFactory, capfd): - """Test lock file is removed on close of embedded application.""" - version = str(embedded_app.version) +def test_launch_ui(embedded_app, tmp_path: pytest.TempPathFactory): + """Test the _launch_ui function with a mock launcher.""" + + class MockLauncher: + """Mock Launcher to test launch_gui functionality.""" + + def __init__(self): + """Initialize the MockLauncher class.""" + self.ops = [] + + def save_original(self, app): + """Save the active mechdb file.""" + self.ops.append("save") + + def save_temp_copy(self, app): + """Save a new mechdb file with a temporary name.""" + # Identify the mechdb of the saved session from save_original() + self.ops.append("get_saved_session") + + # Get name of NamedTemporaryFile + self.ops.append("get_name_temp_file") + + # Save app with name of temporary file + self.ops.append("save_as") + + return "", "" + + def open_original(self, app, mechdb_file): + """Open the original mechdb file from save_original().""" + self.ops.append("open_orig_mechdb") + + def graphically_launch_temp(self, app, mechdb_file, temp_file): + """Launch the GUI for the mechdb file with a temporary name from save_temp_copy().""" + self.ops.append("launch_temp_mechdb") + + return [] + + # Create an instance of the MockLauncher object + m = MockLauncher() + + # Check that _launch_ui raises an Exception when it hasn't been saved yet + with pytest.raises(Exception): + _launch_ui(embedded_app, m) - project_directory = embedded_app.DataModel.Project.ProjectDirectory mechdb_path = os.path.join(tmp_path, "test.mechdb") + # Save a test.mechdb file embedded_app.save(mechdb_path) - embedded_app.launch_gui() + # Launch the UI with the mock class + _launch_ui(embedded_app, False, m) + # Close the embedded_app embedded_app.close() - out, err = capfd.readouterr() - print(out) - assert f"Done launching Ansys Mechanical {version}" in out - - -# def test_launch_ui(app): -# # there are also python frameworks for fake/mock objects... -# # if its one test it can go in the test, -# # if its multiple it can be defined in the test_---.py file -# # or in a fixture in conftest -# class MockLauncher(): -# def __init__(self): -# self.ops = [] - -# def save(self, app): -# self.ops.append("save") -# m = MockLauncher() -# _launch_ui(app, m) -# assert m.ops[0] == "save" + assert m.ops[0] == "save" + assert m.ops[1] == "get_saved_session" + assert m.ops[2] == "get_name_temp_file" + assert m.ops[3] == "save_as" + assert m.ops[4] == "open_orig_mechdb" + assert m.ops[5] == "launch_temp_mechdb" From 118d4d56dce885881da0a0fc51be558c4fbddf89 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 11 Sep 2024 19:58:43 +0000 Subject: [PATCH 15/35] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/ansys/mechanical/core/run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ansys/mechanical/core/run.py b/src/ansys/mechanical/core/run.py index b580b2048..242e94c25 100644 --- a/src/ansys/mechanical/core/run.py +++ b/src/ansys/mechanical/core/run.py @@ -35,7 +35,7 @@ from ansys.mechanical.core.embedding.appdata import UniqueUserProfile from ansys.mechanical.core.feature_flags import get_command_line_arguments, get_feature_flag_names -DRY_RUN = False # control this with an env var to opt into dry run? +DRY_RUN = False # control this with an env var to opt into dry run? """Dry run constant.""" # TODO - add logging options (reuse env var based logging initialization) From cab4089a357533e4180da100f40748faf436cbe4 Mon Sep 17 00:00:00 2001 From: klmcadams <58492561+klmcadams@users.noreply.github.com> Date: Thu, 12 Sep 2024 13:08:28 -0400 Subject: [PATCH 16/35] add cleanup gui test --- .../mechanical/core/embedding/cleanup_gui.py | 10 ++----- src/ansys/mechanical/core/embedding/ui.py | 3 +- src/ansys/mechanical/core/run.py | 2 +- tests/embedding/test_app.py | 28 +++++++++++++++++++ 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/ansys/mechanical/core/embedding/cleanup_gui.py b/src/ansys/mechanical/core/embedding/cleanup_gui.py index 05e33639c..4105cc72a 100644 --- a/src/ansys/mechanical/core/embedding/cleanup_gui.py +++ b/src/ansys/mechanical/core/embedding/cleanup_gui.py @@ -21,7 +21,7 @@ # SOFTWARE. """Clean up temporary mechdb files after GUI is closed.""" -from pathlib import Path, PurePath +from pathlib import Path import shutil import sys import time @@ -44,14 +44,10 @@ def cleanup_gui(pid, temp_mechdb) -> None: time.sleep(1) # Delete the temporary mechdb file once the GUI is closed - Path.unlink(temp_mechdb) + temp_mechdb.unlink() # Delete the temporary mechdb Mech_Files folder - dirname = PurePath(temp_mechdb).parent - basename = PurePath(temp_mechdb).name - pd_index = basename.index(".") - temp_folder = f"{basename[0:pd_index]}_Mech_Files" - temp_folder_path = PurePath.joinpath(dirname, temp_folder) + temp_folder_path = temp_mechdb.parent / f"{temp_mechdb.name.split('.')[0]}_Mech_Files" shutil.rmtree(temp_folder_path) diff --git a/src/ansys/mechanical/core/embedding/ui.py b/src/ansys/mechanical/core/embedding/ui.py index 3d5812000..ab14faf2f 100644 --- a/src/ansys/mechanical/core/embedding/ui.py +++ b/src/ansys/mechanical/core/embedding/ui.py @@ -22,7 +22,6 @@ """Run Mechanical UI from Python.""" -import atexit import os from pathlib import Path from subprocess import Popen @@ -159,7 +158,7 @@ def _launch_ui( # If the user wants the temporary file to be deleted if delete_tmp_on_close: - atexit.register(launcher._cleanup_gui, p, temp_file) + launcher._cleanup_gui(p, temp_file) else: # Let the user know that the mechdb started above will not automatically get cleaned up print( diff --git a/src/ansys/mechanical/core/run.py b/src/ansys/mechanical/core/run.py index b580b2048..bafce0442 100644 --- a/src/ansys/mechanical/core/run.py +++ b/src/ansys/mechanical/core/run.py @@ -35,7 +35,7 @@ from ansys.mechanical.core.embedding.appdata import UniqueUserProfile from ansys.mechanical.core.feature_flags import get_command_line_arguments, get_feature_flag_names -DRY_RUN = False # control this with an env var to opt into dry run? +DRY_RUN = False """Dry run constant.""" # TODO - add logging options (reuse env var based logging initialization) diff --git a/tests/embedding/test_app.py b/tests/embedding/test_app.py index 75a153258..20367fca2 100644 --- a/tests/embedding/test_app.py +++ b/tests/embedding/test_app.py @@ -23,12 +23,14 @@ """Miscellaneous embedding tests""" import os import subprocess +from subprocess import Popen import sys from tempfile import NamedTemporaryFile import time import pytest +from ansys.mechanical.core.embedding.cleanup_gui import cleanup_gui from ansys.mechanical.core.embedding.ui import _launch_ui import ansys.mechanical.core.embedding.utils as utils @@ -374,3 +376,29 @@ def graphically_launch_temp(self, app, mechdb_file, temp_file): assert m.ops[3] == "save_as" assert m.ops[4] == "open_orig_mechdb" assert m.ops[5] == "launch_temp_mechdb" + + +@pytest.mark.embedding +def test_tempfile_cleanup(embedded_app, tmp_path: pytest.TempPathFactory): + """Test cleanup function to remove the temporary mechdb file and folder.""" + temp_file = tmp_path / "tempfiletest.mechdb" + temp_folder = tmp_path / "tempfiletest_Mech_Files" + + # Make temporary file + temp_file.touch() + # Make temporary folder + temp_folder.mkdir() + + # Assert the file and folder exist + assert temp_file.exists() + assert temp_folder.exists() + + # Run process + process = Popen(["sleep", "5"]) + + # Remove the temporary file and folder + cleanup_gui(process.pid, temp_file) + + # Assert the file and folder do not exist + assert not temp_file.exists() + assert not temp_folder.exists() From da5ae3bc508058e63930a5e76d957ee76a9a62b4 Mon Sep 17 00:00:00 2001 From: klmcadams <58492561+klmcadams@users.noreply.github.com> Date: Thu, 12 Sep 2024 13:10:21 -0400 Subject: [PATCH 17/35] sleep 1 --- tests/embedding/test_app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/embedding/test_app.py b/tests/embedding/test_app.py index b1d3a0feb..83209b1fc 100644 --- a/tests/embedding/test_app.py +++ b/tests/embedding/test_app.py @@ -405,7 +405,7 @@ def test_tempfile_cleanup(embedded_app, tmp_path: pytest.TempPathFactory): assert temp_folder.exists() # Run process - process = Popen(["sleep", "5"]) + process = Popen(["sleep", "1"]) # Remove the temporary file and folder cleanup_gui(process.pid, temp_file) From 55498a9912e429b36a8dbc000282c4d9722e5464 Mon Sep 17 00:00:00 2001 From: klmcadams <58492561+klmcadams@users.noreply.github.com> Date: Thu, 12 Sep 2024 13:26:01 -0400 Subject: [PATCH 18/35] add more comments and remove os --- src/ansys/mechanical/core/embedding/ui.py | 37 ++++++++++++++++++----- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/src/ansys/mechanical/core/embedding/ui.py b/src/ansys/mechanical/core/embedding/ui.py index ab14faf2f..39815ecc9 100644 --- a/src/ansys/mechanical/core/embedding/ui.py +++ b/src/ansys/mechanical/core/embedding/ui.py @@ -19,10 +19,8 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. - """Run Mechanical UI from Python.""" -import os from pathlib import Path from subprocess import Popen import sys @@ -56,9 +54,8 @@ def save_temp_copy( # Identify the mechdb of the saved session from save_original() project_directory = Path(app.DataModel.Project.ProjectDirectory) project_directory_parent = project_directory.parent - print(project_directory_parent) - mechdb_file = os.path.join( - project_directory_parent, f"{project_directory.parts[-1].split('_')[0]}.mechdb" + mechdb_file = ( + project_directory_parent / f"{project_directory.parts[-1].split('_')[0]}.mechdb" ) # Get name of NamedTemporaryFile @@ -102,6 +99,7 @@ def graphically_launch_temp( subprocess.Popen The subprocess that launches the GUI for the temporary mechdb file. """ + # The subprocess that uses ansys-mechanical to launch the GUI of the temporary mechdb file p = Popen( [ "ansys-mechanical", @@ -115,8 +113,20 @@ def graphically_launch_temp( return p - def _cleanup_gui(self, process, temp_mechdb_path): - cleanup_script = os.path.join(os.path.dirname(os.path.abspath(__file__)), "cleanup_gui.py") + def _cleanup_gui(self, process: Popen, temp_mechdb_path: Path) -> None: + """Remove the temporary mechdb file and folder when the GUI is closed. + + Parameters + ---------- + process: subprocess.Popen + The subprocess that launched the GUI of the temporary mechdb file. + temp_mechdb_path: pathlib.Path + The full path to the temporary mechdb file. + """ + # Get the path to the cleanup script + cleanup_script = Path(__file__).parent / "cleanup_gui.py" + + # Open a subprocess to remove the temporary mechdb file and folder when the process ends Popen([sys.executable, cleanup_script, str(process.pid), temp_mechdb_path]) @@ -127,6 +137,12 @@ def _is_saved(app: "ansys.mechanical.core.embedding.App") -> bool: ---------- app: ansys.mechanical.core.embedding.app.App A Mechanical embedding application. + + Returns + ------- + bool + ``True`` when the embedded app has been saved. + ``False`` when the embedded app has not been saved. """ try: app.save() @@ -151,13 +167,18 @@ def _launch_ui( Launch the GUI using a temporary mechdb file. """ if _is_saved(app): + # Save the active mechdb file. launcher.save_original(app) + # Save a new mechdb file with a temporary name. mechdb_file, temp_file = launcher.save_temp_copy(app) + # Open the original mechdb file from save_original(). launcher.open_original(app, mechdb_file) + # Launch the GUI for the mechdb file with a temporary name from save_temp_copy(). p = launcher.graphically_launch_temp(app, mechdb_file, temp_file) - # If the user wants the temporary file to be deleted + # If the user wants the temporary file to be deleted. By default, this is True if delete_tmp_on_close: + # Remove the temporary mechdb file and folder when the GUI is closed. launcher._cleanup_gui(p, temp_file) else: # Let the user know that the mechdb started above will not automatically get cleaned up From f3e3c75f7921712890d526356ffa92a3bf148179 Mon Sep 17 00:00:00 2001 From: klmcadams <58492561+klmcadams@users.noreply.github.com> Date: Thu, 12 Sep 2024 13:35:20 -0400 Subject: [PATCH 19/35] add psutil as test dep requirement --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index e11096358..b03d0f456 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,6 +51,7 @@ tests = [ "pytest==8.3.2", "pytest-cov==5.0.0", "pytest-print==1.0.1", + "psutil==6.0.0" ] doc = [ "sphinx==8.0.2", From 76c20cb6a22d7a80827421a15b38f11adb744965 Mon Sep 17 00:00:00 2001 From: klmcadams <58492561+klmcadams@users.noreply.github.com> Date: Thu, 12 Sep 2024 13:48:57 -0400 Subject: [PATCH 20/35] move tempfile cleanup to embedding_scripts --- tests/embedding/test_app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/embedding/test_app.py b/tests/embedding/test_app.py index 83209b1fc..34ac4d266 100644 --- a/tests/embedding/test_app.py +++ b/tests/embedding/test_app.py @@ -389,8 +389,8 @@ def graphically_launch_temp(self, app, mechdb_file, temp_file): assert m.ops[5] == "launch_temp_mechdb" -@pytest.mark.embedding -def test_tempfile_cleanup(embedded_app, tmp_path: pytest.TempPathFactory): +@pytest.mark.embedding_scripts +def test_tempfile_cleanup(tmp_path: pytest.TempPathFactory): """Test cleanup function to remove the temporary mechdb file and folder.""" temp_file = tmp_path / "tempfiletest.mechdb" temp_folder = tmp_path / "tempfiletest_Mech_Files" From 900c4e4696c255d55eaeed31abb220f8f074c2a2 Mon Sep 17 00:00:00 2001 From: klmcadams <58492561+klmcadams@users.noreply.github.com> Date: Thu, 12 Sep 2024 14:36:00 -0400 Subject: [PATCH 21/35] adjust run to return process --- src/ansys/mechanical/core/run.py | 6 +++--- tests/conftest.py | 5 +++-- tests/embedding/test_app.py | 5 ++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ansys/mechanical/core/run.py b/src/ansys/mechanical/core/run.py index bafce0442..4b12f282b 100644 --- a/src/ansys/mechanical/core/run.py +++ b/src/ansys/mechanical/core/run.py @@ -67,7 +67,7 @@ async def _read_and_display(cmd, env, do_display: bool): # wait for the process to exit rc = await process.wait() - return rc, b"".join(stdout), b"".join(stderr) + return rc, process, b"".join(stdout), b"".join(stderr) def _run(args, env, check=False, display=False): @@ -77,13 +77,13 @@ def _run(args, env, check=False, display=False): else: loop = asyncio.get_event_loop() try: - rc, *output = loop.run_until_complete(_read_and_display(args, env, display)) + rc, process, *output = loop.run_until_complete(_read_and_display(args, env, display)) if rc and check: sys.exit("child failed with '{}' exit code".format(rc)) finally: if os.name == "nt": loop.close() - return output + return process, output def _cli_impl( diff --git a/tests/conftest.py b/tests/conftest.py index 3fe042201..31af6bcd1 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -185,10 +185,11 @@ def run_subprocess(): def func(args, env=None, check: bool = None): if check is None: check = _CHECK_PROCESS_RETURN_CODE - stdout, stderr = ansys.mechanical.core.run._run( + process, output = ansys.mechanical.core.run._run( args, env, check, _PRINT_SUBPROCESS_OUTPUT_TO_CONSOLE ) - return stdout, stderr + # process, stdout, stderr + return process, output[0], output[1] return func diff --git a/tests/embedding/test_app.py b/tests/embedding/test_app.py index 34ac4d266..284a4a1e6 100644 --- a/tests/embedding/test_app.py +++ b/tests/embedding/test_app.py @@ -23,7 +23,6 @@ """Miscellaneous embedding tests""" import os import subprocess -from subprocess import Popen import sys from tempfile import NamedTemporaryFile import time @@ -390,7 +389,7 @@ def graphically_launch_temp(self, app, mechdb_file, temp_file): @pytest.mark.embedding_scripts -def test_tempfile_cleanup(tmp_path: pytest.TempPathFactory): +def test_tempfile_cleanup(tmp_path: pytest.TempPathFactory, run_subprocess): """Test cleanup function to remove the temporary mechdb file and folder.""" temp_file = tmp_path / "tempfiletest.mechdb" temp_folder = tmp_path / "tempfiletest_Mech_Files" @@ -405,7 +404,7 @@ def test_tempfile_cleanup(tmp_path: pytest.TempPathFactory): assert temp_folder.exists() # Run process - process = Popen(["sleep", "1"]) + process, stdout, stderr = run_subprocess(["sleep", "1"]) # Remove the temporary file and folder cleanup_gui(process.pid, temp_file) From a7eaafb2f7f85351c94071ce6c15508bfd18990f Mon Sep 17 00:00:00 2001 From: klmcadams <58492561+klmcadams@users.noreply.github.com> Date: Thu, 12 Sep 2024 14:50:11 -0400 Subject: [PATCH 22/35] fix run_subprocess equals --- tests/embedding/test_app.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/embedding/test_app.py b/tests/embedding/test_app.py index 284a4a1e6..67e5eea25 100644 --- a/tests/embedding/test_app.py +++ b/tests/embedding/test_app.py @@ -224,7 +224,7 @@ def test_warning_message(test_env, pytestconfig, run_subprocess, rootdir): # Run embedded instance in virtual env with pythonnet installed embedded_py = os.path.join(rootdir, "tests", "scripts", "run_embedded_app.py") - _, stderr = run_subprocess( + process, stdout, stderr = run_subprocess( [test_env.python, embedded_py, pytestconfig.getoption("ansys_version")] ) @@ -245,7 +245,7 @@ def test_private_appdata(pytestconfig, run_subprocess, rootdir): embedded_py = os.path.join(rootdir, "tests", "scripts", "run_embedded_app.py") run_subprocess([sys.executable, embedded_py, version, "True", "Set"]) - stdout, _ = run_subprocess([sys.executable, embedded_py, version, "True", "Run"]) + process, stdout, stderr = run_subprocess([sys.executable, embedded_py, version, "True", "Run"]) stdout = stdout.decode() assert "ShowTriad value is True" in stdout @@ -259,7 +259,7 @@ def test_normal_appdata(pytestconfig, run_subprocess, rootdir): embedded_py = os.path.join(rootdir, "tests", "scripts", "run_embedded_app.py") run_subprocess([sys.executable, embedded_py, version, "False", "Set"]) - stdout, _ = run_subprocess([sys.executable, embedded_py, version, "False", "Run"]) + process, stdout, stderr = run_subprocess([sys.executable, embedded_py, version, "False", "Run"]) run_subprocess([sys.executable, embedded_py, version, "False", "Reset"]) stdout = stdout.decode() @@ -280,13 +280,15 @@ def test_building_gallery(pytestconfig, run_subprocess, rootdir): embedded_gallery_py = os.path.join(rootdir, "tests", "scripts", "build_gallery_test.py") - _, stderr = run_subprocess([sys.executable, embedded_gallery_py, version, "False"], None, False) + process, stdout, stderr = run_subprocess( + [sys.executable, embedded_gallery_py, version, "False"], None, False + ) stderr = stderr.decode() # Assert Exception assert "Cannot have more than one embedded mechanical instance" in stderr - stdout, _ = run_subprocess([sys.executable, embedded_gallery_py, version, "True"]) + process, stdout, stderr = run_subprocess([sys.executable, embedded_gallery_py, version, "True"]) stdout = stdout.decode() # Assert stdout after launching multiple instances From d9693f494917f27ae1826c712cb43f11b853b422 Mon Sep 17 00:00:00 2001 From: klmcadams <58492561+klmcadams@users.noreply.github.com> Date: Thu, 12 Sep 2024 15:00:38 -0400 Subject: [PATCH 23/35] fix more lines --- tests/embedding/test_background.py | 2 +- tests/embedding/test_logger.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/embedding/test_background.py b/tests/embedding/test_background.py index df667d021..37a9e9178 100644 --- a/tests/embedding/test_background.py +++ b/tests/embedding/test_background.py @@ -34,7 +34,7 @@ def _run_background_app_test_process( """Run the process and return stdout and stderr after it finishes.""" version = pytestconfig.getoption("ansys_version") script = os.path.join(rootdir, "tests", "scripts", "background_app_test.py") - stdout, stderr = run_subprocess( + process, stdout, stderr = run_subprocess( [sys.executable, script, version, testname], None, pass_expected ) return stdout, stderr diff --git a/tests/embedding/test_logger.py b/tests/embedding/test_logger.py index 2794e69e8..2b29d705c 100644 --- a/tests/embedding/test_logger.py +++ b/tests/embedding/test_logger.py @@ -51,7 +51,7 @@ def _run_embedding_log_test_process( """Runs the process and returns it after it finishes""" version = pytestconfig.getoption("ansys_version") embedded_py = os.path.join(rootdir, "tests", "scripts", "embedding_log_test.py") - stdout, stderr = run_subprocess( + process, stdout, stderr = run_subprocess( [sys.executable, embedded_py, version, testname], _get_env_without_logging_variables(), pass_expected, From c1b781319b009448c00094a233251351ec822fc8 Mon Sep 17 00:00:00 2001 From: klmcadams <58492561+klmcadams@users.noreply.github.com> Date: Thu, 12 Sep 2024 15:19:05 -0400 Subject: [PATCH 24/35] try to test launch_gui directly --- tests/embedding/test_app.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/embedding/test_app.py b/tests/embedding/test_app.py index 67e5eea25..b026caa8d 100644 --- a/tests/embedding/test_app.py +++ b/tests/embedding/test_app.py @@ -390,6 +390,20 @@ def graphically_launch_temp(self, app, mechdb_file, temp_file): assert m.ops[5] == "launch_temp_mechdb" +@pytest.mark.embedding +def test_launch_gui(embedded_app, tmp_path: pytest.TempPathFactory, capfd): + """Test lock file is removed on close of embedded application.""" + # version = str(embedded_app.version) + # full_version = f"20{version[0:2]}R{version[-1]}" + mechdb_path = os.path.join(tmp_path, "test.mechdb") + embedded_app.save(mechdb_path) + embedded_app.launch_gui(delete_tmp_on_close=False) + embedded_app.close() + out, err = capfd.readouterr() + # print(out) + assert f"Opened a new Mechanical session based on {mechdb_path}." in out + + @pytest.mark.embedding_scripts def test_tempfile_cleanup(tmp_path: pytest.TempPathFactory, run_subprocess): """Test cleanup function to remove the temporary mechdb file and folder.""" From 1e147ac214c2e304be824ebce66e364e5ce039b0 Mon Sep 17 00:00:00 2001 From: klmcadams <58492561+klmcadams@users.noreply.github.com> Date: Thu, 12 Sep 2024 15:31:35 -0400 Subject: [PATCH 25/35] open string not path --- src/ansys/mechanical/core/embedding/ui.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ansys/mechanical/core/embedding/ui.py b/src/ansys/mechanical/core/embedding/ui.py index 39815ecc9..1ca37dac3 100644 --- a/src/ansys/mechanical/core/embedding/ui.py +++ b/src/ansys/mechanical/core/embedding/ui.py @@ -68,14 +68,14 @@ def save_temp_copy( return mechdb_file, temp_file_name - def open_original(self, app: "ansys.mechanical.core.embedding.App", mechdb_file: Path) -> None: + def open_original(self, app: "ansys.mechanical.core.embedding.App", mechdb_file: str) -> None: """Open the original mechdb file from save_original(). Parameters ---------- app: ansys.mechanical.core.embedding.app.App A Mechanical embedding application. - mechdb_file: pathlib.Path + mechdb_file: str The full path to the active mechdb file. """ app.open(mechdb_file) @@ -172,7 +172,7 @@ def _launch_ui( # Save a new mechdb file with a temporary name. mechdb_file, temp_file = launcher.save_temp_copy(app) # Open the original mechdb file from save_original(). - launcher.open_original(app, mechdb_file) + launcher.open_original(app, str(mechdb_file)) # Launch the GUI for the mechdb file with a temporary name from save_temp_copy(). p = launcher.graphically_launch_temp(app, mechdb_file, temp_file) From 78e3a29c08b26b3d593a41230a2a989918ade2fb Mon Sep 17 00:00:00 2001 From: klmcadams <58492561+klmcadams@users.noreply.github.com> Date: Thu, 12 Sep 2024 15:41:35 -0400 Subject: [PATCH 26/35] fix assert check --- tests/embedding/test_app.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/embedding/test_app.py b/tests/embedding/test_app.py index b026caa8d..3be5ef236 100644 --- a/tests/embedding/test_app.py +++ b/tests/embedding/test_app.py @@ -393,15 +393,12 @@ def graphically_launch_temp(self, app, mechdb_file, temp_file): @pytest.mark.embedding def test_launch_gui(embedded_app, tmp_path: pytest.TempPathFactory, capfd): """Test lock file is removed on close of embedded application.""" - # version = str(embedded_app.version) - # full_version = f"20{version[0:2]}R{version[-1]}" mechdb_path = os.path.join(tmp_path, "test.mechdb") embedded_app.save(mechdb_path) embedded_app.launch_gui(delete_tmp_on_close=False) embedded_app.close() out, err = capfd.readouterr() - # print(out) - assert f"Opened a new Mechanical session based on {mechdb_path}." in out + assert f"Opened a new mechanical session based on {mechdb_path}." in out @pytest.mark.embedding_scripts From dd13a68259f95fd18ac6c0b18e8c9fc21bd3e8dc Mon Sep 17 00:00:00 2001 From: klmcadams <58492561+klmcadams@users.noreply.github.com> Date: Thu, 12 Sep 2024 15:50:11 -0400 Subject: [PATCH 27/35] remove period --- tests/embedding/test_app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/embedding/test_app.py b/tests/embedding/test_app.py index 3be5ef236..b30408b91 100644 --- a/tests/embedding/test_app.py +++ b/tests/embedding/test_app.py @@ -398,7 +398,7 @@ def test_launch_gui(embedded_app, tmp_path: pytest.TempPathFactory, capfd): embedded_app.launch_gui(delete_tmp_on_close=False) embedded_app.close() out, err = capfd.readouterr() - assert f"Opened a new mechanical session based on {mechdb_path}." in out + assert f"Opened a new mechanical session based on {mechdb_path}" in out @pytest.mark.embedding_scripts From 735f47bcfcf6dff53cec48d32e2d5c1e4e9cf46c Mon Sep 17 00:00:00 2001 From: klmcadams <58492561+klmcadams@users.noreply.github.com> Date: Thu, 12 Sep 2024 16:42:19 -0400 Subject: [PATCH 28/35] add pragma no cover lines & more tests --- .../mechanical/core/embedding/cleanup_gui.py | 7 ++++++- src/ansys/mechanical/core/embedding/ui.py | 8 +++++--- tests/embedding/test_app.py | 18 ++++++++++++++---- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/ansys/mechanical/core/embedding/cleanup_gui.py b/src/ansys/mechanical/core/embedding/cleanup_gui.py index 4105cc72a..55f04c6a2 100644 --- a/src/ansys/mechanical/core/embedding/cleanup_gui.py +++ b/src/ansys/mechanical/core/embedding/cleanup_gui.py @@ -51,10 +51,15 @@ def cleanup_gui(pid, temp_mechdb) -> None: shutil.rmtree(temp_folder_path) -if __name__ == "__main__": +def main(): + """Get the process ID and temporary file path to monitor and delete files after use.""" # Convert the process id (pid) argument into an integer pid = int(sys.argv[1]) # Convert the temporary mechdb path into a Path temp_mechdb_path = Path(sys.argv[2]) # Remove the temporary mechdb file when the GUI is closed cleanup_gui(pid, temp_mechdb_path) + + +if __name__ == "__main__": # pragma: no cover + main() diff --git a/src/ansys/mechanical/core/embedding/ui.py b/src/ansys/mechanical/core/embedding/ui.py index 1ca37dac3..e5bcd5d23 100644 --- a/src/ansys/mechanical/core/embedding/ui.py +++ b/src/ansys/mechanical/core/embedding/ui.py @@ -124,10 +124,12 @@ def _cleanup_gui(self, process: Popen, temp_mechdb_path: Path) -> None: The full path to the temporary mechdb file. """ # Get the path to the cleanup script - cleanup_script = Path(__file__).parent / "cleanup_gui.py" + cleanup_script = Path(__file__).parent / "cleanup_gui.py" # pragma: no cover # Open a subprocess to remove the temporary mechdb file and folder when the process ends - Popen([sys.executable, cleanup_script, str(process.pid), temp_mechdb_path]) + Popen( + [sys.executable, cleanup_script, str(process.pid), temp_mechdb_path] + ) # pragma: no cover def _is_saved(app: "ansys.mechanical.core.embedding.App") -> bool: @@ -179,7 +181,7 @@ def _launch_ui( # If the user wants the temporary file to be deleted. By default, this is True if delete_tmp_on_close: # Remove the temporary mechdb file and folder when the GUI is closed. - launcher._cleanup_gui(p, temp_file) + launcher._cleanup_gui(p, temp_file) # pragma: no cover else: # Let the user know that the mechdb started above will not automatically get cleaned up print( diff --git a/tests/embedding/test_app.py b/tests/embedding/test_app.py index b30408b91..894a4ddb5 100644 --- a/tests/embedding/test_app.py +++ b/tests/embedding/test_app.py @@ -29,7 +29,7 @@ import pytest -from ansys.mechanical.core.embedding.cleanup_gui import cleanup_gui +from ansys.mechanical.core.embedding.cleanup_gui import main as cleanup_gui_main from ansys.mechanical.core.embedding.ui import _launch_ui import ansys.mechanical.core.embedding.utils as utils @@ -392,7 +392,7 @@ def graphically_launch_temp(self, app, mechdb_file, temp_file): @pytest.mark.embedding def test_launch_gui(embedded_app, tmp_path: pytest.TempPathFactory, capfd): - """Test lock file is removed on close of embedded application.""" + """Test the GUI is launched for an embedded app.""" mechdb_path = os.path.join(tmp_path, "test.mechdb") embedded_app.save(mechdb_path) embedded_app.launch_gui(delete_tmp_on_close=False) @@ -401,6 +401,15 @@ def test_launch_gui(embedded_app, tmp_path: pytest.TempPathFactory, capfd): assert f"Opened a new mechanical session based on {mechdb_path}" in out +@pytest.mark.embedding +def test_launch_gui_exception(embedded_app, tmp_path: pytest.TempPathFactory, capfd): + """Test an exception is raised when the embedded_app has not been saved yet.""" + # Assert that an exception is raised + with pytest.raises(Exception): + embedded_app.launch_gui(delete_tmp_on_close=False) + embedded_app.close() + + @pytest.mark.embedding_scripts def test_tempfile_cleanup(tmp_path: pytest.TempPathFactory, run_subprocess): """Test cleanup function to remove the temporary mechdb file and folder.""" @@ -417,10 +426,11 @@ def test_tempfile_cleanup(tmp_path: pytest.TempPathFactory, run_subprocess): assert temp_folder.exists() # Run process - process, stdout, stderr = run_subprocess(["sleep", "1"]) + process, stdout, stderr = run_subprocess(["sleep", "3"]) # Remove the temporary file and folder - cleanup_gui(process.pid, temp_file) + sys.argv[1:] = [process.pid, temp_file] + cleanup_gui_main() # Assert the file and folder do not exist assert not temp_file.exists() From 3a3ad30fa2d806b86c0a65f02fc0977c0110125b Mon Sep 17 00:00:00 2001 From: klmcadams <58492561+klmcadams@users.noreply.github.com> Date: Fri, 13 Sep 2024 09:16:51 -0400 Subject: [PATCH 29/35] add dependency and adjust cleanup test --- pyproject.toml | 1 + src/ansys/mechanical/core/embedding/cleanup_gui.py | 6 +----- tests/embedding/test_app.py | 5 ++--- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b03d0f456..bc90d625c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,6 +36,7 @@ dependencies = [ "clr-loader==0.2.6", "grpcio>=1.30.0", "protobuf>=3.12.2,<6", + "psutil==6.0.0", "tqdm>=4.45.0", ] diff --git a/src/ansys/mechanical/core/embedding/cleanup_gui.py b/src/ansys/mechanical/core/embedding/cleanup_gui.py index 55f04c6a2..4294f67c7 100644 --- a/src/ansys/mechanical/core/embedding/cleanup_gui.py +++ b/src/ansys/mechanical/core/embedding/cleanup_gui.py @@ -51,7 +51,7 @@ def cleanup_gui(pid, temp_mechdb) -> None: shutil.rmtree(temp_folder_path) -def main(): +if __name__ == "__main__": # pragma: no cover """Get the process ID and temporary file path to monitor and delete files after use.""" # Convert the process id (pid) argument into an integer pid = int(sys.argv[1]) @@ -59,7 +59,3 @@ def main(): temp_mechdb_path = Path(sys.argv[2]) # Remove the temporary mechdb file when the GUI is closed cleanup_gui(pid, temp_mechdb_path) - - -if __name__ == "__main__": # pragma: no cover - main() diff --git a/tests/embedding/test_app.py b/tests/embedding/test_app.py index 894a4ddb5..558f0c08f 100644 --- a/tests/embedding/test_app.py +++ b/tests/embedding/test_app.py @@ -29,7 +29,7 @@ import pytest -from ansys.mechanical.core.embedding.cleanup_gui import main as cleanup_gui_main +from ansys.mechanical.core.embedding.cleanup_gui import cleanup_gui from ansys.mechanical.core.embedding.ui import _launch_ui import ansys.mechanical.core.embedding.utils as utils @@ -429,8 +429,7 @@ def test_tempfile_cleanup(tmp_path: pytest.TempPathFactory, run_subprocess): process, stdout, stderr = run_subprocess(["sleep", "3"]) # Remove the temporary file and folder - sys.argv[1:] = [process.pid, temp_file] - cleanup_gui_main() + cleanup_gui(process.pid, temp_file) # Assert the file and folder do not exist assert not temp_file.exists() From ed768bb70e28df6fa7255c42b26b3a2278e97217 Mon Sep 17 00:00:00 2001 From: klmcadams <58492561+klmcadams@users.noreply.github.com> Date: Fri, 13 Sep 2024 12:26:50 -0400 Subject: [PATCH 30/35] use dry run in ui --- src/ansys/mechanical/core/embedding/app.py | 4 ++-- src/ansys/mechanical/core/embedding/ui.py | 21 +++++++++++++++------ tests/embedding/test_app.py | 2 +- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/ansys/mechanical/core/embedding/app.py b/src/ansys/mechanical/core/embedding/app.py index 9761cdaf7..ba46ac96b 100644 --- a/src/ansys/mechanical/core/embedding/app.py +++ b/src/ansys/mechanical/core/embedding/app.py @@ -192,9 +192,9 @@ def save_as(self, path): """Save the project as.""" self.DataModel.Project.SaveAs(path) - def launch_gui(self, delete_tmp_on_close: bool = True): + def launch_gui(self, delete_tmp_on_close: bool = True, dry_run: bool = False): """Launch the GUI.""" - launch_ui(self, delete_tmp_on_close) + launch_ui(self, delete_tmp_on_close, dry_run) def new(self): """Clear to a new application.""" diff --git a/src/ansys/mechanical/core/embedding/ui.py b/src/ansys/mechanical/core/embedding/ui.py index e5bcd5d23..cb7c43102 100644 --- a/src/ansys/mechanical/core/embedding/ui.py +++ b/src/ansys/mechanical/core/embedding/ui.py @@ -31,6 +31,10 @@ class UILauncher: """Launch the GUI using a temporary mechdb file.""" + def __init__(self, dry_run: bool = False): + """Initialize UILauncher class.""" + self._dry_run = dry_run + def save_original(self, app: "ansys.mechanical.core.embedding.App") -> None: """Save the active mechdb file. @@ -126,10 +130,11 @@ def _cleanup_gui(self, process: Popen, temp_mechdb_path: Path) -> None: # Get the path to the cleanup script cleanup_script = Path(__file__).parent / "cleanup_gui.py" # pragma: no cover - # Open a subprocess to remove the temporary mechdb file and folder when the process ends - Popen( - [sys.executable, cleanup_script, str(process.pid), temp_mechdb_path] - ) # pragma: no cover + if not self._dry_run: + # Open a subprocess to remove the temporary mechdb file and folder when the process ends + Popen( + [sys.executable, cleanup_script, str(process.pid), temp_mechdb_path] + ) # pragma: no cover def _is_saved(app: "ansys.mechanical.core.embedding.App") -> bool: @@ -190,7 +195,11 @@ def _launch_ui( ) -def launch_ui(app: "ansys.mechanical.core.embedding.App", delete_tmp_on_close: bool = True) -> None: +def launch_ui( + app: "ansys.mechanical.core.embedding.App", + delete_tmp_on_close: bool = True, + dry_run: bool = False, +) -> None: """Launch the Mechanical UI. Precondition: Mechanical has to have already been saved @@ -204,4 +213,4 @@ def launch_ui(app: "ansys.mechanical.core.embedding.App", delete_tmp_on_close: b Whether to delete the temporary mechdb file when the GUI is closed. By default, this is ``True``. """ - _launch_ui(app, delete_tmp_on_close, UILauncher()) + _launch_ui(app, delete_tmp_on_close, UILauncher(dry_run)) diff --git a/tests/embedding/test_app.py b/tests/embedding/test_app.py index 558f0c08f..272995e36 100644 --- a/tests/embedding/test_app.py +++ b/tests/embedding/test_app.py @@ -395,7 +395,7 @@ def test_launch_gui(embedded_app, tmp_path: pytest.TempPathFactory, capfd): """Test the GUI is launched for an embedded app.""" mechdb_path = os.path.join(tmp_path, "test.mechdb") embedded_app.save(mechdb_path) - embedded_app.launch_gui(delete_tmp_on_close=False) + embedded_app.launch_gui(delete_tmp_on_close=False, dry_run=True) embedded_app.close() out, err = capfd.readouterr() assert f"Opened a new mechanical session based on {mechdb_path}" in out From 1d13113d29aa64ce81588b8c9d885e4513f2486e Mon Sep 17 00:00:00 2001 From: klmcadams <58492561+klmcadams@users.noreply.github.com> Date: Fri, 13 Sep 2024 14:41:49 -0400 Subject: [PATCH 31/35] print command for dry run --- src/ansys/mechanical/core/embedding/ui.py | 47 ++++++++++++++--------- tests/embedding/test_app.py | 2 + 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/src/ansys/mechanical/core/embedding/ui.py b/src/ansys/mechanical/core/embedding/ui.py index cb7c43102..8f982a1b8 100644 --- a/src/ansys/mechanical/core/embedding/ui.py +++ b/src/ansys/mechanical/core/embedding/ui.py @@ -85,16 +85,14 @@ def open_original(self, app: "ansys.mechanical.core.embedding.App", mechdb_file: app.open(mechdb_file) def graphically_launch_temp( - self, app: "ansys.mechanical.core.embedding.App", mechdb_file: Path, temp_file: Path - ) -> Popen: + self, app: "ansys.mechanical.core.embedding.App", temp_file: Path + ) -> Popen | str: """Launch the GUI for the mechdb file with a temporary name from save_temp_copy(). Parameters ---------- app: ansys.mechanical.core.embedding.app.App A Mechanical embedding application. - mechdb_file: pathlib.Path - The full path to the active mechdb file. temp_file: pathlib.Path The full path to the temporary mechdb file. @@ -104,18 +102,22 @@ def graphically_launch_temp( The subprocess that launches the GUI for the temporary mechdb file. """ # The subprocess that uses ansys-mechanical to launch the GUI of the temporary mechdb file - p = Popen( - [ - "ansys-mechanical", - "--project-file", - temp_file, - "--graphical", - "--revision", - str(app.version), - ], - ) + if not self._dry_run: + process = Popen( + [ + "ansys-mechanical", + "--project-file", + temp_file, + "--graphical", + "--revision", + str(app.version), + ], + ) - return p + return process + else: + return f"ansys-mechanical --project-file {temp_file} --graphical \ +--revision {str(app.version)}" def _cleanup_gui(self, process: Popen, temp_mechdb_path: Path) -> None: """Remove the temporary mechdb file and folder when the GUI is closed. @@ -181,12 +183,17 @@ def _launch_ui( # Open the original mechdb file from save_original(). launcher.open_original(app, str(mechdb_file)) # Launch the GUI for the mechdb file with a temporary name from save_temp_copy(). - p = launcher.graphically_launch_temp(app, mechdb_file, temp_file) + process = launcher.graphically_launch_temp(app, temp_file) + + # If it's a dry run and graphically_launch_temp returned a string, print the string + if isinstance(process, str): + print(process) - # If the user wants the temporary file to be deleted. By default, this is True - if delete_tmp_on_close: + # If the user wants the temporary file to be deleted and graphically_launch_temp + # returned a process. By default, this is True + if delete_tmp_on_close and not isinstance(process, str): # Remove the temporary mechdb file and folder when the GUI is closed. - launcher._cleanup_gui(p, temp_file) # pragma: no cover + launcher._cleanup_gui(process, temp_file) # pragma: no cover else: # Let the user know that the mechdb started above will not automatically get cleaned up print( @@ -212,5 +219,7 @@ def launch_ui( delete_tmp_on_close: bool Whether to delete the temporary mechdb file when the GUI is closed. By default, this is ``True``. + dry_run: bool + Whether or not to launch the GUI. By default, this is ``False``. """ _launch_ui(app, delete_tmp_on_close, UILauncher(dry_run)) diff --git a/tests/embedding/test_app.py b/tests/embedding/test_app.py index 272995e36..c5ca57630 100644 --- a/tests/embedding/test_app.py +++ b/tests/embedding/test_app.py @@ -398,6 +398,8 @@ def test_launch_gui(embedded_app, tmp_path: pytest.TempPathFactory, capfd): embedded_app.launch_gui(delete_tmp_on_close=False, dry_run=True) embedded_app.close() out, err = capfd.readouterr() + assert f"ansys-mechanical --project-file" in out + assert f"--graphical --revision {str(embedded_app.version)}" in out assert f"Opened a new mechanical session based on {mechdb_path}" in out From 66db83ac3ff6986ffe41e250cc27253cc64bf03f Mon Sep 17 00:00:00 2001 From: klmcadams <58492561+klmcadams@users.noreply.github.com> Date: Fri, 13 Sep 2024 14:55:38 -0400 Subject: [PATCH 32/35] fix return --- src/ansys/mechanical/core/embedding/ui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ansys/mechanical/core/embedding/ui.py b/src/ansys/mechanical/core/embedding/ui.py index 8f982a1b8..8a9c31f06 100644 --- a/src/ansys/mechanical/core/embedding/ui.py +++ b/src/ansys/mechanical/core/embedding/ui.py @@ -86,7 +86,7 @@ def open_original(self, app: "ansys.mechanical.core.embedding.App", mechdb_file: def graphically_launch_temp( self, app: "ansys.mechanical.core.embedding.App", temp_file: Path - ) -> Popen | str: + ) -> typing.Union[Popen, str]: """Launch the GUI for the mechdb file with a temporary name from save_temp_copy(). Parameters From f8f913fb803c4f2e58753795d5b92efb57915e32 Mon Sep 17 00:00:00 2001 From: klmcadams <58492561+klmcadams@users.noreply.github.com> Date: Fri, 13 Sep 2024 15:11:20 -0400 Subject: [PATCH 33/35] fix mock launcher --- tests/embedding/test_app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/embedding/test_app.py b/tests/embedding/test_app.py index c5ca57630..daadc6e65 100644 --- a/tests/embedding/test_app.py +++ b/tests/embedding/test_app.py @@ -361,7 +361,7 @@ def open_original(self, app, mechdb_file): """Open the original mechdb file from save_original().""" self.ops.append("open_orig_mechdb") - def graphically_launch_temp(self, app, mechdb_file, temp_file): + def graphically_launch_temp(self, app, temp_file): """Launch the GUI for the mechdb file with a temporary name from save_temp_copy().""" self.ops.append("launch_temp_mechdb") @@ -404,7 +404,7 @@ def test_launch_gui(embedded_app, tmp_path: pytest.TempPathFactory, capfd): @pytest.mark.embedding -def test_launch_gui_exception(embedded_app, tmp_path: pytest.TempPathFactory, capfd): +def test_launch_gui_exception(embedded_app): """Test an exception is raised when the embedded_app has not been saved yet.""" # Assert that an exception is raised with pytest.raises(Exception): From a19a0dacec51511a81df676687db54ce7a20e406 Mon Sep 17 00:00:00 2001 From: klmcadams <58492561+klmcadams@users.noreply.github.com> Date: Mon, 16 Sep 2024 12:30:29 -0400 Subject: [PATCH 34/35] adjust return in graphically_launch_temp function --- src/ansys/mechanical/core/embedding/ui.py | 28 +++++++++++------------ 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/ansys/mechanical/core/embedding/ui.py b/src/ansys/mechanical/core/embedding/ui.py index 8a9c31f06..fef35a1e5 100644 --- a/src/ansys/mechanical/core/embedding/ui.py +++ b/src/ansys/mechanical/core/embedding/ui.py @@ -101,23 +101,23 @@ def graphically_launch_temp( subprocess.Popen The subprocess that launches the GUI for the temporary mechdb file. """ - # The subprocess that uses ansys-mechanical to launch the GUI of the temporary mechdb file + # The ansys-mechanical command to launch the GUI in a subprocess + args = [ + "ansys-mechanical", + "--project-file", + temp_file, + "--graphical", + "--revision", + str(app.version), + ] if not self._dry_run: - process = Popen( - [ - "ansys-mechanical", - "--project-file", - temp_file, - "--graphical", - "--revision", - str(app.version), - ], - ) - + # The subprocess that uses ansys-mechanical to launch the GUI of the temporary + # mechdb file + process = Popen(args) return process else: - return f"ansys-mechanical --project-file {temp_file} --graphical \ ---revision {str(app.version)}" + # Return a string containing the args + return " ".join(args) def _cleanup_gui(self, process: Popen, temp_mechdb_path: Path) -> None: """Remove the temporary mechdb file and folder when the GUI is closed. From cba9ec55c2aeb92512e1bc638e7c69729ba2891c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 16:32:05 +0000 Subject: [PATCH 35/35] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/embedding/test_app.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/embedding/test_app.py b/tests/embedding/test_app.py index eda416072..a90d3ac7f 100644 --- a/tests/embedding/test_app.py +++ b/tests/embedding/test_app.py @@ -454,4 +454,3 @@ def test_app_execute_script_from_file(embedded_app, rootdir, printer): succes_script_path = os.path.join(rootdir, "tests", "scripts", "run_python_success.py") result = embedded_app.execute_script_from_file(succes_script_path) assert result == "test" -