ansys-pyensight-core 0.8.9__tar.gz → 0.8.10__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.
Potentially problematic release.
This version of ansys-pyensight-core might be problematic. Click here for more details.
- {ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/PKG-INFO +4 -6
- {ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/pyproject.toml +5 -7
- {ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/__init__.py +1 -1
- ansys_pyensight_core-0.8.10/src/ansys/pyensight/core/common.py +216 -0
- {ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/dockerlauncher.py +79 -94
- {ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/enshell_grpc.py +32 -0
- {ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/launch_ensight.py +120 -19
- {ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/launcher.py +4 -60
- ansys_pyensight_core-0.8.10/src/ansys/pyensight/core/libuserd.py +1832 -0
- {ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/locallauncher.py +43 -2
- {ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/renderable.py +30 -0
- {ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/session.py +5 -0
- {ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/utils/readers.py +15 -11
- {ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/LICENSE +0 -0
- {ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/README.rst +0 -0
- {ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/deep_pixel_view.html +0 -0
- {ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/enscontext.py +0 -0
- {ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/ensight_grpc.py +0 -0
- {ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/ensobj.py +0 -0
- {ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/listobj.py +0 -0
- {ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/py.typed +0 -0
- {ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/sgeo_poll.html +0 -0
- {ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/utils/__init__.py +0 -0
- {ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/utils/adr.py +0 -0
- {ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/utils/dsg_server.py +0 -0
- {ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/utils/export.py +0 -0
- {ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/utils/omniverse.py +0 -0
- {ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/utils/omniverse_cli.py +0 -0
- {ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/utils/omniverse_dsg_server.py +0 -0
- {ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/utils/omniverse_glb_server.py +0 -0
- {ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/utils/parts.py +0 -0
- {ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/utils/query.py +0 -0
- {ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/utils/resources/Materials/000_sky.exr +0 -0
- {ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/utils/support.py +0 -0
- {ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/utils/variables.py +0 -0
- {ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/utils/views.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ansys-pyensight-core
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.10
|
|
4
4
|
Summary: A python wrapper for Ansys EnSight
|
|
5
5
|
Author-email: "ANSYS, Inc." <pyansys.core@ansys.com>
|
|
6
6
|
Maintainer-email: "ANSYS, Inc." <pyansys.core@ansys.com>
|
|
@@ -16,7 +16,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.11
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
18
18
|
Requires-Dist: importlib-metadata>=4.0; python_version<='3.8'
|
|
19
|
-
Requires-Dist: ansys-api-pyensight==0.4.
|
|
19
|
+
Requires-Dist: ansys-api-pyensight==0.4.2
|
|
20
20
|
Requires-Dist: requests>=2.28.2
|
|
21
21
|
Requires-Dist: docker>=6.1.0
|
|
22
22
|
Requires-Dist: urllib3<3.0.0
|
|
@@ -24,13 +24,13 @@ Requires-Dist: numpy>=1.21.0,<2
|
|
|
24
24
|
Requires-Dist: Pillow>=9.3.0
|
|
25
25
|
Requires-Dist: pypng>=0.0.20
|
|
26
26
|
Requires-Dist: psutil>=5.9.2
|
|
27
|
+
Requires-Dist: usd-core>=24.8
|
|
28
|
+
Requires-Dist: pygltflib>=1.16.2
|
|
27
29
|
Requires-Dist: build>=0.10.0 ; extra == "dev"
|
|
28
30
|
Requires-Dist: bump2version>=1.0.1 ; extra == "dev"
|
|
29
31
|
Requires-Dist: ipdb>=0.9.4 ; extra == "dev"
|
|
30
32
|
Requires-Dist: dill>=0.3.5.1 ; extra == "dev"
|
|
31
33
|
Requires-Dist: pre-commit>=3.3.3 ; extra == "dev"
|
|
32
|
-
Requires-Dist: usd-core>=24.8 ; extra == "dev"
|
|
33
|
-
Requires-Dist: pygltflib>=1.16.2 ; extra == "dev"
|
|
34
34
|
Requires-Dist: Sphinx==8.0.2 ; extra == "doc"
|
|
35
35
|
Requires-Dist: numpydoc==1.5.0 ; extra == "doc"
|
|
36
36
|
Requires-Dist: ansys-sphinx-theme==0.9.9 ; extra == "doc"
|
|
@@ -53,8 +53,6 @@ Requires-Dist: pytest-mock==3.10.0 ; extra == "tests"
|
|
|
53
53
|
Requires-Dist: urllib3==2.2.2 ; extra == "tests"
|
|
54
54
|
Requires-Dist: requests>=2.28.2 ; extra == "tests"
|
|
55
55
|
Requires-Dist: docker>=6.1.0 ; extra == "tests"
|
|
56
|
-
Requires-Dist: usd-core>=24.8 ; extra == "tests"
|
|
57
|
-
Requires-Dist: pygltflib>=1.16.2 ; extra == "tests"
|
|
58
56
|
Project-URL: Changelog, https://github.com/ansys/pyensight/blob/main/CHANGELOG.rst
|
|
59
57
|
Project-URL: Documentation, https://ensight.docs.pyansys.com/
|
|
60
58
|
Project-URL: Homepage, https://github.com/ansys/pyensight
|
|
@@ -6,7 +6,7 @@ build-backend = "flit_core.buildapi"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "ansys-pyensight-core"
|
|
9
|
-
version = "0.8.
|
|
9
|
+
version = "0.8.10"
|
|
10
10
|
description = "A python wrapper for Ansys EnSight"
|
|
11
11
|
readme = "README.rst"
|
|
12
12
|
requires-python = ">=3.9,<4"
|
|
@@ -27,7 +27,7 @@ classifiers = [
|
|
|
27
27
|
|
|
28
28
|
dependencies = [
|
|
29
29
|
"importlib-metadata>=4.0; python_version<='3.8'",
|
|
30
|
-
"ansys-api-pyensight==0.4.
|
|
30
|
+
"ansys-api-pyensight==0.4.2",
|
|
31
31
|
"requests>=2.28.2",
|
|
32
32
|
"docker>=6.1.0",
|
|
33
33
|
"urllib3<3.0.0",
|
|
@@ -35,6 +35,8 @@ dependencies = [
|
|
|
35
35
|
"Pillow>=9.3.0",
|
|
36
36
|
"pypng>=0.0.20",
|
|
37
37
|
"psutil>=5.9.2",
|
|
38
|
+
"usd-core>=24.8",
|
|
39
|
+
"pygltflib>=1.16.2",
|
|
38
40
|
]
|
|
39
41
|
|
|
40
42
|
[project.optional-dependencies]
|
|
@@ -44,8 +46,6 @@ dev = [
|
|
|
44
46
|
"ipdb>=0.9.4",
|
|
45
47
|
"dill>=0.3.5.1",
|
|
46
48
|
"pre-commit>=3.3.3",
|
|
47
|
-
"usd-core>=24.8",
|
|
48
|
-
"pygltflib>=1.16.2",
|
|
49
49
|
]
|
|
50
50
|
tests = [
|
|
51
51
|
"pytest==8.3.2",
|
|
@@ -55,8 +55,6 @@ tests = [
|
|
|
55
55
|
"urllib3==2.2.2",
|
|
56
56
|
"requests>=2.28.2",
|
|
57
57
|
"docker>=6.1.0",
|
|
58
|
-
"usd-core>=24.8",
|
|
59
|
-
"pygltflib>=1.16.2",
|
|
60
58
|
]
|
|
61
59
|
doc = [
|
|
62
60
|
"Sphinx==8.0.2",
|
|
@@ -164,7 +162,7 @@ python_version = 3.9
|
|
|
164
162
|
strict = false
|
|
165
163
|
namespace_packages = true
|
|
166
164
|
explicit_package_bases = true
|
|
167
|
-
exclude = ["tests/", "^doc"
|
|
165
|
+
exclude = ["tests/", "^doc"]
|
|
168
166
|
no_site_packages = true
|
|
169
167
|
follow_imports = "skip"
|
|
170
168
|
|
{ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/__init__.py
RENAMED
|
@@ -12,7 +12,7 @@ __ansys_version__ = DEFAULT_ANSYS_VERSION
|
|
|
12
12
|
__ansys_version_str__ = f"{2000+(int(__ansys_version__) // 10)} R{int(__ansys_version__) % 10}"
|
|
13
13
|
|
|
14
14
|
from ansys.pyensight.core.dockerlauncher import DockerLauncher
|
|
15
|
-
from ansys.pyensight.core.launch_ensight import launch_ensight
|
|
15
|
+
from ansys.pyensight.core.launch_ensight import launch_ensight, launch_libuserd
|
|
16
16
|
from ansys.pyensight.core.launcher import Launcher
|
|
17
17
|
from ansys.pyensight.core.listobj import ensobjlist
|
|
18
18
|
from ansys.pyensight.core.locallauncher import LocalLauncher
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
""" This module provides a list of common utilities shared between different PyEnSight modules."""
|
|
2
|
+
|
|
3
|
+
import random
|
|
4
|
+
import re
|
|
5
|
+
import socket
|
|
6
|
+
import time
|
|
7
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple
|
|
8
|
+
|
|
9
|
+
from ansys.pyensight.core import enshell_grpc
|
|
10
|
+
import urllib3
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
from simple_upload_server.client import Client
|
|
14
|
+
|
|
15
|
+
simple_upload_server_is_available = True # pragma: no cover
|
|
16
|
+
except Exception:
|
|
17
|
+
simple_upload_server_is_available = False
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from docker import DockerClient
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def find_unused_ports(count: int, avoid: Optional[List[int]] = None) -> Optional[List[int]]:
|
|
24
|
+
"""Find "count" unused ports on the host system
|
|
25
|
+
|
|
26
|
+
A port is considered unused if it does not respond to a "connect" attempt. Walk
|
|
27
|
+
the ports from 'start' to 'end' looking for unused ports and avoiding any ports
|
|
28
|
+
in the 'avoid' list. Stop once the desired number of ports have been
|
|
29
|
+
found. If an insufficient number of ports were found, return None.
|
|
30
|
+
|
|
31
|
+
Parameters
|
|
32
|
+
----------
|
|
33
|
+
count: int :
|
|
34
|
+
Number of unused ports to find
|
|
35
|
+
avoid: Optional[List[int]] :
|
|
36
|
+
An optional list of ports not to check
|
|
37
|
+
|
|
38
|
+
Returns
|
|
39
|
+
-------
|
|
40
|
+
The detected ports or None on failure
|
|
41
|
+
|
|
42
|
+
"""
|
|
43
|
+
if avoid is None:
|
|
44
|
+
avoid = []
|
|
45
|
+
ports = list()
|
|
46
|
+
|
|
47
|
+
# pick a starting port number
|
|
48
|
+
start = random.randint(1024, 64000)
|
|
49
|
+
# We will scan for 65530 ports unless end is specified
|
|
50
|
+
port_mod = 65530
|
|
51
|
+
end = start + port_mod - 1
|
|
52
|
+
# walk the "virtual" port range
|
|
53
|
+
for base_port in range(start, end + 1):
|
|
54
|
+
# Map to physical port range
|
|
55
|
+
# There have been some issues with 65534+ so we stop at 65530
|
|
56
|
+
port = base_port % port_mod
|
|
57
|
+
# port 0 is special
|
|
58
|
+
if port == 0: # pragma: no cover
|
|
59
|
+
continue # pragma: no cover
|
|
60
|
+
# avoid admin ports
|
|
61
|
+
if port < 1024: # pragma: no cover
|
|
62
|
+
continue # pragma: no cover
|
|
63
|
+
# are we supposed to skip this one?
|
|
64
|
+
if port in avoid: # pragma: no cover
|
|
65
|
+
continue # pragma: no cover
|
|
66
|
+
# is anyone listening?
|
|
67
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
68
|
+
result = sock.connect_ex(("127.0.0.1", port))
|
|
69
|
+
if result != 0:
|
|
70
|
+
ports.append(port)
|
|
71
|
+
else:
|
|
72
|
+
sock.close() # pragma: no cover
|
|
73
|
+
if len(ports) >= count:
|
|
74
|
+
return ports
|
|
75
|
+
# in case we failed...
|
|
76
|
+
if len(ports) < count: # pragma: no cover
|
|
77
|
+
return None # pragma: no cover
|
|
78
|
+
return ports # pragma: no cover
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def get_host_port(uri: str) -> Tuple[str, int]:
|
|
82
|
+
"""Get the host port for the input uri
|
|
83
|
+
|
|
84
|
+
Parameters
|
|
85
|
+
----------
|
|
86
|
+
|
|
87
|
+
uri: str
|
|
88
|
+
The Uri to inspect
|
|
89
|
+
|
|
90
|
+
Returns
|
|
91
|
+
-------
|
|
92
|
+
(tuple):
|
|
93
|
+
A tuple containing the host and the port of the input uri
|
|
94
|
+
"""
|
|
95
|
+
parse_results = urllib3.util.parse_url(uri)
|
|
96
|
+
port = (
|
|
97
|
+
parse_results.port
|
|
98
|
+
if parse_results.port
|
|
99
|
+
else (443 if re.search("^https|wss$", parse_results.scheme) else None)
|
|
100
|
+
)
|
|
101
|
+
return (parse_results.host, port)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def get_file_service(pim_instance: Any) -> Optional[Any]: # pragma: no cover
|
|
105
|
+
"""Get the file service object for the input pim instance.
|
|
106
|
+
|
|
107
|
+
Parameters
|
|
108
|
+
----------
|
|
109
|
+
|
|
110
|
+
pim_instance:
|
|
111
|
+
the PIM instance to get the service from.
|
|
112
|
+
|
|
113
|
+
Returns
|
|
114
|
+
-------
|
|
115
|
+
|
|
116
|
+
pim_file_service:
|
|
117
|
+
the PIM file service object
|
|
118
|
+
"""
|
|
119
|
+
if simple_upload_server_is_available is False:
|
|
120
|
+
return None
|
|
121
|
+
if pim_instance is None:
|
|
122
|
+
return None
|
|
123
|
+
|
|
124
|
+
if "http-simple-upload-server" in pim_instance.services:
|
|
125
|
+
pim_file_service = Client(
|
|
126
|
+
token="token",
|
|
127
|
+
url=pim_instance.services["http-simple-upload-server"].uri,
|
|
128
|
+
headers=pim_instance.services["http-simple-upload-server"].headers,
|
|
129
|
+
)
|
|
130
|
+
return pim_file_service
|
|
131
|
+
return None
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def populate_service_host_port( # pragma: no cover
|
|
135
|
+
pim_instance: Any, service_host_port: Dict[str, Tuple[str, int]], webui: bool = False
|
|
136
|
+
) -> Dict[str, Tuple[str, int]]:
|
|
137
|
+
"""Populate the service host port dictionary with the services available in the PIM instance.
|
|
138
|
+
|
|
139
|
+
Parameters
|
|
140
|
+
----------
|
|
141
|
+
pim_instance:
|
|
142
|
+
the PIM instance to get the servicea from.
|
|
143
|
+
service_host_port: dict
|
|
144
|
+
the dictionary to be updated with the services from the PIM instance
|
|
145
|
+
webui: bool
|
|
146
|
+
if True retrieve also the webUI service
|
|
147
|
+
|
|
148
|
+
Returns
|
|
149
|
+
-------
|
|
150
|
+
service_host_port: dict
|
|
151
|
+
the dictionary updated with the services from the PIM instance
|
|
152
|
+
"""
|
|
153
|
+
if not set(("grpc_private", "http", "ws")).issubset(pim_instance.services):
|
|
154
|
+
raise RuntimeError(
|
|
155
|
+
"If channel is specified, the PIM instance must have a list of length 3 "
|
|
156
|
+
+ "containing the appropriate service URIs. It does not."
|
|
157
|
+
)
|
|
158
|
+
service_host_port["grpc_private"] = get_host_port(pim_instance.services["grpc_private"].uri)
|
|
159
|
+
service_host_port["http"] = get_host_port(pim_instance.services["http"].uri)
|
|
160
|
+
service_host_port["ws"] = get_host_port(pim_instance.services["ws"].uri)
|
|
161
|
+
service_host_port["grpc"] = ("127.0.0.1", -1)
|
|
162
|
+
if webui:
|
|
163
|
+
service_host_port["webui"] = get_host_port(pim_instance.services["webui"].uri)
|
|
164
|
+
return service_host_port
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def launch_enshell_interface(
|
|
168
|
+
enshell_grpc_channel: Any, grpc_port: int, timeout: float
|
|
169
|
+
) -> enshell_grpc.EnShellGRPC:
|
|
170
|
+
"""Launch the EnShell gRPC Interface.
|
|
171
|
+
|
|
172
|
+
Parameters
|
|
173
|
+
----------
|
|
174
|
+
enshell_grpc_channel:
|
|
175
|
+
An eventual gRPC channel already available, like in the PIM case
|
|
176
|
+
grpc_port: int
|
|
177
|
+
the gRPC port to connect to
|
|
178
|
+
timeout: float
|
|
179
|
+
a timeout to wait for the gRPC connection
|
|
180
|
+
|
|
181
|
+
Returns
|
|
182
|
+
-------
|
|
183
|
+
enshell: enshell_grpc.EnShellGRPC
|
|
184
|
+
the enshell gRPC interface
|
|
185
|
+
"""
|
|
186
|
+
if enshell_grpc_channel: # pragma: no cover
|
|
187
|
+
enshell = enshell_grpc.EnShellGRPC() # pragma: no cover
|
|
188
|
+
enshell.connect_existing_channel(enshell_grpc_channel) # pragma: no cover
|
|
189
|
+
else:
|
|
190
|
+
enshell = enshell_grpc.EnShellGRPC(port=grpc_port)
|
|
191
|
+
time_start = time.time()
|
|
192
|
+
while time.time() - time_start < timeout: # pragma: no cover
|
|
193
|
+
if enshell.is_connected():
|
|
194
|
+
break
|
|
195
|
+
try:
|
|
196
|
+
enshell.connect(timeout=timeout)
|
|
197
|
+
except OSError: # pragma: no cover
|
|
198
|
+
pass # pragma: no cover
|
|
199
|
+
return enshell
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def pull_image(docker_client: "DockerClient", image_name: str) -> None:
|
|
203
|
+
"""Pull the input docker image using the input Docker Client
|
|
204
|
+
|
|
205
|
+
Parameters
|
|
206
|
+
----------
|
|
207
|
+
docker_client: DockerClient
|
|
208
|
+
the current DockerClient to pull the image with
|
|
209
|
+
image_name: str
|
|
210
|
+
the image to pull
|
|
211
|
+
"""
|
|
212
|
+
try:
|
|
213
|
+
if docker_client is not None: # pragma: no cover
|
|
214
|
+
docker_client.images.pull(image_name)
|
|
215
|
+
except Exception:
|
|
216
|
+
raise RuntimeError(f"Can't pull Docker image: {image_name}")
|
|
@@ -16,14 +16,10 @@ Examples:
|
|
|
16
16
|
"""
|
|
17
17
|
import logging
|
|
18
18
|
import os.path
|
|
19
|
-
import re
|
|
20
19
|
import subprocess
|
|
21
|
-
import
|
|
22
|
-
from typing import Any, Dict, Optional
|
|
20
|
+
from typing import TYPE_CHECKING, Any, Dict, Optional
|
|
23
21
|
import uuid
|
|
24
22
|
|
|
25
|
-
import urllib3
|
|
26
|
-
|
|
27
23
|
try:
|
|
28
24
|
import grpc
|
|
29
25
|
except ModuleNotFoundError: # pragma: no cover
|
|
@@ -31,24 +27,27 @@ except ModuleNotFoundError: # pragma: no cover
|
|
|
31
27
|
except Exception: # pragma: no cover
|
|
32
28
|
raise RuntimeError("Cannot initialize grpc")
|
|
33
29
|
|
|
34
|
-
from ansys.pyensight.core.launcher import Launcher
|
|
35
|
-
import ansys.pyensight.core.session
|
|
36
|
-
from ansys.pyensight.core.session import Session
|
|
37
|
-
|
|
38
30
|
try:
|
|
39
|
-
|
|
31
|
+
import docker
|
|
40
32
|
except ModuleNotFoundError: # pragma: no cover
|
|
41
|
-
raise RuntimeError("The
|
|
33
|
+
raise RuntimeError("The docker module must be installed for DockerLauncher")
|
|
42
34
|
except Exception: # pragma: no cover
|
|
43
|
-
raise RuntimeError("Cannot initialize
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
35
|
+
raise RuntimeError("Cannot initialize Docker")
|
|
36
|
+
|
|
37
|
+
from ansys.pyensight.core.common import (
|
|
38
|
+
find_unused_ports,
|
|
39
|
+
get_file_service,
|
|
40
|
+
launch_enshell_interface,
|
|
41
|
+
populate_service_host_port,
|
|
42
|
+
pull_image,
|
|
43
|
+
)
|
|
44
|
+
from ansys.pyensight.core.launcher import Launcher
|
|
45
|
+
import ansys.pyensight.core.session
|
|
46
|
+
from ansys.pyensight.core.session import Session
|
|
48
47
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
if TYPE_CHECKING:
|
|
49
|
+
from docker import DockerClient
|
|
50
|
+
from docker.models.containers import Container
|
|
52
51
|
|
|
53
52
|
|
|
54
53
|
class DockerLauncher(Launcher):
|
|
@@ -117,8 +116,8 @@ class DockerLauncher(Launcher):
|
|
|
117
116
|
self._enshell_grpc_channel = channel
|
|
118
117
|
self._service_uris: Dict[Any, str] = {}
|
|
119
118
|
self._image_name: Optional[str] = None
|
|
120
|
-
self._docker_client: Optional[
|
|
121
|
-
self._container = None
|
|
119
|
+
self._docker_client: Optional["DockerClient"] = None
|
|
120
|
+
self._container: Optional["Container"] = None
|
|
122
121
|
self._enshell: Optional[Any] = None
|
|
123
122
|
self._pim_instance: Optional[Any] = pim_instance
|
|
124
123
|
|
|
@@ -143,27 +142,20 @@ class DockerLauncher(Launcher):
|
|
|
143
142
|
)
|
|
144
143
|
|
|
145
144
|
if self._enshell_grpc_channel and self._pim_instance:
|
|
146
|
-
|
|
145
|
+
service_set = ["grpc_private", "http", "ws"]
|
|
146
|
+
# if self._launch_webui:
|
|
147
|
+
# service_set.append("webui")
|
|
148
|
+
if not set(service_set).issubset(self._pim_instance.services):
|
|
147
149
|
raise RuntimeError(
|
|
148
150
|
"If channel is specified, the PIM instance must have a list of length 3 "
|
|
149
151
|
+ "containing the appropriate service URIs. It does not."
|
|
150
152
|
)
|
|
151
|
-
self._service_host_port = {}
|
|
152
153
|
# grab the URIs for the 3 required services passed in from PIM
|
|
153
|
-
self._service_host_port
|
|
154
|
-
self._pim_instance.
|
|
155
|
-
)
|
|
156
|
-
self._service_host_port["http"] = self._get_host_port(
|
|
157
|
-
self._pim_instance.services["http"].uri
|
|
158
|
-
)
|
|
159
|
-
self._service_host_port["ws"] = self._get_host_port(
|
|
160
|
-
self._pim_instance.services["ws"].uri
|
|
154
|
+
self._service_host_port = populate_service_host_port(
|
|
155
|
+
self._pim_instance, {}, webui=self._launch_webui
|
|
161
156
|
)
|
|
162
|
-
# for parity, add 'grpc' as a placeholder even though using PIM sets up the gRPC channel.
|
|
163
|
-
# this isn't used in this situation.
|
|
164
|
-
self._service_host_port["grpc"] = ("127.0.0.1", -1)
|
|
165
157
|
# attach to the file service if available
|
|
166
|
-
self.
|
|
158
|
+
self._pim_file_service = get_file_service(self._pim_instance)
|
|
167
159
|
# if using PIM, we have a query parameter to append to http requests
|
|
168
160
|
if self._pim_instance is not None:
|
|
169
161
|
d = {"instance_name": self._pim_instance.name}
|
|
@@ -173,7 +165,10 @@ class DockerLauncher(Launcher):
|
|
|
173
165
|
|
|
174
166
|
# EnShell gRPC port, EnSight gRPC port, HTTP port, WSS port
|
|
175
167
|
# skip 1999 as that internal to the container is used to the container for the VNC connection
|
|
176
|
-
|
|
168
|
+
num_ports = 4
|
|
169
|
+
if self._launch_webui:
|
|
170
|
+
num_ports = 5
|
|
171
|
+
ports = find_unused_ports(num_ports, avoid=[1999])
|
|
177
172
|
if ports is None: # pragma: no cover
|
|
178
173
|
raise RuntimeError(
|
|
179
174
|
"Unable to allocate local ports for EnSight session"
|
|
@@ -183,6 +178,8 @@ class DockerLauncher(Launcher):
|
|
|
183
178
|
self._service_host_port["grpc_private"] = ("127.0.0.1", ports[1])
|
|
184
179
|
self._service_host_port["http"] = ("127.0.0.1", ports[2])
|
|
185
180
|
self._service_host_port["ws"] = ("127.0.0.1", ports[3])
|
|
181
|
+
if self._launch_webui:
|
|
182
|
+
self._service_host_port["webui"] = ("127.0.0.1", ports[4])
|
|
186
183
|
|
|
187
184
|
# get the optional user-specified image name
|
|
188
185
|
# Note: the default name needs to change over time... TODO
|
|
@@ -193,14 +190,7 @@ class DockerLauncher(Launcher):
|
|
|
193
190
|
self._image_name = docker_image_name
|
|
194
191
|
|
|
195
192
|
# Load up Docker from the user's environment
|
|
196
|
-
|
|
197
|
-
import docker
|
|
198
|
-
|
|
199
|
-
self._docker_client = docker.from_env()
|
|
200
|
-
except ModuleNotFoundError:
|
|
201
|
-
raise RuntimeError("The docker module must be installed for DockerLauncher")
|
|
202
|
-
except Exception:
|
|
203
|
-
raise RuntimeError("Cannot initialize Docker")
|
|
193
|
+
self._docker_client = docker.from_env()
|
|
204
194
|
|
|
205
195
|
def ansys_version(self) -> Optional[str]:
|
|
206
196
|
"""Get the Ansys version (three-digit string) found in the Docker container.
|
|
@@ -222,11 +212,7 @@ class DockerLauncher(Launcher):
|
|
|
222
212
|
If the Docker image couldn't be pulled.
|
|
223
213
|
|
|
224
214
|
"""
|
|
225
|
-
|
|
226
|
-
if self._docker_client is not None: # pragma: no cover
|
|
227
|
-
self._docker_client.images.pull(self._image_name)
|
|
228
|
-
except Exception:
|
|
229
|
-
raise RuntimeError(f"Can't pull Docker image: {self._image_name}")
|
|
215
|
+
pull_image(self._docker_client, self._image_name)
|
|
230
216
|
|
|
231
217
|
def _get_container_env(self) -> Dict:
|
|
232
218
|
# Create the environmental variables
|
|
@@ -251,6 +237,10 @@ class DockerLauncher(Launcher):
|
|
|
251
237
|
if "ENSIGHT_ANSYS_APIP_CONFIG" in os.environ:
|
|
252
238
|
container_env["ENSIGHT_ANSYS_APIP_CONFIG"] = os.environ["ENSIGHT_ANSYS_APIP_CONFIG"]
|
|
253
239
|
|
|
240
|
+
if self._launch_webui:
|
|
241
|
+
container_env["SIMBA_WEBSERVER_TOKEN"] = self._secret_key
|
|
242
|
+
container_env["FLUENT_WEBSERVER_TOKEN"] = self._secret_key
|
|
243
|
+
|
|
254
244
|
return container_env
|
|
255
245
|
|
|
256
246
|
def start(self) -> "Session":
|
|
@@ -299,7 +289,13 @@ class DockerLauncher(Launcher):
|
|
|
299
289
|
+ "/tcp": str(self._service_host_port["http"][1]),
|
|
300
290
|
str(self._service_host_port["ws"][1]) + "/tcp": str(self._service_host_port["ws"][1]),
|
|
301
291
|
}
|
|
302
|
-
|
|
292
|
+
if self._launch_webui:
|
|
293
|
+
ports_to_map.update(
|
|
294
|
+
{
|
|
295
|
+
str(self._service_host_port["webui"][1])
|
|
296
|
+
+ "/tcp": str(self._service_host_port["webui"][1])
|
|
297
|
+
}
|
|
298
|
+
)
|
|
303
299
|
# The data directory to map into the container
|
|
304
300
|
data_volume = None
|
|
305
301
|
if self._data_directory:
|
|
@@ -310,13 +306,6 @@ class DockerLauncher(Launcher):
|
|
|
310
306
|
#
|
|
311
307
|
enshell_cmd = "-app -v 3 -grpc_server " + str(grpc_port)
|
|
312
308
|
|
|
313
|
-
try:
|
|
314
|
-
import docker
|
|
315
|
-
except ModuleNotFoundError: # pragma: no cover
|
|
316
|
-
raise RuntimeError("The docker module must be installed for DockerLauncher")
|
|
317
|
-
except Exception: # pragma: no cover
|
|
318
|
-
raise RuntimeError("Cannot initialize Docker")
|
|
319
|
-
|
|
320
309
|
use_egl = self._use_egl()
|
|
321
310
|
|
|
322
311
|
logging.debug("Starting Container...\n")
|
|
@@ -389,6 +378,33 @@ class DockerLauncher(Launcher):
|
|
|
389
378
|
logging.debug("Container started.\n")
|
|
390
379
|
return self.connect()
|
|
391
380
|
|
|
381
|
+
def launch_webui(self, container_env_str):
|
|
382
|
+
# Run websocketserver
|
|
383
|
+
cmd = f"cpython /ansys_inc/v{self._ansys_version}/CEI/"
|
|
384
|
+
cmd += f"nexus{self._ansys_version}/nexus_launcher/webui_launcher.py"
|
|
385
|
+
# websocket port - this needs to come first since we now have
|
|
386
|
+
# --add_header as a optional arg that can take an arbitrary
|
|
387
|
+
# number of optional headers.
|
|
388
|
+
webui_port = self._service_host_port["webui"][1]
|
|
389
|
+
grpc_port = self._service_host_port["grpc_private"][1]
|
|
390
|
+
http_port = self._service_host_port["http"][1]
|
|
391
|
+
ws_port = self._service_host_port["ws"][1]
|
|
392
|
+
cmd += f" --server-listen-port {webui_port}"
|
|
393
|
+
cmd += (
|
|
394
|
+
f" --server-web-roots /ansys_inc/v{self._ansys_version}/CEI/nexus{self._ansys_version}/"
|
|
395
|
+
)
|
|
396
|
+
cmd += f"ansys{self._ansys_version}/ensight/WebUI/web/ui/"
|
|
397
|
+
cmd += f" --ensight-grpc-port {grpc_port}"
|
|
398
|
+
cmd += f" --ensight-html-port {http_port}"
|
|
399
|
+
cmd += f" --ensight-ws-port {ws_port}"
|
|
400
|
+
cmd += f" --ensight-session-directory {self._session_directory}"
|
|
401
|
+
cmd += f" --ensight-secret-key {self._secret_key}"
|
|
402
|
+
logging.debug(f"Starting WebUI: {cmd}\n")
|
|
403
|
+
ret = self._enshell.start_other(cmd, extra_env=container_env_str)
|
|
404
|
+
if ret[0] != 0: # pragma: no cover
|
|
405
|
+
self.stop() # pragma: no cover
|
|
406
|
+
raise RuntimeError(f"Error starting WebUI: {cmd}\n") # pragma: no cover
|
|
407
|
+
|
|
392
408
|
def connect(self):
|
|
393
409
|
"""Create and bind a :class:`Session<ansys.pyensight.core.Session>` instance
|
|
394
410
|
to the created EnSight gRPC connection started by EnShell.
|
|
@@ -408,24 +424,12 @@ class DockerLauncher(Launcher):
|
|
|
408
424
|
#
|
|
409
425
|
#
|
|
410
426
|
# Start up the EnShell gRPC interface
|
|
427
|
+
self._enshell = launch_enshell_interface(
|
|
428
|
+
self._enshell_grpc_channel, self._service_host_port["grpc"][1], self._timeout
|
|
429
|
+
)
|
|
411
430
|
log_dir = "/data"
|
|
412
431
|
if self._enshell_grpc_channel: # pragma: no cover
|
|
413
|
-
self._enshell = enshell_grpc.EnShellGRPC()
|
|
414
|
-
self._enshell.connect_existing_channel(self._enshell_grpc_channel)
|
|
415
432
|
log_dir = "/work"
|
|
416
|
-
else:
|
|
417
|
-
logging.debug(
|
|
418
|
-
f"Connecting to EnShell over gRPC port: {self._service_host_port['grpc'][1]}...\n"
|
|
419
|
-
)
|
|
420
|
-
self._enshell = enshell_grpc.EnShellGRPC(port=self._service_host_port["grpc"][1])
|
|
421
|
-
time_start = time.time()
|
|
422
|
-
while time.time() - time_start < self._timeout: # pragma: no cover
|
|
423
|
-
if self._enshell.is_connected():
|
|
424
|
-
break
|
|
425
|
-
try:
|
|
426
|
-
self._enshell.connect(timeout=self._timeout)
|
|
427
|
-
except OSError: # pragma: no cover
|
|
428
|
-
pass # pragma: no cover
|
|
429
433
|
|
|
430
434
|
if not self._enshell.is_connected(): # pragma: no cover
|
|
431
435
|
self.stop() # pragma: no cover
|
|
@@ -581,10 +585,13 @@ class DockerLauncher(Launcher):
|
|
|
581
585
|
timeout=self._timeout,
|
|
582
586
|
sos=use_sos,
|
|
583
587
|
rest_api=self._enable_rest_api,
|
|
588
|
+
webui_port=self._service_host_port["webui"][1] if self._launch_webui else None,
|
|
584
589
|
)
|
|
585
590
|
session.launcher = self
|
|
586
591
|
self._sessions.append(session)
|
|
587
592
|
|
|
593
|
+
if self._launch_webui:
|
|
594
|
+
self.launch_webui(container_env_str)
|
|
588
595
|
logging.debug("Return session.\n")
|
|
589
596
|
|
|
590
597
|
return session
|
|
@@ -619,32 +626,10 @@ class DockerLauncher(Launcher):
|
|
|
619
626
|
self._pim_instance = None
|
|
620
627
|
super().stop()
|
|
621
628
|
|
|
622
|
-
def _get_file_service(self) -> None: # pragma: no cover
|
|
623
|
-
if simple_upload_server_is_available is False:
|
|
624
|
-
return
|
|
625
|
-
if self._pim_instance is None:
|
|
626
|
-
return
|
|
627
|
-
|
|
628
|
-
if "http-simple-upload-server" in self._pim_instance.services:
|
|
629
|
-
self._pim_file_service = Client(
|
|
630
|
-
token="token",
|
|
631
|
-
url=self._pim_instance.services["http-simple-upload-server"].uri,
|
|
632
|
-
headers=self._pim_instance.services["http-simple-upload-server"].headers,
|
|
633
|
-
)
|
|
634
|
-
|
|
635
629
|
def file_service(self) -> Optional[Any]:
|
|
636
630
|
"""Get the PIM file service object if available."""
|
|
637
631
|
return self._pim_file_service
|
|
638
632
|
|
|
639
|
-
def _get_host_port(self, uri: str) -> tuple:
|
|
640
|
-
parse_results = urllib3.util.parse_url(uri)
|
|
641
|
-
port = (
|
|
642
|
-
parse_results.port
|
|
643
|
-
if parse_results.port
|
|
644
|
-
else (443 if re.search("^https|wss$", parse_results.scheme) else None)
|
|
645
|
-
)
|
|
646
|
-
return (parse_results.host, port)
|
|
647
|
-
|
|
648
633
|
def _is_system_egl_capable(self) -> bool:
|
|
649
634
|
"""Check if the system is EGL capable.
|
|
650
635
|
|
{ansys_pyensight_core-0.8.9 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/enshell_grpc.py
RENAMED
|
@@ -355,6 +355,38 @@ class EnShellGRPC(object):
|
|
|
355
355
|
else:
|
|
356
356
|
return self.run_command_with_env(command_string, ensight_env) # pragma: no cover
|
|
357
357
|
|
|
358
|
+
def start_ensight_server(
|
|
359
|
+
self, ensight_args: Optional[str] = None, ensight_env: Optional[str] = None
|
|
360
|
+
):
|
|
361
|
+
"""Tell EnShell to start the EnSight server.
|
|
362
|
+
|
|
363
|
+
The string will be sent to EnShell via the EnShellService::run_command()
|
|
364
|
+
gRPC call. An IOError exception may be thrown
|
|
365
|
+
if there's a gRPC communication problem. The response
|
|
366
|
+
is the tuple of the EnShell return code and return string.
|
|
367
|
+
|
|
368
|
+
Parameters
|
|
369
|
+
----------
|
|
370
|
+
ensight_args : Optional[str], optional
|
|
371
|
+
ensight_args arguments for the ensight command line, by default None
|
|
372
|
+
|
|
373
|
+
Returns
|
|
374
|
+
-------
|
|
375
|
+
Tuple
|
|
376
|
+
A tuple of (int, string) for (returnCode, returnString)
|
|
377
|
+
"""
|
|
378
|
+
self.connect()
|
|
379
|
+
command_string = (
|
|
380
|
+
f"start_app OTHER /ansys_inc/v{self.ansys_version()}/CEI/bin/ensight_server"
|
|
381
|
+
)
|
|
382
|
+
if ensight_args and (ensight_args != ""):
|
|
383
|
+
command_string += " " + ensight_args
|
|
384
|
+
|
|
385
|
+
if ensight_env is None or ensight_env == "": # pragma: no cover
|
|
386
|
+
return self.run_command(command_string)
|
|
387
|
+
else:
|
|
388
|
+
return self.run_command_with_env(command_string, ensight_env) # pragma: no cover
|
|
389
|
+
|
|
358
390
|
# @brief
|
|
359
391
|
#
|
|
360
392
|
# @param cmd The command line
|