Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/changelog.d/3758.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fix: Improve error handling in *GET method and enhance output logging
2 changes: 1 addition & 1 deletion src/ansys/mapdl/core/mapdl_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,7 @@ def directory(self) -> str:

elif not self._path:
raise MapdlRuntimeError(
f"MAPDL could provide a path using /INQUIRE or the cached path ('{self._path}')."
f"MAPDL could NOT provide a path using /INQUIRE or the cached path ('{self._path}')."
)

return self._path
Expand Down
2 changes: 0 additions & 2 deletions src/ansys/mapdl/core/mapdl_extended.py
Original file line number Diff line number Diff line change
Expand Up @@ -2257,8 +2257,6 @@ def load_table(
if not self._local:
self.upload(filename, progress_bar=False)
filename = base_name
# skip the first line its a header we wrote in np.savetxt
self.tread(name, filename, nskip=1, mute=True)

# skip the first line its a header we wrote in np.savetxt
self.tread(name, filename, nskip=1, mute=True)
Expand Down
35 changes: 27 additions & 8 deletions src/ansys/mapdl/core/mapdl_grpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,11 @@ def __init__(

self._create_session()

# Caching platform.
self._log.info(
f"Connected to MAPDL server running at {self._hostname} on {self.ip}:{self.port} on {self.platform} OS"
)

def _after_run(self, command: str) -> None:
if command[:4].upper() == "/CLE":
# We have reset the database, so we need to create a new session id
Expand Down Expand Up @@ -2245,8 +2250,15 @@ def _get(
cmd = f"{entity},{entnum},{item1},{it1num},{item2},{it2num},{item3}, {it3num}, {item4}, {it4num}"

# not threadsafe; don't allow multiple get commands
timeout_ = kwargs.pop("timeout", 2)
timeout = time.time() + timeout_
while self._get_lock:
time.sleep(0.001)
if time.time() > timeout:
raise MapdlRuntimeError(
f"PyMAPDL couldn't obtain 'get_lock' in {timeout_} seconds "
"and PyMAPDL cannot issue multiple get commands simultaneously."
)

self._get_lock = True
try:
Expand All @@ -2259,16 +2271,23 @@ def _get(
"The 'grpc' get method seems to have failed. Trying old implementation for more verbose output."
)

try:
out = self.run("*GET,__temp__," + cmd)
return float(out.split("VALUE=")[1].strip())
out = self.run("*GET,__temp__," + cmd)
self._log.debug(f"Default *get output:\n{out}")

except MapdlRuntimeError as e:
# Get can thrown some errors, in that case, they are caught in the default run method.
raise e
if out:
from ansys.mapdl.core.misc import is_float

except (IndexError, ValueError):
raise MapdlError("Error when processing '*get' request output.")
if "VALUE=" in out:
out = out.split("VALUE=")[1].strip()

if is_float(out):
return float(out)
else:
return out
elif out is None:
return None
else:
raise MapdlError(f"Error when processing '*get' request output.\n{out}")

if getresponse.type == 1:
return getresponse.dval
Expand Down
6 changes: 6 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,8 @@ def run_before_and_after_tests(
mapdl.prep7()

# Check resetting state
assert not mapdl._store_commands
assert mapdl._stub is not None
assert prev == mapdl.is_local
assert not mapdl.exited, "MAPDL is exited after the test. It should have not!"
assert not mapdl._mapdl_on_hpc, "Mapdl class is on HPC mode. It should not!"
Expand Down Expand Up @@ -666,6 +668,10 @@ def _patch_method(method):
(_patch_method("_subscribe_to_channel"), _returns("")),
(_patch_method("_run_at_connect"), _returns("")),
(_patch_method("_exit_mapdl"), _returns(None)),
(
_patch_method("_check_mapdl_os"),
_returns("linux" if os.name == "posix" else "win"),
),
# non-mapdl methods
("socket.gethostbyname", _returns("123.45.67.99")),
(
Expand Down
3 changes: 3 additions & 0 deletions tests/test_launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -1999,6 +1999,9 @@ def test_args_pass(monkeypatch, arg, value, method):
meth = getattr(mapdl, method)
assert meth == value

mapdl._ctrl = lambda *args, **kwargs: None
del mapdl


def test_check_has_mapdl():
if TESTING_MINIMAL:
Expand Down
2 changes: 1 addition & 1 deletion tests/test_mapdl.py
Original file line number Diff line number Diff line change
Expand Up @@ -2661,7 +2661,7 @@ def test_directory_setter(mapdl, cleared):
mapdl._path = ""
with pytest.raises(
MapdlRuntimeError,
match="MAPDL could provide a path using /INQUIRE or the cached path",
match="MAPDL could NOT provide a path using /INQUIRE or the cached path",
):
mapdl.directory

Expand Down
105 changes: 105 additions & 0 deletions tests/test_mapdl_grpc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Copyright (C) 2016 - 2025 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.

from unittest.mock import patch

from ansys.api.mapdl.v0 import mapdl_pb2 as pb_types
import pytest

from ansys.mapdl.core.mapdl_grpc import MapdlRuntimeError


def test_get_float(mapdl):
response = pb_types.GetResponse(type=1, dval=123.456)

with patch.object(mapdl, "_stub", autospec=True) as mock_stub:
mock_stub.Get.return_value = response
result = mapdl._get(entity="NODE", entnum="1", item1="U", it1num=1)
assert result == 123.456


def test_get_string(mapdl):
response = pb_types.GetResponse(type=2, sval="test_string")

with patch.object(mapdl, "_stub", autospec=True) as mock_stub:
mock_stub.Get.return_value = response
result = mapdl._get(entity="NODE", entnum="1", item1="U", it1num=1)
assert result == "test_string"


def test_get_fallback(mapdl):
response = pb_types.GetResponse(type=0)

with (
patch.object(mapdl, "_stub", autospec=True) as mock_stub,
patch.object(mapdl, "run") as mock_run,
):

mock_run.return_value = "VALUE= 789.012"
mock_stub.Get.return_value = response

result = mapdl._get(entity="NODE", entnum="1", item1="U", it1num=1)
assert result == 789.012


def test_get_fallback_string(mapdl):
response = pb_types.GetResponse(type=0)

with (
patch.object(mapdl, "_stub", autospec=True) as mock_stub,
patch.object(mapdl, "run") as mock_run,
):

mock_run.return_value = "VALUE= test_value"
mock_stub.Get.return_value = response

result = mapdl._get(entity="NODE", entnum="1", item1="U", it1num=1)
assert result == "test_value"


def test_get_lock(mapdl):
mapdl._get_lock = True

with pytest.raises(MapdlRuntimeError):
mapdl._get(entity="NODE", entnum="1", item1="U", it1num=1, timeout=0.5)

mapdl._get_lock = False


def test_get_invalid_response_type(mapdl):
response = pb_types.GetResponse(type=3)

with patch.object(mapdl, "_stub", autospec=True) as mock_stub:
mock_stub.Get.return_value = response

with pytest.raises(MapdlRuntimeError):
mapdl._get(entity="NODE", entnum="1", item1="U", it1num=1)


def test_get_non_interactive_mode(mapdl):
mapdl._store_commands = True

with pytest.raises(MapdlRuntimeError):
mapdl._get(entity="NODE", entnum="1", item1="U", it1num=1)

# reset
mapdl._store_commands = False
Loading