From 51716e51568d78d3bde7e5510b49a3e0f6dc4395 Mon Sep 17 00:00:00 2001 From: "etienne.arnal" Date: Tue, 25 Nov 2025 14:38:27 +0100 Subject: [PATCH 01/10] fet: Add fullpath to set UDS sock filename --- src/ansys/tools/common/cyberchannel.py | 60 ++++++++++++++++++-------- 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/src/ansys/tools/common/cyberchannel.py b/src/ansys/tools/common/cyberchannel.py index 06d6a27c..28b513b6 100644 --- a/src/ansys/tools/common/cyberchannel.py +++ b/src/ansys/tools/common/cyberchannel.py @@ -75,6 +75,7 @@ def create_channel( transport_mode: str, host: str | None = None, port: int | str | None = None, + uds_fullpath: str | Path | None = None, uds_service: str | None = None, uds_dir: str | Path | None = None, uds_id: str | None = None, @@ -97,6 +98,9 @@ def create_channel( Port in which the server is running. By default `None` - however, if not using UDS transport mode, it will be requested. + uds_fullpath : str | Path | None + Full path to the UDS socket file. + By default `None` and thus it will use the `uds_service`, `uds_dir` and `uds_id` parameters. uds_service : str | None Optional service name for the UDS socket. By default `None` - however, if UDS is selected, it will @@ -141,7 +145,7 @@ def check_host_port(transport_mode, host, port) -> tuple[str, str, str]: transport_mode, host, port = check_host_port(transport_mode, host, port) return create_insecure_channel(host, port, grpc_options) case "uds": - return create_uds_channel(uds_service, uds_dir, uds_id, grpc_options) + return create_uds_channel(uds_fullpath, uds_service, uds_dir, uds_id, grpc_options) case "wnua": transport_mode, host, port = check_host_port(transport_mode, host, port) return create_wnua_channel(host, port, grpc_options) @@ -186,7 +190,8 @@ def create_insecure_channel( def create_uds_channel( - uds_service: str | None, + uds_fullpath: str | Path | None = None, + uds_service: str | None = None, uds_dir: str | Path | None = None, uds_id: str | None = None, grpc_options: list[tuple[str, object]] | None = None, @@ -195,6 +200,9 @@ def create_uds_channel( Parameters ---------- + uds_fullpath : str | Path | None + Full path to the UDS socket file. + By default `None` and thus it will use the `uds_service`, `uds_dir` and `uds_id` parameters. uds_service : str Service name for the UDS socket. uds_dir : str | Path | None @@ -218,18 +226,22 @@ def create_uds_channel( if not is_uds_supported(): raise RuntimeError("Unix Domain Sockets are not supported on this platform or gRPC version.") - if not uds_service: - raise ValueError("When using UDS transport mode, 'uds_service' must be provided.") + if uds_fullpath: + target = f"unix:{uds_fullpath}" + else: + if not uds_service: + raise ValueError("When using UDS transport mode, 'uds_service' must be provided.") + + # Determine UDS folder + uds_folder = determine_uds_folder(uds_dir) - # Determine UDS folder - uds_folder = determine_uds_folder(uds_dir) + # Make sure the folder exists + uds_folder.mkdir(parents=True, exist_ok=True) - # Make sure the folder exists - uds_folder.mkdir(parents=True, exist_ok=True) + # Generate socket filename with optional ID + socket_filename = f"{uds_service}-{uds_id}.sock" if uds_id else f"{uds_service}.sock" + target = f"unix:{uds_folder / socket_filename}" - # Generate socket filename with optional ID - socket_filename = f"{uds_service}-{uds_id}.sock" if uds_id else f"{uds_service}.sock" - target = f"unix:{uds_folder / socket_filename}" # Set default authority to "localhost" for UDS connection # This is needed to avoid issues with some gRPC implementations, # see https://github.com/grpc/grpc/issues/34305 @@ -476,7 +488,12 @@ def verify_transport_mode(transport_mode: str, mode: str | None = None) -> None: raise ValueError(f"Invalid transport mode: {transport_mode}. Valid options are: {', '.join(valid_modes)}.") -def verify_uds_socket(uds_service: str, uds_dir: Path | None = None, uds_id: str | None = None) -> bool: +def verify_uds_socket( + uds_fullpath: str | Path | None = None, + uds_service: str | None = None, + uds_dir: Path | None = None, + uds_id: str | None = None, +) -> bool: """Verify that the UDS socket file has been created. Parameters @@ -496,11 +513,18 @@ def verify_uds_socket(uds_service: str, uds_dir: Path | None = None, uds_id: str bool True if the UDS socket file exists, False otherwise. """ - # Generate socket filename with optional ID - uds_filename = f"{uds_service}-{uds_id}.sock" if uds_id else f"{uds_service}.sock" + if uds_fullpath: + return Path(uds_fullpath).exists() + + else: + if not uds_service: + raise ValueError("When using UDS transport mode, 'uds_service' must be provided.") + + # Generate socket filename with optional ID + uds_filename = f"{uds_service}-{uds_id}.sock" if uds_id else f"{uds_service}.sock" - # Full path to the UDS socket file - uds_socket_path = determine_uds_folder(uds_dir) / uds_filename + # Full path to the UDS socket file + uds_socket_path = determine_uds_folder(uds_dir) / uds_filename - # Check if the UDS socket file exists - return uds_socket_path.exists() + # Check if the UDS socket file exists + return uds_socket_path.exists() From 9cc65fa7c067cfc18b6086048cd12a31c2e054db Mon Sep 17 00:00:00 2001 From: pyansys-ci-bot <92810346+pyansys-ci-bot@users.noreply.github.com> Date: Tue, 25 Nov 2025 13:45:27 +0000 Subject: [PATCH 02/10] chore: adding changelog file 114.miscellaneous.md [dependabot-skip] --- doc/source/changelog/114.miscellaneous.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 doc/source/changelog/114.miscellaneous.md diff --git a/doc/source/changelog/114.miscellaneous.md b/doc/source/changelog/114.miscellaneous.md new file mode 100644 index 00000000..6663355c --- /dev/null +++ b/doc/source/changelog/114.miscellaneous.md @@ -0,0 +1 @@ +Feat: Add fullpath to set UDS sock filename From e5b8f4e2df921bf4ff52cd52de3524ae6733cbd2 Mon Sep 17 00:00:00 2001 From: Etienne Arnal <95644345+etiennearnal@users.noreply.github.com> Date: Tue, 25 Nov 2025 15:40:01 +0100 Subject: [PATCH 03/10] Update src/ansys/tools/common/cyberchannel.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/ansys/tools/common/cyberchannel.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ansys/tools/common/cyberchannel.py b/src/ansys/tools/common/cyberchannel.py index 28b513b6..9a0a421c 100644 --- a/src/ansys/tools/common/cyberchannel.py +++ b/src/ansys/tools/common/cyberchannel.py @@ -227,6 +227,8 @@ def create_uds_channel( raise RuntimeError("Unix Domain Sockets are not supported on this platform or gRPC version.") if uds_fullpath: + # Ensure the parent directory exists + Path(uds_fullpath).parent.mkdir(parents=True, exist_ok=True) target = f"unix:{uds_fullpath}" else: if not uds_service: From 830e499536c69dd1ddafa8afc8317d5e5b5897ff Mon Sep 17 00:00:00 2001 From: Etienne Arnal <95644345+etiennearnal@users.noreply.github.com> Date: Tue, 25 Nov 2025 15:40:10 +0100 Subject: [PATCH 04/10] Update src/ansys/tools/common/cyberchannel.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/ansys/tools/common/cyberchannel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ansys/tools/common/cyberchannel.py b/src/ansys/tools/common/cyberchannel.py index 9a0a421c..fc609d6e 100644 --- a/src/ansys/tools/common/cyberchannel.py +++ b/src/ansys/tools/common/cyberchannel.py @@ -203,7 +203,7 @@ def create_uds_channel( uds_fullpath : str | Path | None Full path to the UDS socket file. By default `None` and thus it will use the `uds_service`, `uds_dir` and `uds_id` parameters. - uds_service : str + uds_service : str | None Service name for the UDS socket. uds_dir : str | Path | None Directory to use for Unix Domain Sockets (UDS) transport mode. From ea2ec8f2aadd4241692e4dd29f7f7f16643e439e Mon Sep 17 00:00:00 2001 From: "etienne.arnal" Date: Wed, 26 Nov 2025 16:47:02 +0100 Subject: [PATCH 05/10] fix --- src/ansys/tools/common/cyberchannel.py | 13 ++-- tests/test_cyberchannel.py | 82 ++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 5 deletions(-) create mode 100644 tests/test_cyberchannel.py diff --git a/src/ansys/tools/common/cyberchannel.py b/src/ansys/tools/common/cyberchannel.py index 28b513b6..5e6b50fb 100644 --- a/src/ansys/tools/common/cyberchannel.py +++ b/src/ansys/tools/common/cyberchannel.py @@ -190,19 +190,16 @@ def create_insecure_channel( def create_uds_channel( - uds_fullpath: str | Path | None = None, uds_service: str | None = None, uds_dir: str | Path | None = None, uds_id: str | None = None, grpc_options: list[tuple[str, object]] | None = None, + uds_fullpath: str | Path | None = None, ) -> grpc.Channel: """Create a gRPC channel using Unix Domain Sockets (UDS). Parameters ---------- - uds_fullpath : str | Path | None - Full path to the UDS socket file. - By default `None` and thus it will use the `uds_service`, `uds_dir` and `uds_id` parameters. uds_service : str Service name for the UDS socket. uds_dir : str | Path | None @@ -216,6 +213,9 @@ def create_uds_channel( gRPC channel options to pass when creating the channel. Each option is a tuple of the form ("option_name", value). By default `None` and thus only the default authority option is added. + uds_fullpath : str | Path | None + Full path to the UDS socket file. + By default `None` and thus it will use the `uds_service`, `uds_dir` and `uds_id` parameters. Returns ------- @@ -489,10 +489,10 @@ def verify_transport_mode(transport_mode: str, mode: str | None = None) -> None: def verify_uds_socket( - uds_fullpath: str | Path | None = None, uds_service: str | None = None, uds_dir: Path | None = None, uds_id: str | None = None, + uds_fullpath: str | Path | None = None, ) -> bool: """Verify that the UDS socket file has been created. @@ -507,6 +507,9 @@ def verify_uds_socket( Unique identifier for the UDS socket (optional). By default `None` and thus it will use ".sock". Otherwise, the socket filename will be "-.sock". + uds_fullpath : str | Path | None + Full path to the UDS socket file. + By default `None` and thus it will use the `uds_service`, `uds_dir` and `uds_id` parameters. Returns ------- diff --git a/tests/test_cyberchannel.py b/tests/test_cyberchannel.py new file mode 100644 index 00000000..8fedffae --- /dev/null +++ b/tests/test_cyberchannel.py @@ -0,0 +1,82 @@ +# Copyright (C) 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. +"""Tests for cyberchannel.""" + +from pathlib import Path +import tempfile + +import pytest + +from ansys.tools.common import cyberchannel + + +def test_version_tuple(): + """Test version tuple.""" + assert cyberchannel.version_tuple("1.2.3") == (1, 2, 3) + assert cyberchannel.version_tuple("1.2.3.4") == (1, 2, 3, 4) + assert cyberchannel.version_tuple("1.0.0") == (1, 0, 0) + + +def test_cyberchannel_functions(): + """Test cyberchannel functions.""" + assert cyberchannel.check_grpc_version() + assert cyberchannel.is_uds_supported() + uds_path = cyberchannel.determine_uds_folder() + uds_path.mkdir(parents=True, exist_ok=True) + assert uds_path.is_dir() + assert uds_path.exists() + uds_path.rmdir() + cyberchannel.verify_transport_mode(transport_mode="insecure", mode="local") + with pytest.raises(ValueError): + cyberchannel.verify_transport_mode(transport_mode="invalid_mode", mode="mode1") + + +def test_cyberchannel_insecure(): + """Test cyberchannel insecure.""" + ch = cyberchannel.create_insecure_channel(host="localhost", port=12345) + assert ch is not None + assert ch._channel.target().decode() == "dns:///localhost:12345" + assert not ch.close() + + +def test_cyberchannel_wnua(): + """Test cyberchannel wnua.""" + ch = cyberchannel.create_wnua_channel(host="localhost", port=12345) + assert ch is not None + assert ch._channel.target().decode() == "dns:///localhost:12345" + assert not ch.close() + + +def test_cyberchannel_uds(): + """Test cyberchannel uds.""" + uds_file = Path(tempfile.gettempdir()) / "test_uds.sock" + with uds_file.open("w"): + pass + ch = cyberchannel.create_uds_channel(uds_fullpath=uds_file) + assert ch is not None + assert ch._channel.target().decode() == f"unix:{uds_file}" + assert not ch.close() + + ch = cyberchannel.create_uds_channel("service_name") + assert ch is not None + assert ch._channel.target().decode() == f"unix:{cyberchannel.determine_uds_folder() / 'service_name.sock'}" + assert not ch.close() From 51ac18564f99889e31f50287d39f21d89a156dbe Mon Sep 17 00:00:00 2001 From: "etienne.arnal" Date: Thu, 27 Nov 2025 08:58:25 +0100 Subject: [PATCH 06/10] fix issue --- src/ansys/tools/common/cyberchannel.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ansys/tools/common/cyberchannel.py b/src/ansys/tools/common/cyberchannel.py index 751a9bb9..4487bd44 100644 --- a/src/ansys/tools/common/cyberchannel.py +++ b/src/ansys/tools/common/cyberchannel.py @@ -75,10 +75,10 @@ def create_channel( transport_mode: str, host: str | None = None, port: int | str | None = None, - uds_fullpath: str | Path | None = None, uds_service: str | None = None, uds_dir: str | Path | None = None, uds_id: str | None = None, + uds_fullpath: str | Path | None = None, certs_dir: str | Path | None = None, cert_files: CertificateFiles | None = None, grpc_options: list[tuple[str, object]] | None = None, @@ -98,9 +98,6 @@ def create_channel( Port in which the server is running. By default `None` - however, if not using UDS transport mode, it will be requested. - uds_fullpath : str | Path | None - Full path to the UDS socket file. - By default `None` and thus it will use the `uds_service`, `uds_dir` and `uds_id` parameters. uds_service : str | None Optional service name for the UDS socket. By default `None` - however, if UDS is selected, it will @@ -112,6 +109,9 @@ def create_channel( Optional ID to use for the UDS socket filename. By default `None` and thus it will use ".sock". Otherwise, the socket filename will be "-.sock". + uds_fullpath : str | Path | None + Full path to the UDS socket file. + By default `None` and thus it will use the `uds_service`, `uds_dir` and `uds_id` parameters. certs_dir : str | Path | None Directory to use for TLS certificates. By default `None` and thus search for the "ANSYS_GRPC_CERTIFICATES" environment variable. @@ -145,7 +145,7 @@ def check_host_port(transport_mode, host, port) -> tuple[str, str, str]: transport_mode, host, port = check_host_port(transport_mode, host, port) return create_insecure_channel(host, port, grpc_options) case "uds": - return create_uds_channel(uds_fullpath, uds_service, uds_dir, uds_id, grpc_options) + return create_uds_channel(uds_service, uds_dir, uds_id, grpc_options, uds_fullpath) case "wnua": transport_mode, host, port = check_host_port(transport_mode, host, port) return create_wnua_channel(host, port, grpc_options) @@ -190,7 +190,7 @@ def create_insecure_channel( def create_uds_channel( - uds_service: str | None = None, + uds_service: str | None, uds_dir: str | Path | None = None, uds_id: str | None = None, grpc_options: list[tuple[str, object]] | None = None, From ae065989ba3e145ca3ea3a25037698addf8cbbb7 Mon Sep 17 00:00:00 2001 From: pyansys-ci-bot <92810346+pyansys-ci-bot@users.noreply.github.com> Date: Thu, 27 Nov 2025 07:58:59 +0000 Subject: [PATCH 07/10] chore: adding changelog file 114.miscellaneous.md [dependabot-skip] --- doc/source/changelog/114.miscellaneous.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/changelog/114.miscellaneous.md b/doc/source/changelog/114.miscellaneous.md index 6663355c..97b016a8 100644 --- a/doc/source/changelog/114.miscellaneous.md +++ b/doc/source/changelog/114.miscellaneous.md @@ -1 +1 @@ -Feat: Add fullpath to set UDS sock filename +Feat: add fullpath to set UDS sock filename From d8cf6092139e5c515df08edbd52923231ae2a030 Mon Sep 17 00:00:00 2001 From: "etienne.arnal" Date: Thu, 27 Nov 2025 14:25:15 +0100 Subject: [PATCH 08/10] fix-ut --- src/ansys/tools/common/cyberchannel.py | 2 +- tests/test_cyberchannel.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ansys/tools/common/cyberchannel.py b/src/ansys/tools/common/cyberchannel.py index 4487bd44..f66bfe3a 100644 --- a/src/ansys/tools/common/cyberchannel.py +++ b/src/ansys/tools/common/cyberchannel.py @@ -190,7 +190,7 @@ def create_insecure_channel( def create_uds_channel( - uds_service: str | None, + uds_service: str | None = None, uds_dir: str | Path | None = None, uds_id: str | None = None, grpc_options: list[tuple[str, object]] | None = None, diff --git a/tests/test_cyberchannel.py b/tests/test_cyberchannel.py index 8fedffae..a33d0a2d 100644 --- a/tests/test_cyberchannel.py +++ b/tests/test_cyberchannel.py @@ -21,6 +21,7 @@ # SOFTWARE. """Tests for cyberchannel.""" +import os from pathlib import Path import tempfile @@ -58,6 +59,7 @@ def test_cyberchannel_insecure(): assert not ch.close() +@pytest.mark.skipif(os.name != "nt", reason="WNUA is only supported on Windows.") def test_cyberchannel_wnua(): """Test cyberchannel wnua.""" ch = cyberchannel.create_wnua_channel(host="localhost", port=12345) @@ -71,7 +73,7 @@ def test_cyberchannel_uds(): uds_file = Path(tempfile.gettempdir()) / "test_uds.sock" with uds_file.open("w"): pass - ch = cyberchannel.create_uds_channel(uds_fullpath=uds_file) + ch = cyberchannel.create_uds_channel_fullpath(uds_fullpath=uds_file) assert ch is not None assert ch._channel.target().decode() == f"unix:{uds_file}" assert not ch.close() From dbbbe570fa9e8cd3a185c2366d0f7eadbf064a4f Mon Sep 17 00:00:00 2001 From: "etienne.arnal" Date: Thu, 27 Nov 2025 15:00:49 +0100 Subject: [PATCH 09/10] fix --- tests/test_cyberchannel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cyberchannel.py b/tests/test_cyberchannel.py index a33d0a2d..21d6c9be 100644 --- a/tests/test_cyberchannel.py +++ b/tests/test_cyberchannel.py @@ -73,7 +73,7 @@ def test_cyberchannel_uds(): uds_file = Path(tempfile.gettempdir()) / "test_uds.sock" with uds_file.open("w"): pass - ch = cyberchannel.create_uds_channel_fullpath(uds_fullpath=uds_file) + ch = cyberchannel.create_uds_channel(uds_fullpath=uds_file) assert ch is not None assert ch._channel.target().decode() == f"unix:{uds_file}" assert not ch.close() From fb70c5c02198e549400b8b532939bf0da3b66ff5 Mon Sep 17 00:00:00 2001 From: Roberto Pastor Muela <37798125+RobPasMue@users.noreply.github.com> Date: Thu, 27 Nov 2025 15:17:46 +0100 Subject: [PATCH 10/10] fix: minor changes --- src/ansys/tools/common/cyberchannel.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/ansys/tools/common/cyberchannel.py b/src/ansys/tools/common/cyberchannel.py index f66bfe3a..7977c3bd 100644 --- a/src/ansys/tools/common/cyberchannel.py +++ b/src/ansys/tools/common/cyberchannel.py @@ -200,7 +200,7 @@ def create_uds_channel( Parameters ---------- - uds_service : str + uds_service : str | None Service name for the UDS socket. uds_dir : str | Path | None Directory to use for Unix Domain Sockets (UDS) transport mode. @@ -231,7 +231,7 @@ def create_uds_channel( Path(uds_fullpath).parent.mkdir(parents=True, exist_ok=True) target = f"unix:{uds_fullpath}" else: - if not uds_service: + if uds_service is None: raise ValueError("When using UDS transport mode, 'uds_service' must be provided.") # Determine UDS folder @@ -500,7 +500,7 @@ def verify_uds_socket( Parameters ---------- - uds_service : str + uds_service : str | None Service name for the UDS socket. uds_dir : Path | None Directory where the UDS socket file is expected to be (optional). @@ -520,9 +520,8 @@ def verify_uds_socket( """ if uds_fullpath: return Path(uds_fullpath).exists() - else: - if not uds_service: + if uds_service is None: raise ValueError("When using UDS transport mode, 'uds_service' must be provided.") # Generate socket filename with optional ID