summaryrefslogtreecommitdiff
path: root/Build/source/utils/asymptote/aspy.py
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/utils/asymptote/aspy.py')
-rw-r--r--Build/source/utils/asymptote/aspy.py355
1 files changed, 355 insertions, 0 deletions
diff --git a/Build/source/utils/asymptote/aspy.py b/Build/source/utils/asymptote/aspy.py
new file mode 100644
index 00000000000..6b3fb3ec2e5
--- /dev/null
+++ b/Build/source/utils/asymptote/aspy.py
@@ -0,0 +1,355 @@
+#####
+#
+# aspy.py
+#
+# Andy Hammerlindl 2011/09/03
+#
+# Uses ctypes to interface with the shared library version of Python.
+# Asymptote can run and its datatypes inspected via Python.
+#
+#
+# To use the module:
+# 1. make asymptote.so
+# 2. Ensure that asymptote.so is visable to python, say adding its directory
+# to LD_LIBRARY_PATH
+# 3. Run this module. (See runExample for an example.)
+#
+#####
+
+from ctypes import *
+
+asyInt = c_longlong
+handle_typ = c_void_p
+arguments_typ = c_void_p
+state_typ = c_void_p
+
+function_typ = CFUNCTYPE(None, state_typ, c_void_p)
+
+class string_typ(Structure):
+ _fields_ = [
+ ("buf", c_char_p), # Should be NUL-terminated? Maybe replace with
+ # POINTER(c_char).
+ ("length", asyInt)
+ ]
+
+ErrorCallbackFUNC = CFUNCTYPE(None, string_typ)
+
+NORMAL_ARG = 45000
+REST_ARG = 45001
+
+class Policy(Structure):
+ _fields_ = [
+ ("version", asyInt),
+ ("copyHandle", CFUNCTYPE(handle_typ, handle_typ)),
+ ("releaseHandle", CFUNCTYPE(None, handle_typ)),
+ ("handleFromInt", CFUNCTYPE(handle_typ, asyInt)),
+ ("handleFromBool", CFUNCTYPE(handle_typ, asyInt)),
+ ("handleFromDouble", CFUNCTYPE(handle_typ, c_double)),
+ ("handleFromString", CFUNCTYPE(handle_typ, string_typ)),
+ ("handleFromFunction", CFUNCTYPE(handle_typ,
+ c_char_p,
+ function_typ,
+ c_void_p)),
+ ("IntFromHandle", CFUNCTYPE(asyInt, handle_typ)),
+ ("boolFromHandle", CFUNCTYPE(asyInt, handle_typ)),
+ ("doubleFromHandle", CFUNCTYPE(c_double, handle_typ)),
+ ("stringFromHandle", CFUNCTYPE(string_typ, handle_typ)),
+ ("getField", CFUNCTYPE(handle_typ,
+ handle_typ,
+ c_char_p)),
+ ("getCell", CFUNCTYPE(handle_typ,
+ handle_typ,
+ asyInt)),
+ ("addField", CFUNCTYPE(None,
+ handle_typ,
+ c_char_p,
+ handle_typ)),
+ ("newArguments", CFUNCTYPE(arguments_typ)),
+ ("releaseArguments", CFUNCTYPE(None, arguments_typ)),
+ ("addArgument", CFUNCTYPE(None,
+ arguments_typ,
+ c_char_p,
+ handle_typ,
+ asyInt)),
+ ("call", CFUNCTYPE(handle_typ,
+ handle_typ,
+ arguments_typ)),
+ ("globals", CFUNCTYPE(handle_typ, state_typ)),
+ ("numParams", CFUNCTYPE(asyInt, state_typ)),
+ ("getParam", CFUNCTYPE(handle_typ, state_typ, asyInt)),
+ ("setReturnValue", CFUNCTYPE(None, state_typ, handle_typ)),
+ ("setErrorCallback", CFUNCTYPE(None, ErrorCallbackFUNC)),
+ ]
+
+policy = None
+baseState = None
+def initPolicyAndBaseState():
+ global policy, baseState
+ lib = CDLL("asymptote.so")
+
+ getPolicy = lib._asy_getPolicy
+ getPolicy.restype = POINTER(Policy)
+ policy = getPolicy()
+
+ getState = lib._asy_getState
+ getState.restype = state_typ
+ baseState = getState()
+
+initPolicyAndBaseState()
+
+def pyStringFromAsyString(st):
+ #TODO: Handle strings with null-terminators.
+ return str(st.buf)
+
+def pyStringFromHandle(h):
+ #TODO: Handle strings with null-terminators.
+ st = policy.contents.stringFromHandle(h)
+ checkForErrors()
+ return pyStringFromAsyString(st)
+
+def handleFromPyString(s):
+ st = string_typ(s, len(s))
+ h = policy.contents.handleFromString(st)
+ checkForErrors()
+ return h
+
+def ensureDatum(val):
+ return val if type(val) is Datum else Datum(val)
+
+# The error detection scheme.
+# policyError is set to a string when an error occurs.
+policyError = []
+def pyErrorCallback(s):
+ global policyError
+ policyError.append(pyStringFromAsyString(s))
+
+cErrorCallback = ErrorCallbackFUNC(pyErrorCallback)
+policy.contents.setErrorCallback(cErrorCallback)
+
+class AsyException(Exception):
+ def __init__(self, msg):
+ self.msg = msg
+ def __str__(self):
+ return self.msg
+
+def checkForErrors():
+ """Raises an exception if an error occured."""
+ global policyError
+ if policyError != []:
+ s = policyError[0]
+ if len(policyError) > 1:
+ s += ' (and other errors)'
+ policyError = []
+ raise AsyException(s)
+
+class Datum(object):
+
+ def _setHandle(self, handle):
+ object.__setattr__(self, 'handle', handle)
+
+ def __init__(self, val):
+ self._setHandle(0)
+
+ if val is None:
+ return
+
+ if type(val) is int:
+ self._setHandle(policy.contents.handleFromInt(val))
+ checkForErrors()
+ elif type(val) is bool:
+ self._setHandle(policy.contents.handleFromBool(1 if val else 0))
+ checkForErrors()
+ elif type(val) is float:
+ self._setHandle(policy.contents.handleFromDouble(val))
+ elif type(val) is str:
+ self._setHandle(handleFromPyString(val))
+ checkForErrors()
+ elif type(val) is tuple:
+ # Could do this more efficiently, and avoid a copyHandle
+ ret = state.globals()["operator tuple"](*val)
+ self._setHandle(policy.contents.copyHandle(ret.handle))
+ checkForErrors()
+ elif type(val) is Datum:
+ self._setHandle(policy.contents.copyHandle(val.handle))
+ checkForErrors()
+ else:
+ # TODO: check if val has a toDatum field
+ raise TypeError("cannot initialize Datum from '%s'" %
+ type(val).__name__)
+
+ def __repr__(self):
+ # TODO: Add type-checking to policy.
+ return '<Datum with handle %s>' % hex(self.handle)
+
+ def __int__(self):
+ l = policy.contents.IntFromHandle(self.handle)
+ checkForErrors()
+ return int(l)
+
+ def __nonzero__(self):
+ # This will throw an exception for anything but an underlying bool
+ # type. Perhaps we should be more pythonic.
+ l = policy.contents.boolFromHandle(self.handle)
+ checkForErrors()
+ assert l in [0,1]
+ return l == 1
+
+ def __float__(self):
+ x = policy.contents.doubleFromHandle(self.handle)
+ checkForErrors()
+ return float(x)
+
+ def __str__(self):
+ return pyStringFromHandle(self.handle)
+
+ def __getattr__(self, name):
+ field = policy.contents.getField(self.handle, name)
+ checkForErrors()
+ return DatumFromHandle(field)
+
+ def __getitem__(self, name):
+ assert type(name) == str
+ return self.__getattr__(name)
+ #TODO: raise an IndexError when appropriate.
+ #TODO: implement array indices
+
+ def __setattr__(self, name, val):
+ # TODO: Resolve setting versus declaring.
+ # One idea: d.x = f or d["x"] = f sets and d["int x()"] = f declares
+ # anew.
+ policy.contents.addField(self.handle,
+ name, ensureDatum(val).handle)
+ checkForErrors()
+
+ def __setitem__(self, name, val):
+ assert type(name) == str
+ self.__setattr__(name, val)
+ #TODO: raise an IndexError when appropriate.
+ #TODO: implement array indices
+
+ def __call__(self, *args, **namedArgs):
+ alist = policy.contents.newArguments()
+ checkForErrors()
+
+
+ for arg in args:
+ d = ensureDatum(arg)
+ policy.contents.addArgument(alist, "", d.handle, NORMAL_ARG)
+ checkForErrors()
+
+ for name,arg in namedArgs.items():
+ d = ensureDatum(arg)
+ policy.contents.addArgument(alist, name, d.handle, NORMAL_ARG)
+ checkForErrors()
+
+ ret = policy.contents.call(self.handle, alist)
+ checkForErrors()
+
+ policy.contents.releaseArguments(alist)
+ checkForErrors()
+
+ if ret != None:
+ return DatumFromHandle(ret)
+
+ def __add__(self, other):
+ return state.globals()["operator +"](self, other)
+ def __sub__(self, other):
+ return state.globals()["operator -"](self, other)
+ def __mul__(self, other):
+ return state.globals()["operator *"](self, other)
+ def __div__(self, other):
+ return state.globals()["operator /"](self, other)
+ def __truediv__(self, other):
+ return state.globals()["operator /"](self, other)
+ def __mod__(self, other):
+ return state.globals()["operator %"](self, other)
+ def __pow__(self, other):
+ return state.globals()["operator ^"](self, other)
+ def __and__(self, other):
+ return state.globals()["operator &"](self, other)
+ def __or__(self, other):
+ return state.globals()["operator |"](self, other)
+ def __neg__(self, other):
+ return state.globals()["operator -"](self)
+
+ def __lt__(self, other):
+ return state.globals()["operator <"](self, other)
+ def __le__(self, other):
+ return state.globals()["operator <="](self, other)
+ def __eq__(self, other):
+ return state.globals()["operator =="](self, other)
+ def __ne__(self, other):
+ return state.globals()["operator !="](self, other)
+ def __gt__(self, other):
+ return state.globals()["operator >"](self, other)
+ def __ge__(self, other):
+ return state.globals()["operator >="](self, other)
+
+def DatumFromHandle(handle):
+ """Initializes a Datum from a given low-level handle. Does not invoke
+ copyHandle."""
+ d = Datum(None)
+ d._setHandle(handle)
+ return d
+
+class State(object):
+ def __init__(self, base):
+ self.base = base
+
+ def globals(self):
+ handle = policy.contents.globals(self.base)
+ checkForErrors()
+ return DatumFromHandle(handle)
+
+ def params(self):
+ p = []
+
+ numParams = policy.contents.numParams(self.base)
+ checkForErrors()
+
+ for i in range(numParams):
+ h = policy.contents.getParam(self.base, i)
+ checkForErrors()
+ p.append(DatumFromHandle(h))
+
+ assert len(p) == numParams
+ return p
+
+ def setReturnValue(self, val):
+ policy.contents.setReturnValue(self.base, ensureDatum(val).handle)
+ checkForErrors()
+
+# Keep a link to all of the callbacks created, so they aren't garbage
+# collected. TODO: See if this is neccessary.
+storedCallbacks = []
+
+def DatumFromCallable(f):
+ def wrapped(s, d):
+ state = State(s)
+ params = state.params()
+ r = f(*params)
+ if r != None:
+ state.setReturnValue(r)
+
+ cf = function_typ(wrapped)
+ storedCallbacks.append(cf)
+
+ h = policy.contents.handleFromFunction(f.__name__, cf, None)
+ checkForErrors()
+
+ return DatumFromHandle(h)
+
+print "version", policy.contents.version
+
+state = State(baseState)
+
+# An example
+def runExample():
+ g = state.globals()
+
+ g.eval("path p = (0,0) -- (100,100) -- (200,0)", embedded=True)
+ g.draw(g.p)
+ g.shipout("frompython")
+
+ g.draw(g.circle(100), g.red)
+