-
Notifications
You must be signed in to change notification settings - Fork 41
/
__init__.py
366 lines (300 loc) · 13.8 KB
/
__init__.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
"""
Package containing the binding for the core of Sofa
-------------------------------------------------
Example of use:
.. code-block:: python
import Sofa.Core
import Sofa.Simulation
import SofaRuntime
SofaRuntime.importPlugin("Sofa.Component")
n = Sofa.Core.Node("MyNode")
n.addChild("Node2")
n.addObject("MechanicalObject", name="dofs")
Sofa.Simulation.init(n)
Sofa.Simulation.print(n)
Submodules:
.. autosummary::
:toctree: _autosummary
Sofa.Core
Sofa.Simulation
Sofa.Types
Sofa.Helper
"""
import sys
import os
import inspect
import functools
import traceback
import importlib
print("---------------------------------------")
print("Checking SOFA_ROOT and SOFAPYTHON3_ROOT")
# check if SOFA_ROOT has been (well) set
sofa_root = os.environ.get('SOFA_ROOT')
if sofa_root:
print("Using environment variable SOFA_ROOT: " + sofa_root)
else:
print("Warning: environment variable SOFA_ROOT is empty. Trying to guess it.")
# try a guess from <sofa_root>/plugins/SofaPython3/lib/python3/site-packages/Sofa
sofa_root_guess = os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + '/../../../../../..')
if os.path.isdir(os.path.abspath(sofa_root_guess + '/lib' )):
print("Guessed SOFA_ROOT: " + sofa_root_guess)
sofa_root = sofa_root_guess
os.environ["SOFA_ROOT"] = sofa_root
else:
print("Warning: cannot guess SOFA_ROOT",
"Loading SOFA libraries will likely fail and/or SOFA won't find its resources.")
if sofa_root and sys.platform == 'win32':
# check if SOFAPYTHON3_ROOT has been (well) set, only useful for Windows
sofapython3_root = os.environ.get('SOFAPYTHON3_ROOT')
if sofapython3_root:
print("Using environment variable SOFAPYTHON3_ROOT: " + sofapython3_root)
else:
print("Warning: environment variable SOFAPYTHON3_ROOT is empty. Trying to guess it.")
# try a guess from <sofapython3_root>/lib/python3/site-packages/Sofa
sofapython3_root_guess = os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + '/../../../..')
if os.path.isdir(os.path.abspath(sofapython3_root_guess + '/lib' )):
print("Guessed SOFAPYTHON3_ROOT: " + sofapython3_root_guess)
sofapython3_root = sofapython3_root_guess
os.environ["SOFAPYTHON3_ROOT"] = sofapython3_root
else:
print("Warning: cannot guess SOFAPYTHON3_ROOT",
"Loading SofaPython3 modules will likely fail.")
# Windows-only: starting from python 3.8, python wont read the env. variable PATH to get SOFA's dlls.
# os.add_dll_directory() is the new way to add paths for python to get external libraries.
sofa_bin_path = os.path.join(sofa_root, "bin")
sofapython3_bin_path = os.path.join(sofapython3_root, "bin")
compilation_modes = ["Release", "RelWithDebInfo", "Debug", "MinSizeRel"]
sofa_bin_compilation_modes = []
sofapython3_bin_compilation_modes = []
for mode in compilation_modes:
if os.path.isdir(os.path.abspath(os.path.join(sofa_bin_path, mode))):
sofa_bin_compilation_modes.append(os.path.join(sofa_bin_path, mode))
if os.path.isdir(os.path.abspath(os.path.join(sofapython3_bin_path, mode))):
sofapython3_bin_compilation_modes.append(os.path.join(sofapython3_bin_path, mode))
if sofa_bin_compilation_modes:
print("Detected SOFA development build")
if sofapython3_bin_compilation_modes:
print("Detected SofaPython3 development build")
sofa_bin_candidates = [sofa_bin_path] + sofa_bin_compilation_modes
sofapython3_bin_candidates = [sofapython3_bin_path] + sofapython3_bin_compilation_modes
for candidate in sofa_bin_candidates:
sofa_file_test = os.path.join(candidate, "Sofa.Helper.dll")
if os.path.isfile(sofa_file_test):
print("Found Sofa.Helper.dll in " + candidate)
sofa_bin_path = candidate
break
sofa_file_test = os.path.join(sofa_bin_path, "Sofa.Helper.dll")
for candidate in sofapython3_bin_candidates:
sofapython3_file_test = os.path.join(candidate, "SofaPython3.dll")
if os.path.isfile(sofapython3_file_test):
print("Found SofaPython3.dll in " + candidate)
sofapython3_bin_path = candidate
break
sofapython3_file_test = os.path.join(sofapython3_bin_path, "SofaPython3.dll")
if not os.path.isfile(sofa_file_test):
print("Warning: environment variable SOFA_ROOT is set but seems invalid.",
"Loading SOFA libraries will likely fail.")
print("SOFA_ROOT is currently: " + sofa_root)
if not os.path.isfile(sofapython3_file_test):
print("Warning: cannot find SofaPython3.dll at path: " + sofapython3_bin_path)
print("This path will NOT be added to the DLL search path.",
"Loading SofaPython3 python modules will likely fail.")
if sys.version_info.minor >= 8:
# Starting from python3.8 we need to explicitly find SOFA libraries
if os.path.isfile(sofa_file_test):
os.add_dll_directory(sofa_bin_path)
if os.path.isfile(sofapython3_file_test):
os.add_dll_directory(sofapython3_bin_path)
else:
# Add temporarily the bin/lib path to the env variable PATH
if os.path.isfile(sofa_file_test):
os.environ['PATH'] = sofa_bin_path + os.pathsep + os.environ['PATH']
if os.path.isfile(sofapython3_file_test):
os.environ['PATH'] = sofapython3_bin_path + os.pathsep + os.environ['PATH']
print("---------------------------------------")
sys.stdout.flush()
import Sofa.constants
import Sofa.Helper
import Sofa.Core
import Sofa.Simulation
import Sofa.Types
import Sofa.Components
import SofaTypes
from .prefab import *
__all__=["constants", "Helper", "Core", "Simulation", "Types", "SofaTypes", "prefab"]
# Keep a list of the modules always imported in the Sofa-PythonEnvironment
try:
__SofaPythonEnvironment_importedModules__
except:
__SofaPythonEnvironment_importedModules__ = sys.modules.copy()
# some modules could be added here manually and can be modified procedurally
# e.g. plugin's modules defined from c++
__SofaPythonEnvironment_modulesExcludedFromReload = []
def unloadModules():
""" call this function to unload python modules and to force their reload
(useful to take into account their eventual modifications since
their last import).
"""
global __SofaPythonEnvironment_importedModules__
toremove = [name for name in sys.modules if not name in __SofaPythonEnvironment_importedModules__ and not name in __SofaPythonEnvironment_modulesExcludedFromReload ]
for name in toremove:
del(sys.modules[name]) # unload it
def formatStackForSofa(o):
""" format the stack trace provided as a parameter into a string like that:
in filename.py:10:functioname()
-> the line of code.
in filename2.py:101:functioname1()
-> the line of code.
in filename3.py:103:functioname2()
-> the line of code.
"""
ss='At: '
for entry in o:
ss+= '\n '+ str(entry[1]) + '(' + str(entry[2]) + '): '+ entry[3]
return ss + "\n"
def getStackForSofa():
"""returns the current stack with a "informal" formatting. """
## we exclude the first level in the stack because it is the getStackForSofa() function itself.
ss=inspect.stack()[1:]
return formatStackForSofa(ss)
def getPythonCallingPointAsString():
"""returns the last entry with an "informal" formatting. """
## we exclude the first level in the stack because it is the getStackForSofa() function itself.
ss=inspect.stack()[1:2]
return formatStackForSofa(ss)
def getPythonCallingPoint():
"""returns the tuple with closest filename & line. """
## we exclude the first level in the stack because it is the getStackForSofa() function itself.
ss=inspect.stack()[1:2]
tmp=(os.path.abspath(ss[1]), ss[2])
return tmp
def sendMessageFromException(e):
exc_type, exc_value, exc_tb = sys.exc_info()
sofaExceptHandler(exc_type, exc_value, exc_tb)
def sofaFormatHandler(type, value, tb):
global oldexcepthook
"""This exception handler, convert python exceptions & traceback into more classical sofa error messages of the form:
Message Description
Python Stack (most recent are at the end)
File file1.py line 4 ...
File file1.py line 10 ...
File file1.py line 40 ...
File file1.py line 23 ...
faulty line
"""
s="\nPython Stack (most recent are at the end): \n"
for line in traceback.format_tb(tb):
s += line
return repr(value)+"\n"+s
def getSofaFormattedStringFromException(e):
exc_type, exc_value, exc_tb = sys.exc_info()
return sofaFormatHandler(exc_type, exc_value, exc_tb)
def sofaExceptHandler(type, value, tb):
global oldexcepthook
"""This exception handler, convert python exceptions & traceback into more classical sofa error messages of the form:
Message Description
Python Stack (most recent are at the end)
File file1.py line 4 ...
File file1.py line 10 ...
File file1.py line 40 ...
File file1.py line 23 ...
faulty line
"""
h = type.__name__
if str(value) != '':
h += ': ' + str(value)
s = ''.join(traceback.format_tb(tb))
Sofa.Helper.msg_error(h + '\n' + s, "line", 7)
sys.excepthook=sofaExceptHandler
def pyType2sofaType(v):
if isinstance(v, bool):
return "bool"
if isinstance(v, str):
return "string"
if isinstance(v, int):
return "int"
if isinstance(v, float):
return "double"
if isinstance(v, list) and len(v)==3:
return "Vec3d"
if isinstance(v, list):
return "vector<double>"
if isinstance(v, Sofa.PyTypes.DataType):
return v.sofaTypeName
return None
def msg_error(target, message):
frameinfo = inspect.getframeinfo(inspect.currentframe().f_back)
Sofa.Helper.msg_error(target, message, frameinfo.filename, frameinfo.lineno)
def msg_info(target, message):
frameinfo = inspect.getframeinfo(inspect.currentframe().f_back)
Sofa.Helper.msg_info(target, message, frameinfo.filename, frameinfo.lineno)
def msg_warning(target, message):
frameinfo = inspect.getframeinfo(inspect.currentframe().f_back)
Sofa.Helper.msg_warning(target, message, frameinfo.filename, frameinfo.lineno)
def msg_deprecated(target, message):
frameinfo = inspect.getframeinfo(inspect.currentframe().f_back)
Sofa.Helper.msg_deprecated(target, message, frameinfo.filename, frameinfo.lineno)
import inspect
def PrefabBuilder(f):
frameinfo = inspect.getframeinfo(inspect.currentframe().f_back)
definedloc = (frameinfo.filename, frameinfo.lineno)
def SofaPrefabF(*args, **kwargs):
class InnerSofaPrefab(Sofa.Core.RawPrefab):
def __init__(self, *args, **kwargs):
Sofa.Core.RawPrefab.__init__(self, *args, **kwargs)
self.isValid = True
def doReInit(self):
if not self.isValid:
return
try:
argnames = inspect.getfullargspec(f).args
kkwargs = {}
kkwargs["self"] = self
for name in argnames[:]:
if name != "self":
kkwargs[name] = self.__data__[name].value
self.cb(**kkwargs)
except Exception as e:
self.isValid = False
exc_type, exc_value, exc_tb = sys.exc_info()
Sofa.Helper.msg_error(self, "Unable to build prefab \n "+getSofaFormattedStringFromException(e))
try:
selfnode = None
kwargs["name"] = kwargs.get("name", f.__code__.co_name)
selfnode = InnerSofaPrefab(*args, **kwargs)
selfnode.setDefinitionSourceFileName(definedloc[0])
selfnode.setDefinitionSourceFilePos(definedloc[1])
selfnode.setSourceTracking(definedloc[0])
selfnode.cb = f
## retrieve meta data from decorated class:
selfnode.addData(name="prefabname", value=f.__code__.co_name,
type="string", help="The prefab's name", group="Infos")
selfnode.addData(name="docstring", value=f.__doc__,
type="string", help="This prefab's docstring", group="Infos")
## Now we retrieve all params passed to the prefab and add them as datafields:
argnames = inspect.getfullargspec(f).args
defaults = inspect.getfullargspec(f).defaults
if argnames is None:
argnames = []
defaults = []
if defaults is None:
defaults = []
i = len(argnames) - len(defaults)
for n in range(0, len(defaults)):
if argnames[i+n] not in selfnode.__data__:
if pyType2sofaType(defaults[n]) != None:
selfnode.addPrefabParameter(name=argnames[i+n],
default=kwargs.get(argnames[i+n], defaults[n]),
type=pyType2sofaType(defaults[n]), help="Undefined")
else:
Sofa.Helper.msg_error("Missing type for parameters: ", argnames[i+n])
selfnode.init()
except Exception as e:
if selfnode is not None:
selfnode.isValid=False
Sofa.Helper.msg_error(selfnode, "Unable to create prefab because: "+getSofaFormattedStringFromException(e))
else:
Sofa.Helper.msg_error("PrefabBuilder", "Unable to create prefab because: "+getSofaFormattedStringFromException(e))
return selfnode
SofaPrefabF.__dict__["__original__"] = f
return SofaPrefabF