Skip to content

Commit 2b1af7b

Browse files
committed
squashed commit of the changes
1 parent bec3013 commit 2b1af7b

21 files changed

+85
-43
lines changed

doc/changelog.d/4171.fixed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
container timeout issue and new environment variables

doc/source/contributing/environment_variables.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ Following is a list of environment variables that can be set to control various
4343
- Specifies the port of the Fluent server in :func:`connect_to_fluent() <ansys.fluent.core.launcher.launcher.connect_to_fluent>`.
4444
* - PYFLUENT_FLUENT_ROOT
4545
- Specifies the Fluent root directory while launching Fluent in :func:`launch_fluent() <ansys.fluent.core.launcher.launcher.launch_fluent>`.
46+
* - PYFLUENT_FLUENT_LAUNCH_TIMEOUT
47+
- Specifies the timeout, in seconds, for launching Fluent through :func:`launch_fluent() <ansys.fluent.core.launcher.launcher.launch_fluent>`.
48+
* - PYFLUENT_FLUENT_AUTOMATIC_TRANSCRIPT
49+
- Can be used to enable automatic writing of transcript .trn file by Fluent. By default, it is disabled.
4650
* - PYFLUENT_GRPC_LOG_BYTES_LIMIT
4751
- Specifies the length of gRPC logging messages. Set to 0 to disable the limit.
4852
* - PYFLUENT_LAUNCH_CONTAINER

src/ansys/fluent/core/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@
7373
SolverIcing,
7474
)
7575
from ansys.fluent.core.streaming_services.events_streaming import * # noqa: F401, F403
76-
from ansys.fluent.core.utils import fldoc, get_examples_download_dir
76+
from ansys.fluent.core.utils import env_var_to_bool, fldoc, get_examples_download_dir
7777
from ansys.fluent.core.utils.fluent_version import FluentVersion # noqa: F401
7878
from ansys.fluent.core.utils.setup_for_fluent import setup_for_fluent # noqa: F401
7979

@@ -155,7 +155,7 @@ def version_info() -> str:
155155
FLUENT_SHOW_MESH_AFTER_CASE_READ = False
156156

157157
# Whether to write the automatic transcript in Fluent
158-
FLUENT_AUTOMATIC_TRANSCRIPT = False
158+
FLUENT_AUTOMATIC_TRANSCRIPT = env_var_to_bool("PYFLUENT_FLUENT_AUTOMATIC_TRANSCRIPT")
159159

160160
# Whether to interrupt Fluent solver from PyFluent
161161
SUPPORT_SOLVER_INTERRUPT = False

src/ansys/fluent/core/codegen/tuigen.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
convert_path_to_grpc_path,
5757
convert_tui_menu_to_func_name,
5858
)
59+
from ansys.fluent.core.utils import env_var_to_bool
5960
from ansys.fluent.core.utils.fix_doc import escape_wildcards
6061
from ansys.fluent.core.utils.fluent_version import (
6162
FluentVersion,
@@ -95,7 +96,7 @@ def _get_tui_docdir(mode: str):
9596

9697

9798
def _copy_tui_help_xml_file(version: str):
98-
if os.getenv("PYFLUENT_LAUNCH_CONTAINER") == "1":
99+
if env_var_to_bool("PYFLUENT_LAUNCH_CONTAINER"):
99100
image_tag = os.getenv("FLUENT_IMAGE_TAG", "v25.1.0")
100101
image_name = f"ghcr.io/ansys/pyfluent:{image_tag}"
101102
container_name = uuid.uuid4().hex
@@ -346,7 +347,7 @@ def generate(version, static_infos: dict, verbose: bool = False):
346347
api_tree["<solver_session>"] = TUIGenerator(
347348
"solver", version, static_infos, verbose
348349
).generate()
349-
if os.getenv("PYFLUENT_HIDE_LOG_SECRETS") != "1":
350+
if not env_var_to_bool("PYFLUENT_HIDE_LOG_SECRETS"):
350351
logger.info(
351352
"XML help is available but not picked for the following %i paths: ",
352353
len(_XML_HELPSTRINGS),

src/ansys/fluent/core/examples/downloads.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import zipfile
3131

3232
import ansys.fluent.core as pyfluent
33+
from ansys.fluent.core.utils import env_var_to_bool
3334
from ansys.fluent.core.utils.networking import check_url_exists, get_url_content
3435

3536
logger = logging.getLogger("pyfluent.networking")
@@ -180,7 +181,7 @@ def download_file(
180181
'bracket.iges'
181182
"""
182183
if return_without_path is None:
183-
if os.getenv("PYFLUENT_LAUNCH_CONTAINER") == "1":
184+
if env_var_to_bool("PYFLUENT_LAUNCH_CONTAINER"):
184185
if pyfluent.USE_FILE_TRANSFER_SERVICE:
185186
return_without_path = False
186187
else:

src/ansys/fluent/core/fluent_connection.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -746,13 +746,16 @@ def exit(
746746
env_timeout = os.getenv("PYFLUENT_TIMEOUT_FORCE_EXIT")
747747

748748
if env_timeout:
749-
logger.debug("Found PYFLUENT_TIMEOUT_FORCE_EXIT env var")
749+
logger.debug(
750+
f"Found PYFLUENT_TIMEOUT_FORCE_EXIT env var: '{env_timeout}'"
751+
)
750752
try:
751753
timeout = float(env_timeout)
752754
logger.debug(f"Setting TIMEOUT_FORCE_EXIT to {timeout}")
753755
except ValueError:
754756
logger.debug(
755-
"Off or unrecognized PYFLUENT_TIMEOUT_FORCE_EXIT value, not enabling timeout force exit"
757+
"Off or unrecognized PYFLUENT_TIMEOUT_FORCE_EXIT value (floats and integers are also supported), "
758+
"disabling timeout forced exit."
756759
)
757760

758761
if timeout is None:

src/ansys/fluent/core/launcher/container_launcher.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,13 +216,17 @@ def __call__(self):
216216

217217
if is_compose():
218218
port, config_dict, container = start_fluent_container(
219-
self._args, self.argvals["container_dict"]
219+
self._args,
220+
self.argvals["container_dict"],
221+
self.argvals["start_timeout"],
220222
)
221223

222224
_, _, password = _get_server_info_from_container(config_dict=config_dict)
223225
else:
224226
port, password, container = start_fluent_container(
225-
self._args, self.argvals["container_dict"]
227+
self._args,
228+
self.argvals["container_dict"],
229+
self.argvals["start_timeout"],
226230
)
227231

228232
fluent_connection = FluentConnection(

src/ansys/fluent/core/launcher/error_handler.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ class LaunchFluentError(Exception):
7070

7171
def __init__(self, launch_string):
7272
"""__init__ method of LaunchFluentError class."""
73-
details = "\n" + "Fluent Launch string: " + launch_string
73+
details = "\n" + "Fluent Launch command: " + launch_string
7474
super().__init__(details)
7575

7676

src/ansys/fluent/core/launcher/fluent_container.py

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@
8080

8181
import ansys.fluent.core as pyfluent
8282
from ansys.fluent.core.docker.docker_compose import ComposeBasedLauncher
83+
from ansys.fluent.core.launcher.error_handler import (
84+
LaunchFluentError,
85+
)
8386
from ansys.fluent.core.launcher.launcher_utils import is_compose
8487
from ansys.fluent.core.session import _parse_server_info_file
8588
from ansys.fluent.core.utils.deprecate import all_deprecators
@@ -155,7 +158,6 @@ def configure_container_dict(
155158
args: List[str],
156159
mount_source: str | Path | None = None,
157160
mount_target: str | Path | None = None,
158-
timeout: int = 60,
159161
port: int | None = None,
160162
license_server: str | None = None,
161163
container_server_info_file: str | Path | None = None,
@@ -179,8 +181,6 @@ def configure_container_dict(
179181
mount_target : str | Path, optional
180182
Path inside the container where ``mount_source`` will be mounted. This will be the working directory path
181183
visible to the Fluent process running inside the container.
182-
timeout : int, optional
183-
Time limit for the Fluent container to start, in seconds. By default, 30 seconds.
184184
port : int, optional
185185
Port for Fluent container to use.
186186
license_server : str, optional
@@ -206,7 +206,6 @@ def configure_container_dict(
206206
-------
207207
fluent_image : str
208208
container_dict : dict
209-
timeout : int
210209
port : int
211210
host_server_info_file : Path
212211
remove_server_info_file: bool
@@ -444,23 +443,22 @@ def configure_container_dict(
444443
container_dict["mount_target"] = mount_target
445444

446445
logger.debug(
447-
f"Fluent container timeout: {timeout}, container_grpc_port: {container_grpc_port}, "
446+
f"Fluent container container_grpc_port: {container_grpc_port}, "
448447
f"host_server_info_file: '{host_server_info_file}', "
449448
f"remove_server_info_file: {remove_server_info_file}"
450449
)
451450
logger.debug(f"container_dict after processing:\n{dict_to_str(container_dict)}")
452451

453452
return (
454453
container_dict,
455-
timeout,
456454
container_grpc_port,
457455
host_server_info_file,
458456
remove_server_info_file,
459457
)
460458

461459

462460
def start_fluent_container(
463-
args: List[str], container_dict: dict | None = None
461+
args: List[str], container_dict: dict | None = None, start_timeout: int = 60
464462
) -> tuple[int, str, Any]:
465463
"""Start a Fluent container.
466464
@@ -470,6 +468,9 @@ def start_fluent_container(
470468
List of Fluent launch arguments.
471469
container_dict : dict, optional
472470
Dictionary with Docker container configuration.
471+
start_timeout : int, optional
472+
Timeout in seconds for the container to start. If not specified, it defaults to 60
473+
seconds.
473474
474475
Returns
475476
-------
@@ -498,11 +499,11 @@ def start_fluent_container(
498499

499500
(
500501
config_dict,
501-
timeout,
502502
port,
503503
host_server_info_file,
504504
remove_server_info_file,
505505
) = container_vars
506+
launch_string = " ".join(config_dict["command"])
506507

507508
try:
508509
if is_compose():
@@ -536,18 +537,26 @@ def start_fluent_container(
536537
config_dict.pop("fluent_image"), **config_dict
537538
)
538539

540+
logger.debug(
541+
f"Waiting for Fluent container for up to {start_timeout} seconds..."
542+
)
543+
539544
success = timeout_loop(
540-
lambda: host_server_info_file.stat().st_mtime > last_mtime, timeout
545+
lambda: host_server_info_file.stat().st_mtime > last_mtime,
546+
start_timeout,
541547
)
542548

543549
if not success:
544550
raise TimeoutError(
545-
"Fluent container launch has timed out, stop container manually."
551+
f"Fluent container launch has timed out after {start_timeout} seconds. Container will need to be stopped manually."
546552
)
547553
else:
548554
_, _, password = _parse_server_info_file(str(host_server_info_file))
549555

550556
return port, password, container
557+
except Exception as ex:
558+
logger.error(f"Exception caught - {type(ex).__name__}: {ex}")
559+
raise LaunchFluentError(launch_string) from ex
551560
finally:
552561
if remove_server_info_file and host_server_info_file.exists():
553562
host_server_info_file.unlink()

src/ansys/fluent/core/launcher/launch_options.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
from ansys.fluent.core.session_solver import Solver
3737
from ansys.fluent.core.session_solver_aero import SolverAero
3838
from ansys.fluent.core.session_solver_icing import SolverIcing
39+
from ansys.fluent.core.utils import env_var_to_bool
3940
from ansys.fluent.core.utils.fluent_version import FluentVersion
4041
import ansys.platform.instancemanagement as pypim
4142

@@ -271,7 +272,7 @@ def _get_fluent_launch_mode(start_container, container_dict, scheduler_options):
271272
fluent_launch_mode = LaunchMode.PIM
272273
elif start_container is True or (
273274
start_container is None
274-
and (container_dict or os.getenv("PYFLUENT_LAUNCH_CONTAINER") == "1")
275+
and (container_dict or env_var_to_bool("PYFLUENT_LAUNCH_CONTAINER"))
275276
):
276277
fluent_launch_mode = LaunchMode.CONTAINER
277278
# Currently, only Slurm scheduler is supported and within SlurmLauncher we check the value of the scheduler

src/ansys/fluent/core/launcher/launcher.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
from ansys.fluent.core.session_pure_meshing import PureMeshing
5959
from ansys.fluent.core.session_solver import Solver
6060
from ansys.fluent.core.session_solver_icing import SolverIcing
61+
from ansys.fluent.core.utils import env_var_to_bool
6162
from ansys.fluent.core.utils.deprecate import all_deprecators
6263
from ansys.fluent.core.utils.fluent_version import FluentVersion
6364

@@ -103,7 +104,7 @@ def _show_gui_to_ui_mode(old_arg_val, **kwds):
103104
return UIMode.NO_GUI
104105
elif container_dict:
105106
return UIMode.NO_GUI
106-
elif os.getenv("PYFLUENT_LAUNCH_CONTAINER") == "1":
107+
elif env_var_to_bool("PYFLUENT_LAUNCH_CONTAINER"):
107108
return UIMode.NO_GUI
108109
else:
109110
return UIMode.GUI
@@ -206,9 +207,8 @@ def launch_fluent(
206207
The string path to a Fluent journal file, or a list of such paths. Fluent will execute the
207208
journal(s). The default is ``None``.
208209
start_timeout : int, optional
209-
Maximum allowable time in seconds for connecting to the Fluent
210-
server. The default is ``60`` if Fluent is launched outside a Slurm environment,
211-
no timeout if Fluent is launched within a Slurm environment.
210+
Maximum allowable time, in seconds, to connect to the Fluent server after launching it,
211+
before raising a timeout error. The default is ``60`` seconds.
212212
additional_arguments : str, optional
213213
Additional arguments to send to Fluent as a string in the same
214214
format they are normally passed to Fluent on the command line.
@@ -322,6 +322,9 @@ def launch_fluent(
322322
if env is None:
323323
env = {}
324324

325+
if start_timeout is None:
326+
start_timeout = int(os.getenv("PYFLUENT_FLUENT_LAUNCH_TIMEOUT", "60"))
327+
325328
def _mode_to_launcher_type(fluent_launch_mode: LaunchMode):
326329
launcher_mode_type = {
327330
LaunchMode.CONTAINER: DockerLauncher,

src/ansys/fluent/core/launcher/watchdog.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import time
3636

3737
import ansys.fluent.core as pyfluent
38+
from ansys.fluent.core.utils import env_var_to_bool
3839
from ansys.fluent.core.utils.execution import timeout_loop
3940

4041
logger = pyfluent.logger.get_logger("pyfluent.launcher")
@@ -76,8 +77,8 @@ def launch(
7677
)
7778
)
7879

79-
env_watchdog_debug = os.getenv("PYFLUENT_WATCHDOG_DEBUG", "off").upper()
80-
if env_watchdog_debug in ("1", "ON"):
80+
debug_watchdog = env_var_to_bool("PYFLUENT_WATCHDOG_DEBUG")
81+
if debug_watchdog:
8182
logger.debug(
8283
f"PYFLUENT_WATCHDOG_DEBUG environment variable found, "
8384
f"enabling debugging for watchdog ID {watchdog_id}..."
@@ -131,7 +132,7 @@ def launch(
131132
watchdog_id,
132133
]
133134

134-
if env_watchdog_debug in ("1", "ON"):
135+
if debug_watchdog:
135136
logger.debug(f"Starting Watchdog logging to directory {os.getcwd()}")
136137

137138
kwargs = {"env": watchdog_env, "stdin": subprocess.DEVNULL, "close_fds": True}
@@ -151,7 +152,7 @@ def launch(
151152
if os.name == "posix":
152153
kwargs.update(start_new_session=True)
153154

154-
if env_watchdog_debug in ("1", "ON") and os.name != "nt":
155+
if debug_watchdog and os.name != "nt":
155156
kwargs.update(
156157
stdout=open(f"pyfluent_watchdog_out_{watchdog_id}.log", mode="w"),
157158
stderr=open(f"pyfluent_watchdog_err_{watchdog_id}.log", mode="w"),

src/ansys/fluent/core/launcher/watchdog_exec

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ if __name__ == "__main__":
2020
_pid_exists,
2121
get_container,
2222
)
23+
from ansys.fluent.core.utils import env_var_to_bool
2324
from ansys.fluent.core.utils.execution import timeout_exec, timeout_loop
2425

2526
watchdog_id = sys.argv[5]
@@ -34,7 +35,7 @@ if __name__ == "__main__":
3435

3536
logger = pyfluent.logger.get_logger("pyfluent.watchdog")
3637

37-
if os.getenv("PYFLUENT_WATCHDOG_DEBUG", "OFF").upper() in ("1", "ON"):
38+
if env_var_to_bool("PYFLUENT_WATCHDOG_DEBUG"):
3839
pyfluent.logger.enable(custom_config=log_config)
3940
logger.setLevel("DEBUG")
4041
logger.handlers = pyfluent.logger.get_logger(

src/ansys/fluent/core/report.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
"PYFLUENT_FLUENT_IP",
4545
"PYFLUENT_FLUENT_PORT",
4646
"PYFLUENT_FLUENT_ROOT",
47+
"PYFLUENT_FLUENT_LAUNCH_TIMEOUT",
48+
"PYFLUENT_FLUENT_AUTOMATIC_TRANSCRIPT",
4749
"PYFLUENT_GRPC_LOG_BYTES_LIMIT",
4850
"PYFLUENT_LAUNCH_CONTAINER",
4951
"PYFLUENT_LOGGING",

src/ansys/fluent/core/services/interceptors.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import grpc
3333

3434
from ansys.fluent.core.services.batch_ops import BatchOps
35+
from ansys.fluent.core.utils import env_var_to_bool
3536

3637
network_logger: logging.Logger = logging.getLogger("pyfluent.networking")
3738
log_bytes_limit: int = int(os.getenv("PYFLUENT_GRPC_LOG_BYTES_LIMIT", 1000))
@@ -75,7 +76,7 @@ def _intercept_call(
7576
if not response.exception():
7677
# call _truncate_grpc_str early to get the size warning even when hiding secrets
7778
response_str = _truncate_grpc_str(response.result())
78-
if os.getenv("PYFLUENT_HIDE_LOG_SECRETS") != "1":
79+
if not env_var_to_bool("PYFLUENT_HIDE_LOG_SECRETS"):
7980
network_logger.debug(f"GRPC_TRACE: response = {response_str}")
8081
return response
8182

src/ansys/fluent/core/streaming_services/datamodel_event_streaming.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
"""Provides a module for datamodel event streaming."""
2424

2525
import logging
26-
import os
2726
import threading
2827
from typing import Callable
2928

@@ -32,6 +31,7 @@
3231
from ansys.api.fluent.v0 import datamodel_se_pb2 as DataModelProtoModule
3332
from ansys.fluent.core.services.datamodel_se import _convert_variant_to_value
3433
from ansys.fluent.core.streaming_services.streaming import StreamingService
34+
from ansys.fluent.core.utils import env_var_to_bool
3535

3636
network_logger: logging.Logger = logging.getLogger("pyfluent.networking")
3737

@@ -69,7 +69,7 @@ def _process_streaming(self, id, stream_begin_method, started_evt, *args, **kwar
6969
while True:
7070
try:
7171
response: DataModelProtoModule.EventResponse = next(responses)
72-
if os.getenv("PYFLUENT_HIDE_LOG_SECRETS") != "1":
72+
if not env_var_to_bool("PYFLUENT_HIDE_LOG_SECRETS"):
7373
network_logger.debug(
7474
f"GRPC_TRACE: RPC = /grpcRemoting.DataModel/BeginEventStreaming, response = {MessageToDict(response)}"
7575
)

0 commit comments

Comments
 (0)