Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
35391ca
ci: add MapdlInProcessRunner and related tests for Python execution i…
germa89 Aug 19, 2025
5fbe518
chore: adding changelog file 4185.miscellaneous.md [dependabot-skip]
pyansys-ci-bot Aug 19, 2025
52e68da
fix: remove redundant import of ON_LOCAL in test fixture
germa89 Aug 19, 2025
9990f13
fix: correct condition for skipping InProcess tests based on environm…
germa89 Aug 19, 2025
86d8073
Update tests/test_inprocess.py
germa89 Aug 19, 2025
533be47
fix: streamline subprocess execution in MapdlInProcessRunner
germa89 Aug 19, 2025
c4b3bef
fix: correct condition for skipping InProcess tests based on environm…
germa89 Aug 19, 2025
cc926d5
fix: add support for additional switches in subprocess execution
germa89 Aug 19, 2025
cb34418
fix: improve command execution in MapdlInProcessRunner and update tes…
germa89 Aug 20, 2025
39148d7
fix: update skip message for InProcess tests to improve clarity
germa89 Aug 20, 2025
537a789
Merge remote-tracking branch 'origin/main' into ci/adding-inprocess-t…
germa89 Aug 20, 2025
5b22f01
fix: enhance MapdlInProcessRunner with virtual environment detection …
germa89 Aug 21, 2025
a0274dc
fix: enhance requirement checking and add virtual environment tests
germa89 Aug 21, 2025
4c5e970
fix: keep only tests from mapdl, from pymapdl and check python path w…
valallansys Aug 22, 2025
448e2be
ci: auto fixes from pre-commit.com hooks.
pre-commit-ci[bot] Aug 22, 2025
7aff019
fix: update MapdlInProcessRunner to include version parameter and adj…
germa89 Aug 22, 2025
ccf7276
fix: Set venv to the one we're using for pymapdl
valallansys Aug 22, 2025
c6445a3
ci: auto fixes from pre-commit.com hooks.
pre-commit-ci[bot] Aug 22, 2025
cf68c90
fix: add mapdl_inprocess parameter to test_start_python_from_pymapdl …
germa89 Aug 28, 2025
0a914af
chore: adding changelog file 4185.maintenance.md [dependabot-skip]
pyansys-ci-bot Aug 28, 2025
970fb24
fix: Test correction for Windows
valallansys Sep 2, 2025
9b326f0
ci: auto fixes from pre-commit.com hooks.
pre-commit-ci[bot] Sep 2, 2025
47da5e7
Merge branch 'main' into ci/adding-inprocess-testing
germa89 Sep 29, 2025
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/4185.maintenance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add InProcess testing module
220 changes: 220 additions & 0 deletions tests/test_inprocess.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
# 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.

import os
import subprocess
import sys
from typing import TYPE_CHECKING

import pytest

if TYPE_CHECKING:
from ansys.mapdl.core import Mapdl

from conftest import ON_LOCAL


class MapdlInProcessRunner:
def __init__(self, wdir: str, version: float, exec_path: str | None = None) -> None:
self.wdir = str(wdir)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this runner does not use the current pymapdl library within *PYTHON, which somewhat defeats the purpose of the test. The purpose of test is to answer the following question:

Does the *PYTHON feature work as expected using the current pymapdl package (which is the python environment from which pymapdl is installed and used by pytest). In this context, only python3.10 should be used for this kind of test and the python environment used by the test should be used also by *PYTHON.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

*PYTHON only works with Python3.10?? I dont think it is that restrictive is it? @valallansys ??

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not surprised but to be fair I've never tried earlier versions, we set up the proper python venv for it to work. I think Mohamed wants the creation of a python venv with the *PYT requirements during the test and set up via the environment variable MAPDL_PYTHON_ENV or via /PYC when the mapdl version is recent enough.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's supposed to be fixed now, I kept the check python path to make sure we're using the right python used for pymapdl, but had to change from sys.executable because it wouldn't work without a virtual env in an embedded python interpreter

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@germa89 I confirm it only works with python 3.10

self.version = version
self.completed_process: subprocess.CompletedProcess[bytes] | None = None
self._exec_path: str | None = exec_path
self._output: str | None = None

# Detecting the venv
if sys.prefix == sys.base_prefix:
pytest.warns("Running from global python interpreter is not recommended.")
os.environ["MAPDL_PYTHON_ENV"] = sys.prefix

@property
def exec_path(self) -> str:
if self._exec_path is None:

self._exec_path = os.getenv("PYMAPDL_MAPDL_EXEC")
if self._exec_path is None:
from ansys.tools.path import get_mapdl_path

version: float | None = (
float(os.getenv("PYMAPDL_MAPDL_VERSION", 0)) or None
)
self._exec_path = get_mapdl_path(version=version)

if self._exec_path is None:
raise ValueError(
"MAPDL executable path is not set. Set the PYMAPDL_MAPDL_EXEC "
"environment variable or use get_mapdl_path() to find it."
)

return self._exec_path

@property
def stdout(self) -> str | None:
"""Return the standard output of the completed process. If the process fail, this returns None."""
if self.completed_process:
return self.completed_process.stdout.decode()

@property
def stderr(self) -> str | None:
"""Return the standard error output of the completed process. If the process fail, this returns None."""
if self.completed_process:
return self.completed_process.stderr.decode()

@property
def status_code(self) -> float | None:
"""Return the status code of the completed process. If the process fail, this returns None."""
if self.completed_process:
return float(self.completed_process.returncode)

@property
def exist_output(self) -> bool:
"""Return True if the output file exists, False otherwise."""
return os.path.exists(os.path.join(self.wdir, "out.out"))

def output(self) -> str | None:
if not self._output and self.exist_output:
with open(os.path.join(self.wdir, "out.out"), "r") as f:
self._output = f.read()
return self._output

def run(self, cmds: str) -> str | None:
"""Run commands in MAPDL on batch mode."""
with open(os.path.join(self.wdir, "input.mac"), "w") as f:
f.write(cmds)

exec_path = self.exec_path
# Security: Validate the executable path
if not os.path.isabs(exec_path) or not os.path.isfile(exec_path):
raise ValueError(
"MAPDL executable path must be an absolute path to an existing file."
)

try:
args = [
exec_path,
"-b",
"-i",
"input.mac",
"-o",
"out.out",
os.getenv("PYMAPDL_ADDITIONAL_SWITCHES", ""),
]

self.completed_process = subprocess.run(
args=args,
cwd=self.wdir,
check=True,
capture_output=True,
# # it does not support shell=True, because it does not
# # generate the out.out file
# # TODO: Why shell should be false in order to generate the output file?
shell=False, # nosec B603
)

except subprocess.CalledProcessError as e:
error_msg = (
f"MAPDL execution failed with return code {e.returncode}.\n"
f"Command: {' '.join(e.cmd)}\n"
f"Stdout:\n{e.stdout.decode() if e.stdout else ''}\n"
f"Stderr:\n{e.stderr.decode() if e.stderr else ''}\n"
)
pytest.fail(error_msg)

return self.output()


@pytest.fixture()
def mapdl_version(mapdl: "Mapdl"):
return mapdl.version


@pytest.fixture()
def mapdl_inprocess(mapdl_version: float, tmp_path: str) -> MapdlInProcessRunner:

# check if MAPDL has *PYTHON
if mapdl_version < 25.2:
pytest.skip("To test InProcess interface MAPDL 25.2 or higher is required")

if not ON_LOCAL:
pytest.skip("InProcess interface can only be tested on local machines")

if not (os.getenv("TEST_INPROCESS", "").lower() == "true"):
pytest.skip("Set TEST_INPROCESS environment variable to run them.")

mapdl_inprocess = MapdlInProcessRunner(tmp_path, version=mapdl_version)

return mapdl_inprocess


def test_python_path(mapdl_inprocess: MapdlInProcessRunner):
"""This test makes sure we are using the correct Python venv"""
cmds = """
/com, Testing Python path
*PYTHON
import shutil
print(shutil.which("python"))
*ENDPY
/com, test ends
"""
output_content = mapdl_inprocess.run(cmds)
assert mapdl_inprocess.status_code == 0
assert output_content is not None
assert "Testing Python path" in output_content
assert sys.executable.replace("\\\\", "\\").lower() in output_content.lower()
assert "test ends" in output_content


def test_start_python_from_pymapdl(
mapdl: "Mapdl", mapdl_inprocess: MapdlInProcessRunner
):
# calling mapdl_inprocess just to make sure we do not
# run it in PyMAPDL versions below 25.2
output = mapdl.input_strings(
"""
*PYTHON
print("Hello from MAPDL")
*ENDPY
"""
)

assert output is not None
assert "START PYTHON COMMAND BLOCK" in output
assert "Hello from MAPDL" in output
assert "END PYTHON COMMAND BLOCK" in output


def test_start_python_from_mapdl(mapdl_inprocess: MapdlInProcessRunner):
"""Test that MAPDL starts Python correctly."""
cmds = """
/com, Starting Python commands
*PYTHON
print('Hello from MAPDL!')
*ENDPY
/com, test ends
"""
output_content = mapdl_inprocess.run(cmds)
assert mapdl_inprocess.status_code == 0

assert output_content is not None
assert "Starting Python commands" in output_content
assert "Hello from MAPDL!" in output_content
assert "test ends" in output_content
Loading