diff --git a/.github/workflows/test-podman-compose.yml b/.github/workflows/test-podman-compose.yml index 66e1bf9b0fb5..2624f8d50fd5 100644 --- a/.github/workflows/test-podman-compose.yml +++ b/.github/workflows/test-podman-compose.yml @@ -19,7 +19,6 @@ env: ANSYSLMD_LICENSE_FILE: ${{ format('1055@{0}', secrets.LICENSE_SERVER) }} PYFLUENT_TIMEOUT_FORCE_EXIT: 30 PYFLUENT_LAUNCH_CONTAINER: 1 - PYFLUENT_USE_PODMAN_COMPOSE: 1 PYFLUENT_LOGGING: "DEBUG" PYFLUENT_WATCHDOG_DEBUG: "OFF" PYFLUENT_HIDE_LOG_SECRETS: 1 diff --git a/doc/changelog.d/4284.added.md b/doc/changelog.d/4284.added.md new file mode 100644 index 000000000000..907c64806d0d --- /dev/null +++ b/doc/changelog.d/4284.added.md @@ -0,0 +1 @@ +Replace compose env vars with parameters \ No newline at end of file diff --git a/doc/deprecated_pyfluent_apis.py b/doc/deprecated_pyfluent_apis.py index 541056dc0fa9..7232ea710b7c 100644 --- a/doc/deprecated_pyfluent_apis.py +++ b/doc/deprecated_pyfluent_apis.py @@ -251,4 +251,15 @@ "0.23.dev0", "`surfaces`", ), + # Environment variables + ( + "The `PYFLUENT_USE_DOCKER_COMPOSE` environment variable", + "0.34.0", + "``use_docker_compose`` argument of :py:func:`ansys.fluent.core.launcher.launcher.launch_fluent `", + ), + ( + "The `PYFLUENT_USE_PODMAN_COMPOSE` environment variable", + "0.34.0", + "``use_podman_compose`` argument of :py:func:`ansys.fluent.core.launcher.launcher.launch_fluent `", + ), ] diff --git a/doc/source/contributing/environment_variables.rst b/doc/source/contributing/environment_variables.rst index 940f177f83a7..b4ad7062c888 100644 --- a/doc/source/contributing/environment_variables.rst +++ b/doc/source/contributing/environment_variables.rst @@ -74,7 +74,3 @@ Following is a list of environment variables that can be set to control various - Specifies the IP address of the Fluent server while launching Fluent in :func:`launch_fluent() `. * - SERVER_INFO_DIR - Specifies the directory where the server-info file is created while launching Fluent in :func:`launch_fluent() `. - * - PYFLUENT_USE_DOCKER_COMPOSE - - Specifies Docker as a compose source to launch Fluent in container mode. - * - PYFLUENT_USE_PODMAN_COMPOSE - - Specifies Podman as a compose source to launch Fluent in container mode. diff --git a/doc/source/user_guide/make_container_image.rst b/doc/source/user_guide/make_container_image.rst index ec852f047697..d108b0e327e5 100644 --- a/doc/source/user_guide/make_container_image.rst +++ b/doc/source/user_guide/make_container_image.rst @@ -86,10 +86,9 @@ Run Docker container using PyFluent import os import ansys.fluent.core as pyfluent - os.environ["PYFLUENT_USE_DOCKER_COMPOSE"] = "1" os.environ["ANSYSLMD_LICENSE_FILE"] = "" custom_config = {'fluent_image': 'ansys_inc:latest', 'mount_source': f"{os.getcwd()}", 'auto_remove': False} - solver_session = pyfluent.launch_fluent(container_dict=custom_config) + solver_session = pyfluent.launch_fluent(container_dict=custom_config, user_docker_compose=True) Run Podman container using the command line @@ -137,7 +136,6 @@ Run Podman container using PyFluent import os import ansys.fluent.core as pyfluent os.environ["ANSYSLMD_LICENSE_FILE"] = "" - os.environ["PYFLUENT_USE_PODMAN_COMPOSE"] = "1" custom_config = {'fluent_image': 'ansys_inc:latest', 'mount_source': f"{os.getcwd()}", 'auto_remove': False} - solver_session = pyfluent.launch_fluent(container_dict=custom_config) + solver_session = pyfluent.launch_fluent(container_dict=custom_config, use_podman_compose=True) diff --git a/doc/source/user_guide/session/launching_ansys_fluent.rst b/doc/source/user_guide/session/launching_ansys_fluent.rst index 7523bf01c61a..42dba36bf277 100644 --- a/doc/source/user_guide/session/launching_ansys_fluent.rst +++ b/doc/source/user_guide/session/launching_ansys_fluent.rst @@ -42,6 +42,7 @@ Launch in a container --------------------- The :meth:`from_container() ` method launches Fluent inside a Docker container. +Pass ``use_docker_compose=True`` or ``use_podman_compose=True`` to use Docker Compose or Podman Compose, respectively. Use this method when: @@ -55,7 +56,6 @@ Use this method when: import os os.environ["PYFLUENT_LAUNCH_CONTAINER"] = "1" - os.environ["PYFLUENT_USE_DOCKER_COMPOSE"] = "1" # or os.environ["PYFLUENT_USE_PODMAN_COMPOSE"] = "1" import ansys.fluent.core as pyfluent from ansys.fluent.core.utils.networking import get_free_port @@ -64,12 +64,12 @@ Use this method when: port_2 = get_free_port() container_dict = {"ports": {f"{port_1}": port_1, f"{port_2}": port_2}} - meshing = pyfluent.Meshing.from_container(container_dict=container_dict, product_version=pyfluent.FluentVersion.v252) - pure_meshing = pyfluent.PureMeshing.from_container(container_dict=container_dict, product_version=pyfluent.FluentVersion.v252) - solver = pyfluent.Solver.from_container(container_dict=container_dict, product_version=pyfluent.FluentVersion.v252) - solver_aero = pyfluent.SolverAero.from_container(container_dict=container_dict, product_version=pyfluent.FluentVersion.v252) - solver_icing = pyfluent.SolverIcing.from_container(container_dict=container_dict, product_version=pyfluent.FluentVersion.v252) - pre_post = pyfluent.PrePost.from_container(container_dict=container_dict, product_version=pyfluent.FluentVersion.v252) + meshing = pyfluent.Meshing.from_container(container_dict=container_dict, product_version=pyfluent.FluentVersion.v252, use_docker_compose=True) + pure_meshing = pyfluent.PureMeshing.from_container(container_dict=container_dict, product_version=pyfluent.FluentVersion.v252, use_podman_compose=True) + solver = pyfluent.Solver.from_container(container_dict=container_dict, product_version=pyfluent.FluentVersion.v252, use_docker_compose=True) + solver_aero = pyfluent.SolverAero.from_container(container_dict=container_dict, product_version=pyfluent.FluentVersion.v252, use_podman_compose=True) + solver_icing = pyfluent.SolverIcing.from_container(container_dict=container_dict, product_version=pyfluent.FluentVersion.v252, use_docker_compose=True) + pre_post = pyfluent.PrePost.from_container(container_dict=container_dict, product_version=pyfluent.FluentVersion.v252, use_podman_compose=True) Connect to an existing session @@ -433,6 +433,7 @@ Launching Fluent in container mode with Docker Compose or Podman Compose ------------------------------------------------------------------------ Use PyFluent with Docker Compose or Podman Compose to run Fluent in a consistent, reproducible containerized environment. +Pass ``use_docker_compose=True`` or ``use_podman_compose=True`` to use Docker Compose or Podman Compose, respectively. 1. **Docker Compose** @@ -458,7 +459,6 @@ Set environment variables to select the container engine: >>> import os >>> os.environ["PYFLUENT_LAUNCH_CONTAINER"] = "1" - >>> os.environ["PYFLUENT_USE_DOCKER_COMPOSE"] = "1" # or os.environ["PYFLUENT_USE_PODMAN_COMPOSE"] = "1" Then launch: @@ -467,7 +467,7 @@ Then launch: >>> import ansys.fluent.core as pyfluent >>> from ansys.fluent.core import examples - >>> solver_session = pyfluent.launch_fluent() + >>> solver_session = pyfluent.launch_fluent(use_docker_compose=True) >>> case_file_name = examples.download_file("mixing_elbow.cas.h5", "pyfluent/mixing_elbow") >>> solver_session.file.read(file_name=case_file_name, file_type="case") >>> solver_session.exit() diff --git a/src/ansys/fluent/core/docker/docker_compose.py b/src/ansys/fluent/core/docker/docker_compose.py index f387128872e4..7769c0da6202 100644 --- a/src/ansys/fluent/core/docker/docker_compose.py +++ b/src/ansys/fluent/core/docker/docker_compose.py @@ -32,7 +32,8 @@ class ComposeBasedLauncher: """Launch Fluent through docker or Podman compose.""" - def __init__(self, *, container_dict): + def __init__(self, compose_config, container_dict): + self._compose_config = compose_config self._compose_name = f"pyfluent_compose_{uuid.uuid4().hex}" self._container_dict = container_dict image_tag = os.getenv("FLUENT_IMAGE_TAG") @@ -45,9 +46,6 @@ def __init__(self, *, container_dict): self._compose_file = self._get_compose_file(container_dict) - def _is_podman_selected(self): - return os.getenv("PYFLUENT_USE_PODMAN_COMPOSE") == "1" - def _get_compose_file(self, container_dict): """Generates compose file for the Docker Compose setup. @@ -132,14 +130,14 @@ def _set_compose_cmds(self): """ # Determine the compose command - if os.getenv("PYFLUENT_USE_PODMAN_COMPOSE") == "1": + if self._compose_config.use_podman_compose: self._compose_cmds = ( ["sudo", "podman", "compose"] if hasattr(self, "_container_source") and "sudo" in self._container_source else ["podman", "compose"] ) - elif os.getenv("PYFLUENT_USE_DOCKER_COMPOSE") == "1": + elif self._compose_config.use_docker_compose: self._compose_cmds = ["docker", "compose"] else: raise RuntimeError("Neither Docker nor Podman is specified.") @@ -151,7 +149,7 @@ def check_image_exists(self) -> bool: try: cmd = self._container_source + ["images", "-q", self._image_name] # Podman users do not always configure rootless mode in /etc/subuids and /etc/subgids - if self._is_podman_selected(): + if self._compose_config.use_podman_compose: sudo_cmd = ["sudo"] + cmd output_1 = subprocess.check_output(cmd) output_2 = subprocess.check_output(sudo_cmd) diff --git a/src/ansys/fluent/core/fluent_connection.py b/src/ansys/fluent/core/fluent_connection.py index a48d8cd3c03d..3fb9dc0a2c64 100644 --- a/src/ansys/fluent/core/fluent_connection.py +++ b/src/ansys/fluent/core/fluent_connection.py @@ -42,7 +42,7 @@ import grpc import ansys.fluent.core as pyfluent -from ansys.fluent.core.launcher.launcher_utils import is_compose +from ansys.fluent.core.launcher.launcher_utils import ComposeConfig from ansys.fluent.core.services import service_creator from ansys.fluent.core.services.app_utilities import ( AppUtilitiesOld, @@ -381,6 +381,7 @@ def __init__( slurm_job_id: str | None = None, inside_container: bool | None = None, container: ContainerT | None = None, + compose_config: ComposeConfig | None = None, ): """Initialize a Session. @@ -420,12 +421,15 @@ def __init__( container: ContainerT, optional The container instance if the Fluent session is running inside a container. + compose_config: ComposeConfig, optional + Configuration for Docker Compose or Podman Compose. Raises ------ PortNotProvided If port is not provided. """ + self._compose_config = compose_config if compose_config else ComposeConfig() self._error_state = ErrorState() self._data_valid = False self._channel_str = None @@ -472,7 +476,7 @@ def __init__( and cortex_host is not None ): logger.info("Checking if Fluent is running inside a container.") - if not is_compose(): + if not self._compose_config.is_compose: inside_container = get_container(cortex_host) logger.debug(f"get_container({cortex_host}): {inside_container}") if inside_container is False: @@ -549,7 +553,10 @@ def force_exit(self): >>> session = pyfluent.launch_fluent() >>> session.force_exit() """ - if self.connection_properties.inside_container or is_compose(): + if ( + self.connection_properties.inside_container + or self._compose_config.is_compose + ): self._force_exit_container() elif self._remote_instance is not None: logger.error("Cannot execute cleanup script, Fluent running remotely.") @@ -601,7 +608,7 @@ def force_exit(self): def _force_exit_container(self): """Immediately terminates the Fluent client running inside a container, losing unsaved progress and data.""" - if is_compose() and self._container: + if self._compose_config.is_compose and self._container: self._container.stop() else: container = self.connection_properties.inside_container @@ -679,7 +686,10 @@ def wait_process_finished(self, wait: float | int | bool = 60): else: raise WaitTypeError() - if self.connection_properties.inside_container and not is_compose(): + if ( + self.connection_properties.inside_container + and not self._compose_config.is_compose + ): _response = timeout_loop( get_container, wait, diff --git a/src/ansys/fluent/core/launcher/container_launcher.py b/src/ansys/fluent/core/launcher/container_launcher.py index 723c8a2a8e21..ad24c691c5a4 100644 --- a/src/ansys/fluent/core/launcher/container_launcher.py +++ b/src/ansys/fluent/core/launcher/container_launcher.py @@ -56,7 +56,7 @@ UIMode, _get_argvals_and_session, ) -from ansys.fluent.core.launcher.launcher_utils import is_compose +from ansys.fluent.core.launcher.launcher_utils import ComposeConfig from ansys.fluent.core.launcher.process_launch_string import ( _build_fluent_launch_args_string, ) @@ -108,6 +108,8 @@ def __init__( gpu: bool | None = None, start_watchdog: bool | None = None, file_transfer_service: Any | None = None, + use_docker_compose: bool | None = None, + use_podman_compose: bool | None = None, ): """ Launch a Fluent session in container mode. @@ -161,6 +163,10 @@ def __init__( GUI-less Fluent sessions started by PyFluent are properly closed when the current Python process ends. file_transfer_service : Any, optional Service for uploading/downloading files to/from the server. + use_docker_compose: bool + Whether to use Docker Compose to launch Fluent. + use_podman_compose: bool + Whether to use Podman Compose to launch Fluent. Returns ------- @@ -198,12 +204,15 @@ def __init__( self._args = _build_fluent_launch_args_string(**self.argvals).split() if FluentMode.is_meshing(self.argvals["mode"]): self._args.append(" -meshing") + self._compose_config = ComposeConfig(use_docker_compose, use_podman_compose) def __call__(self): if self.argvals["dry_run"]: config_dict, *_ = configure_container_dict( - self._args, **self.argvals["container_dict"] + self._args, + compose_config=self._compose_config, + **self.argvals["container_dict"], ) dict_str = dict_to_str(config_dict) print("\nDocker container run configuration:\n") @@ -214,11 +223,12 @@ def __call__(self): logger.debug(f"Fluent container launcher args: {self._args}") logger.debug(f"Fluent container launcher argvals:\n{dict_to_str(self.argvals)}") - if is_compose(): + if self._compose_config.is_compose: port, config_dict, container = start_fluent_container( self._args, self.argvals["container_dict"], self.argvals["start_timeout"], + compose_config=self._compose_config, ) _, _, password = _get_server_info_from_container(config_dict=config_dict) @@ -227,6 +237,7 @@ def __call__(self): self._args, self.argvals["container_dict"], self.argvals["start_timeout"], + compose_config=self._compose_config, ) fluent_connection = FluentConnection( @@ -237,18 +248,22 @@ def __call__(self): slurm_job_id=self.argvals and self.argvals.get("slurm_job_id"), inside_container=True, container=container, + compose_config=self._compose_config, ) + self.argvals["compose_config"] = self._compose_config + session = self.new_session( fluent_connection=fluent_connection, scheme_eval=fluent_connection._connection_interface.scheme_eval, file_transfer_service=self.file_transfer_service, start_transcript=self.argvals["start_transcript"], + launcher_args=self.argvals, ) session._container = container - if not is_compose(): + if not self._compose_config.is_compose: if ( self.argvals["start_watchdog"] is None and self.argvals["cleanup_on_exit"] diff --git a/src/ansys/fluent/core/launcher/fluent_container.py b/src/ansys/fluent/core/launcher/fluent_container.py index 3356ae1da221..04de490020ad 100644 --- a/src/ansys/fluent/core/launcher/fluent_container.py +++ b/src/ansys/fluent/core/launcher/fluent_container.py @@ -85,7 +85,7 @@ from ansys.fluent.core.launcher.error_handler import ( LaunchFluentError, ) -from ansys.fluent.core.launcher.launcher_utils import is_compose +from ansys.fluent.core.launcher.launcher_utils import ComposeConfig from ansys.fluent.core.pyfluent_warnings import PyFluentDeprecationWarning from ansys.fluent.core.session import _parse_server_info_file from ansys.fluent.core.utils.deprecate import all_deprecators @@ -170,6 +170,7 @@ def configure_container_dict( image_name: str | None = None, image_tag: str | None = None, file_transfer_service: Any | None = None, + compose_config: ComposeConfig | None = None, **container_dict, ) -> (dict, int, int, Path, bool): """Parses the parameters listed below, and sets up the container configuration file. @@ -204,6 +205,8 @@ def configure_container_dict( Ignored if ``fluent_image`` has been specified. file_transfer_service : optional Supports file upload and download. + compose_config : ComposeConfig, optional + Configuration for Docker Compose, if using Docker Compose to launch the container. **container_dict Additional keyword arguments can be specified, they will be treated as Docker container run options to be passed directly to the Docker run execution. See examples below and `Docker run`_ documentation. @@ -237,6 +240,8 @@ def configure_container_dict( See also :func:`start_fluent_container`. """ + compose_config = compose_config if compose_config else ComposeConfig() + if timeout is not None: warnings.warn( "configure_container_dict(timeout) is deprecated, use launch_fluent(start_timeout) instead.", @@ -452,7 +457,7 @@ def configure_container_dict( host_server_info_file = Path(mount_source) / container_server_info_file.name - if is_compose(): + if compose_config.is_compose: container_dict["host_server_info_file"] = host_server_info_file container_dict["mount_source"] = mount_source container_dict["mount_target"] = mount_target @@ -474,7 +479,10 @@ def configure_container_dict( def start_fluent_container( - args: List[str], container_dict: dict | None = None, start_timeout: int = 60 + args: List[str], + container_dict: dict | None = None, + start_timeout: int = 60, + compose_config: ComposeConfig | None = None, ) -> tuple[int, str, Any]: """Start a Fluent container. @@ -487,6 +495,8 @@ def start_fluent_container( start_timeout : int, optional Timeout in seconds for the container to start. If not specified, it defaults to 60 seconds. + compose_config : ComposeConfig, optional + Configuration for Docker Compose, if using Docker Compose to launch the container. Returns ------- @@ -508,10 +518,16 @@ def start_fluent_container( :func:`~ansys.fluent.core.launcher.launcher.launch_fluent()`. """ + compose_config = compose_config if compose_config else ComposeConfig() + if container_dict is None: container_dict = {} - container_vars = configure_container_dict(args, **container_dict) + container_vars = configure_container_dict( + args, + compose_config=compose_config, + **container_dict, + ) ( config_dict, @@ -530,10 +546,13 @@ def start_fluent_container( del timeout try: - if is_compose(): + if compose_config.is_compose: config_dict["fluent_port"] = port - compose_container = ComposeBasedLauncher(container_dict=config_dict) + compose_container = ComposeBasedLauncher( + compose_config=compose_config, + container_dict=config_dict, + ) if not compose_container.check_image_exists(): logger.debug( diff --git a/src/ansys/fluent/core/launcher/launcher.py b/src/ansys/fluent/core/launcher/launcher.py index 122e65fd49f4..aae8cdc10a56 100644 --- a/src/ansys/fluent/core/launcher/launcher.py +++ b/src/ansys/fluent/core/launcher/launcher.py @@ -174,6 +174,8 @@ def launch_fluent( start_watchdog: bool | None = None, scheduler_options: dict | None = None, file_transfer_service: Any | None = None, + use_docker_compose: bool | None = None, + use_podman_compose: bool | None = None, ) -> Meshing | PureMeshing | Solver | SolverIcing | SlurmFuture | dict: """Launch Fluent locally in server mode or connect to a running Fluent server instance. @@ -299,6 +301,10 @@ def launch_fluent( specified in a similar manner to Fluent's scheduler options. file_transfer_service : optional File transfer service. Uploads/downloads files to/from the server. + use_docker_compose: bool + Whether to use Docker Compose to launch Fluent. + use_podman_compose: bool + Whether to use Podman Compose to launch Fluent. Returns ------- @@ -312,6 +318,8 @@ def launch_fluent( ------ UnexpectedKeywordArgument If an unexpected keyword argument is provided. + ValueError + If both ``use_docker_compose`` and ``use_podman_compose`` are set to ``True``. Notes ----- @@ -322,6 +330,11 @@ def launch_fluent( if env is None: env = {} + if use_docker_compose and use_podman_compose: + raise ValueError( + "Cannot use both 'use_docker_compose' and 'use_podman_compose' at the same time." + ) + if start_timeout is None: start_timeout = int(os.getenv("PYFLUENT_FLUENT_LAUNCH_TIMEOUT", "60")) diff --git a/src/ansys/fluent/core/launcher/launcher_utils.py b/src/ansys/fluent/core/launcher/launcher_utils.py index d896df0e2ebc..395de645a1bc 100644 --- a/src/ansys/fluent/core/launcher/launcher_utils.py +++ b/src/ansys/fluent/core/launcher/launcher_utils.py @@ -30,19 +30,59 @@ import subprocess import time from typing import Any, Dict +import warnings from ansys.fluent.core.exceptions import InvalidArgument +from ansys.fluent.core.pyfluent_warnings import PyFluentDeprecationWarning from ansys.fluent.core.utils.networking import find_remoting_ip logger = logging.getLogger("pyfluent.launcher") -def is_compose() -> bool: - """Check if the Fluent launch is through compose.""" - return ( - os.getenv("PYFLUENT_USE_DOCKER_COMPOSE") == "1" - or os.getenv("PYFLUENT_USE_PODMAN_COMPOSE") == "1" - ) +class ComposeConfig: + """Configuration for Docker or Podman Compose usage in PyFluent.""" + + def __init__( + self, + use_docker_compose: bool | None = None, + use_podman_compose: bool | None = None, + ): + self._env_docker = os.getenv("PYFLUENT_USE_DOCKER_COMPOSE") == "1" + self._env_podman = os.getenv("PYFLUENT_USE_PODMAN_COMPOSE") == "1" + + self._use_docker = use_docker_compose + self._use_podman = use_podman_compose + + if use_docker_compose is None and self._env_docker: + self._warn_env_deprecated() + if use_podman_compose is None and self._env_podman: + self._warn_env_deprecated() + + def _warn_env_deprecated(self): + warnings.warn( + ( + "The environment variables 'PYFLUENT_USE_DOCKER_COMPOSE' and " + "'PYFLUENT_USE_PODMAN_COMPOSE' are deprecated. " + "Use the 'use_docker_compose' and 'use_podman_compose' parameters instead." + ), + category=PyFluentDeprecationWarning, + stacklevel=3, + ) + + @property + def use_docker_compose(self) -> bool: + """Check if Docker Compose is configured to be used.""" + return self._use_docker if self._use_docker is not None else self._env_docker + + @property + def use_podman_compose(self) -> bool: + """Check if Podman Compose is configured to be used.""" + return self._use_podman if self._use_podman is not None else self._env_podman + + @property + def is_compose(self) -> bool: + """Check if either Docker Compose or Podman Compose is configured to be used.""" + return self.use_docker_compose or self.use_podman_compose def is_windows(): diff --git a/src/ansys/fluent/core/report.py b/src/ansys/fluent/core/report.py index 561111ceaa90..b119b15ddd70 100644 --- a/src/ansys/fluent/core/report.py +++ b/src/ansys/fluent/core/report.py @@ -58,8 +58,6 @@ "REMOTING_PORTS", "REMOTING_SERVER_ADDRESS", "SERVER_INFO_DIR", - "PYFLUENT_USE_DOCKER_COMPOSE", - "PYFLUENT_USE_PODMAN_COMPOSE", ] diff --git a/src/ansys/fluent/core/session.py b/src/ansys/fluent/core/session.py index 44bea8490f61..4d89b4af756f 100644 --- a/src/ansys/fluent/core/session.py +++ b/src/ansys/fluent/core/session.py @@ -33,7 +33,6 @@ from ansys.fluent.core.fluent_connection import FluentConnection from ansys.fluent.core.journaling import Journal -from ansys.fluent.core.launcher.launcher_utils import is_compose from ansys.fluent.core.pyfluent_warnings import ( PyFluentDeprecationWarning, PyFluentUserWarning, @@ -151,6 +150,7 @@ def __init__( file_transfer_service, event_type, get_zones_info, + launcher_args, ) self.register_finalizer_callback = fluent_connection.register_finalizer_cb @@ -161,10 +161,12 @@ def _build_from_fluent_connection( file_transfer_service: Any | None = None, event_type=None, get_zones_info: weakref.WeakMethod[Callable[[], list[ZoneInfo]]] | None = None, + launcher_args: Dict[str, Any] | None = None, ): """Build a BaseSession object from fluent_connection object.""" self._fluent_connection = fluent_connection self._file_transfer_service = file_transfer_service + self._launcher_args = launcher_args self._error_state = fluent_connection._error_state self.scheme = scheme_eval self.rp_vars = RPVars(self.scheme.string_eval) @@ -354,8 +356,12 @@ def get_fluent_version(self) -> FluentVersion: return FluentVersion(self.scheme.version) def _exit_compose_service(self): - if self._fluent_connection._container and is_compose(): - self._fluent_connection._container.stop() + args = self._launcher_args or {} + compose_config = args.get("compose_config", None) + + container = self._fluent_connection._container + if compose_config and compose_config.is_compose: + container.stop() def exit(self, **kwargs) -> None: """Exit session.""" diff --git a/src/ansys/fluent/core/session_solver.py b/src/ansys/fluent/core/session_solver.py index 8317c53a9884..23427a47ad52 100644 --- a/src/ansys/fluent/core/session_solver.py +++ b/src/ansys/fluent/core/session_solver.py @@ -119,13 +119,16 @@ def __init__( get_zones_info=weakref.WeakMethod(self._get_zones_info), ) self._settings = None - self._build_from_fluent_connection(fluent_connection, scheme_eval) + self._build_from_fluent_connection( + fluent_connection, scheme_eval, launcher_args=launcher_args + ) def _build_from_fluent_connection( self, fluent_connection, scheme_eval: SchemeEval, file_transfer_service: Any | None = None, + launcher_args: Dict[str, Any] | None = None, ): self._tui_service = self._datamodel_service_tui self._se_service = self._datamodel_service_se @@ -134,6 +137,7 @@ def _build_from_fluent_connection( self._system_coupling = None self._fluent_version = None self._bg_session_threads = [] + self._launcher_args = launcher_args self._solution_variable_service = service_creator("svar").create( fluent_connection._channel, fluent_connection._metadata ) @@ -296,10 +300,12 @@ def _start_bg_session_and_sync(self, launcher_args): bg_session._fluent_connection, bg_session._fluent_connection._connection_interface.scheme_eval, event_type=SolverEvent, + launcher_args=launcher_args, ) self._build_from_fluent_connection( bg_session._fluent_connection, bg_session._fluent_connection._connection_interface.scheme_eval, + launcher_args=launcher_args, ) # TODO temporary fix till set_state at settings root is fixed _set_state_safe(self.settings, state) diff --git a/tests/podman_compose.py b/tests/podman_compose.py index 6cd3b0a0bb7c..0888f612f40d 100644 --- a/tests/podman_compose.py +++ b/tests/podman_compose.py @@ -30,7 +30,7 @@ port_2 = get_free_port() container_dict = {"ports": {f"{port_1}": port_1, f"{port_2}": port_2}} -solver = pyfluent.launch_fluent(container_dict=container_dict) +solver = pyfluent.launch_fluent(container_dict=container_dict, use_podman_compose=True) assert len(solver._container.ports) == 2 case_file_name = examples.download_file("mixing_elbow.cas.h5", "pyfluent/mixing_elbow") solver.file.read(file_name=case_file_name, file_type="case") diff --git a/tests/test_launcher.py b/tests/test_launcher.py index e634a3d1c4e2..c496b513cef2 100644 --- a/tests/test_launcher.py +++ b/tests/test_launcher.py @@ -50,6 +50,7 @@ ) from ansys.fluent.core.launcher.launcher import create_launcher from ansys.fluent.core.launcher.launcher_utils import ( + ComposeConfig, _build_journal_argument, is_windows, ) @@ -644,7 +645,6 @@ def test_report(): @pytest.mark.fluent_version(">=23.1") def test_docker_compose(monkeypatch): - monkeypatch.setenv("PYFLUENT_USE_DOCKER_COMPOSE", "1") import ansys.fluent.core as pyfluent from ansys.fluent.core import examples from ansys.fluent.core.utils.networking import get_free_port @@ -652,7 +652,9 @@ def test_docker_compose(monkeypatch): port_1 = get_free_port() port_2 = get_free_port() container_dict = {"ports": {f"{port_1}": port_1, f"{port_2}": port_2}} - solver = pyfluent.launch_fluent(container_dict=container_dict) + solver = pyfluent.launch_fluent( + container_dict=container_dict, use_docker_compose=True + ) assert len(solver._container.ports) == 2 case_file_name = examples.download_file( "mixing_elbow.cas.h5", "pyfluent/mixing_elbow" @@ -733,3 +735,18 @@ def test_no_warning_for_none_values(caplog): driver = _get_graphics_driver(graphics_driver=None, ui_mode=None) # noqa: F841 assert "PyFluentUserWarning" not in caplog.text caplog.clear() + + +def test_error_for_selecting_both_compose_sources(): + with pytest.raises(ValueError): + pyfluent.launch_fluent(use_docker_compose=True, use_podman_compose=True) + + +def test_warning_for_deprecated_compose_env_vars(monkeypatch): + monkeypatch.setenv("PYFLUENT_USE_DOCKER_COMPOSE", "1") + with pytest.warns(PyFluentDeprecationWarning): + ComposeConfig() + + monkeypatch.setenv("PYFLUENT_USE_PODMAN_COMPOSE", "1") + with pytest.warns(PyFluentDeprecationWarning): + ComposeConfig()