Skip to content

Commit 07ef370

Browse files
authored
Merge branch 'main' into feat/launcher-grpc-transport-options
2 parents 7b68a2b + 73485da commit 07ef370

File tree

6 files changed

+136
-22
lines changed

6 files changed

+136
-22
lines changed

.github/workflows/cicd.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ jobs:
119119
persist-credentials: false
120120

121121
- name: Set up uv
122-
uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # 7.1.3
122+
uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # 7.1.4
123123
with:
124124
python-version: ${{ env.MAIN_PYTHON_VERSION }}
125125
enable-cache: false

.github/workflows/run_mapdl_tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ jobs:
4242
persist-credentials: false
4343

4444
- name: Set up uv
45-
uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # 7.1.3
45+
uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # 7.1.4
4646
with:
4747
python-version: ${{ env.MAIN_PYTHON_VERSION }}
4848
enable-cache: false
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Feat: add fullpath to set UDS sock filename
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Ci: bump astral-sh/setup-uv from 7.1.3 to 7.1.4

src/ansys/tools/common/cyberchannel.py

Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ def create_channel(
7878
uds_service: str | None = None,
7979
uds_dir: str | Path | None = None,
8080
uds_id: str | None = None,
81+
uds_fullpath: str | Path | None = None,
8182
certs_dir: str | Path | None = None,
8283
cert_files: CertificateFiles | None = None,
8384
grpc_options: list[tuple[str, object]] | None = None,
@@ -108,6 +109,9 @@ def create_channel(
108109
Optional ID to use for the UDS socket filename.
109110
By default `None` and thus it will use "<uds_service>.sock".
110111
Otherwise, the socket filename will be "<uds_service>-<uds_id>.sock".
112+
uds_fullpath : str | Path | None
113+
Full path to the UDS socket file.
114+
By default `None` and thus it will use the `uds_service`, `uds_dir` and `uds_id` parameters.
111115
certs_dir : str | Path | None
112116
Directory to use for TLS certificates.
113117
By default `None` and thus search for the "ANSYS_GRPC_CERTIFICATES" environment variable.
@@ -141,7 +145,7 @@ def check_host_port(transport_mode, host, port) -> tuple[str, str, str]:
141145
transport_mode, host, port = check_host_port(transport_mode, host, port)
142146
return create_insecure_channel(host, port, grpc_options)
143147
case "uds":
144-
return create_uds_channel(uds_service, uds_dir, uds_id, grpc_options)
148+
return create_uds_channel(uds_service, uds_dir, uds_id, grpc_options, uds_fullpath)
145149
case "wnua":
146150
transport_mode, host, port = check_host_port(transport_mode, host, port)
147151
return create_wnua_channel(host, port, grpc_options)
@@ -186,16 +190,17 @@ def create_insecure_channel(
186190

187191

188192
def create_uds_channel(
189-
uds_service: str | None,
193+
uds_service: str | None = None,
190194
uds_dir: str | Path | None = None,
191195
uds_id: str | None = None,
192196
grpc_options: list[tuple[str, object]] | None = None,
197+
uds_fullpath: str | Path | None = None,
193198
) -> grpc.Channel:
194199
"""Create a gRPC channel using Unix Domain Sockets (UDS).
195200
196201
Parameters
197202
----------
198-
uds_service : str
203+
uds_service : str | None
199204
Service name for the UDS socket.
200205
uds_dir : str | Path | None
201206
Directory to use for Unix Domain Sockets (UDS) transport mode.
@@ -208,6 +213,9 @@ def create_uds_channel(
208213
gRPC channel options to pass when creating the channel.
209214
Each option is a tuple of the form ("option_name", value).
210215
By default `None` and thus only the default authority option is added.
216+
uds_fullpath : str | Path | None
217+
Full path to the UDS socket file.
218+
By default `None` and thus it will use the `uds_service`, `uds_dir` and `uds_id` parameters.
211219
212220
Returns
213221
-------
@@ -218,18 +226,24 @@ def create_uds_channel(
218226
if not is_uds_supported():
219227
raise RuntimeError("Unix Domain Sockets are not supported on this platform or gRPC version.")
220228

221-
if not uds_service:
222-
raise ValueError("When using UDS transport mode, 'uds_service' must be provided.")
229+
if uds_fullpath:
230+
# Ensure the parent directory exists
231+
Path(uds_fullpath).parent.mkdir(parents=True, exist_ok=True)
232+
target = f"unix:{uds_fullpath}"
233+
else:
234+
if uds_service is None:
235+
raise ValueError("When using UDS transport mode, 'uds_service' must be provided.")
223236

224-
# Determine UDS folder
225-
uds_folder = determine_uds_folder(uds_dir)
237+
# Determine UDS folder
238+
uds_folder = determine_uds_folder(uds_dir)
226239

227-
# Make sure the folder exists
228-
uds_folder.mkdir(parents=True, exist_ok=True)
240+
# Make sure the folder exists
241+
uds_folder.mkdir(parents=True, exist_ok=True)
242+
243+
# Generate socket filename with optional ID
244+
socket_filename = f"{uds_service}-{uds_id}.sock" if uds_id else f"{uds_service}.sock"
245+
target = f"unix:{uds_folder / socket_filename}"
229246

230-
# Generate socket filename with optional ID
231-
socket_filename = f"{uds_service}-{uds_id}.sock" if uds_id else f"{uds_service}.sock"
232-
target = f"unix:{uds_folder / socket_filename}"
233247
# Set default authority to "localhost" for UDS connection
234248
# This is needed to avoid issues with some gRPC implementations,
235249
# see https://github.com/grpc/grpc/issues/34305
@@ -476,12 +490,17 @@ def verify_transport_mode(transport_mode: str, mode: str | None = None) -> None:
476490
raise ValueError(f"Invalid transport mode: {transport_mode}. Valid options are: {', '.join(valid_modes)}.")
477491

478492

479-
def verify_uds_socket(uds_service: str, uds_dir: Path | None = None, uds_id: str | None = None) -> bool:
493+
def verify_uds_socket(
494+
uds_service: str | None = None,
495+
uds_dir: Path | None = None,
496+
uds_id: str | None = None,
497+
uds_fullpath: str | Path | None = None,
498+
) -> bool:
480499
"""Verify that the UDS socket file has been created.
481500
482501
Parameters
483502
----------
484-
uds_service : str
503+
uds_service : str | None
485504
Service name for the UDS socket.
486505
uds_dir : Path | None
487506
Directory where the UDS socket file is expected to be (optional).
@@ -490,17 +509,26 @@ def verify_uds_socket(uds_service: str, uds_dir: Path | None = None, uds_id: str
490509
Unique identifier for the UDS socket (optional).
491510
By default `None` and thus it will use "<uds_service>.sock".
492511
Otherwise, the socket filename will be "<uds_service>-<uds_id>.sock".
512+
uds_fullpath : str | Path | None
513+
Full path to the UDS socket file.
514+
By default `None` and thus it will use the `uds_service`, `uds_dir` and `uds_id` parameters.
493515
494516
Returns
495517
-------
496518
bool
497519
True if the UDS socket file exists, False otherwise.
498520
"""
499-
# Generate socket filename with optional ID
500-
uds_filename = f"{uds_service}-{uds_id}.sock" if uds_id else f"{uds_service}.sock"
521+
if uds_fullpath:
522+
return Path(uds_fullpath).exists()
523+
else:
524+
if uds_service is None:
525+
raise ValueError("When using UDS transport mode, 'uds_service' must be provided.")
526+
527+
# Generate socket filename with optional ID
528+
uds_filename = f"{uds_service}-{uds_id}.sock" if uds_id else f"{uds_service}.sock"
501529

502-
# Full path to the UDS socket file
503-
uds_socket_path = determine_uds_folder(uds_dir) / uds_filename
530+
# Full path to the UDS socket file
531+
uds_socket_path = determine_uds_folder(uds_dir) / uds_filename
504532

505-
# Check if the UDS socket file exists
506-
return uds_socket_path.exists()
533+
# Check if the UDS socket file exists
534+
return uds_socket_path.exists()

tests/test_cyberchannel.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# Copyright (C) 2025 ANSYS, Inc. and/or its affiliates.
2+
# SPDX-License-Identifier: MIT
3+
#
4+
#
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy
6+
# of this software and associated documentation files (the "Software"), to deal
7+
# in the Software without restriction, including without limitation the rights
8+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
# copies of the Software, and to permit persons to whom the Software is
10+
# furnished to do so, subject to the following conditions:
11+
#
12+
# The above copyright notice and this permission notice shall be included in all
13+
# copies or substantial portions of the Software.
14+
#
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
# SOFTWARE.
22+
"""Tests for cyberchannel."""
23+
24+
import os
25+
from pathlib import Path
26+
import tempfile
27+
28+
import pytest
29+
30+
from ansys.tools.common import cyberchannel
31+
32+
33+
def test_version_tuple():
34+
"""Test version tuple."""
35+
assert cyberchannel.version_tuple("1.2.3") == (1, 2, 3)
36+
assert cyberchannel.version_tuple("1.2.3.4") == (1, 2, 3, 4)
37+
assert cyberchannel.version_tuple("1.0.0") == (1, 0, 0)
38+
39+
40+
def test_cyberchannel_functions():
41+
"""Test cyberchannel functions."""
42+
assert cyberchannel.check_grpc_version()
43+
assert cyberchannel.is_uds_supported()
44+
uds_path = cyberchannel.determine_uds_folder()
45+
uds_path.mkdir(parents=True, exist_ok=True)
46+
assert uds_path.is_dir()
47+
assert uds_path.exists()
48+
uds_path.rmdir()
49+
cyberchannel.verify_transport_mode(transport_mode="insecure", mode="local")
50+
with pytest.raises(ValueError):
51+
cyberchannel.verify_transport_mode(transport_mode="invalid_mode", mode="mode1")
52+
53+
54+
def test_cyberchannel_insecure():
55+
"""Test cyberchannel insecure."""
56+
ch = cyberchannel.create_insecure_channel(host="localhost", port=12345)
57+
assert ch is not None
58+
assert ch._channel.target().decode() == "dns:///localhost:12345"
59+
assert not ch.close()
60+
61+
62+
@pytest.mark.skipif(os.name != "nt", reason="WNUA is only supported on Windows.")
63+
def test_cyberchannel_wnua():
64+
"""Test cyberchannel wnua."""
65+
ch = cyberchannel.create_wnua_channel(host="localhost", port=12345)
66+
assert ch is not None
67+
assert ch._channel.target().decode() == "dns:///localhost:12345"
68+
assert not ch.close()
69+
70+
71+
def test_cyberchannel_uds():
72+
"""Test cyberchannel uds."""
73+
uds_file = Path(tempfile.gettempdir()) / "test_uds.sock"
74+
with uds_file.open("w"):
75+
pass
76+
ch = cyberchannel.create_uds_channel(uds_fullpath=uds_file)
77+
assert ch is not None
78+
assert ch._channel.target().decode() == f"unix:{uds_file}"
79+
assert not ch.close()
80+
81+
ch = cyberchannel.create_uds_channel("service_name")
82+
assert ch is not None
83+
assert ch._channel.target().decode() == f"unix:{cyberchannel.determine_uds_folder() / 'service_name.sock'}"
84+
assert not ch.close()

0 commit comments

Comments
 (0)