diff --git a/doc/changelog.d/4558.added.md b/doc/changelog.d/4558.added.md new file mode 100644 index 000000000000..d69c90b17e99 --- /dev/null +++ b/doc/changelog.d/4558.added.md @@ -0,0 +1 @@ +PathLike support throughout diff --git a/src/ansys/fluent/core/_types.py b/src/ansys/fluent/core/_types.py new file mode 100644 index 000000000000..639a5018146b --- /dev/null +++ b/src/ansys/fluent/core/_types.py @@ -0,0 +1,35 @@ +# Copyright (C) 2021 - 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. + + +"""Common type aliases for PyFluent. + +This module centralizes reusable typing constructs +""" + +from __future__ import annotations + +import os +from typing import TypeAlias + +PathType: TypeAlias = "os.PathLike[str] | os.PathLike[bytes] | str | bytes" +"""Type alias for file system paths.""" diff --git a/src/ansys/fluent/core/examples/downloads.py b/src/ansys/fluent/core/examples/downloads.py index f41527e71ccd..c5bf7405418d 100644 --- a/src/ansys/fluent/core/examples/downloads.py +++ b/src/ansys/fluent/core/examples/downloads.py @@ -30,6 +30,7 @@ import zipfile import ansys.fluent.core as pyfluent +from ansys.fluent.core._types import PathType from ansys.fluent.core.utils.networking import check_url_exists, get_url_content logger = logging.getLogger("pyfluent.networking") @@ -65,16 +66,14 @@ def _decompress(file_name: str) -> None: def _get_file_url(file_name: str, directory: str | None = None) -> str: """Get file URL.""" if directory: - return ( - "https://github.com/ansys/example-data/raw/main/" f"{directory}/{file_name}" - ) + return f"https://github.com/ansys/example-data/raw/main/{directory}/{file_name}" return f"https://github.com/ansys/example-data/raw/main/{file_name}" def _retrieve_file( url: str, file_name: str, - save_path: str | None = None, + save_path: "PathType | None" = None, return_without_path: bool | None = False, ) -> str: """Download specified file from specified URL.""" @@ -121,7 +120,7 @@ def _retrieve_file( def download_file( file_name: str, directory: str | None = None, - save_path: str | None = None, + save_path: "PathType | None" = None, return_without_path: bool | None = None, ) -> str: """Download specified example file from the Ansys example data repository. diff --git a/src/ansys/fluent/core/filereader/case_file.py b/src/ansys/fluent/core/filereader/case_file.py index 6150307632ee..20fce2b726c3 100644 --- a/src/ansys/fluent/core/filereader/case_file.py +++ b/src/ansys/fluent/core/filereader/case_file.py @@ -48,6 +48,7 @@ import defusedxml.ElementTree as ET import numpy as np +from ansys.fluent.core._types import PathType from ansys.fluent.core.solver.error_message import allowed_name_error_message from . import lispy @@ -616,17 +617,17 @@ class CaseFile(RPVarProcessor): def __init__( self, - case_file_name: str | None = None, - project_file_name: str | None = None, + case_file_name: "PathType | None" = None, + project_file_name: "PathType | None" = None, ) -> None: """Initialize a CaseFile object. Exactly one file path argument must be specified. Parameters ---------- - case_file_name : str + case_file_name : :class:`os.PathLike` | str | None The path of a case file. - project_file_name : str + project_file_name : :class:`os.PathLike` | str | None The path of a project file from which the case file is selected. """ self._is_case_file = False diff --git a/src/ansys/fluent/core/launcher/launcher.py b/src/ansys/fluent/core/launcher/launcher.py index 335c7c0ea82c..ecb8d8555194 100644 --- a/src/ansys/fluent/core/launcher/launcher.py +++ b/src/ansys/fluent/core/launcher/launcher.py @@ -32,6 +32,7 @@ from typing import Any, Dict import ansys.fluent.core as pyfluent +from ansys.fluent.core._types import PathType from ansys.fluent.core.fluent_connection import FluentConnection from ansys.fluent.core.launcher.container_launcher import DockerLauncher from ansys.fluent.core.launcher.launch_options import ( @@ -167,14 +168,14 @@ def launch_fluent( graphics_driver: ( FluentWindowsGraphicsDriver | FluentLinuxGraphicsDriver | str | None ) = None, - case_file_name: str | None = None, - case_data_file_name: str | None = None, + case_file_name: "PathType | None" = None, + case_data_file_name: "PathType | None" = None, lightweight_mode: bool | None = None, mode: FluentMode | str | None = None, py: bool | None = None, gpu: bool | list[int] | None = None, - cwd: str | None = None, - fluent_path: str | None = None, + cwd: "PathType | None" = None, + fluent_path: "PathType | None" = None, topy: str | list | None = None, start_watchdog: bool | None = None, scheduler_options: dict | None = None, @@ -255,9 +256,9 @@ def launch_fluent( ``"null"``, ``"x11"``, ``"opengl2"``, ``"opengl"`` or ``"auto"``. The default is ``FluentWindowsGraphicsDriver.AUTO`` in Windows and ``FluentLinuxGraphicsDriver.AUTO`` in Linux. - case_file_name : str, optional + case_file_name : :class:`os.PathLike` or str, optional If provided, the case file at ``case_file_name`` is read into the Fluent session. - case_data_file_name : str, optional + case_data_file_name : :class:`os.PathLike` or str, optional If provided, the case and data files at ``case_data_file_name`` are read into the Fluent session. lightweight_mode : bool, optional Whether to run in lightweight mode. In lightweight mode, the lightweight settings are read into the @@ -279,9 +280,9 @@ def launch_fluent( clamped to the value of ``processor_count``. Please refer to *Starting the Fluent GPU Solver* section in *Fluent's User Guide* for more information like how to determine the GPU IDs. - cwd : str, Optional + cwd : :class:`os.PathLike` or str, optional Working directory for the Fluent client. - fluent_path: str, Optional + fluent_path: :class:`os.PathLike` or str, optional User provided Fluent installation path. topy : bool or str, optional A boolean flag to write the equivalent Python journal(s) from the journal(s) passed. diff --git a/src/ansys/fluent/core/launcher/slurm_launcher.py b/src/ansys/fluent/core/launcher/slurm_launcher.py index 90af1df1f9fd..08a7683a8cf6 100644 --- a/src/ansys/fluent/core/launcher/slurm_launcher.py +++ b/src/ansys/fluent/core/launcher/slurm_launcher.py @@ -70,6 +70,7 @@ import time from typing import Any, Callable, Dict +from ansys.fluent.core._types import PathType from ansys.fluent.core.exceptions import InvalidArgument from ansys.fluent.core.launcher.launch_options import ( Dimension, @@ -298,13 +299,13 @@ def __init__( env: Dict[str, Any] | None = None, cleanup_on_exit: bool = True, start_transcript: bool = True, - case_file_name: str | None = None, - case_data_file_name: str | None = None, + case_file_name: "PathType | None" = None, + case_data_file_name: "PathType | None" = None, lightweight_mode: bool | None = None, py: bool | None = None, gpu: bool | None = None, - cwd: str | None = None, - fluent_path: str | None = None, + cwd: "PathType | None" = None, + fluent_path: "PathType | None" = None, topy: str | list | None = None, start_watchdog: bool | None = None, scheduler_options: dict | None = None, @@ -360,10 +361,10 @@ def __init__( default is ``True``. You can stop and start the streaming of the Fluent transcript subsequently via the method calls, ``transcript.start()`` and ``transcript.stop()`` on the session object. - case_file_name : str, optional + case_file_name : :class:`os.PathLike` or str, optional Name of the case file to read into the Fluent session. The default is ``None``. - case_data_file_name : str, optional + case_data_file_name : :class:`os.PathLike` or str, optional Name of the case data file. If names of both a case file and case data file are provided, they are read into the Fluent session. lightweight_mode : bool, optional Whether to run in lightweight mode. In lightweight mode, the lightweight settings are read into the @@ -376,9 +377,9 @@ def __init__( If True, Fluent will run in Python mode. Default is None. gpu : bool, optional If True, Fluent will start with GPU Solver. - cwd : str, Optional + cwd : :class:`os.PathLike` or str, optional Working directory for the Fluent client. - fluent_path: str, Optional + fluent_path: :class:`os.PathLike` or str, optional User provided Fluent installation path. topy : bool or str, optional A boolean flag to write the equivalent Python journal(s) from the journal(s) passed. diff --git a/src/ansys/fluent/core/launcher/standalone_launcher.py b/src/ansys/fluent/core/launcher/standalone_launcher.py index 34ca6a85640e..1c52ab562569 100644 --- a/src/ansys/fluent/core/launcher/standalone_launcher.py +++ b/src/ansys/fluent/core/launcher/standalone_launcher.py @@ -42,6 +42,7 @@ import subprocess from typing import Any, Dict +from ansys.fluent.core._types import PathType from ansys.fluent.core.launcher.error_handler import ( LaunchFluentError, _raise_non_gui_exception_in_windows, @@ -95,13 +96,13 @@ def __init__( cleanup_on_exit: bool = True, dry_run: bool = False, start_transcript: bool = True, - case_file_name: str | None = None, - case_data_file_name: str | None = None, + case_file_name: "PathType | None" = None, + case_data_file_name: "PathType | None" = None, lightweight_mode: bool | None = None, py: bool | None = None, gpu: bool | None = None, - cwd: str | None = None, - fluent_path: str | None = None, + cwd: "PathType | None" = None, + fluent_path: "PathType | None" = None, topy: str | list | None = None, start_watchdog: bool | None = None, file_transfer_service: Any | None = None, @@ -151,9 +152,9 @@ def __init__( start_transcript : bool, optional 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. - case_file_name : str, optional + case_file_name : :class:`os.PathLike` or str, optional Name of the case file to read into the Fluent session. Defaults to None. - case_data_file_name : str, optional + case_data_file_name : :class:`os.PathLike` or str, optional Name of the case data file. If both case and data files are provided, they are read into the session. lightweight_mode : bool, optional If True, runs in lightweight mode where mesh settings are read into a background solver session, @@ -162,9 +163,9 @@ def __init__( If True, runs Fluent in Python mode. Defaults to None. gpu : bool, optional If True, starts Fluent with GPU Solver enabled. - cwd : str, optional + cwd : :class:`os.PathLike` or str, optional Working directory for the Fluent client. - fluent_path: str, optional + fluent_path: :class:`os.PathLike` or str, optional User-specified path for Fluent installation. topy : bool or str, optional A flag indicating whether to write equivalent Python journals from provided journal files; can also specify diff --git a/src/ansys/fluent/core/meshing/meshing_workflow.py b/src/ansys/fluent/core/meshing/meshing_workflow.py index 2996c37a5231..a35dddc9141d 100644 --- a/src/ansys/fluent/core/meshing/meshing_workflow.py +++ b/src/ansys/fluent/core/meshing/meshing_workflow.py @@ -26,7 +26,9 @@ from __future__ import annotations from enum import Enum +import os +from ansys.fluent.core._types import PathType from ansys.fluent.core.services.datamodel_se import PyMenuGeneric from ansys.fluent.core.utils.fluent_version import FluentVersion from ansys.fluent.core.workflow import Workflow @@ -272,7 +274,7 @@ def __init__( self, workflow: PyMenuGeneric, meshing: PyMenuGeneric, - file_path: str, + file_path: PathType, fluent_version: FluentVersion, ) -> None: """Initialize a ``LoadWorkflow`` instance. @@ -283,8 +285,8 @@ def __init__( Underlying workflow object. meshing : PyMenuGeneric Meshing object. - file_path: str - Path to the saved workflow. + file_path: os.PathLike[str | bytes] | str | bytes + Path to the saved workflow file. fluent_version: FluentVersion Version of Fluent in this session. """ @@ -293,7 +295,7 @@ def __init__( ) self._meshing = meshing self._unsubscribe_root_affected_callback() - self._load_workflow(file_path=file_path) + self._load_workflow(file_path=os.fspath(file_path)) class CreateWorkflow(Workflow): diff --git a/src/ansys/fluent/core/parametric.py b/src/ansys/fluent/core/parametric.py index fb5c9ae19e79..2fc2889473dc 100644 --- a/src/ansys/fluent/core/parametric.py +++ b/src/ansys/fluent/core/parametric.py @@ -51,8 +51,10 @@ """ from math import ceil +import os from typing import Any, Dict +from ansys.fluent.core._types import PathType from ansys.fluent.core.launcher.launcher import launch_fluent from ansys.fluent.core.utils.execution import asynchronous @@ -60,7 +62,7 @@ def convert_design_point_parameter_units( - value: Dict[str, float | int | str] + value: Dict[str, float | int | str], ) -> Dict[str, float | int]: """Convert design point parameter units.""" @@ -293,13 +295,15 @@ class LocalParametricStudy: If the design point is not found. """ - def __init__(self, case_filepath: str, base_design_point_name: str = "Base DP"): + def __init__( + self, case_filepath: PathType, base_design_point_name: str = "Base DP" + ): """Initialize LocalParametricStudy.""" from ansys.fluent.core.filereader.casereader import CaseReader - self.case_filepath = case_filepath + self.case_filepath = os.fspath(case_filepath) base_design_point = LocalDesignPoint(base_design_point_name) - case_reader = CaseReader(case_file_name=case_filepath) + case_reader = CaseReader(case_file_name=self.case_filepath) base_design_point.input_parameters = { p.name: p.value for p in case_reader.input_parameters() diff --git a/src/ansys/fluent/core/services/app_utilities.py b/src/ansys/fluent/core/services/app_utilities.py index 3f24675a2fac..c19a8e26e514 100644 --- a/src/ansys/fluent/core/services/app_utilities.py +++ b/src/ansys/fluent/core/services/app_utilities.py @@ -24,12 +24,14 @@ from dataclasses import dataclass from enum import Enum +import os from typing import List, Tuple import grpc from ansys.api.fluent.v0 import app_utilities_pb2 as AppUtilitiesProtoModule from ansys.api.fluent.v0 import app_utilities_pb2_grpc as AppUtilitiesGrpcModule +from ansys.fluent.core._types import PathType from ansys.fluent.core.services.interceptors import ( BatchInterceptor, ErrorStateInterceptor, @@ -306,7 +308,7 @@ def register_pause_on_solution_events(self, solution_event: SolverEvent) -> int: ) () ) - {'#t' if solution_event == SolverEvent.TIMESTEP_ENDED else '#f'} + {"#t" if solution_event == SolverEvent.TIMESTEP_ENDED else "#f"} ) (car ids) ) @@ -328,9 +330,9 @@ def exit(self) -> None: """Exit.""" self.scheme.exec(("(exit-server)",)) - def set_working_directory(self, path: str) -> None: + def set_working_directory(self, path: PathType) -> None: """Change client cortex dir.""" - self.scheme.eval(f'(syncdir "{path}")') + self.scheme.eval(f'(syncdir "{os.fspath(path)}")') class AppUtilities: @@ -474,10 +476,10 @@ def exit(self) -> None: request = AppUtilitiesProtoModule.ExitRequest() self.service.exit(request) - def set_working_directory(self, path: str) -> None: + def set_working_directory(self, path: PathType) -> None: """Change client cortex dir.""" request = AppUtilitiesProtoModule.SetWorkingDirectoryRequest() - request.path = path + request.path = os.fspath(path) self.service.set_working_directory(request) diff --git a/src/ansys/fluent/core/session.py b/src/ansys/fluent/core/session.py index 67439b2d6c46..c5fd156c278d 100644 --- a/src/ansys/fluent/core/session.py +++ b/src/ansys/fluent/core/session.py @@ -25,12 +25,14 @@ from enum import Enum import json import logging +import os from typing import Any, Callable, Dict import warnings import weakref from deprecated.sphinx import deprecated +from ansys.fluent.core._types import PathType from ansys.fluent.core.fluent_connection import FluentConnection from ansys.fluent.core.journaling import Journal from ansys.fluent.core.pyfluent_warnings import ( @@ -452,15 +454,15 @@ def download(self, file_name: str, local_directory: str | None = None): if self._file_transfer_service: return self._file_transfer_service.download(file_name, local_directory) - def chdir(self, path: str) -> None: + def chdir(self, path: PathType) -> None: """Change Fluent working directory. Parameters ---------- - path : str + path : os.PathLike[str | bytes] | str | bytes Path of the directory to change. """ - self._app_utilities.set_working_directory(path) + self._app_utilities.set_working_directory(os.fspath(path)) def __enter__(self): return self diff --git a/src/ansys/fluent/core/session_base_meshing.py b/src/ansys/fluent/core/session_base_meshing.py index dd1772cd7579..a13f415a598a 100644 --- a/src/ansys/fluent/core/session_base_meshing.py +++ b/src/ansys/fluent/core/session_base_meshing.py @@ -23,7 +23,9 @@ """Provides a module to get base Meshing session.""" import logging +import os +from ansys.fluent.core._types import PathType from ansys.fluent.core.fluent_connection import FluentConnection from ansys.fluent.core.meshing.meshing_workflow import ( CreateWorkflow, @@ -177,12 +179,12 @@ def topology_based_meshing_workflow(self, initialize: bool = True): ) return self._current_workflow - def load_workflow(self, file_path: str): + def load_workflow(self, file_path: PathType): """Datamodel root of workflow.""" self._current_workflow = LoadWorkflow( _make_datamodel_module(self, "workflow"), self.meshing, - file_path, + os.fspath(file_path), self.get_fluent_version(), ) return self._current_workflow diff --git a/src/ansys/fluent/core/session_pure_meshing.py b/src/ansys/fluent/core/session_pure_meshing.py index bfe5fcf94b3a..5ad58a9c2f6f 100644 --- a/src/ansys/fluent/core/session_pure_meshing.py +++ b/src/ansys/fluent/core/session_pure_meshing.py @@ -23,9 +23,11 @@ """Module containing class encapsulating Fluent connection.""" import functools +import os from typing import Any, Dict import ansys.fluent.core as pyfluent +from ansys.fluent.core._types import PathType from ansys.fluent.core.data_model_cache import DataModelCache, NameKey from ansys.fluent.core.exceptions import BetaFeaturesNotEnabled from ansys.fluent.core.fluent_connection import FluentConnection @@ -164,9 +166,9 @@ def two_dimensional_meshing(self): """Get a new 2D meshing workflow.""" return self._base_meshing.two_dimensional_meshing_workflow() - def load_workflow(self, file_path: str): + def load_workflow(self, file_path: PathType): """Load a saved workflow.""" - return self._base_meshing.load_workflow(file_path=file_path) + return self._base_meshing.load_workflow(file_path=os.fspath(file_path)) def create_workflow(self): """Create a meshing workflow.""" diff --git a/src/ansys/fluent/core/session_utilities.py b/src/ansys/fluent/core/session_utilities.py index 37784857c27f..34ec296d91f2 100644 --- a/src/ansys/fluent/core/session_utilities.py +++ b/src/ansys/fluent/core/session_utilities.py @@ -25,6 +25,7 @@ from typing import Any, Dict import ansys.fluent.core as pyfluent +from ansys.fluent.core._types import PathType from ansys.fluent.core.launcher.container_launcher import DockerLauncher from ansys.fluent.core.launcher.launch_options import ( Dimension, @@ -74,13 +75,13 @@ def from_install( cleanup_on_exit: bool = True, dry_run: bool = False, start_transcript: bool = True, - case_file_name: str | None = None, - case_data_file_name: str | None = None, + case_file_name: "PathType | None" = None, + case_data_file_name: "PathType | None" = None, lightweight_mode: bool | None = None, py: bool | None = None, gpu: bool | None = None, - cwd: str | None = None, - fluent_path: str | None = None, + cwd: "PathType | None" = None, + fluent_path: "PathType | None" = None, topy: str | list | None = None, start_watchdog: bool | None = None, file_transfer_service: Any | None = None, @@ -128,9 +129,9 @@ def from_install( start_transcript : bool, optional 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. - case_file_name : str, optional + case_file_name : :class:`os.PathLike` or str, optional Name of the case file to read into the Fluent session. Defaults to None. - case_data_file_name : str, optional + case_data_file_name : :class:`os.PathLike` or str, optional Name of the case data file. If both case and data files are provided, they are read into the session. lightweight_mode : bool, optional If True, runs in lightweight mode where mesh settings are read into a background solver session, @@ -139,9 +140,9 @@ def from_install( If True, runs Fluent in Python mode. Defaults to None. gpu : bool, optional If True, starts Fluent with GPU Solver enabled. - cwd : str, optional + cwd : :class:`os.PathLike` or str, optional Working directory for the Fluent client. - fluent_path: str, optional + fluent_path: :class:`os.PathLike` or str, optional User-specified path for Fluent installation. topy : bool or str, optional A flag indicating whether to write equivalent Python journals from provided journal files; can also specify