ansys-pyensight-core 0.11.0__py3-none-any.whl

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 (37) hide show
  1. ansys/pyensight/core/__init__.py +41 -0
  2. ansys/pyensight/core/common.py +341 -0
  3. ansys/pyensight/core/deep_pixel_view.html +98 -0
  4. ansys/pyensight/core/dockerlauncher.py +1124 -0
  5. ansys/pyensight/core/dvs.py +872 -0
  6. ansys/pyensight/core/enscontext.py +345 -0
  7. ansys/pyensight/core/enshell_grpc.py +641 -0
  8. ansys/pyensight/core/ensight_grpc.py +874 -0
  9. ansys/pyensight/core/ensobj.py +515 -0
  10. ansys/pyensight/core/launch_ensight.py +296 -0
  11. ansys/pyensight/core/launcher.py +388 -0
  12. ansys/pyensight/core/libuserd.py +2110 -0
  13. ansys/pyensight/core/listobj.py +280 -0
  14. ansys/pyensight/core/locallauncher.py +579 -0
  15. ansys/pyensight/core/py.typed +0 -0
  16. ansys/pyensight/core/renderable.py +880 -0
  17. ansys/pyensight/core/session.py +1923 -0
  18. ansys/pyensight/core/sgeo_poll.html +24 -0
  19. ansys/pyensight/core/utils/__init__.py +21 -0
  20. ansys/pyensight/core/utils/adr.py +111 -0
  21. ansys/pyensight/core/utils/dsg_server.py +1220 -0
  22. ansys/pyensight/core/utils/export.py +606 -0
  23. ansys/pyensight/core/utils/omniverse.py +769 -0
  24. ansys/pyensight/core/utils/omniverse_cli.py +614 -0
  25. ansys/pyensight/core/utils/omniverse_dsg_server.py +1196 -0
  26. ansys/pyensight/core/utils/omniverse_glb_server.py +848 -0
  27. ansys/pyensight/core/utils/parts.py +1221 -0
  28. ansys/pyensight/core/utils/query.py +487 -0
  29. ansys/pyensight/core/utils/readers.py +300 -0
  30. ansys/pyensight/core/utils/resources/Materials/000_sky.exr +0 -0
  31. ansys/pyensight/core/utils/support.py +128 -0
  32. ansys/pyensight/core/utils/variables.py +2019 -0
  33. ansys/pyensight/core/utils/views.py +674 -0
  34. ansys_pyensight_core-0.11.0.dist-info/METADATA +309 -0
  35. ansys_pyensight_core-0.11.0.dist-info/RECORD +37 -0
  36. ansys_pyensight_core-0.11.0.dist-info/WHEEL +4 -0
  37. ansys_pyensight_core-0.11.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,41 @@
1
+ # Copyright (C) 2022 - 2026 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
+ try:
24
+ import importlib.metadata as importlib_metadata # type: ignore
25
+ except ModuleNotFoundError: # pragma: no cover
26
+ import importlib_metadata # type: ignore
27
+ __version__ = importlib_metadata.version(__name__.replace(".", "-"))
28
+
29
+ VERSION = __version__
30
+ DEFAULT_ANSYS_VERSION = "251"
31
+
32
+ # Ansys version number that this release is associated with
33
+ __ansys_version__ = DEFAULT_ANSYS_VERSION
34
+ __ansys_version_str__ = f"{2000+(int(__ansys_version__) // 10)} R{int(__ansys_version__) % 10}"
35
+
36
+ from ansys.pyensight.core.dockerlauncher import DockerLauncher
37
+ from ansys.pyensight.core.launch_ensight import launch_ensight, launch_libuserd
38
+ from ansys.pyensight.core.launcher import Launcher
39
+ from ansys.pyensight.core.listobj import ensobjlist
40
+ from ansys.pyensight.core.locallauncher import LocalLauncher
41
+ from ansys.pyensight.core.session import Session
@@ -0,0 +1,341 @@
1
+ # Copyright (C) 2022 - 2026 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
+ """ This module provides a list of common utilities shared between different PyEnSight modules."""
24
+
25
+ import os
26
+ import random
27
+ import re
28
+ import socket
29
+ import time
30
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple
31
+
32
+ from ansys.pyensight.core import enshell_grpc
33
+ import urllib3
34
+
35
+ try:
36
+ from simple_upload_server.client import Client
37
+
38
+ simple_upload_server_is_available = True # pragma: no cover
39
+ except Exception:
40
+ simple_upload_server_is_available = False
41
+
42
+ if TYPE_CHECKING:
43
+ from docker import DockerClient
44
+
45
+ GRPC_VERSIONS = ["2025 R2.3", "2025 R1.4", "2024 R2.5"]
46
+ GRPC_WARNING_MESSAGE = "The EnSight version being used uses an insecure gRPC connection."
47
+ GRPC_WARNING_MESSAGE += "Consider upgrading to a version higher than 2025 R2.3, or "
48
+ GRPC_WARNING_MESSAGE += "2024 R2.5 or higher service packs, or 2025 R1.4 or higher service packs."
49
+
50
+
51
+ def find_unused_ports(count: int, avoid: Optional[List[int]] = None) -> Optional[List[int]]:
52
+ """Find "count" unused ports on the host system
53
+
54
+ A port is considered unused if it does not respond to a "connect" attempt. Walk
55
+ the ports from 'start' to 'end' looking for unused ports and avoiding any ports
56
+ in the 'avoid' list. Stop once the desired number of ports have been
57
+ found. If an insufficient number of ports were found, return None.
58
+
59
+ Parameters
60
+ ----------
61
+ count: int :
62
+ Number of unused ports to find
63
+ avoid: Optional[List[int]] :
64
+ An optional list of ports not to check
65
+
66
+ Returns
67
+ -------
68
+ The detected ports or None on failure
69
+
70
+ """
71
+ if avoid is None:
72
+ avoid = []
73
+ ports = list()
74
+
75
+ # pick a starting port number
76
+ start = random.randint(1024, 64000)
77
+ # We will scan for 65530 ports unless end is specified
78
+ port_mod = 65530
79
+ end = start + port_mod - 1
80
+ # walk the "virtual" port range
81
+ for base_port in range(start, end + 1):
82
+ # Map to physical port range
83
+ # There have been some issues with 65534+ so we stop at 65530
84
+ port = base_port % port_mod
85
+ # port 0 is special
86
+ if port == 0: # pragma: no cover
87
+ continue # pragma: no cover
88
+ # avoid admin ports
89
+ if port < 1024: # pragma: no cover
90
+ continue # pragma: no cover
91
+ # are we supposed to skip this one?
92
+ if port in avoid: # pragma: no cover
93
+ continue # pragma: no cover
94
+ # is anyone listening?
95
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
96
+ result = sock.connect_ex(("127.0.0.1", port))
97
+ if result != 0:
98
+ ports.append(port)
99
+ else:
100
+ sock.close() # pragma: no cover
101
+ if len(ports) >= count:
102
+ return ports
103
+ # in case we failed...
104
+ if len(ports) < count: # pragma: no cover
105
+ return None # pragma: no cover
106
+ return ports # pragma: no cover
107
+
108
+
109
+ def get_host_port(uri: str) -> Tuple[str, int]:
110
+ """Get the host port for the input uri
111
+
112
+ Parameters
113
+ ----------
114
+
115
+ uri: str
116
+ The Uri to inspect
117
+
118
+ Returns
119
+ -------
120
+ (tuple):
121
+ A tuple containing the host and the port of the input uri
122
+ """
123
+ parse_results = urllib3.util.parse_url(uri)
124
+ port = (
125
+ parse_results.port
126
+ if parse_results.port
127
+ else (443 if re.search(r"^https|wss$", parse_results.scheme) else None)
128
+ )
129
+ return (parse_results.host, port)
130
+
131
+
132
+ def get_file_service(pim_instance: Any) -> Optional[Any]: # pragma: no cover
133
+ """Get the file service object for the input pim instance.
134
+
135
+ Parameters
136
+ ----------
137
+
138
+ pim_instance:
139
+ the PIM instance to get the service from.
140
+
141
+ Returns
142
+ -------
143
+
144
+ pim_file_service:
145
+ the PIM file service object
146
+ """
147
+ if simple_upload_server_is_available is False:
148
+ return None
149
+ if pim_instance is None:
150
+ return None
151
+
152
+ if "http-simple-upload-server" in pim_instance.services:
153
+ pim_file_service = Client(
154
+ token="token",
155
+ url=pim_instance.services["http-simple-upload-server"].uri,
156
+ headers=pim_instance.services["http-simple-upload-server"].headers,
157
+ )
158
+ return pim_file_service
159
+ return None
160
+
161
+
162
+ def populate_service_host_port( # pragma: no cover
163
+ pim_instance: Any, service_host_port: Dict[str, Tuple[str, int]], webui: bool = False
164
+ ) -> Dict[str, Tuple[str, int]]:
165
+ """Populate the service host port dictionary with the services available in the PIM instance.
166
+
167
+ Parameters
168
+ ----------
169
+ pim_instance:
170
+ the PIM instance to get the servicea from.
171
+ service_host_port: dict
172
+ the dictionary to be updated with the services from the PIM instance
173
+ webui: bool
174
+ if True retrieve also the webUI service
175
+
176
+ Returns
177
+ -------
178
+ service_host_port: dict
179
+ the dictionary updated with the services from the PIM instance
180
+ """
181
+ if not set(("grpc_private", "http", "ws")).issubset(pim_instance.services):
182
+ raise RuntimeError(
183
+ "If channel is specified, the PIM instance must have a list of length 3 "
184
+ + "containing the appropriate service URIs. It does not."
185
+ )
186
+ service_host_port["grpc_private"] = get_host_port(pim_instance.services["grpc_private"].uri)
187
+ service_host_port["http"] = get_host_port(pim_instance.services["http"].uri)
188
+ service_host_port["ws"] = get_host_port(pim_instance.services["ws"].uri)
189
+ service_host_port["grpc"] = ("127.0.0.1", -1)
190
+ if webui:
191
+ service_host_port["webui"] = get_host_port(pim_instance.services["webui"].uri)
192
+ return service_host_port
193
+
194
+
195
+ def launch_enshell_interface(
196
+ enshell_grpc_channel: Any,
197
+ grpc_port: int,
198
+ timeout: float,
199
+ secret_key: str,
200
+ grpc_use_tcp_sockets: bool = False,
201
+ grpc_allow_network_connections: bool = False,
202
+ grpc_disable_tls: bool = False,
203
+ grpc_uds_pathname: Optional[str] = None,
204
+ disable_grpc_options: bool = False,
205
+ ) -> enshell_grpc.EnShellGRPC:
206
+ """Launch the EnShell gRPC Interface.
207
+
208
+ Parameters
209
+ ----------
210
+ enshell_grpc_channel:
211
+ An eventual gRPC channel already available, like in the PIM case
212
+ grpc_port: int
213
+ the gRPC port to connect to
214
+ timeout: float
215
+ a timeout to wait for the gRPC connection
216
+ grpc_use_tcp_sockets: bool
217
+ If using gRPC, and if True, then allow TCP Socket based connections
218
+ instead of only local connections.
219
+ grpc_allow_network_connections: bool
220
+ If using gRPC and using TCP Socket based connections, listen on all networks.
221
+ grpc_disable_tls: bool
222
+ If using gRPC and using TCP Socket based connections, disable TLS.
223
+ grpc_uds_pathname: bool
224
+ If using gRPC and using Unix Domain Socket based connections, explicitly
225
+ set the pathname to the shared UDS file instead of using the default.
226
+ disable_grpc_options: bool, optional
227
+ Whether to disable the gRPC options check, and allow to run older
228
+ versions of EnSight
229
+
230
+ Returns
231
+ -------
232
+ enshell: enshell_grpc.EnShellGRPC
233
+ the enshell gRPC interface
234
+
235
+ WARNING:
236
+ Overriding the default values for these options: grpc_use_tcp_sockets, grpc_allow_network_connections,
237
+ and grpc_disable_tls
238
+ can possibly permit control of this computer and any data which resides on it.
239
+ Modification of this configuration is not recommended. Please see the
240
+ documentation for your installed product for additional information.
241
+ """
242
+ if enshell_grpc_channel: # pragma: no cover
243
+ enshell = enshell_grpc.EnShellGRPC(
244
+ grpc_use_tcp_sockets=grpc_use_tcp_sockets,
245
+ grpc_allow_network_connections=grpc_allow_network_connections,
246
+ grpc_disable_tls=grpc_disable_tls,
247
+ grpc_uds_pathname=grpc_uds_pathname,
248
+ disable_grpc_options=disable_grpc_options,
249
+ ) # pragma: no cover
250
+ # If disable_grpc_options is True, it means that in the launcher
251
+ # the ensight version was checked and it hasn't got the new
252
+ # grpc options. This means that enshell doesn't accept a string
253
+ # as security token
254
+ if not disable_grpc_options:
255
+ enshell.security_token = secret_key
256
+ enshell.connect_existing_channel(enshell_grpc_channel) # pragma: no cover
257
+ else:
258
+ enshell = enshell_grpc.EnShellGRPC(
259
+ port=grpc_port,
260
+ grpc_use_tcp_sockets=grpc_use_tcp_sockets,
261
+ grpc_allow_network_connections=grpc_allow_network_connections,
262
+ grpc_disable_tls=grpc_disable_tls,
263
+ grpc_uds_pathname=grpc_uds_pathname,
264
+ disable_grpc_options=disable_grpc_options,
265
+ )
266
+ # If disable_grpc_options is True, it means that in the launcher
267
+ # the ensight version was checked and it hasn't got the new
268
+ # grpc options. This means that enshell doesn't accept a string
269
+ # as security token
270
+ if not disable_grpc_options:
271
+ enshell.security_token = secret_key
272
+ time_start = time.time()
273
+ while time.time() - time_start < timeout: # pragma: no cover
274
+ if enshell.is_connected():
275
+ break
276
+ try:
277
+ enshell.connect(timeout=timeout)
278
+ except OSError: # pragma: no cover
279
+ pass # pragma: no cover
280
+ return enshell
281
+
282
+
283
+ def pull_image(docker_client: "DockerClient", image_name: str) -> None:
284
+ """Pull the input docker image using the input Docker Client
285
+
286
+ Parameters
287
+ ----------
288
+ docker_client: DockerClient
289
+ the current DockerClient to pull the image with
290
+ image_name: str
291
+ the image to pull
292
+ """
293
+ try:
294
+ if docker_client is not None: # pragma: no cover
295
+ docker_client.images.pull(image_name)
296
+ except Exception:
297
+ raise RuntimeError(f"Can't pull Docker image: {image_name}")
298
+
299
+
300
+ def _is_within_directory(directory, target):
301
+ """Check if target is inside of the input directory."""
302
+ abs_directory = os.path.abspath(directory)
303
+ abs_target = os.path.abspath(target)
304
+ return os.path.commonprefix([abs_directory, abs_target]) == abs_directory
305
+
306
+
307
+ def safe_extract(tar, path=".", members=None, *, numeric_owner=False):
308
+ """Utility to check tar extraction to avoid bandit check issue."""
309
+ for member in tar.getmembers():
310
+ member_path = os.path.join(path, member.name)
311
+ if not _is_within_directory(path, member_path):
312
+ raise Exception("Attempted Path Traversal in Tar File")
313
+ tar.extractall(path, members, numeric_owner=numeric_owner)
314
+
315
+
316
+ def grpc_version_check(internal_version, ensight_full_version):
317
+ """Check if the gRPC security options apply to the EnSight install."""
318
+ if int(internal_version) < 242:
319
+ return False
320
+ # From 261 onward always available
321
+ if int(internal_version) >= 261:
322
+ return True
323
+ # Exactly matches one of the service pack that introduced the changes
324
+ if ensight_full_version in GRPC_VERSIONS:
325
+ return True
326
+ split_version = ensight_full_version.split(" ")
327
+ year = split_version[0]
328
+ # Not a service pack
329
+ if len(split_version[1].split(".")) == 1:
330
+ return False
331
+ r_version = split_version[1].split(".")[0]
332
+ dot_version = int(split_version[1].split(".")[1])
333
+ # Check service pack version
334
+ if year == "2024" and dot_version < 5:
335
+ return False
336
+ if year == "2025":
337
+ if r_version == "R1" and dot_version < 4:
338
+ return False
339
+ if r_version == "R2" and dot_version < 3:
340
+ return False
341
+ return True
@@ -0,0 +1,98 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <link rel="stylesheet" type="text/css" href="/bootstrap.min.cssOPTIONAL_QUERY"/>
5
+
6
+ <script src='/jqueryJQUERY_VERSION.min.jsOPTIONAL_QUERY'></script>
7
+ <script src='/geotiff.jsOPTIONAL_QUERY'></script>
8
+ <script src='/geotiff_nexus.jsOPTIONAL_QUERY'></script>
9
+ <script src="/bootstrap.min.jsOPTIONAL_QUERY"></script>
10
+
11
+ <script>
12
+ function updatepick_ITEMID(e) {
13
+ let x = e.offsetX;
14
+ let y = e.offsetY;
15
+ let image_elem = document.getElementById('nexus_image_ITEMID');
16
+ const origWidth = image_elem.nexus_width;
17
+ const origHeight = image_elem.nexus_height;
18
+ x = (x * (origWidth / image_elem.width)) | 0;
19
+ y = (y * (origHeight / image_elem.height)) | 0;
20
+ x = x >= origWidth ? origWidth - 1 : x;
21
+ x = x < 0 ? 0 : x;
22
+ y = y >= origHeight ? origHeight - 1 : y;
23
+ y = y < 0 ? 0 : y;
24
+ let idx = y * origWidth + x;
25
+
26
+ let probeResult = "";
27
+ if ('nexus_pick_buffer' in image_elem) {
28
+ let partid = image_elem.nexus_pick_buffer[idx * 4] + 256 * image_elem.nexus_pick_buffer[idx * 4 + 1];
29
+ if (partid in image_elem.nexus_metadata) {
30
+ let partinfo = image_elem.nexus_metadata[partid];
31
+ probeResult += " Part: '" + partinfo.name + "'";
32
+ if ('variable' in partinfo) {
33
+ if ('nexus_var_buffer' in image_elem) {
34
+ let vardata = image_elem.nexus_var_buffer[idx];
35
+ let varinfo = image_elem.nexus_varinfo[partinfo.variable];
36
+ probeResult += " Var: '" + varinfo.name + "' " + vardata.toExponential(5);
37
+ if (varinfo.unit_label.length) {
38
+ probeResult += " [" + varinfo.unit_label + "]";
39
+ }
40
+ }
41
+ }
42
+ } else {
43
+ probeResult += "Part: None";
44
+ }
45
+ }
46
+
47
+ let probeDisplay = $('#probe_display_ITEMID');
48
+ // dispose first so it doesn't use tooltips created previously
49
+ probeDisplay.tooltip('dispose');
50
+ probeDisplay.tooltip('show');
51
+ // display coordinates
52
+ $("#probe_xy_ITEMID").text(`${x}, ${y}`);
53
+ // display values
54
+ $("#probe_result_ITEMID").html(probeResult);
55
+ }
56
+
57
+ function clearpick_ITEMID() {
58
+ let probeDisplay = $('#probe_display_ITEMID');
59
+ // hide and dispose existing tooltips
60
+ probeDisplay.tooltip('hide');
61
+ probeDisplay.tooltip('dispose');
62
+ }
63
+
64
+ async function tiff_image_ITEMID_loader() {
65
+ const response = await fetch("TIFF_URL");
66
+ const arrayBuffer = await response.arrayBuffer();
67
+ const tiff_promise_ITEMID = GeoTIFF.fromArrayBuffer(arrayBuffer);
68
+ tiff_promise_ITEMID.then(nexus_image_load_tiff_image.bind(null, "nexus_image_ITEMID"), nexus_image_general_error);
69
+ }
70
+
71
+ tiff_image_ITEMID_loader();
72
+
73
+ </script>
74
+ </head>
75
+ <body style="margin:0px;padding:0px;">
76
+ <div style="margin: 0 auto; display:flex; justify-content:center;">
77
+ <!-- tooltip parent for img --->
78
+ <div id="probe_display_ITEMID"
79
+ data-BS_PREFIXtoggle="tooltip"
80
+ data-BS_PREFIXplacement="bottom"
81
+ data-BS_PREFIXfallbackPlacement="['top', 'right']"
82
+ data-BS_PREFIXhtml="true"
83
+ data-BS_PREFIXcontainer="body"
84
+ data-BS_PREFIXboundary="viewport"
85
+ data-BS_PREFIXanimation="false"
86
+ data-BS_PREFIXtrigger="manual"
87
+ data-BS_PREFIXtitle='<span class="f-1r">
88
+ <span>X, Y : <span id="probe_xy_ITEMID">0, 0</span></span>
89
+ <br>
90
+ <span id="probe_result_ITEMID"></span>
91
+ </span>'
92
+ >
93
+ <img id="nexus_image_ITEMID" onmousemove="updatepick_ITEMID(event)"
94
+ onmouseout="clearpick_ITEMID()" class="img-fluid"><br>
95
+ </div>
96
+ </div>
97
+ </body>
98
+ </html>