ansys-mechanical-core 0.11.13__py3-none-any.whl → 0.11.15__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.
- ansys/mechanical/core/__init__.py +3 -4
- ansys/mechanical/core/embedding/app.py +97 -12
- ansys/mechanical/core/embedding/appdata.py +26 -22
- ansys/mechanical/core/embedding/enum_importer.py +5 -0
- ansys/mechanical/core/embedding/global_importer.py +50 -0
- ansys/mechanical/core/embedding/{viz → graphics}/embedding_plotter.py +1 -1
- ansys/mechanical/core/embedding/imports.py +30 -58
- ansys/mechanical/core/embedding/initializer.py +76 -4
- ansys/mechanical/core/embedding/messages.py +195 -0
- ansys/mechanical/core/embedding/resolver.py +1 -1
- ansys/mechanical/core/embedding/rpc/__init__.py +3 -7
- ansys/mechanical/core/embedding/rpc/client.py +55 -19
- ansys/mechanical/core/embedding/rpc/default_server.py +131 -0
- ansys/mechanical/core/embedding/rpc/server.py +171 -162
- ansys/mechanical/core/embedding/rpc/utils.py +18 -2
- ansys/mechanical/core/embedding/runtime.py +6 -0
- ansys/mechanical/core/embedding/transaction.py +51 -0
- ansys/mechanical/core/ide_config.py +22 -7
- ansys/mechanical/core/mechanical.py +86 -18
- {ansys_mechanical_core-0.11.13.dist-info → ansys_mechanical_core-0.11.15.dist-info}/METADATA +21 -17
- ansys_mechanical_core-0.11.15.dist-info/RECORD +53 -0
- {ansys_mechanical_core-0.11.13.dist-info → ansys_mechanical_core-0.11.15.dist-info}/WHEEL +1 -1
- ansys_mechanical_core-0.11.13.dist-info/RECORD +0 -49
- /ansys/mechanical/core/embedding/{viz → graphics}/__init__.py +0 -0
- /ansys/mechanical/core/embedding/{viz → graphics}/usd_converter.py +0 -0
- /ansys/mechanical/core/embedding/{viz → graphics}/utils.py +0 -0
- {ansys_mechanical_core-0.11.13.dist-info → ansys_mechanical_core-0.11.15.dist-info}/entry_points.txt +0 -0
- {ansys_mechanical_core-0.11.13.dist-info → ansys_mechanical_core-0.11.15.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,195 @@
|
|
1
|
+
# Copyright (C) 2022 - 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
|
+
"""Message Manager for App."""
|
24
|
+
|
25
|
+
# TODO: add functionality to filter only errors, warnings, info
|
26
|
+
# TODO: add max number of messages to display
|
27
|
+
# TODO: implement pep8 formatting
|
28
|
+
|
29
|
+
try: # noqa: F401
|
30
|
+
import pandas as pd
|
31
|
+
|
32
|
+
HAS_PANDAS = True
|
33
|
+
"""Whether or not pandas exists."""
|
34
|
+
except ImportError:
|
35
|
+
HAS_PANDAS = False
|
36
|
+
|
37
|
+
|
38
|
+
class MessageManager:
|
39
|
+
"""Message manager for adding, fetching, and printing messages."""
|
40
|
+
|
41
|
+
def __init__(self, app):
|
42
|
+
"""Initialize the message manager."""
|
43
|
+
self._app = app
|
44
|
+
|
45
|
+
# Import necessary classes
|
46
|
+
from Ansys.Mechanical.Application import Message
|
47
|
+
from Ansys.Mechanical.DataModel.Enums import MessageSeverityType
|
48
|
+
|
49
|
+
self._message_severity = MessageSeverityType
|
50
|
+
self._message = Message
|
51
|
+
self._messages = self._app.ExtAPI.Application.Messages
|
52
|
+
|
53
|
+
def _create_messages_data(self): # pragma: no cover
|
54
|
+
"""Update the local cache of messages."""
|
55
|
+
data = {
|
56
|
+
"Severity": [],
|
57
|
+
"TimeStamp": [],
|
58
|
+
"DisplayString": [],
|
59
|
+
"Source": [],
|
60
|
+
"StringID": [],
|
61
|
+
"Location": [],
|
62
|
+
"RelatedObjects": [],
|
63
|
+
}
|
64
|
+
for msg in self._app.ExtAPI.Application.Messages:
|
65
|
+
data["Severity"].append(str(msg.Severity).upper())
|
66
|
+
data["TimeStamp"].append(msg.TimeStamp)
|
67
|
+
data["DisplayString"].append(msg.DisplayString)
|
68
|
+
data["Source"].append(msg.Source)
|
69
|
+
data["StringID"].append(msg.StringID)
|
70
|
+
data["Location"].append(msg.Location)
|
71
|
+
data["RelatedObjects"].append(msg.RelatedObjects)
|
72
|
+
|
73
|
+
return data
|
74
|
+
|
75
|
+
def __repr__(self): # pragma: no cover
|
76
|
+
"""Provide a DataFrame representation of all messages."""
|
77
|
+
if not HAS_PANDAS:
|
78
|
+
return "Pandas is not available. Please pip install pandas to display messages."
|
79
|
+
data = self._create_messages_data()
|
80
|
+
return repr(pd.DataFrame(data))
|
81
|
+
|
82
|
+
def __str__(self):
|
83
|
+
"""Provide a custom string representation of the messages."""
|
84
|
+
if self._messages.Count == 0:
|
85
|
+
return "No messages to display."
|
86
|
+
|
87
|
+
formatted_messages = [f"[{msg.Severity}] : {msg.DisplayString}" for msg in self._messages]
|
88
|
+
return "\n".join(formatted_messages)
|
89
|
+
|
90
|
+
def __getitem__(self, index):
|
91
|
+
"""Allow indexed access to messages."""
|
92
|
+
if len(self._messages) == 0:
|
93
|
+
raise IndexError("No messages are available.")
|
94
|
+
if index >= len(self._messages) or index < 0:
|
95
|
+
raise IndexError("Message index out of range.")
|
96
|
+
return self._messages[index]
|
97
|
+
|
98
|
+
def __len__(self):
|
99
|
+
"""Return the number of messages."""
|
100
|
+
return self._messages.Count
|
101
|
+
|
102
|
+
def add(self, severity: str, text: str):
|
103
|
+
"""Add a message and update the cache.
|
104
|
+
|
105
|
+
Parameters
|
106
|
+
----------
|
107
|
+
severity : str
|
108
|
+
Severity of the message. Can be "info", "warning", or "error".
|
109
|
+
text : str
|
110
|
+
Message text.
|
111
|
+
|
112
|
+
Examples
|
113
|
+
--------
|
114
|
+
>>> app.messages.add("info", "User clicked the start button.")
|
115
|
+
"""
|
116
|
+
severity_map = {
|
117
|
+
"info": self._message_severity.Info,
|
118
|
+
"warning": self._message_severity.Warning,
|
119
|
+
"error": self._message_severity.Error,
|
120
|
+
}
|
121
|
+
|
122
|
+
if severity.lower() not in severity_map:
|
123
|
+
raise ValueError(f"Invalid severity: {severity}")
|
124
|
+
|
125
|
+
_msg = self._message(text, severity_map[severity.lower()])
|
126
|
+
self._messages.Add(_msg)
|
127
|
+
|
128
|
+
def remove(self, index: int):
|
129
|
+
"""Remove a message by index.
|
130
|
+
|
131
|
+
Parameters
|
132
|
+
----------
|
133
|
+
index : int
|
134
|
+
Index of the message to remove.
|
135
|
+
|
136
|
+
Examples
|
137
|
+
--------
|
138
|
+
>>> app.messages.remove(0)
|
139
|
+
"""
|
140
|
+
if index >= len(self._app.ExtAPI.Application.Messages) or index < 0:
|
141
|
+
raise IndexError("Message index out of range.")
|
142
|
+
_msg = self._messages[index]
|
143
|
+
self._messages.Remove(_msg)
|
144
|
+
|
145
|
+
def _show_string(self, filter: str = "Severity;DisplayString") -> str:
|
146
|
+
if self._messages.Count == 0:
|
147
|
+
return "No messages to display."
|
148
|
+
|
149
|
+
if filter == "*":
|
150
|
+
selected_columns = [
|
151
|
+
"TimeStamp",
|
152
|
+
"Severity",
|
153
|
+
"DisplayString",
|
154
|
+
"Source",
|
155
|
+
"StringID",
|
156
|
+
"Location",
|
157
|
+
"RelatedObjects",
|
158
|
+
]
|
159
|
+
else:
|
160
|
+
selected_columns = [col.strip() for col in filter.split(";")]
|
161
|
+
|
162
|
+
lines = []
|
163
|
+
for msg in self._messages:
|
164
|
+
for key in selected_columns:
|
165
|
+
line = f"{key}: {getattr(msg, key, 'Specified attribute not found.')}"
|
166
|
+
lines.append(line)
|
167
|
+
return "\n".join(lines)
|
168
|
+
|
169
|
+
def show(self, filter="Severity;DisplayString") -> None:
|
170
|
+
"""Print all messages with full details.
|
171
|
+
|
172
|
+
Parameters
|
173
|
+
----------
|
174
|
+
filter : str, optional
|
175
|
+
Semicolon separated list of message attributes to display.
|
176
|
+
Default is "severity;message".
|
177
|
+
if filter is "*", all available attributes will be displayed.
|
178
|
+
|
179
|
+
Examples
|
180
|
+
--------
|
181
|
+
>>> app.messages.show()
|
182
|
+
... severity: info
|
183
|
+
... message: Sample message.
|
184
|
+
|
185
|
+
>>> app.messages.show(filter="time_stamp;severity;message")
|
186
|
+
... time_stamp: 1/30/2025 12:10:35 PM
|
187
|
+
... severity: info
|
188
|
+
... message: Sample message.
|
189
|
+
"""
|
190
|
+
show_string = self._show_string(filter)
|
191
|
+
print(show_string)
|
192
|
+
|
193
|
+
def clear(self):
|
194
|
+
"""Clear all messages."""
|
195
|
+
self._messages.Clear()
|
@@ -41,7 +41,7 @@ def resolve(version):
|
|
41
41
|
resolve_handler = assembly_resolver.MechanicalResolveEventHandler
|
42
42
|
System.AppDomain.CurrentDomain.AssemblyResolve += resolve_handler
|
43
43
|
except AttributeError:
|
44
|
-
error_msg =
|
44
|
+
error_msg = """Unable to resolve Mechanical assemblies. Please ensure the following:
|
45
45
|
1. Mechanical is installed.
|
46
46
|
2. A folder with the name "Ansys" does not exist in the same directory as the script being run.
|
47
47
|
"""
|
@@ -22,15 +22,11 @@
|
|
22
22
|
|
23
23
|
"""RPC and Mechanical service implementation."""
|
24
24
|
from .client import Client
|
25
|
-
|
26
|
-
# todo - provide an implementation of Server (RemoteMechancial) that installs the below
|
27
|
-
# from .default_server import RemoteMechanical
|
28
|
-
# and remove them from this import statement
|
29
|
-
# todo - combine Server and MechanicalService
|
25
|
+
from .default_server import DefaultServiceMethods, MechanicalDefaultServer
|
30
26
|
from .server import (
|
31
|
-
DefaultServiceMethods,
|
32
|
-
MechanicalDefaultServer,
|
33
27
|
MechanicalEmbeddedServer,
|
34
28
|
MechanicalService,
|
35
29
|
)
|
36
30
|
from .utils import get_remote_methods, remote_method
|
31
|
+
|
32
|
+
# todo - combine Server and MechanicalService
|
@@ -27,13 +27,16 @@ import time
|
|
27
27
|
|
28
28
|
import rpyc
|
29
29
|
|
30
|
+
from ansys.mechanical.core.embedding.rpc.utils import PYMECHANICAL_DEFAULT_RPC_PORT
|
30
31
|
from ansys.mechanical.core.mechanical import DEFAULT_CHUNK_SIZE
|
31
32
|
|
32
33
|
|
33
34
|
class Client:
|
34
35
|
"""Client for connecting to Mechanical services."""
|
35
36
|
|
36
|
-
def __init__(
|
37
|
+
def __init__(
|
38
|
+
self, host: str, port: int, timeout: float = 120.0, cleanup_on_exit=True, process=None
|
39
|
+
):
|
37
40
|
"""Initialize the client.
|
38
41
|
|
39
42
|
Parameters
|
@@ -43,18 +46,28 @@ class Client:
|
|
43
46
|
in which case ``localhost`` is used.
|
44
47
|
port : int, optional
|
45
48
|
Port to connect to the Mecahnical server. The default is ``None``,
|
46
|
-
in which case ``
|
49
|
+
in which case ``20000`` is used.
|
47
50
|
timeout : float, optional
|
48
51
|
Maximum allowable time for connecting to the Mechanical server.
|
49
52
|
The default is ``60.0``.
|
53
|
+
process: subprocess.Popen, optional
|
54
|
+
The process object that was connected to
|
50
55
|
|
51
56
|
"""
|
57
|
+
if host is None:
|
58
|
+
host = "localhost"
|
52
59
|
self.host = host
|
60
|
+
if port is None:
|
61
|
+
port = PYMECHANICAL_DEFAULT_RPC_PORT
|
53
62
|
self.port = port
|
63
|
+
self._process = process
|
54
64
|
self.timeout = timeout
|
55
65
|
self.connection = None
|
56
66
|
self.root = None
|
57
67
|
self._connect()
|
68
|
+
self._cleanup_on_exit = cleanup_on_exit
|
69
|
+
self._error_type = Exception
|
70
|
+
self._has_exited = False
|
58
71
|
|
59
72
|
def __getattr__(self, attr):
|
60
73
|
"""Get attribute from the root object."""
|
@@ -77,29 +90,38 @@ class Client:
|
|
77
90
|
|
78
91
|
def _connect(self):
|
79
92
|
self._wait_until_ready()
|
80
|
-
self.connection = rpyc.connect(self.host, self.port)
|
81
93
|
self.root = self.connection.root
|
82
94
|
print(f"Connected to {self.host}:{self.port}")
|
83
|
-
|
95
|
+
|
96
|
+
def _exponential_backoff(self, max_time=60.0, base_time=0.1, factor=2):
|
97
|
+
"""Generate exponential backoff timing."""
|
98
|
+
t_max = time.time() + max_time
|
99
|
+
t = base_time
|
100
|
+
while time.time() < t_max:
|
101
|
+
yield t
|
102
|
+
t = min(t * factor, max_time)
|
84
103
|
|
85
104
|
def _wait_until_ready(self):
|
105
|
+
"""Wait until the server is ready."""
|
86
106
|
t_max = time.time() + self.timeout
|
87
|
-
|
107
|
+
for delay in self._exponential_backoff(max_time=self.timeout):
|
108
|
+
if time.time() >= t_max:
|
109
|
+
break # Exit if the timeout is reached
|
88
110
|
try:
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
)
|
111
|
+
self.connection = rpyc.connect(self.host, self.port)
|
112
|
+
self.connection.ping()
|
113
|
+
print("Server is ready.")
|
114
|
+
return
|
115
|
+
except Exception:
|
116
|
+
time.sleep(delay)
|
117
|
+
|
118
|
+
raise TimeoutError(
|
119
|
+
f"Server at {self.host}:{self.port} not ready within {self.timeout} seconds."
|
120
|
+
)
|
100
121
|
|
101
122
|
def close(self):
|
102
123
|
"""Close the connection."""
|
124
|
+
print("Closing the connection")
|
103
125
|
self.connection.close()
|
104
126
|
print(f"Connection to {self.host}:{self.port} closed")
|
105
127
|
|
@@ -220,6 +242,11 @@ class Client:
|
|
220
242
|
list_of_files.extend(temp_files)
|
221
243
|
return list_of_files
|
222
244
|
|
245
|
+
@property
|
246
|
+
def backend(self) -> str:
|
247
|
+
"""Get the backend type."""
|
248
|
+
return "python"
|
249
|
+
|
223
250
|
@property
|
224
251
|
def is_alive(self):
|
225
252
|
"""Check if the Mechanical instance is alive."""
|
@@ -231,7 +258,16 @@ class Client:
|
|
231
258
|
|
232
259
|
def exit(self):
|
233
260
|
"""Shuts down the Mechanical instance."""
|
234
|
-
|
235
|
-
|
236
|
-
self.
|
261
|
+
if self._has_exited:
|
262
|
+
return
|
263
|
+
self.close()
|
264
|
+
self._has_exited = True
|
237
265
|
print("Disconnected from server")
|
266
|
+
|
267
|
+
def __del__(self): # pragma: no cover
|
268
|
+
"""Clean up on exit."""
|
269
|
+
if self._cleanup_on_exit:
|
270
|
+
try:
|
271
|
+
self.exit()
|
272
|
+
except Exception as e:
|
273
|
+
print(f"Failed to exit cleanly: {e}")
|
@@ -0,0 +1,131 @@
|
|
1
|
+
# Copyright (C) 2022 - 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
|
+
"""Remote Procedure Call (RPC) server."""
|
23
|
+
|
24
|
+
import fnmatch
|
25
|
+
import os
|
26
|
+
|
27
|
+
from ansys.mechanical.core.embedding.app import App
|
28
|
+
|
29
|
+
from .server import MechanicalEmbeddedServer
|
30
|
+
from .utils import remote_method
|
31
|
+
|
32
|
+
|
33
|
+
class DefaultServiceMethods:
|
34
|
+
"""Default service methods for MechanicalEmbeddedServer."""
|
35
|
+
|
36
|
+
def __init__(self, app: App):
|
37
|
+
"""Initialize the DefaultServiceMethods."""
|
38
|
+
self._app = app
|
39
|
+
|
40
|
+
def __repr__(self):
|
41
|
+
"""Return the representation of the instance."""
|
42
|
+
return '"ServiceMethods instance"'
|
43
|
+
|
44
|
+
@remote_method
|
45
|
+
def run_python_script(
|
46
|
+
self, script: str, enable_logging=False, log_level="WARNING", progress_interval=2000
|
47
|
+
):
|
48
|
+
"""Run scripts using Internal python engine."""
|
49
|
+
result = self._app.execute_script(script)
|
50
|
+
return result
|
51
|
+
|
52
|
+
@remote_method
|
53
|
+
def run_python_script_from_file(
|
54
|
+
self,
|
55
|
+
file_path: str,
|
56
|
+
enable_logging=False,
|
57
|
+
log_level="WARNING",
|
58
|
+
progress_interval=2000,
|
59
|
+
):
|
60
|
+
"""Run scripts using Internal python engine."""
|
61
|
+
return self._app.execute_script_from_file(file_path)
|
62
|
+
|
63
|
+
@remote_method
|
64
|
+
def clear(self):
|
65
|
+
"""Clear the current project."""
|
66
|
+
self._app.new()
|
67
|
+
|
68
|
+
@property
|
69
|
+
@remote_method
|
70
|
+
def project_directory(self):
|
71
|
+
"""Get the project directory."""
|
72
|
+
return self._app.ExtAPI.DataModel.Project.ProjectDirectory
|
73
|
+
|
74
|
+
@remote_method
|
75
|
+
def list_files(self):
|
76
|
+
"""List all files in the project directory."""
|
77
|
+
list = []
|
78
|
+
mechdbPath = self._app.ExtAPI.DataModel.Project.FilePath
|
79
|
+
if mechdbPath != "":
|
80
|
+
list.append(mechdbPath)
|
81
|
+
rootDir = self._app.ExtAPI.DataModel.Project.ProjectDirectory
|
82
|
+
|
83
|
+
for dirPath, dirNames, fileNames in os.walk(rootDir):
|
84
|
+
for fileName in fileNames:
|
85
|
+
list.append(os.path.join(dirPath, fileName))
|
86
|
+
files_out = "\n".join(list).splitlines()
|
87
|
+
if not files_out: # pragma: no cover
|
88
|
+
print("No files listed")
|
89
|
+
return files_out
|
90
|
+
|
91
|
+
@remote_method
|
92
|
+
def _get_files(self, files, recursive=False):
|
93
|
+
self_files = self.list_files() # to avoid calling it too much
|
94
|
+
|
95
|
+
if isinstance(files, str):
|
96
|
+
if files in self_files:
|
97
|
+
list_files = [files]
|
98
|
+
elif "*" in files:
|
99
|
+
list_files = fnmatch.filter(self_files, files)
|
100
|
+
if not list_files:
|
101
|
+
raise ValueError(
|
102
|
+
f"The `'files'` parameter ({files}) didn't match any file using "
|
103
|
+
f"glob expressions in the remote server."
|
104
|
+
)
|
105
|
+
else:
|
106
|
+
raise ValueError(
|
107
|
+
f"The `'files'` parameter ('{files}') does not match any file or pattern."
|
108
|
+
)
|
109
|
+
|
110
|
+
elif isinstance(files, (list, tuple)):
|
111
|
+
if not all([isinstance(each, str) for each in files]):
|
112
|
+
raise ValueError(
|
113
|
+
"The parameter `'files'` can be a list or tuple, but it "
|
114
|
+
"should only contain strings."
|
115
|
+
)
|
116
|
+
list_files = files
|
117
|
+
else:
|
118
|
+
raise ValueError(
|
119
|
+
f"The `file` parameter type ({type(files)}) is not supported."
|
120
|
+
"Only strings, tuple of strings, or list of strings are allowed."
|
121
|
+
)
|
122
|
+
|
123
|
+
return list_files
|
124
|
+
|
125
|
+
|
126
|
+
class MechanicalDefaultServer(MechanicalEmbeddedServer):
|
127
|
+
"""Default server with default service methods."""
|
128
|
+
|
129
|
+
def __init__(self, **kwargs):
|
130
|
+
"""Initialize the MechanicalDefaultServer."""
|
131
|
+
super().__init__(impl=DefaultServiceMethods, **kwargs)
|