"""Framsticks as a Python module. Static FramScript objects are available inside the module under their well known names (frams.Simulator, frams.GenePools, etc.) These objects and all values passed to and from Framsticks are instances of frams.ExtValue. Python values are automatically converted to Framstics data types. Use frams.ExtValue._makeInt()/_makeDouble()/_makeString()/_makeNull() for explicit conversions. Simple values returned from Framsticks can be converted to their natural Python counterparts using _value() (or forced to a specific type with _int()/_double()/_string()). All non-Framsticks Python attributes start with '_' to avoid conflicts with Framsticks attributes. Framsticks names that are Python reserved words are prefixed with 'x' (currently just Simulator.ximport). """ import ctypes, re, sys, os c_api = None # will be initialized in init(). Global because ExtValue uses it. class ExtValue(object): _reInsideParens = re.compile('\((.*)\)') _reservedWords = ['import'] # this list is scanned during every attribute access, only add what is really clashing with framsticks properties _reservedXWords = ['x' + word for word in _reservedWords] _encoding = 'utf-8' def __init__(self, arg=None, dontinit=False): if dontinit: return if isinstance(arg, int): self._initFromInt(arg) elif isinstance(arg, str): self._initFromString(arg) elif isinstance(arg, float): self._initFromDouble(arg) elif arg == None: self._initFromNull() else: raise ArgumentError("Can't make ExtValue from '" + str(arg) + "'") def __del__(self): c_api.extFree(self.__ptr) def _initFromNull(self): self.__ptr = c_api.extFromNull() def _initFromInt(self, v): self.__ptr = c_api.extFromInt(v) def _initFromDouble(self, v): self.__ptr = c_api.extFromDouble(v) def _initFromString(self, v): self.__ptr = c_api.extFromString(ExtValue._cstringFromPython(v)) @classmethod def _makeNull(cls, v): e = ExtValue(None, True) e._initFromNull() return e @classmethod def _makeInt(cls, v): e = ExtValue(None, True) e._initFromInt(v) return e @classmethod def _makeDouble(cls, v): e = ExtValue(None, True) e._initFromDouble(v) return e @classmethod def _makeString(cls, v): e = ExtValue(None, True) e._initFromString(v) return e @classmethod def _rootObject(cls): e = ExtValue(None, True) e.__ptr = c_api.rootObject() return e @classmethod def _stringFromC(cls, cptr): return cptr.decode(ExtValue._encoding) @classmethod def _cstringFromPython(cls, s): return ctypes.c_char_p(s.encode(ExtValue._encoding)) def _type(self): return c_api.extType(self.__ptr) def _class(self): cls = c_api.extClass(self.__ptr) if cls == None: return None else: return ExtValue._stringFromC(cls) def _value(self): t = self._type() if t == 0: return None elif t == 1: return self._int() elif t == 2: return self._double() elif t == 3: return self._string() else: return self def _int(self): return c_api.extIntValue(self.__ptr) def _double(self): return c_api.extDoubleValue(self.__ptr) def _string(self): return ExtValue._stringFromC(c_api.extStringValue(self.__ptr)) def __str__(self): return self._string() def __dir__(self): ids = dir(type(self)) if self._type() == 4: for i in range(c_api.extPropCount(self.__ptr)): name = ExtValue._stringFromC(c_api.extPropId(self.__ptr, i)) if name in ExtValue._reservedWords: name = 'x' + name ids.append(name) return ids def __getattr__(self, key): if key[0] == '_': return self.__dict__[key] if key in ExtValue._reservedXWords: key = key[1:] prop_i = c_api.extPropFind(self.__ptr, ExtValue._cstringFromPython(key)) if prop_i < 0: raise AttributeError('no ' + str(key) + ' in ' + str(self)) t = ExtValue._stringFromC(c_api.extPropType(self.__ptr, prop_i)) if t[0] == 'p': arg_types = ExtValue._reInsideParens.search(t) if arg_types: arg_types = arg_types.group(1).split(',') # anyone wants to add argument type validation using param type declarations? def fun(*args): ext_args = [] ext_pointers = [] for a in args: if isinstance(a, ExtValue): ext = a else: ext = ExtValue(a) ext_args.append(ext) ext_pointers.append(ext.__ptr) ret = ExtValue(None, True) args_array = (ctypes.c_void_p * len(args))(*ext_pointers) ret.__ptr = c_api.extPropCall(self.__ptr, prop_i, len(args), args_array) return ret return fun else: ret = ExtValue(None, True) ret.__ptr = c_api.extPropGet(self.__ptr, prop_i) return ret def __setattr__(self, key, value): if key[0] == '_': self.__dict__[key] = value else: if key in ExtValue._reservedXWords: key = key[1:] prop_i = c_api.extPropFind(self.__ptr, ExtValue._cstringFromPython(key)) if prop_i < 0: raise AttributeError("No '" + str(key) + "' in '" + str(self) + "'") if not isinstance(value, ExtValue): value = ExtValue(value) c_api.extPropSet(self.__ptr, prop_i, value.__ptr) def __getitem__(self, key): return self.get(key) def __setitem__(self, key, value): return self.set(key, value) def __len__(self): try: return self.size._int() except: return 0 def __iter__(self): class It(object): def __init__(self, container, frams_it): self.container = container self.frams_it = frams_it def __iter__(self): return self def __next__(self): if self.frams_it.next._int() != 0: return self.frams_it.value else: raise StopIteration() return It(self, self.iterator) def init(dllpath, *args): original_dir = os.getcwd() 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 global c_api c_api = ctypes.CDLL('frams_c_api.so' if os.name == 'posix' else 'frams.dll') os.chdir(original_dir) c_api.init.argtypes = [ctypes.c_int, ctypes.POINTER(ctypes.c_char_p)] c_api.init.restype = None c_api.extFree.argtypes = [ctypes.c_void_p] c_api.extFree.restype = None c_api.extType.argtypes = [ctypes.c_void_p] c_api.extType.restype = ctypes.c_int c_api.extFromNull.argtypes = [] c_api.extFromNull.restype = ctypes.c_void_p c_api.extFromInt.argtypes = [ctypes.c_int] c_api.extFromInt.restype = ctypes.c_void_p c_api.extFromDouble.argtypes = [ctypes.c_double] c_api.extFromDouble.restype = ctypes.c_void_p c_api.extFromString.argtypes = [ctypes.c_char_p] c_api.extFromString.restype = ctypes.c_void_p c_api.extIntValue.argtypes = [ctypes.c_void_p] c_api.extIntValue.restype = ctypes.c_int c_api.extDoubleValue.argtypes = [ctypes.c_void_p] c_api.extDoubleValue.restype = ctypes.c_double c_api.extStringValue.argtypes = [ctypes.c_void_p] c_api.extStringValue.restype = ctypes.c_char_p c_api.extClass.argtypes = [ctypes.c_void_p] c_api.extClass.restype = ctypes.c_char_p c_api.extPropCount.argtypes = [ctypes.c_void_p] c_api.extPropCount.restype = ctypes.c_int c_api.extPropId.argtypes = [ctypes.c_void_p, ctypes.c_int] c_api.extPropId.restype = ctypes.c_char_p c_api.extPropType.argtypes = [ctypes.c_void_p, ctypes.c_int] c_api.extPropType.restype = ctypes.c_char_p c_api.extPropFind.argtypes = [ctypes.c_void_p, ctypes.c_char_p] c_api.extPropFind.restype = ctypes.c_int c_api.extPropGet.argtypes = [ctypes.c_void_p, ctypes.c_int] c_api.extPropGet.restype = ctypes.c_void_p c_api.extPropSet.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p] c_api.extPropSet.restype = ctypes.c_int c_api.extPropCall.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_int, ctypes.c_void_p] c_api.extPropCall.restype = ctypes.c_void_p c_api.rootObject.argtypes = [] c_api.rootObject.restype = ctypes.c_void_p c_args = (ctypes.c_char_p * (len(args) + 1))(*([b''] + list(a.encode(ExtValue._encoding) for a in args))) c_api.init(len(args) + 1, c_args) Root = ExtValue._rootObject() for n in dir(Root): if n[0].isalpha(): attr = getattr(Root, n) if isinstance(attr, ExtValue): attr = attr._value() setattr(sys.modules[__name__], n, attr) print('Using Framsticks version: ' + str(Simulator.version_string)) print('Home (writable) dir : ' + home_dir) print('Resources dir : ' + res_dir) print()