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,296 @@
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
+ """launch_ensight module
24
+
25
+ The launch_ensight module provides pyensight with the ability to launch an
26
+ EnSight session using PyPIM. This leverages the DockerLauncher module.
27
+
28
+ Examples
29
+ --------
30
+ >>> from ansys.pyensight.core import launch_ensight
31
+ >>> session = launch_ensight()
32
+ >>> # do pyensight stuff with the session
33
+ >>> session.close()
34
+ """
35
+
36
+ import logging
37
+ from typing import Optional
38
+
39
+ from ansys.pyensight.core.locallauncher import LocalLauncher
40
+ from ansys.pyensight.core.session import Session
41
+
42
+ pim_is_available = False # pragma: no cover
43
+ try:
44
+ import ansys.platform.instancemanagement as pypim
45
+
46
+ pim_is_available = True # pragma: no cover
47
+ except Exception: # pragma: no cover
48
+ pass # pragma: no cover
49
+ logging.debug(f"pim_is_available: {pim_is_available}\n")
50
+
51
+ docker_is_available = False
52
+ try:
53
+ from ansys.pyensight.core.dockerlauncher import DockerLauncher
54
+
55
+ docker_is_available = True
56
+ except Exception: # pragma: no cover
57
+ pass # pragma: no cover
58
+ logging.debug(f"docker_is_available: {docker_is_available}\n")
59
+
60
+
61
+ if pim_is_available: # pragma: no cover
62
+
63
+ def _prepare_pim( # pragma: no cover
64
+ product_version: Optional[str] = None,
65
+ ):
66
+ """Create a PIM instance and gRPC channel for the input version of EnSight.
67
+
68
+ Parameters
69
+ ----------
70
+ product_version : str, optional
71
+ Version of the product. For example, "232". The default is "None"
72
+
73
+ """
74
+ pim = pypim.connect()
75
+ instance = pim.create_instance(
76
+ product_name="ensight",
77
+ product_version=product_version,
78
+ )
79
+ instance.wait_for_ready()
80
+ # use defaults as specified by PIM
81
+ channel = instance.build_grpc_channel(
82
+ options=[
83
+ ("grpc.max_receive_message_length", -1),
84
+ ("grpc.max_send_message_length", -1),
85
+ ("grpc.testing.fixed_reconnect_backoff_ms", 1100),
86
+ ]
87
+ )
88
+ return instance, channel
89
+
90
+ def _launch_ensight_with_pim( # pragma: no cover
91
+ product_version: Optional[str] = None,
92
+ **kwargs,
93
+ ) -> "Session":
94
+ """Internal function.
95
+ Start via PyPIM the EnSight Docker container with EnShell as the ENTRYPOINT.
96
+ Create and bind a Session instance to the created gRPC session. Return that session.
97
+
98
+ Parameters
99
+ ----------
100
+ product_version : str, optional
101
+ Version of the product. For example, "232". The default is "None"
102
+ use_egl : bool, optional
103
+ If True, EGL hardware accelerated graphics will be used. The platform
104
+ must be able to support it.
105
+ use_sos : int, optional
106
+ If None, don't use SOS. Otherwise, it's the number of EnSight Servers to use (int).
107
+
108
+ Returns
109
+ -------
110
+ Session
111
+ pyensight Session object instance
112
+
113
+ """
114
+ instance, channel = _prepare_pim(product_version=product_version)
115
+ launcher = DockerLauncher(channel=channel, pim_instance=instance, **kwargs)
116
+ return launcher.connect()
117
+
118
+ def _launch_libuserd_with_pim(
119
+ product_version: Optional[str] = None, **kwargs
120
+ ): # pragma: no cover
121
+ from ansys.pyensight.core.libuserd import LibUserd
122
+
123
+ instance, channel = _prepare_pim(product_version=product_version)
124
+ libuserd = LibUserd(channel=channel, pim_instance=instance, **kwargs)
125
+ libuserd.initialize()
126
+ return libuserd
127
+
128
+
129
+ def launch_ensight(
130
+ product_version: Optional[str] = None,
131
+ use_pim: bool = True,
132
+ use_docker: bool = True,
133
+ data_directory: Optional[str] = None,
134
+ docker_image_name: Optional[str] = None,
135
+ use_dev: bool = False,
136
+ ansys_installation: Optional[str] = None,
137
+ application: str = "ensight",
138
+ batch: bool = True,
139
+ **kwargs,
140
+ ) -> "Session":
141
+ """Start an EnSight session via EnShell using the Docker EnSight Image.
142
+ Return that session.
143
+
144
+ Parameters
145
+ ----------
146
+ product_version : str, optional
147
+ Select an installed version of ANSYS. The string must be in a format like
148
+ "232" (for 2023 R2). The default is "None", in which case the newest installed
149
+ version is used.
150
+ use_pim : bool, optional
151
+ If True, then PyPIM is used to launch EnSight.
152
+ use_docker : bool, optional
153
+ If True, use DockerLaucher. If use_pim is True, this option is ignored.
154
+ data_directory: str, optional
155
+ Host directory to make into the Docker container at /data
156
+ Only used if use_docker is True.
157
+ docker_image_name: str, optional
158
+ Optional Docker Image name to use
159
+ use_dev: bool, optional
160
+ Option to use the latest ensight_dev Docker Image; overridden by docker_image_name if specified.
161
+ ansys_installation: str, optional
162
+ Location of the ANSYS installation, including the version.
163
+ directory Default: None (causes common locations to be scanned).
164
+ If use_pim is True, this option is ignored. If use_docker is True, this option is ignored.
165
+ application: str, optional
166
+ The application to be launched. By default, "ensight", but
167
+ "envision" is also available.
168
+ batch: bool, optional
169
+ By default, the EnSight/EnVision instance will run in batch mode.
170
+ If batch is set to False, the full GUI will be presented.
171
+ Only used if use_pim and use_docker are False.
172
+ use_egl: bool, optional
173
+ If True, EGL hardware accelerated graphics will be used. The platform
174
+ must be able to support it.
175
+ use_sos: int, optional
176
+ If None, don't use SOS. Otherwise, it's the number of EnSight Servers to use (int).
177
+ timeout: float, optional
178
+ In some cases where the EnSight session can take a significant amount of
179
+ time to start up, this is the number of seconds to wait before failing
180
+ the connection. The default is 120.0.
181
+
182
+ Returns
183
+ -------
184
+ type
185
+ pyensight Session object instance
186
+
187
+ Raises
188
+ ------
189
+ RuntimeError
190
+ variety of error conditions
191
+
192
+ """
193
+
194
+ logging.debug(f"pim_is_available: {pim_is_available} use_pim: {use_pim}\n") # pragma: no cover
195
+ if pim_is_available and use_pim: # pragma: no cover
196
+ if pypim.is_configured():
197
+ return _launch_ensight_with_pim(product_version=product_version, **kwargs)
198
+
199
+ # not using PIM, but use Docker
200
+ logging.debug(f"docker_is_available: {docker_is_available} use_docker: {use_docker}\n")
201
+ if docker_is_available and use_docker:
202
+ launcher = DockerLauncher(
203
+ data_directory=data_directory,
204
+ docker_image_name=docker_image_name,
205
+ use_dev=use_dev,
206
+ **kwargs,
207
+ )
208
+ return launcher.start()
209
+
210
+ # use local installation of EnSight
211
+ launcher = LocalLauncher( # pragma: no cover
212
+ ansys_installation=ansys_installation, # pragma: no cover
213
+ application=application, # pragma: no cover
214
+ batch=batch, # pragma: no cover
215
+ **kwargs, # pragma: no cover
216
+ ) # pragma: no cover
217
+ return launcher.start() # pragma: no cover
218
+
219
+
220
+ def launch_libuserd( # pragma: no cover
221
+ product_version: Optional[str] = None,
222
+ use_pim: bool = True,
223
+ use_docker: bool = True,
224
+ data_directory: Optional[str] = None,
225
+ docker_image_name: Optional[str] = None,
226
+ use_dev: bool = False,
227
+ ansys_installation: Optional[str] = None,
228
+ timeout: float = 120.0,
229
+ pull_image_if_not_available: bool = False,
230
+ ):
231
+ """Start an EnSight session via EnShell using the Docker EnSight Image.
232
+ Return that session.
233
+
234
+ Parameters
235
+ ----------
236
+ product_version : str, optional
237
+ Select an installed version of ANSYS. The string must be in a format like
238
+ "232" (for 2023 R2). The default is "None", in which case the newest installed
239
+ version is used.
240
+ use_pim : bool, optional
241
+ If True, then PyPIM is used to launch the EnSight image.
242
+ use_docker : bool, optional
243
+ If True, use DockerLaucher. If use_pim is True, this option is ignored.
244
+ data_directory: str, optional
245
+ Host directory to make into the Docker container at /data
246
+ Only used if use_docker is True.
247
+ docker_image_name: str, optional
248
+ Optional Docker Image name to use
249
+ use_dev: bool, optional
250
+ Option to use the latest ensight_dev Docker Image; overridden by docker_image_name if specified.
251
+ ansys_installation: str, optional
252
+ Location of the ANSYS installation, including the version.
253
+ directory Default: None (causes common locations to be scanned).
254
+ If use_pim is True, this option is ignored. If use_docker is True, this option is ignored.
255
+ application: str, optional
256
+ The application to be launched. By default, "ensight", but
257
+ "envision" is also available.
258
+ timeout: float, optional
259
+ In some cases where the EnSight session can take a significant amount of
260
+ time to start up, this is the number of seconds to wait before failing
261
+ the connection. The default is 120.0.
262
+ pull_image_if_not_available: bool
263
+ If True, the image will be pulled using Docker. If use_pim is True this option
264
+ is ignored.
265
+ Returns
266
+ -------
267
+ type
268
+ LibUserd object instance
269
+
270
+ Raises
271
+ ------
272
+ RuntimeError
273
+ variety of error conditions
274
+
275
+ """
276
+ from ansys.pyensight.core.libuserd import LibUserd
277
+
278
+ logging.debug(f"pim_is_available: {pim_is_available} use_pim: {use_pim}\n") # pragma: no cover
279
+ if pim_is_available and use_pim: # pragma: no cover
280
+ if pypim.is_configured():
281
+ return _launch_libuserd_with_pim(product_version=product_version, timeout=timeout)
282
+ logging.debug(f"docker_is_available: {docker_is_available} use_docker: {use_docker}\n")
283
+ if docker_is_available and use_docker:
284
+ libuserd = LibUserd(
285
+ data_directory=data_directory,
286
+ docker_image_name=docker_image_name,
287
+ use_dev=use_dev,
288
+ use_docker=use_docker,
289
+ timeout=timeout,
290
+ pull_image_if_not_available=pull_image_if_not_available,
291
+ )
292
+ libuserd.initialize()
293
+ return libuserd
294
+ libuserd = LibUserd(ansys_installation=ansys_installation, timeout=timeout)
295
+ libuserd.initialize()
296
+ return libuserd
@@ -0,0 +1,388 @@
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
+ """Launcher module.
24
+
25
+ The Launcher module provides a base class responsible for managing an EnSight
26
+ :class:`Session<ansys.pyensight.core.Session>` instance. Subclasses of the
27
+ class implement specific launching paradigms.
28
+
29
+ Examples:
30
+ ::
31
+
32
+ from ansys.pyensight.core import LocalLauncher
33
+ session = LocalLauncher().start()
34
+
35
+ """
36
+ import os.path
37
+ import platform
38
+ import re
39
+ from typing import TYPE_CHECKING, Dict, List, Optional
40
+ import warnings
41
+
42
+ import psutil
43
+ import requests
44
+
45
+ if TYPE_CHECKING:
46
+ from ansys.pyensight.core import Session
47
+
48
+ # Don't remove this line. The idna encoding
49
+ # is used by getaddrinfo when dealing with unicode hostnames,
50
+ # and in some cases, there appears to be a race condition
51
+ # where threads will get a LookupError on getaddrinfo() saying
52
+ # that the encoding doesn't exist. Using the idna encoding before
53
+ # running any CLI code (and any threads it may create) ensures that
54
+ # the encodings.idna is imported and registered in the codecs registry,
55
+ # which will stop the LookupErrors from happening.
56
+ # See: https://bugs.python.org/issue29288
57
+ "".encode("idna")
58
+
59
+ # The user doesn't know "eth" and "ib" what they mean. Use more meaningful
60
+ # keywords.
61
+ INTERCONNECT_MAP = {"ethernet": "eth", "infiniband": "ib"}
62
+
63
+ MPI_TYPES = ["intel2018", "intel2021", "openmpi"]
64
+
65
+
66
+ class Launcher:
67
+ """Provides the EnSight ``Launcher`` base class.
68
+
69
+ A ``Launcher`` instance is used to start and end an EnSight session.
70
+ Specific subclasses handle different types of launching semantics.
71
+ A launcher can create only a single EnSight instance. If you need to
72
+ have more than one EnSight instance, a new launcher instance is required.
73
+
74
+ Parameters
75
+ ----------
76
+ timeout : float, optional
77
+ Number of seconds to try a gRPC connection before giving up.
78
+ The default is ``120``.
79
+ use_egl : bool, optional
80
+ Whether to use EGL hardware for accelerated graphics. The platform
81
+ must be able to support this hardware. The default is ``False``.
82
+ use_sos : int, optional
83
+ Number of EnSight servers to use for SOS (Server of Server) mode.
84
+ The default is ``None``, in which case SOS mode is not used.
85
+ enable_rest_api : bool, optional
86
+ Whether to enable the EnSight REST API. The default is ``False``.
87
+ This parameter is supported in EnSight 2024 R1 and later.
88
+ additional_command_line_options: list, optional
89
+ Additional command line options to be used to launch EnSight.
90
+ Please note, when using DockerLauncher, arguments that contain spaces
91
+ are not supported.
92
+ launch_web_ui : bool, optional
93
+ Whether to launch the webUI from EnSight
94
+ use_mpi: str, optional
95
+ If set, EnSight will be launched with the MPI type selected. The valid
96
+ values depend on the EnSight version to be used. The user can see
97
+ the specific list starting the EnSight Launcher manually and specifying the options
98
+ to launch EnSight in parallel and MPI. Here are reported the values for releases
99
+ 2024R2 and 2025R1.
100
+
101
+ =================== =========================================
102
+ Release Valid MPI Types
103
+ =================== =========================================
104
+ 2024R2 intel2021, intel2018, openmpi
105
+ 2025R1 intel2021, intel2018, openmpi
106
+ =================== =========================================
107
+
108
+ The remote nodes must be Linux nodes.
109
+ This option is valid only if a LocalLauncher is used.
110
+ interconnet: str, optional
111
+ If set, EnSight will be launched with the MPI Interconnect selected. Valid values
112
+ are "ethernet", "infiniband". It requires use_mpi to be set.
113
+ If use_mpi is set and interconnect is not, "ethernet" will be used.
114
+ This option is valid only if a LocalLauncher is used.
115
+ server_hosts: List[str], optional
116
+ A list of hostnames where the server processes should be spawned on when MPI is selected.
117
+ If use_mpi is set and server_hosts not, it will default to "localhost".
118
+ This option is valid only if a LocalLauncher is used.
119
+ """
120
+
121
+ def __init__(
122
+ self,
123
+ timeout: float = 120.0,
124
+ use_egl: bool = False,
125
+ use_sos: Optional[int] = None,
126
+ enable_rest_api: bool = False,
127
+ additional_command_line_options: Optional[List] = None,
128
+ launch_webui: bool = False,
129
+ use_mpi: Optional[str] = None,
130
+ interconnect: Optional[str] = None,
131
+ server_hosts: Optional[List[str]] = None,
132
+ rest_ws_separate_loops: bool = False,
133
+ do_not_start_ws: bool = False,
134
+ liben_rest: bool = False,
135
+ vtk_ws: bool = False,
136
+ ) -> None:
137
+ self._timeout = timeout
138
+ self._use_egl_param_val: bool = use_egl
139
+ self._use_sos = use_sos
140
+ self._use_mpi = use_mpi
141
+ self._interconnect = interconnect
142
+ self._vtk_ws_port = vtk_ws
143
+ if self._use_mpi and self._use_mpi not in MPI_TYPES:
144
+ raise RuntimeError(f"{self._use_mpi} is not a valid MPI option.")
145
+ if self._use_mpi and not self._interconnect:
146
+ self._interconnect = "ethernet"
147
+ if self._interconnect:
148
+ if self._interconnect not in list(INTERCONNECT_MAP.values()):
149
+ raise RuntimeError(f"{self._interconnect} is not a valid MPI interconnect option.")
150
+ self._interconnect = INTERCONNECT_MAP.get(self._interconnect)
151
+ self._server_hosts = server_hosts
152
+ if self._use_mpi and not self._server_hosts:
153
+ self._server_hosts = ["localhost"]
154
+ self._enable_rest_api = enable_rest_api
155
+
156
+ self._sessions: List[Session] = []
157
+ self._session_directory: str = "."
158
+
159
+ self._is_egl_capable: Optional[bool] = None
160
+ self._egl_env_val: Optional[bool] = None
161
+ egl_env = os.environ.get("PYENSIGHT_FORCE_ENSIGHT_EGL")
162
+ if egl_env is not None:
163
+ if egl_env == "1": # pragma: no cover
164
+ self._egl_env_val = True # pragma: no cover
165
+ else:
166
+ self._egl_env_val = False
167
+ # a dict of any optional launcher specific query parameters for URLs
168
+ self._query_parameters: Dict[str, str] = {}
169
+ self._additional_command_line_options = additional_command_line_options
170
+ self._launch_webui = launch_webui
171
+ self._do_not_start_ws = do_not_start_ws
172
+ self._liben_rest = liben_rest
173
+ self._rest_ws_separate_loops = rest_ws_separate_loops
174
+ self._has_grpc_changes = False
175
+
176
+ @property
177
+ def session_directory(self) -> str:
178
+ """Root directory for HTML files.
179
+
180
+ The contents of this directory can be accessed at ``hostname:port``.
181
+ """
182
+ return self._session_directory
183
+
184
+ @session_directory.setter
185
+ def session_directory(self, value: str):
186
+ self._session_directory = value
187
+
188
+ def close(self, session: "Session") -> None:
189
+ """Shut down the launched EnSight session.
190
+
191
+ This method closes all associated sessions and then stops the
192
+ launched EnSight instance.
193
+
194
+ Parameters
195
+ ----------
196
+ session : ``pyensight.Session``
197
+ Session to close.
198
+
199
+ Raises
200
+ ------
201
+ RuntimeError
202
+ If the session was not launched by this launcher.
203
+
204
+ """
205
+ if session not in self._sessions:
206
+ raise RuntimeError("Session not associated with this Launcher")
207
+ self._sessions.remove(session)
208
+ if self._sessions:
209
+ # stop the grpc session interface
210
+ session.grpc.shutdown(stop_ensight=False)
211
+ return
212
+
213
+ # if the session list is empty, stop the EnSight instance (via session grpc interface)
214
+ session.grpc.shutdown(stop_ensight=True, force=True)
215
+
216
+ # stop the websocketserver instance
217
+ url = f"http://{session.hostname}:{session.html_port}/v1/stop"
218
+ if session.secret_key: # pragma: no cover
219
+ url += f"?security_token={session.secret_key}"
220
+ try:
221
+ _ = requests.get(url)
222
+ except requests.exceptions.ConnectionError:
223
+ pass
224
+
225
+ # Stop the launcher instance
226
+ self.stop()
227
+
228
+ def start(self) -> Optional["Session"]:
229
+ """Start a session using the current launcher
230
+
231
+ The start() method will only allocate a single instance of
232
+ a Session object. If called a second time, return the
233
+ result of the first call.
234
+
235
+ Returns
236
+ -------
237
+ If start() has been called previously, return that session
238
+ and emit a warning. If start() has not been called, return None.
239
+
240
+ """
241
+ if len(self._sessions):
242
+ msg = "The launcher start() method may only be called once. "
243
+ msg += "Create a new launcher instance to start a new EnSight instance."
244
+ warnings.warn(msg, RuntimeWarning)
245
+ return self._sessions[0]
246
+ return None
247
+
248
+ def stop(self) -> None:
249
+ """Base method for stopping a session initiated by start()
250
+
251
+ Notes
252
+ -----
253
+ The session object is responsible for making the EnSight 'Exit' and websocketserver
254
+ calls. This method can be used to clean up any additional resources being used
255
+ by the launching method.
256
+ """
257
+ return
258
+
259
+ def _find_ports_used_by_other_pyensight_and_ensight(self):
260
+ """Find ports to avoid when looking for empty ports.
261
+
262
+ The ports are found iterating the current processes and
263
+ looking for PyEnSight/EnSight sessions and their command
264
+ lines.
265
+ """
266
+ pyensight_found = []
267
+ ensight_found = []
268
+ for process in psutil.process_iter():
269
+ try:
270
+ process_cmdline = process.cmdline()
271
+ except (psutil.AccessDenied, psutil.ZombieProcess, OSError, psutil.NoSuchProcess):
272
+ continue
273
+ if not process_cmdline:
274
+ continue
275
+ if len(process_cmdline) > 1:
276
+ if "websocketserver.py" in os.path.basename(process_cmdline[1]):
277
+ pyensight_found.append(process_cmdline)
278
+ if any(["ensight" in os.path.basename(x) for x in process_cmdline]):
279
+ if any([x == "-ports" for x in process_cmdline]):
280
+ ensight_found.append(process_cmdline)
281
+ ports = []
282
+ for command_line in pyensight_found:
283
+ for command in command_line:
284
+ if re.match(r"^\d{4,5}$", command):
285
+ ports.append(int(command))
286
+ for command_line in ensight_found:
287
+ idx = command_line.index("-ports") + 1
288
+ ports.append(int(command_line[idx]))
289
+ return list(set(ports))
290
+
291
+ def _use_egl(self) -> bool:
292
+ """Return True if the system supports the EGL and if EGL was desired.
293
+
294
+ Returns
295
+ -------
296
+ bool
297
+ A bool value that is True if we should use EGL.
298
+
299
+ """
300
+ if self._is_egl_capable is None:
301
+ # if we haven't checked with the subclasss if the system can do EGL
302
+ self._is_egl_capable = self._is_system_egl_capable()
303
+
304
+ if self._is_egl_capable is False:
305
+ # if the system can't do it, return False now
306
+ return False
307
+
308
+ if self._egl_env_val is not None: # pragma: no cover
309
+ # if the environment variable was set, that overrides the constructor option
310
+ return self._egl_env_val
311
+
312
+ # otherwise, use the arg passed to the constructor
313
+ return self._use_egl_param_val # pragma: no cover
314
+
315
+ def _is_system_egl_capable(self) -> bool: # pragma: no cover
316
+ """Return True if the system supports the EGL launch.
317
+
318
+ Returns
319
+ -------
320
+ bool
321
+ A bool value that is True if the system supports the EGL launch.
322
+
323
+ """
324
+ raise RuntimeError("Unsupported method for this configuration")
325
+
326
+ def _is_windows(self) -> bool:
327
+ """Return True if it is Windows
328
+
329
+ Returns
330
+ -------
331
+ bool
332
+ a bool that is True if the platform is Windows
333
+
334
+ """
335
+ return platform.system() == "Windows"
336
+
337
+ def _get_query_parameters(self) -> Dict[str, str]:
338
+ """Return optional http query parameters as a dict.
339
+ It may be empty if there are None.
340
+ If query parameters exist, they should be added to any
341
+ http/https URL intended for the WSS web server.
342
+ This is used by things such as Ansys Lab.
343
+
344
+ Returns
345
+ -------
346
+ dict
347
+ query parameters that should be appended to any queries
348
+ """
349
+ return self._query_parameters
350
+
351
+ def _add_query_parameters(self, params: Dict[str, str]) -> None:
352
+ """Add query parameters supplied by params to the
353
+ overall dict of query parameters.
354
+
355
+ Parameters
356
+ ----------
357
+ params: dict :
358
+ query parameters to add to overall dict
359
+ """
360
+ for item, value in params.items(): # pragma: no cover
361
+ self._query_parameters[item] = value # pragma: no cover
362
+
363
+ def _delete_query_parameters(self, params: List[str]) -> None:
364
+ """Delete query parameters supplied by params from the
365
+ overall dict of query parameters.
366
+
367
+ Parameters
368
+ ----------
369
+ params: list :
370
+ query parameters to delete from the overall dict
371
+ """
372
+ for item in params: # pragma: no cover
373
+ try: # pragma: no cover
374
+ del self._query_parameters[item] # pragma: no cover
375
+ except Exception: # pragma: no cover
376
+ pass # pragma: no cover
377
+
378
+ def _get_versionfrom_buildinfo(self, buildinfo):
379
+ """Check if the gRPC security options apply to the EnSight install."""
380
+ version_match = re.search("Version: (.*)\n", buildinfo)
381
+ internal_version_match = re.search("Internal: (.*)\n", buildinfo)
382
+ if not internal_version_match:
383
+ raise RuntimeError("Couldn't parse EnSight internal version in BUILDINFO file.")
384
+ internal_version = internal_version_match.group(1)
385
+ if not version_match:
386
+ raise RuntimeError("Couldn't parse EnSight version in BUILDINFO file.")
387
+ ensight_full_version = version_match.group(1)
388
+ return internal_version, ensight_full_version