source: framspy/frams.py @ 1106

Last change on this file since 1106 was 1106, checked in by Maciej Komosinski, 23 months ago

Since 5.0rc19 there is no need to restore the current working directory because Framsticks does not change it

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