1 | """Framsticks as a Python module.
|
---|
2 |
|
---|
3 | Static FramScript objects are available inside the module under their well known names
|
---|
4 | (frams.Simulator, frams.GenePools, etc.)
|
---|
5 |
|
---|
6 | These objects and all values passed to and from Framsticks are instances of frams.ExtValue.
|
---|
7 | Python values are automatically converted to Framstics data types.
|
---|
8 | Use frams.ExtValue._makeInt()/_makeDouble()/_makeString()/_makeNull() for explicit conversions.
|
---|
9 | Simple values returned from Framsticks can be converted to their natural Python
|
---|
10 | counterparts using _value() (or forced to a specific type with _int()/_double()/_string()).
|
---|
11 |
|
---|
12 | All non-Framsticks Python attributes start with '_' to avoid conflicts with Framsticks attributes.
|
---|
13 | Framsticks names that are Python reserved words are prefixed with 'x' (currently just Simulator.ximport).
|
---|
14 |
|
---|
15 | """
|
---|
16 |
|
---|
17 | import ctypes, re, sys, os
|
---|
18 |
|
---|
19 | c_api = None # will be initialized in init(). Global because ExtValue uses it.
|
---|
20 |
|
---|
21 |
|
---|
22 | class ExtValue(object):
|
---|
23 | _reInsideParens = re.compile('\((.*)\)')
|
---|
24 | _reservedWords = ['import'] # this list is scanned during every attribute access, only add what is really clashing with framsticks properties
|
---|
25 | _reservedXWords = ['x' + word for word in _reservedWords]
|
---|
26 | _encoding = 'utf-8'
|
---|
27 |
|
---|
28 |
|
---|
29 | def __init__(self, arg=None, dontinit=False):
|
---|
30 | if dontinit:
|
---|
31 | return
|
---|
32 | if isinstance(arg, int):
|
---|
33 | self._initFromInt(arg)
|
---|
34 | elif isinstance(arg, str):
|
---|
35 | self._initFromString(arg)
|
---|
36 | elif isinstance(arg, float):
|
---|
37 | self._initFromDouble(arg)
|
---|
38 | elif arg == None:
|
---|
39 | self._initFromNull()
|
---|
40 | else:
|
---|
41 | raise ArgumentError("Can't make ExtValue from '" + str(arg) + "'")
|
---|
42 |
|
---|
43 |
|
---|
44 | def __del__(self):
|
---|
45 | c_api.extFree(self.__ptr)
|
---|
46 |
|
---|
47 |
|
---|
48 | def _initFromNull(self):
|
---|
49 | self.__ptr = c_api.extFromNull()
|
---|
50 |
|
---|
51 |
|
---|
52 | def _initFromInt(self, v):
|
---|
53 | self.__ptr = c_api.extFromInt(v)
|
---|
54 |
|
---|
55 |
|
---|
56 | def _initFromDouble(self, v):
|
---|
57 | self.__ptr = c_api.extFromDouble(v)
|
---|
58 |
|
---|
59 |
|
---|
60 | def _initFromString(self, v):
|
---|
61 | self.__ptr = c_api.extFromString(ExtValue._cstringFromPython(v))
|
---|
62 |
|
---|
63 |
|
---|
64 | @classmethod
|
---|
65 | def _makeNull(cls, v):
|
---|
66 | e = ExtValue(None, True)
|
---|
67 | e._initFromNull()
|
---|
68 | return e
|
---|
69 |
|
---|
70 |
|
---|
71 | @classmethod
|
---|
72 | def _makeInt(cls, v):
|
---|
73 | e = ExtValue(None, True)
|
---|
74 | e._initFromInt(v)
|
---|
75 | return e
|
---|
76 |
|
---|
77 |
|
---|
78 | @classmethod
|
---|
79 | def _makeDouble(cls, v):
|
---|
80 | e = ExtValue(None, True)
|
---|
81 | e._initFromDouble(v)
|
---|
82 | return e
|
---|
83 |
|
---|
84 |
|
---|
85 | @classmethod
|
---|
86 | def _makeString(cls, v):
|
---|
87 | e = ExtValue(None, True)
|
---|
88 | e._initFromString(v)
|
---|
89 | return e
|
---|
90 |
|
---|
91 |
|
---|
92 | @classmethod
|
---|
93 | def _rootObject(cls):
|
---|
94 | e = ExtValue(None, True)
|
---|
95 | e.__ptr = c_api.rootObject()
|
---|
96 | return e
|
---|
97 |
|
---|
98 |
|
---|
99 | @classmethod
|
---|
100 | def _stringFromC(cls, cptr):
|
---|
101 | return cptr.decode(ExtValue._encoding)
|
---|
102 |
|
---|
103 |
|
---|
104 | @classmethod
|
---|
105 | def _cstringFromPython(cls, s):
|
---|
106 | return ctypes.c_char_p(s.encode(ExtValue._encoding))
|
---|
107 |
|
---|
108 |
|
---|
109 | def _type(self):
|
---|
110 | return c_api.extType(self.__ptr)
|
---|
111 |
|
---|
112 |
|
---|
113 | def _class(self):
|
---|
114 | cls = c_api.extClass(self.__ptr)
|
---|
115 | if cls == None:
|
---|
116 | return None
|
---|
117 | else:
|
---|
118 | return ExtValue._stringFromC(cls)
|
---|
119 |
|
---|
120 |
|
---|
121 | def _value(self):
|
---|
122 | t = self._type()
|
---|
123 | if t == 0:
|
---|
124 | return None
|
---|
125 | elif t == 1:
|
---|
126 | return self._int()
|
---|
127 | elif t == 2:
|
---|
128 | return self._double()
|
---|
129 | elif t == 3:
|
---|
130 | return self._string()
|
---|
131 | else:
|
---|
132 | return self
|
---|
133 |
|
---|
134 |
|
---|
135 | def _int(self):
|
---|
136 | return c_api.extIntValue(self.__ptr)
|
---|
137 |
|
---|
138 |
|
---|
139 | def _double(self):
|
---|
140 | return c_api.extDoubleValue(self.__ptr)
|
---|
141 |
|
---|
142 |
|
---|
143 | def _string(self):
|
---|
144 | return ExtValue._stringFromC(c_api.extStringValue(self.__ptr))
|
---|
145 |
|
---|
146 |
|
---|
147 | def __str__(self):
|
---|
148 | return self._string()
|
---|
149 |
|
---|
150 |
|
---|
151 | def __dir__(self):
|
---|
152 | ids = dir(type(self))
|
---|
153 | if self._type() == 4:
|
---|
154 | for i in range(c_api.extPropCount(self.__ptr)):
|
---|
155 | name = ExtValue._stringFromC(c_api.extPropId(self.__ptr, i))
|
---|
156 | if name in ExtValue._reservedWords:
|
---|
157 | name = 'x' + name
|
---|
158 | ids.append(name)
|
---|
159 | return ids
|
---|
160 |
|
---|
161 |
|
---|
162 | def __getattr__(self, key):
|
---|
163 | if key[0] == '_':
|
---|
164 | return self.__dict__[key]
|
---|
165 | if key in ExtValue._reservedXWords:
|
---|
166 | key = key[1:]
|
---|
167 | prop_i = c_api.extPropFind(self.__ptr, ExtValue._cstringFromPython(key))
|
---|
168 | if prop_i < 0:
|
---|
169 | raise AttributeError('no ' + str(key) + ' in ' + str(self))
|
---|
170 | t = ExtValue._stringFromC(c_api.extPropType(self.__ptr, prop_i))
|
---|
171 | if t[0] == 'p':
|
---|
172 | arg_types = ExtValue._reInsideParens.search(t)
|
---|
173 | if arg_types:
|
---|
174 | arg_types = arg_types.group(1).split(',') # anyone wants to add argument type validation using param type declarations?
|
---|
175 |
|
---|
176 |
|
---|
177 | def fun(*args):
|
---|
178 | ext_args = []
|
---|
179 | ext_pointers = []
|
---|
180 | for a in args:
|
---|
181 | if isinstance(a, ExtValue):
|
---|
182 | ext = a
|
---|
183 | else:
|
---|
184 | ext = ExtValue(a)
|
---|
185 | ext_args.append(ext)
|
---|
186 | ext_pointers.append(ext.__ptr)
|
---|
187 | ret = ExtValue(None, True)
|
---|
188 | args_array = (ctypes.c_void_p * len(args))(*ext_pointers)
|
---|
189 | ret.__ptr = c_api.extPropCall(self.__ptr, prop_i, len(args), args_array)
|
---|
190 | return ret
|
---|
191 |
|
---|
192 |
|
---|
193 | return fun
|
---|
194 | else:
|
---|
195 | ret = ExtValue(None, True)
|
---|
196 | ret.__ptr = c_api.extPropGet(self.__ptr, prop_i)
|
---|
197 | return ret
|
---|
198 |
|
---|
199 |
|
---|
200 | def __setattr__(self, key, value):
|
---|
201 | if key[0] == '_':
|
---|
202 | self.__dict__[key] = value
|
---|
203 | else:
|
---|
204 | if key in ExtValue._reservedXWords:
|
---|
205 | key = key[1:]
|
---|
206 | prop_i = c_api.extPropFind(self.__ptr, ExtValue._cstringFromPython(key))
|
---|
207 | if prop_i < 0:
|
---|
208 | raise AttributeError("No '" + str(key) + "' in '" + str(self) + "'")
|
---|
209 | if not isinstance(value, ExtValue):
|
---|
210 | value = ExtValue(value)
|
---|
211 | c_api.extPropSet(self.__ptr, prop_i, value.__ptr)
|
---|
212 |
|
---|
213 |
|
---|
214 | def __getitem__(self, key):
|
---|
215 | return self.get(key)
|
---|
216 |
|
---|
217 |
|
---|
218 | def __setitem__(self, key, value):
|
---|
219 | return self.set(key, value)
|
---|
220 |
|
---|
221 |
|
---|
222 | def __len__(self):
|
---|
223 | try:
|
---|
224 | return self.size._int()
|
---|
225 | except:
|
---|
226 | return 0
|
---|
227 |
|
---|
228 |
|
---|
229 | def __iter__(self):
|
---|
230 | class It(object):
|
---|
231 | def __init__(self, container, frams_it):
|
---|
232 | self.container = container
|
---|
233 | self.frams_it = frams_it
|
---|
234 |
|
---|
235 |
|
---|
236 | def __iter__(self):
|
---|
237 | return self
|
---|
238 |
|
---|
239 |
|
---|
240 | def __next__(self):
|
---|
241 | if self.frams_it.next._int() != 0:
|
---|
242 | return self.frams_it.value
|
---|
243 | else:
|
---|
244 | raise StopIteration()
|
---|
245 |
|
---|
246 | return It(self, self.iterator)
|
---|
247 |
|
---|
248 |
|
---|
249 | def init(dllpath, *args):
|
---|
250 | original_dir = os.getcwd()
|
---|
251 | os.chdir(dllpath) # because under Windows, frams.dll requires other dll's which reside in the same directory, so we must change current dir for them to be found
|
---|
252 | global c_api
|
---|
253 | c_api = ctypes.CDLL('frams_c_api.so' if os.name == 'posix' else 'frams.dll')
|
---|
254 | os.chdir(original_dir)
|
---|
255 |
|
---|
256 | c_api.init.argtypes = [ctypes.c_int, ctypes.POINTER(ctypes.c_char_p)]
|
---|
257 | c_api.init.restype = None
|
---|
258 | c_api.extFree.argtypes = [ctypes.c_void_p]
|
---|
259 | c_api.extFree.restype = None
|
---|
260 | c_api.extType.argtypes = [ctypes.c_void_p]
|
---|
261 | c_api.extType.restype = ctypes.c_int
|
---|
262 | c_api.extFromNull.argtypes = []
|
---|
263 | c_api.extFromNull.restype = ctypes.c_void_p
|
---|
264 | c_api.extFromInt.argtypes = [ctypes.c_int]
|
---|
265 | c_api.extFromInt.restype = ctypes.c_void_p
|
---|
266 | c_api.extFromDouble.argtypes = [ctypes.c_double]
|
---|
267 | c_api.extFromDouble.restype = ctypes.c_void_p
|
---|
268 | c_api.extFromString.argtypes = [ctypes.c_char_p]
|
---|
269 | c_api.extFromString.restype = ctypes.c_void_p
|
---|
270 | c_api.extIntValue.argtypes = [ctypes.c_void_p]
|
---|
271 | c_api.extIntValue.restype = ctypes.c_int
|
---|
272 | c_api.extDoubleValue.argtypes = [ctypes.c_void_p]
|
---|
273 | c_api.extDoubleValue.restype = ctypes.c_double
|
---|
274 | c_api.extStringValue.argtypes = [ctypes.c_void_p]
|
---|
275 | c_api.extStringValue.restype = ctypes.c_char_p
|
---|
276 | c_api.extClass.argtypes = [ctypes.c_void_p]
|
---|
277 | c_api.extClass.restype = ctypes.c_char_p
|
---|
278 | c_api.extPropCount.argtypes = [ctypes.c_void_p]
|
---|
279 | c_api.extPropCount.restype = ctypes.c_int
|
---|
280 | c_api.extPropId.argtypes = [ctypes.c_void_p, ctypes.c_int]
|
---|
281 | c_api.extPropId.restype = ctypes.c_char_p
|
---|
282 | c_api.extPropType.argtypes = [ctypes.c_void_p, ctypes.c_int]
|
---|
283 | c_api.extPropType.restype = ctypes.c_char_p
|
---|
284 | c_api.extPropFind.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
|
---|
285 | c_api.extPropFind.restype = ctypes.c_int
|
---|
286 | c_api.extPropGet.argtypes = [ctypes.c_void_p, ctypes.c_int]
|
---|
287 | c_api.extPropGet.restype = ctypes.c_void_p
|
---|
288 | c_api.extPropSet.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p]
|
---|
289 | c_api.extPropSet.restype = ctypes.c_int
|
---|
290 | c_api.extPropCall.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_int, ctypes.c_void_p]
|
---|
291 | c_api.extPropCall.restype = ctypes.c_void_p
|
---|
292 | c_api.rootObject.argtypes = []
|
---|
293 | c_api.rootObject.restype = ctypes.c_void_p
|
---|
294 |
|
---|
295 | c_args = (ctypes.c_char_p * (len(args) + 1))(*([b''] + list(a.encode(ExtValue._encoding) for a in args)))
|
---|
296 | c_api.init(len(args) + 1, c_args)
|
---|
297 | Root = ExtValue._rootObject()
|
---|
298 | for n in dir(Root):
|
---|
299 | if n[0].isalpha():
|
---|
300 | attr = getattr(Root, n)
|
---|
301 | if isinstance(attr, ExtValue):
|
---|
302 | attr = attr._value()
|
---|
303 | setattr(sys.modules[__name__], n, attr)
|
---|
304 | print('Using Framsticks version: ' + str(Simulator.version_string))
|
---|
305 | print('Home (writable) dir : ' + home_dir)
|
---|
306 | print('Resources dir : ' + res_dir)
|
---|
307 | print()
|
---|