Skip to content
Open
Show file tree
Hide file tree
Changes from 52 commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
fc8c72b
refactor: added the changes
mayankansys Mar 12, 2026
f94faa6
chore: adding changelog file 4990.miscellaneous.md [dependabot-skip]
pyansys-ci-bot Mar 12, 2026
25a3143
update
mayankansys Mar 17, 2026
2065946
Merge branch 'main' into refactor/couple_issues_launch_fluent
mayankansys Mar 17, 2026
c359acd
Merge branch 'refactor/couple_issues_launch_fluent' of https://github…
mayankansys Mar 17, 2026
9aea156
Merge branch 'main' into refactor/couple_issues_launch_fluent
mayankansys Mar 18, 2026
481f46f
Merge branch 'main' into refactor/couple_issues_launch_fluent
mayankansys Mar 20, 2026
8d64547
Merge branch 'main' into refactor/couple_issues_launch_fluent
mayankansys Mar 25, 2026
ae8f3cc
Merge branch 'main' into refactor/couple_issues_launch_fluent
mayankansys Apr 1, 2026
bf31ed6
Merge branch 'refactor/couple_issues_launch_fluent' of https://github…
mayankansys Jun 5, 2026
26d259f
updated the standalone_launcher
mayankansys Jun 16, 2026
bbcd2fd
resolved the conflict
mayankansys Jun 17, 2026
482b20f
update
mayankansys Mar 17, 2026
7c5fd0c
chore: adding changelog file 4990.miscellaneous.md [dependabot-skip]
pyansys-ci-bot Mar 12, 2026
b74d226
updated the standalone_launcher
mayankansys Jun 16, 2026
5ec458e
resolved the conflict
mayankansys Jun 17, 2026
28135e9
Merge branch 'main' into refactor/couple_issues_launch_fluent
mayankansys Jun 17, 2026
98945db
Merge branch 'main' into refactor/couple_issues_launch_fluent
mayankansys Jun 22, 2026
1bc029c
standalone launcher updated for the lightweight_mode, case_file and j…
mayankansys Jun 22, 2026
3563a14
Merge branch 'refactor/couple_issues_launch_fluent' of https://github…
mayankansys Jun 22, 2026
0a2861f
updated the condition logic & minor things
mayankansys Jun 22, 2026
16f320a
Merge branch
mayankansys Jun 29, 2026
a9b23b9
Merge branch 'main' of https://github.com/ansys/pyfluent
mayankansys Jul 5, 2026
3423fa1
Merge branch 'main' of https://github.com/ansys/pyfluent
mayankansys Jul 8, 2026
e0c0475
Merge branch 'main' of https://github.com/ansys/pyfluent
mayankansys Jul 8, 2026
765b43e
added the condition & changed docstring
mayankansys Jul 8, 2026
53399af
chore: adding changelog file 5242.miscellaneous.md [dependabot-skip]
pyansys-ci-bot Jul 8, 2026
b60bdf3
Merge branch 'main' of https://github.com/ansys/pyfluent
mayankansys Jul 9, 2026
b03e175
Merge branch 'main' into refactor/avoid_deleting_5145
mayankansys Jul 10, 2026
3da7114
resolved the conflict
mayankansys Jul 14, 2026
011c0d4
Merge branch 'main' into refactor/avoid_deleting_5145
mayankansys Jul 14, 2026
d8c4cf4
added the tests
mayankansys Jul 14, 2026
2c4c1c4
Merge branch 'refactor/avoid_deleting_5145' of https://github.com/ans…
mayankansys Jul 14, 2026
8e3563e
Merge branch 'main' into refactor/avoid_deleting_5145
mayankansys Jul 14, 2026
15713e8
Merge branch 'main' of https://github.com/ansys/pyfluent
mayankansys Jul 14, 2026
dd05b8e
Merge branch 'main' of https://github.com/ansys/pyfluent
mayankansys Jul 15, 2026
6df1995
Merge branch 'main' into refactor/avoid_deleting_5145
mayankansys Jul 15, 2026
a176ec0
Merge branch 'main' of https://github.com/ansys/pyfluent
mayankansys Jul 15, 2026
16ca1a4
added the condition & changed docstring
mayankansys Jul 8, 2026
3c05c73
chore: adding changelog file 5242.miscellaneous.md [dependabot-skip]
pyansys-ci-bot Jul 8, 2026
b5a18a3
resolved the conflict
mayankansys Jul 14, 2026
cf6eba3
added the tests
mayankansys Jul 14, 2026
d439a2f
Merge branch 'refactor/avoid_deleting_5145' of https://github.com/ans…
mayankansys Jul 15, 2026
1a75495
added the standalone_launcher as well.
mayankansys Jul 15, 2026
53d63ba
updated the tests
mayankansys Jul 15, 2026
fec936a
test update
mayankansys Jul 15, 2026
ca79203
updated the .exits & tmp_file.write()
mayankansys Jul 18, 2026
8389028
Merge branch 'main' into refactor/avoid_deleting_5145
mayankansys Jul 18, 2026
9c5c991
updated the test case
mayankansys Jul 20, 2026
e0925c9
updated changes
mayankansys Jul 20, 2026
6d876b0
updated the test file
mayankansys Jul 20, 2026
ff200a0
Merge branch 'main' into refactor/avoid_deleting_5145
mayankansys Jul 20, 2026
d50d644
added minor change in the fleutn_container due to which CI fails
mayankansys Jul 20, 2026
fca9825
Merge branch 'main' into refactor/avoid_deleting_5145
mayankansys Jul 20, 2026
7b74ebd
Merge branch 'main' into refactor/avoid_deleting_5145
mayankansys Jul 23, 2026
df73f8d
refactor: revert from preserve_info_file to cleanup_on_exit parameter
mayankansys Jul 25, 2026
dbf08cc
fix: fallback to the cleanup_on_exit
mayankansys Jul 25, 2026
9dc1751
updated the standalone_launcher file
mayankansys Jul 25, 2026
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/5242.miscellaneous.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Avoid deleting server-info file
24 changes: 17 additions & 7 deletions src/ansys/fluent/core/launcher/container_launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
CERTIFICATES_FOLDER_NOT_PROVIDED_AT_LAUNCH,
)
from ansys.fluent.core.launcher.fluent_container import (
_cleanup_on_exit_to_preserve_info_file_converter,
configure_container_dict,
dict_to_str,
start_fluent_container,
Expand All @@ -64,6 +65,7 @@
)
import ansys.fluent.core.launcher.watchdog as watchdog
from ansys.fluent.core.session import _parse_server_info_file
from ansys.fluent.core.utils.deprecate import deprecate_arguments
from ansys.fluent.core.utils.fluent_version import FluentVersion

if TYPE_CHECKING:
Expand Down Expand Up @@ -139,6 +141,12 @@ def _get_server_info_from_container(config_dict):
class DockerLauncher:
"""Instantiates Fluent session in container mode."""

@deprecate_arguments(
old_args="cleanup_on_exit",
new_args="preserve_info_file",
version="0.42.0",
converter=_cleanup_on_exit_to_preserve_info_file_converter,
)
def __init__(
self,
**kwargs: Unpack[ContainerArgs],
Expand Down Expand Up @@ -181,9 +189,9 @@ def __init__(
dry_run : bool, optional
If True, does not launch Fluent but prints configuration information instead. If dry running a
container start, this method will return the configured ``container_dict``. Defaults to False.
cleanup_on_exit : bool
Determines whether to shut down the connected Fluent session upon exit or when calling
the session's `exit()` method. Defaults to True.
preserve_info_file : bool, optional
If True, the server-info file will be preserved for debugging. If False, the server-info file
will be deleted when the session exits. Defaults to False.
start_transcript : bool
Indicates whether to start streaming the Fluent transcript in the client. Defaults to True;
streaming can be controlled via `transcript.start()` and `transcript.stop()` methods on the session object.
Expand Down Expand Up @@ -237,8 +245,8 @@ def __init__(
raise ValueError(CERTIFICATES_FOLDER_NOT_PROVIDED_AT_LAUNCH)

self.argvals, self.new_session = _get_argvals_and_session(kwargs)
if self.argvals.get("cleanup_on_exit") is None:
self.argvals["cleanup_on_exit"] = True
if self.argvals.get("preserve_info_file") is None:
self.argvals["preserve_info_file"] = False
if self.argvals.get("start_transcript") is None:
self.argvals["start_transcript"] = True
if "start_watchdog" not in self.argvals:
Expand Down Expand Up @@ -300,6 +308,7 @@ def __call__(
self.argvals["container_dict"],
self.argvals["start_timeout"],
compose_config=self._compose_config,
preserve_info_file=self.argvals["preserve_info_file"],
)

try:
Expand All @@ -317,6 +326,7 @@ def __call__(
self.argvals["container_dict"],
self.argvals["start_timeout"],
compose_config=self._compose_config,
preserve_info_file=self.argvals["preserve_info_file"],
)

allow_remote_host = (
Expand All @@ -330,7 +340,7 @@ def __call__(
certificates_folder=self.argvals["certificates_folder"],
insecure_mode=self.argvals["insecure_mode"],
file_transfer_service=self.file_transfer_service,
cleanup_on_exit=self.argvals["cleanup_on_exit"],
cleanup_on_exit=not self.argvals["preserve_info_file"],
slurm_job_id=self.argvals and self.argvals.get("slurm_job_id"),
inside_container=True,
container=container,
Expand All @@ -352,7 +362,7 @@ def __call__(
if not self._compose_config.is_compose:
if (
self.argvals["start_watchdog"] is None
and self.argvals["cleanup_on_exit"]
and not self.argvals["preserve_info_file"]
):
self.argvals["start_watchdog"] = True
if self.argvals["start_watchdog"]:
Expand Down
51 changes: 49 additions & 2 deletions src/ansys/fluent/core/launcher/fluent_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,32 @@
logger = logging.getLogger("pyfluent.launcher")


def _cleanup_on_exit_to_preserve_info_file_converter(
kwargs: dict[str, Any],
old_args: list[str],
new_args: list[str],
) -> dict[str, Any]:
"""
Convert deprecated cleanup_on_exit to preserve_info_file with inverted logic.

cleanup_on_exit=True (clean up the file) → preserve_info_file=False (don't preserve)
cleanup_on_exit=False (keep the file) → preserve_info_file=True (preserve)
"""
if "cleanup_on_exit" in kwargs:
cleanup_on_exit = kwargs.pop("cleanup_on_exit")
if "preserve_info_file" in kwargs:
warnings.warn(
"Both deprecated argument 'cleanup_on_exit' and new argument 'preserve_info_file' were provided. "
"Ignoring cleanup_on_exit.",
PyFluentDeprecationWarning,
stacklevel=3,
)
else:
# Invert the logic: cleanup_on_exit → preserve_info_file
kwargs["preserve_info_file"] = not cleanup_on_exit
return kwargs


class FluentImageNameTagNotSpecified(ValueError):
"""Raised when Fluent image name or image tag is not specified."""

Expand Down Expand Up @@ -471,11 +497,18 @@ def configure_container_dict(
)


@deprecate_arguments(
old_args="cleanup_on_exit",
new_args="preserve_info_file",
version="0.42.0",
converter=_cleanup_on_exit_to_preserve_info_file_converter,
)
def start_fluent_container(
args: list[str],
container_dict: dict | None = None,
start_timeout: int = 100,
compose_config: ComposeConfig | None = None,
preserve_info_file: bool = False,
) -> tuple[int, str, Any]:
"""Start a Fluent container.

Expand All @@ -490,6 +523,9 @@ def start_fluent_container(
seconds.
compose_config : ComposeConfig, optional
Configuration for Docker Compose, if using Docker Compose to launch the container.
preserve_info_file : bool, optional
If True, the server-info file will be preserved for debugging.
If False, the server-info file will be deleted when the container exits. Defaults to False.

Returns
-------
Expand All @@ -502,6 +538,8 @@ def start_fluent_container(
------
TimeoutError
If Fluent container launch reaches timeout.
ValueError
If ``remove_server_info_file`` and ``preserve_info_file`` have contradictory values.

Notes
-----
Expand Down Expand Up @@ -606,5 +644,14 @@ def start_fluent_container(
logger.error(f"Exception caught - {type(ex).__name__}: {ex}")
raise LaunchFluentError(launch_string) from ex
finally:
if remove_server_info_file and host_server_info_file.exists():
host_server_info_file.unlink()
# Validate that remove_server_info_file and cleanup_on_exit don't contradict.
# should_preserve is True if we want to keep the file
should_preserve = preserve_info_file or not remove_server_info_file
if remove_server_info_file and preserve_info_file:
raise ValueError(
"Contradictory file cleanup parameters: cannot both set remove_server_info_file=True "
"and preserve_info_file=True. Either remove the file or preserve it, not both."
)
# Delete file only if both flags agree not to preserve it.
if not should_preserve:
host_server_info_file.unlink(missing_ok=True)
28 changes: 19 additions & 9 deletions src/ansys/fluent/core/launcher/standalone_launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@
from ansys.fluent.core.launcher.error_handler import (
LaunchFluentError,
)
from ansys.fluent.core.launcher.fluent_container import (
_cleanup_on_exit_to_preserve_info_file_converter,
)
from ansys.fluent.core.launcher.launch_options import (
FluentMode,
UIMode,
Expand All @@ -68,6 +71,7 @@
_get_server_info_file_names,
)
import ansys.fluent.core.launcher.watchdog as watchdog
from ansys.fluent.core.utils.deprecate import deprecate_arguments
from ansys.fluent.core.utils.fluent_version import FluentVersion

if TYPE_CHECKING:
Expand Down Expand Up @@ -133,6 +137,12 @@ class StandaloneArgs(
class StandaloneLauncher:
"""Instantiates Fluent session in standalone mode."""

@deprecate_arguments(
old_args="cleanup_on_exit",
new_args="preserve_info_file",
version="0.42.0",
converter=_cleanup_on_exit_to_preserve_info_file_converter,
)
def __init__(
self,
**kwargs: Unpack[StandaloneArgs],
Expand Down Expand Up @@ -173,9 +183,9 @@ def __init__(
Additional command-line arguments for Fluent, formatted as they would be on the command line.
env : dict[str, str], optional
A mapping for modifying environment variables in Fluent. Defaults to ``None``.
cleanup_on_exit : bool, optional

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

How shall I call the launcher now if I don't want to shut down the Fluent process on session.exit()?

@mayankansys mayankansys Jul 20, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

@mainakk If I understood properly your point then we can preserve the server info file and avoid cleanup on session.exit() , call the launcher with

session = pyfluent.launch_fluent(preserve_info_file = True)
session.exit()

Kindly let me know, if I misunderstood your question or any further query or any change needed.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@mkundu1 is intended above.

@seanpearsonuk seanpearsonuk Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@mayankansys is responding to my comment above but that comment does not request any changes. If we agree to follow up on that idea, we would create a separate issue.

Determines whether to shut down the connected Fluent session when exiting PyFluent or calling
the session's `exit()` method. Defaults to True.
preserve_info_file : bool, optional
If True, the server-info file will be preserved for debugging.
If False, the server-info file will be deleted when the session exits. Defaults to False.
dry_run : bool, optional
If True, does not launch Fluent but prints configuration information instead. The `call()` method
returns a tuple containing the launch string and server info file name. Defaults to False.
Expand All @@ -201,7 +211,7 @@ def __init__(
A flag indicating whether to write equivalent Python journals from provided journal files; can also specify
a filename for the new Python journal.
start_watchdog : bool, optional
When `cleanup_on_exit` is True, defaults to True; an independent watchdog process ensures that any local
When `preserve_info_file` is False (default cleanup enabled), defaults to True; an independent watchdog process ensures that any local
GUI-less Fluent sessions started by PyFluent are properly closed when the current Python process ends.
file_transfer_service : Any
Service for uploading/downloading files to/from the server.
Expand Down Expand Up @@ -327,15 +337,15 @@ def __call__(
session = self.new_session._create_from_server_info_file(
server_info_file_name=self._server_info_file_name,
file_transfer_service=self.file_transfer_service,
cleanup_on_exit=self.argvals.get("cleanup_on_exit"),
preserve_info_file=self.argvals.get("preserve_info_file", False),
start_transcript=self.argvals.get("start_transcript"),
launcher_args=self.argvals,
inside_container=False,
)
session._process = process
start_watchdog = _confirm_watchdog_start(
self.argvals.get("start_watchdog"),
self.argvals.get("cleanup_on_exit"),
not self.argvals.get("preserve_info_file", False),
session._fluent_connection,
)
if start_watchdog:
Expand Down Expand Up @@ -378,6 +388,6 @@ def __call__(
logger.error(f"Exception caught - {type(ex).__name__}: {ex}")
raise LaunchFluentError(self._launch_cmd) from ex
finally:
server_info_file = Path(self._server_info_file_name)
if server_info_file.exists():
server_info_file.unlink()
# preserve_info_file defaults to False, meaning cleanup happens by default
if not self.argvals.get("preserve_info_file", False):
Path(self._server_info_file_name).unlink(missing_ok=True)
118 changes: 118 additions & 0 deletions tests/test_launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import platform
import tempfile
from tempfile import TemporaryDirectory
from unittest.mock import MagicMock, Mock, patch
import warnings

import pytest

Expand Down Expand Up @@ -799,3 +801,119 @@ def test_idle_timeout(monkeypatch):
StandaloneLauncher._construct_timeout_arg(200)
== ' -command="(set-session-idle-timeoutPLF+5)"'
)


@seanpearsonuk seanpearsonuk Jul 20, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Do we know the history of this class? Why are PyFluent tests checking the mechanics of Python's documented file handling functionality?

We should just delete this - and anything else with a similar smell.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Noted.

def test_standalone_launcher_cleanup_on_exit_false_preserves_file():
"""Verify old cleanup_on_exit=False parameter still works (backward compatibility)."""
from pathlib import Path

with tempfile.TemporaryDirectory() as tmp_dir:
server_info_file = Path(tmp_dir) / "serverinfo-test.txt"
server_info_file.write_text("test content")

cleanup_on_exit = False
if cleanup_on_exit:
server_info_file.unlink(missing_ok=True)

assert (
server_info_file.exists()
), "File should be preserved with cleanup_on_exit=False"


def test_deprecation_cleanup_on_exit_true_converts_to_preserve_false():
"""Verify cleanup_on_exit=True converts to preserve_info_file=False (inverted logic)."""
from ansys.fluent.core.launcher.fluent_container import (
_cleanup_on_exit_to_preserve_info_file_converter,
)

# The converter function doesn't issue warnings; the decorator does
kwargs = {"cleanup_on_exit": True}
result = _cleanup_on_exit_to_preserve_info_file_converter(
kwargs, ["cleanup_on_exit"], ["preserve_info_file"]
)

# Should be converted to preserve_info_file=False
assert (
result.get("preserve_info_file") is False
), "cleanup_on_exit=True should convert to preserve_info_file=False"
assert (
"cleanup_on_exit" not in result
), "cleanup_on_exit should be removed from kwargs"


def test_deprecation_cleanup_on_exit_false_converts_to_preserve_true():
"""Verify cleanup_on_exit=False converts to preserve_info_file=True (inverted logic)."""
from ansys.fluent.core.launcher.fluent_container import (
_cleanup_on_exit_to_preserve_info_file_converter,
)

# The converter function doesn't issue warnings; the decorator does
kwargs = {"cleanup_on_exit": False}
result = _cleanup_on_exit_to_preserve_info_file_converter(
kwargs, ["cleanup_on_exit"], ["preserve_info_file"]
)

# Should be converted to preserve_info_file=True
assert (
result.get("preserve_info_file") is True
), "cleanup_on_exit=False should convert to preserve_info_file=True"
assert (
"cleanup_on_exit" not in result
), "cleanup_on_exit should be removed from kwargs"


def test_new_preserve_info_file_no_deprecation_warning():
"""Verify new preserve_info_file parameter works without deprecation warnings."""
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
from ansys.fluent.core.launcher.fluent_container import (
_cleanup_on_exit_to_preserve_info_file_converter,
)

# Test with preserve_info_file=True
kwargs = {"preserve_info_file": True}
result = _cleanup_on_exit_to_preserve_info_file_converter(
kwargs, ["cleanup_on_exit"], ["preserve_info_file"]
)
deprecation_warnings = [
warning
for warning in w
if issubclass(warning.category, PyFluentDeprecationWarning)
]
assert len(deprecation_warnings) == 0, "Should not warn with new parameter"
assert result.get("preserve_info_file") is True

# Test with preserve_info_file=False
w.clear()
kwargs = {"preserve_info_file": False}
result = _cleanup_on_exit_to_preserve_info_file_converter(
kwargs, ["cleanup_on_exit"], ["preserve_info_file"]
)
deprecation_warnings = [
warning
for warning in w
if issubclass(warning.category, PyFluentDeprecationWarning)
]
assert len(deprecation_warnings) == 0, "Should not warn with new parameter"
assert result.get("preserve_info_file") is False


def test_both_cleanup_on_exit_and_preserve_info_file_provided():
"""Verify providing both parameters warns and uses new one."""
import pytest

with pytest.warns(PyFluentDeprecationWarning, match="Both deprecated"):
from ansys.fluent.core.launcher.fluent_container import (
_cleanup_on_exit_to_preserve_info_file_converter,
)

kwargs = {"cleanup_on_exit": True, "preserve_info_file": False}
result = _cleanup_on_exit_to_preserve_info_file_converter(
kwargs, ["cleanup_on_exit"], ["preserve_info_file"]
)

# Should keep the new parameter value
assert (
result.get("preserve_info_file") is False
), "New preserve_info_file parameter should take precedence"
assert "cleanup_on_exit" not in result, "cleanup_on_exit should be removed"
Loading