source: framspy/frams.py @ 1085

Last change on this file since 1085 was 1085, checked in by sz, 3 years ago
  • more sophisticated frams.py init() so frams-test.py (and other python programs) don't have to bother with framsticks paths
  • './'+name for loading .so from the current directory in linux
File size: 10.3 KB
Line 
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
16import ctypes, re, sys, os
17
18c_api = None  # will be initialized in init(). Global because ExtValue uses it.
19
20
21class ExtValue(object):
22        """All Framsticks objects and values are instances of this class. Read the documentation of the 'frams' module for more information."""
23
24        _reInsideParens = re.compile('\((.*)\)')
25        _reservedWords = ['import']  # this list is scanned during every attribute access, only add what is really clashing with framsticks properties
26        _reservedXWords = ['x' + word for word in _reservedWords]
27        _encoding = 'utf-8'
28
29
30        def __init__(self, arg=None, dontinit=False):
31                if dontinit:
32                        return
33                if isinstance(arg, int):
34                        self._initFromInt(arg)
35                elif isinstance(arg, str):
36                        self._initFromString(arg)
37                elif isinstance(arg, float):
38                        self._initFromDouble(arg)
39                elif arg == None:
40                        self._initFromNull()
41                else:
42                        raise ArgumentError("Can't make ExtValue from '" + str(arg) + "'")
43
44
45        def __del__(self):
46                c_api.extFree(self.__ptr)
47
48
49        def _initFromNull(self):
50                self.__ptr = c_api.extFromNull()
51
52
53        def _initFromInt(self, v):
54                self.__ptr = c_api.extFromInt(v)
55
56
57        def _initFromDouble(self, v):
58                self.__ptr = c_api.extFromDouble(v)
59
60
61        def _initFromString(self, v):
62                self.__ptr = c_api.extFromString(ExtValue._cstringFromPython(v))
63
64
65        @classmethod
66        def _makeNull(cls, v):
67                e = ExtValue(None, True)
68                e._initFromNull()
69                return e
70
71
72        @classmethod
73        def _makeInt(cls, v):
74                e = ExtValue(None, True)
75                e._initFromInt(v)
76                return e
77
78
79        @classmethod
80        def _makeDouble(cls, v):
81                e = ExtValue(None, True)
82                e._initFromDouble(v)
83                return e
84
85
86        @classmethod
87        def _makeString(cls, v):
88                e = ExtValue(None, True)
89                e._initFromString(v)
90                return e
91
92
93        @classmethod
94        def _rootObject(cls):
95                e = ExtValue(None, True)
96                e.__ptr = c_api.rootObject()
97                return e
98
99
100        @classmethod
101        def _stringFromC(cls, cptr):
102                return cptr.decode(ExtValue._encoding)
103
104
105        @classmethod
106        def _cstringFromPython(cls, s):
107                return ctypes.c_char_p(s.encode(ExtValue._encoding))
108
109
110        def _type(self):
111                return c_api.extType(self.__ptr)
112
113
114        def _class(self):
115                cls = c_api.extClass(self.__ptr)
116                if cls == None:
117                        return None
118                else:
119                        return ExtValue._stringFromC(cls)
120
121
122        def _value(self):
123                t = self._type()
124                if t == 0:
125                        return None
126                elif t == 1:
127                        return self._int()
128                elif t == 2:
129                        return self._double()
130                elif t == 3:
131                        return self._string()
132                else:
133                        return self
134
135
136        def _int(self):
137                return c_api.extIntValue(self.__ptr)
138
139
140        def _double(self):
141                return c_api.extDoubleValue(self.__ptr)
142
143
144        def _string(self):
145                return ExtValue._stringFromC(c_api.extStringValue(self.__ptr))
146
147
148        def __str__(self):
149                return self._string()
150
151
152        def __dir__(self):
153                ids = dir(type(self))
154                if self._type() == 4:
155                        for i in range(c_api.extPropCount(self.__ptr)):
156                                name = ExtValue._stringFromC(c_api.extPropId(self.__ptr, i))
157                                if name in ExtValue._reservedWords:
158                                        name = 'x' + name
159                                ids.append(name)
160                return ids
161
162
163        def __getattr__(self, key):
164                if key[0] == '_':
165                        return self.__dict__[key]
166                if key in ExtValue._reservedXWords:
167                        key = key[1:]
168                prop_i = c_api.extPropFind(self.__ptr, ExtValue._cstringFromPython(key))
169                if prop_i < 0:
170                        raise AttributeError('no ' + str(key) + ' in ' + str(self))
171                t = ExtValue._stringFromC(c_api.extPropType(self.__ptr, prop_i))
172                if t[0] == 'p':
173                        arg_types = ExtValue._reInsideParens.search(t)
174                        if arg_types:
175                                arg_types = arg_types.group(1).split(',')  # anyone wants to add argument type validation using param type declarations?
176
177
178                        def fun(*args):
179                                ext_args = []
180                                ext_pointers = []
181                                for a in args:
182                                        if isinstance(a, ExtValue):
183                                                ext = a
184                                        else:
185                                                ext = ExtValue(a)
186                                        ext_args.append(ext)
187                                        ext_pointers.append(ext.__ptr)
188                                ret = ExtValue(None, True)
189                                args_array = (ctypes.c_void_p * len(args))(*ext_pointers)
190                                ret.__ptr = c_api.extPropCall(self.__ptr, prop_i, len(args), args_array)
191                                return ret
192
193
194                        return fun
195                else:
196                        ret = ExtValue(None, True)
197                        ret.__ptr = c_api.extPropGet(self.__ptr, prop_i)
198                        return ret
199
200
201        def __setattr__(self, key, value):
202                if key[0] == '_':
203                        self.__dict__[key] = value
204                else:
205                        if key in ExtValue._reservedXWords:
206                                key = key[1:]
207                        prop_i = c_api.extPropFind(self.__ptr, ExtValue._cstringFromPython(key))
208                        if prop_i < 0:
209                                raise AttributeError("No '" + str(key) + "' in '" + str(self) + "'")
210                        if not isinstance(value, ExtValue):
211                                value = ExtValue(value)
212                        c_api.extPropSet(self.__ptr, prop_i, value.__ptr)
213
214
215        def __getitem__(self, key):
216                return self.get(key)
217
218
219        def __setitem__(self, key, value):
220                return self.set(key, value)
221
222
223        def __len__(self):
224                try:
225                        return self.size._int()
226                except:
227                        return 0
228
229
230        def __iter__(self):
231                class It(object):
232                        def __init__(self, container, frams_it):
233                                self.container = container
234                                self.frams_it = frams_it
235
236
237                        def __iter__(self):
238                                return self
239
240
241                        def __next__(self):
242                                if self.frams_it.next._int() != 0:
243                                        return self.frams_it.value
244                                else:
245                                        raise StopIteration()
246
247                return It(self, self.iterator)
248
249
250def init(*args):
251        """
252        Initializes the connection to Framsticks dll/so.
253
254        Python programs do not have to know the framstics path but if they know, just pass the path as the first argument.
255        Similarly '-dPATH' and '-DPATH' needed by framsticks are optional and derived from the first path, unless they are specified as args in init().
256        '-lNAME' is the optional library name (full name including the file name extension), default is 'frams-objects.dll/.so' depending on the platform.
257        All other arguments are passed to framsticks and not interpreted by this function.
258
259        """
260        # goals:
261        frams_d=None
262        frams_D=None
263        lib_path=None
264        lib_name='frams-objects.so' if os.name == 'posix' else 'frams-objects.dll'
265        initargs=[]
266        for a in args:
267                if a[:2]=='-d':
268                        frams_d=a
269                elif a[:2]=='-D':
270                        frams_D=a
271                elif a[:2]=='-l':
272                        lib_name=a[2:]
273                elif lib_path==None:
274                        lib_path=a
275                else:
276                        initargs.append(a)
277        if lib_path==None:
278                #todo: use env variable and/or the zip distribution we are in when the path is not specified in arg
279                #for now just assume the current dir is framsticks
280                lib_path='.'
281
282        original_dir = os.getcwd()
283        os.chdir(lib_path)  # 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 while loading the main dll
284        abs_data=os.path.abspath('data') #use absolute path for -d and -D so python is free to cd anywhere without confusing the frams
285        # for the hypothetical case without lib_path the abs_data must be obtained from somewhere else
286        if frams_d==None:
287                frams_d='-d'+abs_data
288        if frams_D==None:
289                frams_D='-D'+abs_data
290        initargs.insert(0,frams_d)
291        initargs.insert(0,frams_D)
292        initargs.insert(0,'dummy.exe')
293       
294        global c_api  # access global variable
295        if lib_path!=None and os.name == 'posix':
296                lib_name = './'+lib_name #currently we always have lib_path (even if it is incorrect) but hypothetically it could work with lib_path==None and load .so from some default system path without './'
297        c_api = ctypes.CDLL(lib_name)
298        os.chdir(original_dir)
299       
300        c_api.init.argtypes = [ctypes.c_int, ctypes.POINTER(ctypes.c_char_p)]
301        c_api.init.restype = None
302        c_api.extFree.argtypes = [ctypes.c_void_p]
303        c_api.extFree.restype = None
304        c_api.extType.argtypes = [ctypes.c_void_p]
305        c_api.extType.restype = ctypes.c_int
306        c_api.extFromNull.argtypes = []
307        c_api.extFromNull.restype = ctypes.c_void_p
308        c_api.extFromInt.argtypes = [ctypes.c_int]
309        c_api.extFromInt.restype = ctypes.c_void_p
310        c_api.extFromDouble.argtypes = [ctypes.c_double]
311        c_api.extFromDouble.restype = ctypes.c_void_p
312        c_api.extFromString.argtypes = [ctypes.c_char_p]
313        c_api.extFromString.restype = ctypes.c_void_p
314        c_api.extIntValue.argtypes = [ctypes.c_void_p]
315        c_api.extIntValue.restype = ctypes.c_int
316        c_api.extDoubleValue.argtypes = [ctypes.c_void_p]
317        c_api.extDoubleValue.restype = ctypes.c_double
318        c_api.extStringValue.argtypes = [ctypes.c_void_p]
319        c_api.extStringValue.restype = ctypes.c_char_p
320        c_api.extClass.argtypes = [ctypes.c_void_p]
321        c_api.extClass.restype = ctypes.c_char_p
322        c_api.extPropCount.argtypes = [ctypes.c_void_p]
323        c_api.extPropCount.restype = ctypes.c_int
324        c_api.extPropId.argtypes = [ctypes.c_void_p, ctypes.c_int]
325        c_api.extPropId.restype = ctypes.c_char_p
326        c_api.extPropType.argtypes = [ctypes.c_void_p, ctypes.c_int]
327        c_api.extPropType.restype = ctypes.c_char_p
328        c_api.extPropFind.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
329        c_api.extPropFind.restype = ctypes.c_int
330        c_api.extPropGet.argtypes = [ctypes.c_void_p, ctypes.c_int]
331        c_api.extPropGet.restype = ctypes.c_void_p
332        c_api.extPropSet.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p]
333        c_api.extPropSet.restype = ctypes.c_int
334        c_api.extPropCall.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_int, ctypes.c_void_p]
335        c_api.extPropCall.restype = ctypes.c_void_p
336        c_api.rootObject.argtypes = []
337        c_api.rootObject.restype = ctypes.c_void_p
338
339        c_args = (ctypes.c_char_p * len(initargs))(*list(a.encode(ExtValue._encoding) for a in initargs))
340        c_api.init(len(initargs), c_args)
341        Root = ExtValue._rootObject()
342        for n in dir(Root):
343                if n[0].isalpha():
344                        attr = getattr(Root, n)
345                        if isinstance(attr, ExtValue):
346                                attr = attr._value()
347                        setattr(sys.modules[__name__], n, attr)
348        print('Using Framsticks version: ' + str(Simulator.version_string))
349        print('Home (writable) dir     : ' + home_dir)
350        print('Resources dir           : ' + res_dir)
351        print()
Note: See TracBrowser for help on using the repository browser.