diff --git a/doc/changelog.d/1038.added.md b/doc/changelog.d/1038.added.md new file mode 100644 index 000000000..093e54d4f --- /dev/null +++ b/doc/changelog.d/1038.added.md @@ -0,0 +1 @@ +add poster method that raises an exception \ No newline at end of file diff --git a/src/ansys/mechanical/core/embedding/poster.py b/src/ansys/mechanical/core/embedding/poster.py index 93a994cc0..c9ccd1ae8 100644 --- a/src/ansys/mechanical/core/embedding/poster.py +++ b/src/ansys/mechanical/core/embedding/poster.py @@ -25,6 +25,19 @@ import typing +class PosterError(Exception): + """Class which holds errors from the background thread posting system.""" + + def __init__(self, error: Exception): + """Create an instance to hold the given error.""" + self._error = error + + @property + def error(self) -> Exception: + """Get the underlying exception.""" + return self._error + + class Poster: """Class which can post a python callable function to Mechanical's main thread.""" @@ -37,7 +50,26 @@ def __init__(self): self._poster = Ans.Common.WB1ManagedUtils.TaskPoster - def post(self, callable: typing.Callable): + def try_post(self, callable: typing.Callable) -> typing.Any: + """Post the callable to Mechanical's main thread. + + This does the same thing as `post` but if `callable` + raises an exception, try_post will raise the same + exception to the caller of `try_post`. + """ + + def wrapped(): + try: + return callable() + except Exception as e: + return PosterError(e) + + result = self.post(wrapped) + if isinstance(result, PosterError): + raise result.error + return result + + def post(self, callable: typing.Callable) -> typing.Any: """Post the callable to Mechanical's main thread. The main thread needs to be receiving posted messages diff --git a/tests/embedding/test_app.py b/tests/embedding/test_app.py index 4d4f01a90..910d83c8d 100644 --- a/tests/embedding/test_app.py +++ b/tests/embedding/test_app.py @@ -169,6 +169,7 @@ def test_app_poster(embedded_app): poster = embedded_app.poster name = [] + error = [] def change_name_async(poster): """Change_name_async will run a background thread @@ -182,9 +183,19 @@ def get_name(): def change_name(): embedded_app.DataModel.Project.Name = "foo" + def raise_ex(): + raise Exception("Exception") + name.append(poster.post(get_name)) poster.post(change_name) + try: + poster.try_post() + except Exception as e: + error.append(e) + + name.append(poster.try_post(get_name)) + import threading change_name_thread = threading.Thread(target=change_name_async, args=(poster,)) @@ -196,9 +207,11 @@ def change_name(): # thread, e.g. `change_name` that was posted by the poster. utils.sleep(400) change_name_thread.join() - assert len(name) == 1 + assert len(name) == 2 assert name[0] == "Project" + assert name[1] == "foo" assert embedded_app.DataModel.Project.Name == "foo" + assert len(error) == 1 @pytest.mark.embedding