Skip to content

Commit

Permalink
Merge branch 'main' into fix/new-lines-in-test-arguments
Browse files Browse the repository at this point in the history
  • Loading branch information
germa89 authored Dec 13, 2024
2 parents 9b523d6 + 3c81f2d commit ed108db
Show file tree
Hide file tree
Showing 16 changed files with 163 additions and 36 deletions.
1 change: 1 addition & 0 deletions doc/changelog.d/3596.miscellaneous.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
feat: redirect MAPDL console output to a file
1 change: 1 addition & 0 deletions doc/changelog.d/3601.dependencies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
build: bump grpcio from 1.68.0 to 1.68.1 in the grpc-deps group
1 change: 1 addition & 0 deletions doc/changelog.d/3603.dependencies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
build: bump pytest from 8.3.3 to 8.3.4 in the testing group
1 change: 1 addition & 0 deletions doc/changelog.d/3604.dependencies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
build: bump pyfakefs from 5.7.1 to 5.7.2
1 change: 1 addition & 0 deletions doc/changelog.d/3606.miscellaneous.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
feat: avoid errors when retrieving invalid routine
1 change: 1 addition & 0 deletions doc/changelog.d/3612.dependencies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
build: bump the core group across 1 directory with 3 updates
1 change: 1 addition & 0 deletions doc/changelog.d/3617.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fix: exit getting frozen if routine is not finished
1 change: 1 addition & 0 deletions doc/changelog.d/3619.maintenance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
build: bump numpy from 2.1.3 to 2.2.0 in the minimal group
2 changes: 1 addition & 1 deletion minimum_requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
ansys-api-mapdl==0.5.2
numpy==2.1.3
numpy==2.2.0
platformdirs==4.3.6
psutil==6.1.0
pyansys-tools-versioning==0.6.0
16 changes: 8 additions & 8 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,34 +55,34 @@ jupyter = [

tests = [
"ansys-dpf-core==0.10.1",
"ansys-tools-visualization-interface==0.5.0",
"ansys-tools-visualization-interface==0.6.0",
"autopep8==2.3.1",
"matplotlib==3.9.2",
"matplotlib==3.9.3",
"pandas==2.2.3",
"pyansys-tools-report==0.8.1",
"pyfakefs==5.7.1",
"pyfakefs==5.7.2",
"pyiges[full]==0.3.1",
"pytest-cov==6.0.0",
"pytest-memprof<0.3.0",
"pytest-pyvista==0.1.9",
"pytest-random-order==1.1.1",
"pytest-rerunfailures==15.0",
"pytest==8.3.3",
"pytest==8.3.4",
"scipy==1.14.1",
"vtk==9.3.1",
]
doc = [
"ansys-dpf-core==0.10.1",
"ansys-mapdl-reader==0.54.2",
"ansys-sphinx-theme==1.2.2",
"ansys-tools-visualization-interface==0.5.0",
"grpcio==1.68.0",
"ansys-sphinx-theme==1.2.3",
"ansys-tools-visualization-interface==0.6.0",
"grpcio==1.68.1",
"imageio-ffmpeg==0.5.1",
"imageio==2.36.1",
"jupyter_sphinx==0.5.3",
"jupyter==1.1.1",
"jupyterlab>=3.2.8",
"matplotlib==3.9.2",
"matplotlib==3.9.3",
"nbformat==5.10.4",
"numpydoc==1.8.0",
"pandas==2.2.3",
Expand Down
49 changes: 39 additions & 10 deletions src/ansys/mapdl/core/launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
import subprocess # nosec B404
import threading
import time
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Union
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple, Union
import warnings

import psutil
Expand Down Expand Up @@ -115,6 +115,7 @@ def version_from_path(*args, **kwargs):
"license_type",
"log_apdl",
"loglevel",
"mapdl_output",
"mode",
"nproc",
"override",
Expand Down Expand Up @@ -437,6 +438,7 @@ def launch_grpc(
run_location: str = None,
env_vars: Optional[Dict[str, str]] = None,
launch_on_hpc: bool = False,
mapdl_output: Optional[str] = None,
) -> subprocess.Popen:
"""Start MAPDL locally in gRPC mode.
Expand All @@ -456,6 +458,9 @@ def launch_grpc(
If running on an HPC, this needs to be :class:`True` to avoid the
temporary file creation on Windows.
mapdl_output : str, optional
Whether redirect MAPDL console output (stdout and stderr) to a file.
Returns
-------
subprocess.Popen
Expand Down Expand Up @@ -487,6 +492,13 @@ def launch_grpc(
"\n============"
)

if mapdl_output:
stdout = open(str(mapdl_output), "wb", 0)
stderr = subprocess.STDOUT
else:
stdout = subprocess.PIPE
stderr = subprocess.PIPE

if os.name == "nt":
# getting tmp file name
if not launch_on_hpc:
Expand All @@ -505,8 +517,8 @@ def launch_grpc(
shell=shell, # sbatch does not work without shell.
cwd=run_location,
stdin=subprocess.DEVNULL,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
stdout=stdout,
stderr=stderr,
env_vars=env_vars,
)

Expand Down Expand Up @@ -554,7 +566,7 @@ def check_mapdl_launch(

if os.name == "posix" and not ON_WSL:
LOG.debug("Checking if gRPC server is alive.")
_check_server_is_alive(stdout_queue, run_location, timeout)
_check_server_is_alive(stdout_queue, timeout)

except MapdlDidNotStart as e: # pragma: no cover
msg = (
Expand Down Expand Up @@ -596,7 +608,11 @@ def _check_file_error_created(run_location, timeout):
raise MapdlDidNotStart(msg)


def _check_server_is_alive(stdout_queue, run_location, timeout):
def _check_server_is_alive(stdout_queue, timeout):
if not stdout_queue:
LOG.debug("No STDOUT queue. Not checking MAPDL this way.")
return

t0 = time.time()
empty_attemps = 3
empty_i = 0
Expand Down Expand Up @@ -629,6 +645,9 @@ def _check_server_is_alive(stdout_queue, run_location, timeout):


def _get_std_output(std_queue, timeout=1):
if not std_queue:
return [None]

lines = []
reach_empty = False
t0 = time.time()
Expand All @@ -642,10 +661,15 @@ def _get_std_output(std_queue, timeout=1):
return lines


def _create_queue_for_std(std):
def _create_queue_for_std(
std: subprocess.PIPE,
) -> Tuple[Optional[Queue[str]], Optional[threading.Thread]]:
"""Create a queue and thread objects for a given PIPE std"""
if not std:
LOG.debug("No STDOUT. Not checking MAPDL this way.")
return None, None

def enqueue_output(out, queue):
def enqueue_output(out: subprocess.PIPE, queue: Queue[str]) -> None:
try:
for line in iter(out.readline, b""):
queue.put(line)
Expand All @@ -655,16 +679,16 @@ def enqueue_output(out, queue):
# ValueError: PyMemoryView_FromBuffer(): info -> buf must not be NULL
pass

q = Queue()
t = threading.Thread(target=enqueue_output, args=(std, q))
q: Queue[str] = Queue()
t: threading.Thread = threading.Thread(target=enqueue_output, args=(std, q))
t.daemon = True # thread dies with the program
t.start()

return q, t


def launch_remote_mapdl(
version: str = None,
version: Optional[str] = None,
cleanup_on_exit: bool = True,
) -> MapdlGrpc:
"""Start MAPDL remotely using the product instance management API.
Expand Down Expand Up @@ -1020,6 +1044,7 @@ def launch_mapdl(
version: Optional[Union[int, str]] = None,
running_on_hpc: bool = True,
launch_on_hpc: bool = False,
mapdl_output: Optional[str] = None,
**kwargs: Dict[str, Any],
) -> Union[MapdlGrpc, "MapdlConsole"]:
"""Start MAPDL locally.
Expand Down Expand Up @@ -1205,6 +1230,9 @@ def launch_mapdl(
to specify the scheduler arguments as a string or as a dictionary.
For more information, see :ref:`ref_hpc_slurm`.
mapdl_output : str, optional
Redirect the MAPDL console output to a given file.
kwargs : dict, Optional
These keyword arguments are interface-specific or for
development purposes. For more information, see Notes.
Expand Down Expand Up @@ -1575,6 +1603,7 @@ def launch_mapdl(
run_location=args["run_location"],
env_vars=env_vars,
launch_on_hpc=args.get("launch_on_hpc"),
mapdl_output=args.get("mapdl_output"),
)

if args["launch_on_hpc"]:
Expand Down
32 changes: 17 additions & 15 deletions src/ansys/mapdl/core/mapdl_grpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -650,7 +650,7 @@ def _read_stds(self):
_get_std_output, # Avoid circular import error
)

if self._mapdl_process is None:
if self._mapdl_process is None or not self._mapdl_process.stdout:
return

self._log.debug("Reading stdout")
Expand Down Expand Up @@ -1187,12 +1187,13 @@ def _exit_mapdl(self, path: str = None) -> None:
if self._local:
self._cache_pids() # Recache processes

if os.name == "nt":
self._kill_server()
self._exit_mapdl_server()

self._close_process()

self._remove_lock_file(path)
else:
self._kill_server()
self._exit_mapdl_server()

def _remove_temp_dir_on_exit(self, path=None):
"""Removes the temporary directory created by the launcher.
Expand All @@ -1218,7 +1219,7 @@ def _remove_temp_dir_on_exit(self, path=None):
tmp_dir,
)

def _kill_server(self):
def _exit_mapdl_server(self):
"""Call exit(0) on the server.
Notes
Expand All @@ -1231,14 +1232,18 @@ def _kill_server(self):
if self._exited:
return

if (
self._version and self._version >= 24.2
): # We can't use the non-cached version because of recursion error.
# self.run("/EXIT,NOSAVE,,,,,SERVER")
# Default
if self._version is None or self._version < 24.1:
self._ctrl("EXIT")
else:

elif self._version >= 24.1:
# We can't use the non-cached version because of recursion error.
# self.run("/EXIT,NOSAVE,,,,,SERVER")
self.finish()
self._ctrl("EXIT")

return

def _kill_process(self):
"""Kill process stored in self._mapdl_process"""
if self._mapdl_process is not None:
Expand Down Expand Up @@ -1287,15 +1292,12 @@ def _close_process(self, timeout=2): # pragma: no cover
Notes
-----
This is effectively the only way to completely close down MAPDL locally on
linux. Just killing the server with ``_kill_server`` leaves orphaned
linux. Just killing the server with ``_exit_mapdl_server`` leaves orphaned
processes making this method ineffective for a local instance of MAPDL.
"""
self._log.debug("Closing processes")
if self._local:
# killing server process
self._kill_server()

# killing main process (subprocess)
self._kill_process()

Expand Down Expand Up @@ -2699,7 +2701,7 @@ def _download_as_raw(self, target_name: str) -> str:
@property
def is_alive(self) -> bool:
"""True when there is an active connect to the gRPC server"""
if self.channel_state not in ["IDLE", "READY"]:
if self.channel_state not in ["IDLE", "READY", None]:
self._log.debug(
"MAPDL instance is not alive because the channel is not 'IDLE' o 'READY'."
)
Expand Down
9 changes: 8 additions & 1 deletion src/ansys/mapdl/core/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,14 @@ def routine(self) -> str:
>>> mapdl.parameters.routine
'PREP7'
"""
value = self._mapdl.get_value("ACTIVE", item1="ROUT")
value = int(self._mapdl.get_value("ACTIVE", item1="ROUT"))
if value not in ROUTINE_MAP:
self._mapdl.logger.info(
f"Getting a valid routine number failed. Routine obtained is {value}. Executing 'FINISH'."
)
self._mapdl.finish()
value = 0

return ROUTINE_MAP[int(value)]

@property
Expand Down
57 changes: 57 additions & 0 deletions tests/test_launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -1947,3 +1947,60 @@ def raising():
@patch("ansys.mapdl.core.launcher.check_valid_ansys", raising)
def test_check_has_mapdl_failed():
assert check_has_mapdl() is False


@requires("local")
@patch("ansys.mapdl.core.launcher._is_ubuntu", lambda *args, **kwargs: True)
@patch("ansys.mapdl.core.launcher.check_mapdl_launch", lambda *args, **kwargs: None)
def test_mapdl_output_pass_arg(tmpdir):
def submitter(*args, **kwargs):
from _io import FileIO

# Checking we are passing the arguments
assert isinstance(kwargs["stdout"], FileIO)
assert kwargs["stderr"] is subprocess.STDOUT

return

with patch("ansys.mapdl.core.launcher.submitter", submitter) as mck_sub:
mapdl_output = os.path.join(tmpdir, "apdl.txt")
args = launch_mapdl(just_launch=True, mapdl_output=mapdl_output)

assert isinstance(args, list)


@requires("local")
@requires("nostudent")
def test_mapdl_output(tmpdir):
mapdl_output = os.path.join(tmpdir, "apdl.txt")
mapdl = launch_mapdl(mapdl_output=mapdl_output, port=50058)

assert os.path.exists(mapdl_output)

mapdl.prep7()
mapdl.exit(force=True)

with open(mapdl_output, "r") as fid:
content = fid.read()

assert "Beta activation of the GRPC server." in content
assert "### START GRPC SERVER ###" in content
assert "Server listening on" in content


def test_check_server_is_alive_no_queue():
from ansys.mapdl.core.launcher import _check_server_is_alive

assert _check_server_is_alive(None, 30) is None


def test_get_std_output_no_queue():
from ansys.mapdl.core.launcher import _get_std_output

assert _get_std_output(None, 30) == [None]


def test_create_queue_for_std_no_queue():
from ansys.mapdl.core.launcher import _create_queue_for_std

assert _create_queue_for_std(None) == (None, None)
Loading

0 comments on commit ed108db

Please sign in to comment.