ansys-speos-core 0.6.2__tar.gz → 0.7.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/PKG-INFO +17 -20
  2. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/README.rst +3 -3
  3. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/pyproject.toml +18 -17
  4. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/generic/general_methods.py +3 -7
  5. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/kernel/body.py +2 -1
  6. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/kernel/client.py +80 -30
  7. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/kernel/face.py +3 -3
  8. ansys_speos_core-0.7.0/src/ansys/speos/core/kernel/grpc/transport_options.py +171 -0
  9. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/kernel/intensity_template.py +3 -2
  10. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/kernel/job.py +39 -2
  11. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/kernel/part.py +2 -1
  12. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/kernel/scene.py +3 -2
  13. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/kernel/sensor_template.py +3 -2
  14. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/kernel/simulation_template.py +3 -2
  15. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/kernel/sop_template.py +3 -2
  16. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/kernel/source_template.py +2 -1
  17. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/kernel/spectrum.py +3 -2
  18. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/kernel/vop_template.py +3 -2
  19. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/launcher.py +43 -11
  20. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/lxp.py +1 -0
  21. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/project.py +6 -6
  22. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/simulation.py +1 -0
  23. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/speos.py +28 -19
  24. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/LICENSE +0 -0
  25. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/__init__.py +0 -0
  26. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/body.py +0 -0
  27. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/bsdf.py +4 -4
  28. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/face.py +0 -0
  29. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/generic/constants.py +0 -0
  30. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/generic/visualization_methods.py +0 -0
  31. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/geo_ref.py +0 -0
  32. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/intensity.py +0 -0
  33. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/kernel/__init__.py +0 -0
  34. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/kernel/crud.py +0 -0
  35. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/kernel/proto_message_utils.py +0 -0
  36. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/logger.py +0 -0
  37. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/opt_prop.py +0 -0
  38. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/part.py +0 -0
  39. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/proto_message_utils.py +0 -0
  40. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/py.typed +0 -0
  41. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/sensor.py +1 -1
  42. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/source.py +1 -1
  43. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/spectrum.py +0 -0
  44. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/workflow/__init__.py +0 -0
  45. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/workflow/combine_speos.py +0 -0
  46. {ansys_speos_core-0.6.2 → ansys_speos_core-0.7.0}/src/ansys/speos/core/workflow/open_result.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ansys-speos-core
3
- Version: 0.6.2
3
+ Version: 0.7.0
4
4
  Summary: A Python wrapper for Ansys Speos
5
5
  Author-email: "ANSYS, Inc." <pyansys.core@ansys.com>
6
6
  Maintainer-email: "ANSYS, Inc." <pyansys.core@ansys.com>
@@ -21,28 +21,25 @@ Classifier: Topic :: Scientific/Engineering :: Physics
21
21
  Classifier: Topic :: Scientific/Engineering :: Information Analysis
22
22
  License-File: LICENSE
23
23
  Requires-Dist: protobuf>=3.20,<7
24
- Requires-Dist: grpcio>=1.50.0,<1.75
24
+ Requires-Dist: grpcio>=1.50.0,<1.77
25
25
  Requires-Dist: grpcio-health-checking>=1.45.0,<1.68
26
26
  Requires-Dist: ansys-api-speos==0.15.3
27
- Requires-Dist: ansys-tools-path>=0.3.1
27
+ Requires-Dist: ansys-tools-common>=0.3.1
28
28
  Requires-Dist: numpy>=1.20.3,<3
29
29
  Requires-Dist: comtypes>=1.4,<1.5; platform_system=='Windows'
30
- Requires-Dist: ansys-sphinx-theme==1.6.0 ; extra == "doc"
31
- Requires-Dist: numpydoc==1.8.0 ; extra == "doc"
30
+ Requires-Dist: ansys-sphinx-theme[autoapi]==1.6.3 ; extra == "doc"
31
+ Requires-Dist: ansys-tools-visualization-interface>=0.8.3 ; extra == "doc"
32
+ Requires-Dist: numpydoc==1.10.0 ; extra == "doc"
32
33
  Requires-Dist: Sphinx==8.1.3 ; extra == "doc"
33
34
  Requires-Dist: sphinx-copybutton==0.5.2 ; extra == "doc"
34
- Requires-Dist: sphinx-autoapi==3.6.0 ; extra == "doc"
35
- Requires-Dist: sphinx_design==0.6.1 ; extra == "doc"
36
- Requires-Dist: sphinx-jinja==2.0.2 ; extra == "doc"
37
- Requires-Dist: sphinxcontrib-mermaid==1.0.0 ; extra == "doc"
35
+ Requires-Dist: sphinxcontrib-mermaid==1.2.3 ; extra == "doc"
38
36
  Requires-Dist: myst-parser==4.0.1 ; extra == "doc"
39
- Requires-Dist: nbsphinx==0.9.7 ; extra == "doc"
40
- Requires-Dist: jupytext==1.17.3 ; extra == "doc"
41
- Requires-Dist: jupyterlab==4.4.7 ; extra == "doc"
37
+ Requires-Dist: nbsphinx==0.9.8 ; extra == "doc"
38
+ Requires-Dist: jupytext==1.18.1 ; extra == "doc"
39
+ Requires-Dist: jupyterlab==4.5.0 ; extra == "doc"
42
40
  Requires-Dist: jupyter-server==2.17.0 ; extra == "doc"
43
41
  Requires-Dist: nbconvert==7.16.6 ; extra == "doc"
44
42
  Requires-Dist: pyvista[jupyter]>=0.43,<0.47 ; extra == "doc"
45
- Requires-Dist: ansys-tools-visualization-interface>=0.8.3 ; extra == "doc"
46
43
  Requires-Dist: pyvista>=0.40.0,<0.47 ; extra == "graphics"
47
44
  Requires-Dist: ansys-tools-visualization-interface>=0.8.3 ; extra == "graphics"
48
45
  Requires-Dist: matplotlib ; extra == "jupyter"
@@ -50,13 +47,13 @@ Requires-Dist: jupyterlab>=3 ; extra == "jupyter"
50
47
  Requires-Dist: ipywidgets ; extra == "jupyter"
51
48
  Requires-Dist: pyvista[jupyter]>=0.43,<0.47 ; extra == "jupyter"
52
49
  Requires-Dist: ansys-tools-visualization-interface>=0.8.3 ; extra == "jupyter"
53
- Requires-Dist: notebook==7.4.5 ; extra == "jupyter"
54
- Requires-Dist: psutil==7.0.0 ; extra == "tests"
55
- Requires-Dist: pytest==8.4.2 ; extra == "tests"
50
+ Requires-Dist: notebook==7.5.0 ; extra == "jupyter"
51
+ Requires-Dist: psutil==7.1.3 ; extra == "tests"
52
+ Requires-Dist: pytest==9.0.1 ; extra == "tests"
56
53
  Requires-Dist: pyvista>=0.40.0,<0.47 ; extra == "tests"
57
54
  Requires-Dist: ansys-tools-visualization-interface>=0.8.3 ; extra == "tests"
58
55
  Requires-Dist: ansys-platform-instancemanagement>=1.0.3 ; extra == "tests"
59
- Requires-Dist: pytest-cov==6.3.0 ; extra == "tests"
56
+ Requires-Dist: pytest-cov==7.0.0 ; extra == "tests"
60
57
  Project-URL: Changelog, https://github.com/ansys/pyspeos/blob/main/doc/source/changelog.rst
61
58
  Project-URL: Discussions, https://github.com/ansys/pyspeos/discussions
62
59
  Project-URL: Documentation, https://speos.docs.pyansys.com/
@@ -143,7 +140,7 @@ All sources are located in `<src/>`_ folder.
143
140
 
144
141
  from ansys.speos.core.speos import Speos
145
142
 
146
- speos = Speos(host="localhost", port=50098)
143
+ speos = Speos()
147
144
 
148
145
  Documentation and issues
149
146
  ------------------------
@@ -213,11 +210,11 @@ Then, to launch SpeosRPC server with product version 2025.1, you can run:
213
210
 
214
211
  cat GH_TOKEN.txt | docker login ghcr.io -u "$GH_USERNAME" --password-stdin
215
212
  docker pull ghcr.io/ansys/speos-rpc:252
216
- docker run --detach --name speos-rpc -p 50098:50098 -e ANSYSLMD_LICENSE_FILE=$LICENSE_SERVER --entrypoint /app/SpeosRPC_Server.x ghcr.io/ansys/speos-rpc:252
213
+ docker run --detach --name speos-rpc -p 127.0.0.1:50098:50098 -e ANSYSLMD_LICENSE_FILE=$LICENSE_SERVER --entrypoint /app/SpeosRPC_Server.x ghcr.io/ansys/speos-rpc:252 --transport_insecure --host 0.0.0.0
217
214
 
218
215
  .. note::
219
216
 
220
- To use the latest image in development, you can use `ghcr.io/ansys/speos-rpc:252`.
217
+ To use the latest image in development, you can use `ghcr.io/ansys/speos-rpc:dev`.
221
218
 
222
219
  On the other hand, the SpeosRPC server can be started locally.
223
220
 
@@ -73,7 +73,7 @@ All sources are located in `<src/>`_ folder.
73
73
 
74
74
  from ansys.speos.core.speos import Speos
75
75
 
76
- speos = Speos(host="localhost", port=50098)
76
+ speos = Speos()
77
77
 
78
78
  Documentation and issues
79
79
  ------------------------
@@ -143,11 +143,11 @@ Then, to launch SpeosRPC server with product version 2025.1, you can run:
143
143
 
144
144
  cat GH_TOKEN.txt | docker login ghcr.io -u "$GH_USERNAME" --password-stdin
145
145
  docker pull ghcr.io/ansys/speos-rpc:252
146
- docker run --detach --name speos-rpc -p 50098:50098 -e ANSYSLMD_LICENSE_FILE=$LICENSE_SERVER --entrypoint /app/SpeosRPC_Server.x ghcr.io/ansys/speos-rpc:252
146
+ docker run --detach --name speos-rpc -p 127.0.0.1:50098:50098 -e ANSYSLMD_LICENSE_FILE=$LICENSE_SERVER --entrypoint /app/SpeosRPC_Server.x ghcr.io/ansys/speos-rpc:252 --transport_insecure --host 0.0.0.0
147
147
 
148
148
  .. note::
149
149
 
150
- To use the latest image in development, you can use `ghcr.io/ansys/speos-rpc:252`.
150
+ To use the latest image in development, you can use `ghcr.io/ansys/speos-rpc:dev`.
151
151
 
152
152
  On the other hand, the SpeosRPC server can be started locally.
153
153
 
@@ -4,7 +4,7 @@ build-backend = "flit_core.buildapi"
4
4
 
5
5
  [project]
6
6
  name = "ansys-speos-core"
7
- version = "0.6.2"
7
+ version = "0.7.0"
8
8
  description = "A Python wrapper for Ansys Speos"
9
9
  readme = "README.rst"
10
10
  requires-python = ">=3.10"
@@ -28,10 +28,10 @@ classifiers=[
28
28
  ]
29
29
  dependencies=[
30
30
  "protobuf>=3.20,<7",
31
- "grpcio>=1.50.0,<1.75",
31
+ "grpcio>=1.50.0,<1.77",
32
32
  "grpcio-health-checking>=1.45.0,<1.68",
33
33
  "ansys-api-speos==0.15.3",
34
- "ansys-tools-path>=0.3.1",
34
+ "ansys-tools-common>=0.3.1",
35
35
  "numpy>=1.20.3,<3",
36
36
  "comtypes>=1.4,<1.5; platform_system=='Windows'",
37
37
  ]
@@ -42,12 +42,12 @@ graphics = [
42
42
  "ansys-tools-visualization-interface>=0.8.3",
43
43
  ]
44
44
  tests = [
45
- "psutil==7.0.0",
46
- "pytest==8.4.2",
45
+ "psutil==7.1.3",
46
+ "pytest==9.0.1",
47
47
  "pyvista>=0.40.0,<0.47",
48
48
  "ansys-tools-visualization-interface>=0.8.3",
49
49
  "ansys-platform-instancemanagement>=1.0.3",
50
- "pytest-cov==6.3.0",
50
+ "pytest-cov==7.0.0",
51
51
  ]
52
52
  jupyter = [
53
53
  "matplotlib",
@@ -55,25 +55,22 @@ jupyter = [
55
55
  "ipywidgets",
56
56
  "pyvista[jupyter]>=0.43,<0.47",
57
57
  "ansys-tools-visualization-interface>=0.8.3",
58
- "notebook==7.4.5",
58
+ "notebook==7.5.0",
59
59
  ]
60
60
  doc = [
61
- "ansys-sphinx-theme==1.6.0",
62
- "numpydoc==1.8.0",
61
+ "ansys-sphinx-theme[autoapi]==1.6.3",
62
+ "ansys-tools-visualization-interface>=0.8.3",
63
+ "numpydoc==1.10.0",
63
64
  "Sphinx==8.1.3",
64
65
  "sphinx-copybutton==0.5.2",
65
- "sphinx-autoapi==3.6.0",
66
- "sphinx_design==0.6.1",
67
- "sphinx-jinja==2.0.2",
68
- "sphinxcontrib-mermaid==1.0.0",
66
+ "sphinxcontrib-mermaid==1.2.3",
69
67
  "myst-parser==4.0.1",
70
- "nbsphinx==0.9.7",
71
- "jupytext==1.17.3",
72
- "jupyterlab==4.4.7",
68
+ "nbsphinx==0.9.8",
69
+ "jupytext==1.18.1",
70
+ "jupyterlab==4.5.0",
73
71
  "jupyter-server==2.17.0",
74
72
  "nbconvert==7.16.6",
75
73
  "pyvista[jupyter]>=0.43,<0.47",
76
- "ansys-tools-visualization-interface>=0.8.3",
77
74
  ]
78
75
 
79
76
  [project.urls]
@@ -147,6 +144,10 @@ show_missing = true
147
144
  minversion = "7.1"
148
145
  addopts = "-vvv --color=yes -ra --durations=25 --cov=ansys.speos --cov-report html:.cov/html --cov-report xml:.cov/xml --cov-report term"
149
146
  testpaths = ["tests"]
147
+ markers = [
148
+ "all_speos_versions: Supported on all Speos versions.",
149
+ "supported_speos_versions(min, max): Feature only supported from minimal to maximal Speos versions."
150
+ ]
150
151
 
151
152
  [tool.mypy]
152
153
  python_version = "3.10"
@@ -34,10 +34,10 @@ from pathlib import Path
34
34
  from typing import List, Optional, Union, cast
35
35
  import warnings
36
36
 
37
+ from ansys.tools.common.path import get_available_ansys_installations
37
38
  import numpy as np
38
39
 
39
40
  from ansys.speos.core.generic.constants import DEFAULT_VERSION
40
- from ansys.tools.path import get_available_ansys_installations
41
41
 
42
42
  _GRAPHICS_AVAILABLE = None
43
43
 
@@ -90,9 +90,8 @@ def run_if_graphics_required(warning=False):
90
90
  global _GRAPHICS_AVAILABLE
91
91
  if _GRAPHICS_AVAILABLE is None:
92
92
  try:
93
- import pyvista as pv # noqa: F401
94
-
95
93
  from ansys.tools.visualization_interface import Plotter # noqa: F401
94
+ import pyvista as pv # noqa: F401
96
95
 
97
96
  _GRAPHICS_AVAILABLE = True
98
97
  except ImportError: # pragma: no cover
@@ -179,11 +178,8 @@ def error_no_install(install_path: Union[Path, str], version: Union[int, str]):
179
178
  version : Union[int, str]
180
179
  Version
181
180
  """
182
- install_loc_msg = ""
183
- if install_path:
184
- install_loc_msg = f"at {Path(install_path).parent}"
185
181
  raise FileNotFoundError(
186
- f"Ansys Speos RPC server installation not found{install_loc_msg}. "
182
+ f"Ansys Speos RPC server installation not found at {install_path}. "
187
183
  f"Please define AWP_ROOT{version} environment variable"
188
184
  )
189
185
 
@@ -28,6 +28,7 @@ from ansys.api.speos.part.v1 import (
28
28
  body_pb2 as messages,
29
29
  body_pb2_grpc as service,
30
30
  )
31
+
31
32
  from ansys.speos.core.kernel.crud import CrudItem, CrudStub
32
33
  from ansys.speos.core.kernel.proto_message_utils import protobuf_message_to_str
33
34
 
@@ -94,7 +95,7 @@ class BodyStub(CrudStub):
94
95
  Like in the following example:
95
96
 
96
97
  >>> from ansys.speos.core.speos import Speos
97
- >>> speos = Speos(host="localhost", port=50098)
98
+ >>> speos = Speos()
98
99
  >>> body_db = speos.client.bodies()
99
100
 
100
101
  """
@@ -25,14 +25,15 @@
25
25
  import logging
26
26
  import os
27
27
  from pathlib import Path
28
- import subprocess
28
+ import subprocess # nosec
29
+ import tempfile
29
30
  import time
30
31
  from typing import TYPE_CHECKING, List, Optional, Union
31
32
 
33
+ from ansys.api.speos.part.v1 import body_pb2, face_pb2, part_pb2
32
34
  import grpc
33
35
  from grpc._channel import _InactiveRpcError
34
36
 
35
- from ansys.api.speos.part.v1 import body_pb2, face_pb2, part_pb2
36
37
  from ansys.speos.core.generic.constants import (
37
38
  DEFAULT_HOST,
38
39
  DEFAULT_PORT,
@@ -42,6 +43,13 @@ from ansys.speos.core.generic.constants import (
42
43
  from ansys.speos.core.generic.general_methods import retrieve_speos_install_dir
43
44
  from ansys.speos.core.kernel.body import BodyLink, BodyStub
44
45
  from ansys.speos.core.kernel.face import FaceLink, FaceStub
46
+ from ansys.speos.core.kernel.grpc.transport_options import (
47
+ InsecureOptions,
48
+ TransportMode,
49
+ TransportOptions,
50
+ UDSOptions,
51
+ WNUAOptions,
52
+ )
45
53
  from ansys.speos.core.kernel.intensity_template import (
46
54
  IntensityTemplateLink,
47
55
  IntensityTemplateStub,
@@ -82,7 +90,7 @@ def wait_until_healthy(channel: grpc.Channel, timeout: float):
82
90
 
83
91
  Parameters
84
92
  ----------
85
- channel : ~grpc.Channel
93
+ channel : grpc.Channel
86
94
  Channel to wait until established and healthy.
87
95
  timeout : float
88
96
  Timeout in seconds. One attempt will be made each 100 milliseconds
@@ -107,24 +115,45 @@ def wait_until_healthy(channel: grpc.Channel, timeout: float):
107
115
  )
108
116
 
109
117
 
118
+ def default_docker_channel(
119
+ host: Optional[str] = DEFAULT_HOST,
120
+ port: Union[str, int] = DEFAULT_PORT,
121
+ message_size: int = MAX_CLIENT_MESSAGE_SIZE,
122
+ ) -> grpc.Channel:
123
+ """Create default transport options for docker on CI."""
124
+ return TransportOptions(
125
+ mode=TransportMode.INSECURE,
126
+ options=InsecureOptions(host=host, port=port, allow_remote_host=True),
127
+ ).create_channel(grpc_options=[("grpc.max_receive_message_length", message_size)])
128
+
129
+
130
+ def default_local_channel(
131
+ port: Union[str, int] = DEFAULT_PORT, message_size: int = MAX_CLIENT_MESSAGE_SIZE
132
+ ) -> grpc.Channel:
133
+ """Create default transport options, WNUA on Windows, UDS on Linux."""
134
+ if os.name == "nt":
135
+ transport = TransportOptions(
136
+ mode=TransportMode.WNUA, options=WNUAOptions(host=DEFAULT_HOST, port=port)
137
+ )
138
+ else:
139
+ sock_file = Path(tempfile.gettempdir()) / f"speosrpc_sock_{port}"
140
+ transport = TransportOptions(
141
+ mode=TransportMode.UDS, options=UDSOptions(uds_fullpath=str(sock_file))
142
+ )
143
+ return transport.create_channel(
144
+ grpc_options=[("grpc.max_receive_message_length", message_size)]
145
+ )
146
+
147
+
110
148
  class SpeosClient:
111
149
  """
112
150
  Wraps a speos gRPC connection.
113
151
 
114
152
  Parameters
115
153
  ----------
116
- host : str, optional
117
- Host where the server is running.
118
- By default, ``DEFAULT_HOST``.
119
- port : Union[str, int], optional
120
- Port number where the server is running.
121
- By default, ``DEFAULT_PORT``.
122
- channel : ~grpc.Channel, optional
154
+ channel : grpc.Channel, optional
123
155
  gRPC channel for server communication.
124
156
  By default, ``None``.
125
- message_size: int
126
- Maximum Message size of a newly generated channel
127
- By default, ``MAX_CLIENT_MESSAGE_SIZE``.
128
157
  remote_instance : ansys.platform.instancemanagement.Instance
129
158
  The corresponding remote instance when the Speos Service
130
159
  is launched through PyPIM. This instance will be deleted when calling
@@ -143,11 +172,8 @@ class SpeosClient:
143
172
 
144
173
  def __init__(
145
174
  self,
146
- host: Optional[str] = DEFAULT_HOST,
147
- port: Union[str, int] = DEFAULT_PORT,
148
175
  version: str = DEFAULT_VERSION,
149
176
  channel: Optional[grpc.Channel] = None,
150
- message_size: int = MAX_CLIENT_MESSAGE_SIZE,
151
177
  remote_instance: Optional["Instance"] = None,
152
178
  timeout: Optional[int] = 60,
153
179
  logging_level: Optional[int] = logging.INFO,
@@ -170,23 +196,17 @@ class SpeosClient:
170
196
  else:
171
197
  self._version = version
172
198
  if channel:
173
- # Used for PyPIM when directly providing a channel
199
+ # grpc channel is provided by caller, used by PyPIM or Docker server
174
200
  self._channel = channel
175
- self._target = str(channel)
176
201
  else:
177
- self._host = host
178
- self._port = port
179
- self._target = f"{host}:{port}"
180
- self._channel = grpc.insecure_channel(
181
- self._target,
182
- options=[("grpc.max_receive_message_length", message_size)],
183
- )
202
+ self._channel = default_local_channel()
203
+
184
204
  # do not finish initialization until channel is healthy
185
205
  wait_until_healthy(self._channel, timeout)
186
206
 
187
207
  # once connection with the client is established, create a logger
188
208
  self._log = LOGGER.add_instance_logger(
189
- name=self._target, client_instance=self, level=logging_level
209
+ name=self.target(), client_instance=self, level=logging_level
190
210
  )
191
211
  if logging_file:
192
212
  if isinstance(logging_file, Path):
@@ -492,7 +512,7 @@ List[ansys.speos.core.kernel.face.FaceLink]]
492
512
  """Represent the client as a string."""
493
513
  lines = []
494
514
  lines.append(f"Ansys Speos client ({hex(id(self))})")
495
- lines.append(f" Target: {self._target}")
515
+ lines.append(f" Target: {self.target()}")
496
516
  if self._closed:
497
517
  lines.append(" Connection: Closed")
498
518
  elif self.healthy:
@@ -504,6 +524,11 @@ List[ansys.speos.core.kernel.face.FaceLink]]
504
524
  def close(self):
505
525
  """Close the channel.
506
526
 
527
+ .. warning::
528
+
529
+ Do not execute this function with untrusted environment variables.
530
+ See the :ref:`security guide<ref_security_consideration>` for details.
531
+
507
532
  Returns
508
533
  -------
509
534
  bool
@@ -517,7 +542,7 @@ List[ansys.speos.core.kernel.face.FaceLink]]
517
542
  wait_time = 0
518
543
  if self._remote_instance:
519
544
  self._remote_instance.delete()
520
- elif self._host in ["localhost", "0.0.0.0", "127.0.0.1"] and self.__speos_exec:
545
+ elif self.__speos_exec:
521
546
  self.__close_local_speos_rpc_server()
522
547
  while self.healthy and wait_time < 15:
523
548
  time.sleep(1)
@@ -543,5 +568,30 @@ List[ansys.speos.core.kernel.face.FaceLink]]
543
568
  return self._closed
544
569
 
545
570
  def __close_local_speos_rpc_server(self):
546
- command = [self.__speos_exec, "-s{}".format(self._port)]
547
- subprocess.run(command, check=True)
571
+ """Close a locally started Speos RPC server.
572
+
573
+ .. warning::
574
+ Do not execute this function after modifying protected or private
575
+ attributes of the SpeosClient class or in a context with untrusted
576
+ environment variables.
577
+ See the :ref:`security guide<ref_security_consideration>` for details.
578
+
579
+ """
580
+ try:
581
+ # Extract port number at end of target string
582
+ target = self.target()
583
+ if ":" in target:
584
+ port = target.split(":")[-1]
585
+ else:
586
+ port = target.split("_")[-1]
587
+ int(port)
588
+ except ValueError:
589
+ raise RuntimeError("The port of the local server is not a valid integer.")
590
+ if (
591
+ not Path(self.__speos_exec).is_file()
592
+ or Path(self.__speos_exec).stem != "SpeosRPC_Server"
593
+ ):
594
+ raise RuntimeError("Unexpected executable path for Speos rpc executable.")
595
+
596
+ command = [self.__speos_exec, f"-s{port}"]
597
+ subprocess.run(command, check=True) # nosec
@@ -24,12 +24,12 @@
24
24
 
25
25
  from typing import Iterator, List
26
26
 
27
- from grpc import RpcError
28
-
29
27
  from ansys.api.speos.part.v1 import (
30
28
  face_pb2 as messages,
31
29
  face_pb2_grpc as service,
32
30
  )
31
+ from grpc import RpcError
32
+
33
33
  from ansys.speos.core.generic.general_methods import min_speos_version
34
34
  from ansys.speos.core.kernel.crud import CrudItem, CrudStub
35
35
  from ansys.speos.core.kernel.proto_message_utils import protobuf_message_to_str
@@ -97,7 +97,7 @@ class FaceStub(CrudStub):
97
97
  Like in the following example:
98
98
 
99
99
  >>> from ansys.speos.core.speos import Speos
100
- >>> speos = Speos(host="localhost", port=50098)
100
+ >>> speos = Speos()
101
101
  >>> face_db = speos.client.faces()
102
102
 
103
103
  """
@@ -0,0 +1,171 @@
1
+ # Copyright (C) 2021 - 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
+
23
+ """Define supported transport options for the FileTransfer Tool client.
24
+
25
+ This module provides classes and enumerations to configure and manage
26
+ different transport modes (UDS, mTLS, Insecure) for the FileTransfer Tool.
27
+ """
28
+
29
+ from dataclasses import dataclass
30
+ import enum
31
+ from pathlib import Path
32
+
33
+ from ansys.tools.common.cyberchannel import create_channel
34
+
35
+
36
+ class TransportMode(enum.Enum):
37
+ """Enumeration of transport modes supported by the FileTransfer Tool."""
38
+
39
+ UDS = "uds"
40
+ MTLS = "mtls"
41
+ INSECURE = "insecure"
42
+ WNUA = "wnua"
43
+
44
+
45
+ @dataclass(kw_only=True)
46
+ class UDSOptions:
47
+ """Options for UDS transport mode."""
48
+
49
+ uds_service: str | None = None
50
+ uds_dir: str | Path | None = None
51
+ uds_id: str | None = None
52
+ uds_fullpath: str | Path | None = None
53
+
54
+ def _to_cyberchannel_kwargs(self):
55
+ return {
56
+ "uds_service": self.uds_service,
57
+ "uds_dir": self.uds_dir,
58
+ "uds_id": self.uds_id,
59
+ "uds_fullpath": self.uds_fullpath,
60
+ }
61
+
62
+
63
+ @dataclass(kw_only=True)
64
+ class MTLSOptions:
65
+ """Options for mTLS transport mode."""
66
+
67
+ certs_dir: str | Path | None = None
68
+ host: str = "localhost"
69
+ port: int
70
+ allow_remote_host: bool = False
71
+
72
+ def _to_cyberchannel_kwargs(self):
73
+ if not self.allow_remote_host:
74
+ if self.host not in ("localhost", "127.0.0.1"):
75
+ raise ValueError(
76
+ f"Remote host '{self.host}' is not allowed when "
77
+ "'allow_remote_host' is set to False."
78
+ )
79
+ return {
80
+ "certs_dir": self.certs_dir,
81
+ "host": self.host,
82
+ "port": self.port,
83
+ }
84
+
85
+
86
+ @dataclass(kw_only=True)
87
+ class InsecureOptions:
88
+ """Options for insecure transport mode."""
89
+
90
+ host: str = "localhost"
91
+ port: int
92
+ allow_remote_host: bool = False
93
+
94
+ def _to_cyberchannel_kwargs(self):
95
+ if not self.allow_remote_host:
96
+ if self.host not in ("localhost", "127.0.0.1"):
97
+ raise ValueError(
98
+ f"Remote host '{self.host}' is not allowed when "
99
+ "'allow_remote_host' is set to False."
100
+ )
101
+ return {
102
+ "host": self.host,
103
+ "port": self.port,
104
+ }
105
+
106
+
107
+ @dataclass(kw_only=True)
108
+ class WNUAOptions:
109
+ """Options for Windows Named User Authentication transport mode."""
110
+
111
+ host: str = "localhost"
112
+ port: int
113
+
114
+ def _to_cyberchannel_kwargs(self):
115
+ return {
116
+ "host": self.host,
117
+ "port": self.port,
118
+ }
119
+
120
+
121
+ @dataclass(kw_only=True)
122
+ class TransportOptions:
123
+ """Transport options for the FileTransfer Tool client."""
124
+
125
+ mode: TransportMode
126
+ options: UDSOptions | MTLSOptions | InsecureOptions | WNUAOptions
127
+
128
+ def __init__(
129
+ self,
130
+ mode: TransportMode | str = "uds",
131
+ options: UDSOptions | MTLSOptions | InsecureOptions | WNUAOptions | None = None,
132
+ ):
133
+ if isinstance(mode, str):
134
+ mode = TransportMode(mode)
135
+ if options is None:
136
+ if mode != TransportMode.UDS:
137
+ raise RuntimeError("TransportOptions must be provided for modes other than UDS.")
138
+ # The default cannot be set in the constructor signature
139
+ # since '_get_uds_dir_default' may raise.
140
+ options = UDSOptions()
141
+
142
+ if mode == TransportMode.UDS:
143
+ if not isinstance(options, UDSOptions):
144
+ raise TypeError("For UDS transport mode, options must be of type UDSOptions.")
145
+ elif mode == TransportMode.MTLS:
146
+ if not isinstance(options, MTLSOptions):
147
+ raise TypeError("For mTLS transport mode, options must be of type MTLSOptions.")
148
+ elif mode == TransportMode.INSECURE:
149
+ if not isinstance(options, InsecureOptions):
150
+ raise TypeError(
151
+ "For Insecure transport mode, options must be of type InsecureOptions."
152
+ )
153
+ elif mode == TransportMode.WNUA:
154
+ if not isinstance(options, WNUAOptions):
155
+ raise TypeError("For WNUA transport mode, options must be of type WNUAOptions.")
156
+ else:
157
+ raise ValueError(f"Unsupported transport mode: {mode}")
158
+
159
+ self.mode = mode
160
+ self.options = options
161
+
162
+ def _to_cyberchannel_kwargs(self):
163
+ """Convert transport options to cyberchannel kwargs."""
164
+ return {
165
+ "transport_mode": self.mode.value,
166
+ **self.options._to_cyberchannel_kwargs(),
167
+ }
168
+
169
+ def create_channel(self, grpc_options):
170
+ """Create a gRPC channel based on the transport options."""
171
+ return create_channel(**self._to_cyberchannel_kwargs(), grpc_options=grpc_options)
@@ -28,6 +28,7 @@ from ansys.api.speos.intensity.v1 import (
28
28
  intensity_pb2 as messages,
29
29
  intensity_pb2_grpc as service,
30
30
  )
31
+
31
32
  from ansys.speos.core.kernel.crud import CrudItem, CrudStub
32
33
  from ansys.speos.core.kernel.proto_message_utils import protobuf_message_to_str
33
34
 
@@ -55,7 +56,7 @@ class IntensityTemplateLink(CrudItem):
55
56
  >>> from ansys.speos.core.kernel.intensity_template import (
56
57
  ... ProtoIntensityTemplate,
57
58
  ... )
58
- >>> speos = Speos(host="localhost", port=50098)
59
+ >>> speos = Speos()
59
60
  >>> int_t_db = speos.client.intensity_templates()
60
61
  >>> int_t_message = ProtoIntensityTemplate(name="Cos_3_170")
61
62
  >>> int_t_message.cos.N = 3.0
@@ -126,7 +127,7 @@ class IntensityTemplateStub(CrudStub):
126
127
  intensity_templates() method. Like in the following example:
127
128
 
128
129
  >>> from ansys.speos.core.speos import Speos
129
- >>> speos = Speos(host="localhost", port=50098)
130
+ >>> speos = Speos()
130
131
  >>> int_t_db = speos.client.intensity_templates()
131
132
 
132
133
  """