source: framspy/frams.py @ 1078

Last change on this file since 1078 was 1078, checked in by Maciej Komosinski, 3 years ago

Direct connection from python to Framsticks .so/.dll

File size: 8.4 KB
RevLine 
[1078]1"""Framsticks as a Python module.
2
3Static FramScript objects are available inside the module under their well known names
4(frams.Simulator, frams.GenePools, etc.)
5
6These objects and all values passed to and from Framsticks are instances of frams.ExtValue.
7Python values are automatically converted to Framstics data types.
8Use frams.ExtValue._makeInt()/_makeDouble()/_makeString()/_makeNull() for explicit conversions.
9Simple values returned from Framsticks can be converted to their natural Python
10counterparts using _value() (or forced to a specific type with  _int()/_double()/_string()).
11
12All non-Framsticks Python attributes start with '_' to avoid conflicts with Framsticks attributes.
13Framsticks names that are Python reserved words are prefixed with 'x' (currently just Simulator.ximport).
14
15"""
16
17import ctypes, re, sys, os
18
19c_api = None  # will be initialized in init(). Global because ExtValue uses it.
20
21
22class 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
249def 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()
Note: See TracBrowser for help on using the repository browser.