/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* $Rev$ $Date$ */ #ifndef tuscany_python_eval_hpp #define tuscany_python_eval_hpp /** * Python script evaluation logic. */ #if PYTHON_VERSION == 27 #include #else #include #endif #include "list.hpp" #include "value.hpp" namespace tuscany { namespace python { /** * Represent a Python runtime. */ class PythonRuntime { public: PythonRuntime() { debug("python::pythonruntime"); if (Py_IsInitialized()) return; Py_InitializeEx(0); const char* arg0 = ""; PySys_SetArgv(0, const_cast(&arg0)); } ~PythonRuntime() { debug("python::~pythonruntime"); } }; /** * Return the last python error. */ const string lastError() { if(PyErr_Occurred()) { PyObject* type; PyObject* val; PyObject* trace; PyErr_Fetch(&type, &val, &trace); if (type != NULL && val != NULL) { PyObject* stype = PyObject_Str(type); PyObject* sval = PyObject_Str(val); string msg = string() + PyString_AsString(stype) + " : " + PyString_AsString(sval); Py_DECREF(stype); Py_DECREF(sval); PyErr_Restore(type, val, trace); PyErr_Print(); return msg; } PyErr_Restore(type, val, trace); PyErr_Print(); return "Unknown Python error"; } return ""; } /** * Declare conversion functions. */ PyObject* valueToPyObject(const value& v); const value pyObjectToValue(PyObject *o); PyObject* valuesToPyTuple(const list& v); const list pyTupleToValues(PyObject* o); /** * Callable python type used to represent a lambda expression. */ typedef struct { PyObject_HEAD lambda&)> func; } pyLambda; PyObject *mkPyLambda(const lambda&)>& l); void pyLambda_dealloc(PyObject* self) { debug(self, "python::pylambda_dealloc"); PyObject_Del(self); } const string pyRepr(PyObject* o) { PyObject* r = PyObject_Repr(o); const string s = PyString_AsString(r); Py_DECREF(r); return s; } PyObject* pyLambda_call(PyObject* self, PyObject* args, unused PyObject* kwds) { debug("python::call"); const pyLambda* pyl = (pyLambda*)self; const value result = pyl->func(pyTupleToValues(args)); debug(result, "python::call::result"); PyObject *pyr = valueToPyObject(result); return pyr; } struct pyProxy { const value name; const lambda&)> func; pyProxy(const value& name, const lambda&)>& func) : name(name), func(func) { } const value operator()(const list& args) const { debug(name, "python::proxy::name"); const value result = func(cons(name, args)); debug(result, "python::proxy::result"); return result; } }; PyObject* pyLambda_getattr(PyObject *self, PyObject *attrname) { const string name = PyString_AsString(attrname); if (substr(name, 0, 1) == "_") return PyObject_GenericGetAttr(self, attrname); if (name == "eval") { Py_INCREF(self); return self; } const pyLambda* pyl = (pyLambda*)self; debug(name, "python::getattr::name"); PyObject* pyr = mkPyLambda(pyProxy(name, pyl->func)); return pyr; } PyTypeObject pyLambda_type = { PyObject_HEAD_INIT(0) 0, "lambda", sizeof(pyLambda), 0, (destructor)pyLambda_dealloc, 0, 0, 0, 0, 0, 0, 0, 0, 0, (ternaryfunc)pyLambda_call, 0, (binaryfunc)pyLambda_getattr, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /** * Create a new python object representing a lambda expression. */ PyObject *mkPyLambda(const lambda&)>& l) { pyLambda* pyl = NULL; pyl = PyObject_New(pyLambda, &pyLambda_type); if (pyl != NULL) pyl->func = l; debug(pyl, "python::mkpylambda"); return (PyObject *)pyl; } /** * Convert a list of values to a python list. */ PyObject* valuesToPyListHelper(PyObject* l, const list& v) { if (isNil(v)) return l; PyObject* pyv = valueToPyObject(car(v)); PyList_Append(l, pyv); Py_DECREF(pyv); return valuesToPyListHelper(l, cdr(v)); } PyObject* valuesToPyTuple(const list& v) { PyObject* pyl = valuesToPyListHelper(PyList_New(0), v); PyObject* pyt = PyList_AsTuple(pyl); Py_DECREF(pyl); return pyt; } /** * Convert a value to a python object. */ PyObject* valueToPyObject(const value& v) { switch (type(v)) { case value::List: return valuesToPyTuple(v); case value::Lambda: return mkPyLambda(v); case value::Symbol: return PyString_FromString(c_str(string("'") + v)); case value::String: { const string s = (string)v; return PyString_FromStringAndSize(c_str(s), length(s)); } case value::Number: return PyFloat_FromDouble((double)v); case value::Bool: return (bool)v? Py_True : Py_False; default: return Py_None; } } /** * Convert a python tuple to a list of values. */ const list pyTupleToValuesHelper(PyObject* o, const size_t i, const size_t size) { if (i == size) return list(); return cons(pyObjectToValue(PyTuple_GetItem(o, i)), pyTupleToValuesHelper(o, i + 1, size)); } const list pyTupleToValues(PyObject* o) { return pyTupleToValuesHelper(o, 0, PyTuple_Size(o)); } /** * Lambda function used to represent a python callable object. */ struct pyCallable { PyObject* func; pyCallable(PyObject* func) : func(func) { Py_INCREF(func); } ~pyCallable() { Py_DECREF(func); } const value operator()(const list& args) const { PyObject* pyargs = valuesToPyTuple(args); PyObject* result = PyObject_CallObject(func, pyargs); const value v = pyObjectToValue(result); Py_DECREF(pyargs); Py_DECREF(result); return v; } }; /** * Convert a python object to a value. */ const value pyObjectToValue(PyObject *o) { if (PyString_Check(o)) { char* s = NULL; Py_ssize_t l = 0; PyString_AsStringAndSize(o, &s, &l); if (l != 0 && *s == '\'') return value(s + 1); return value(string(s, l)); } if (PyBool_Check(o)) return value(o == Py_True); if (PyInt_Check(o)) return value((double)PyInt_AsLong(o)); if (PyLong_Check(o)) return value((double)PyLong_AsLong(o)); if (PyFloat_Check(o)) return value((double)PyFloat_AsDouble(o)); if (PyTuple_Check(o)) return pyTupleToValues(o); if (PyCallable_Check(o)) return lambda&)>(pyCallable(o)); return value(); } /** * Convert a python script path to a module name. */ const string moduleName(const string& path) { return join(".", tokenize("/", substr(path, 0, length(path) -3))); } /** * Evaluate an expression against a script provided as a python object. */ const failable evalScript(const value& expr, PyObject* script) { // Get the requested function PyObject* func = PyObject_GetAttrString(script, c_str(car(expr))); if (func == NULL) { // The start, stop, and restart functions are optional const value fn = car(expr); if (fn == "start" || fn == "stop") { PyErr_Clear(); return value(lambda&)>()); } return mkfailure(string("Couldn't find function: ") + car(expr) + " : " + lastError()); } if (!PyCallable_Check(func)) { Py_DECREF(func); return mkfailure(string("Couldn't find callable function: ") + car(expr)); } // Convert args to python objects PyObject* args = valuesToPyTuple(cdr(expr)); // Call the function PyObject* result = PyObject_CallObject(func, args); Py_DECREF(func); Py_DECREF(args); if (result == NULL) return mkfailure(string("Function call failed: ") + car(expr) + " : " + lastError()); // Convert python result to a value const value v = pyObjectToValue(result); Py_DECREF(result); return v; } /** * Read a python script from an input stream. */ const failable readScript(const string& name, const string& path, istream& is) { const list ls = streamList(is); ostringstream os; write(ls, os); PyObject* code = Py_CompileStringFlags(c_str(str(os)), c_str(path), Py_file_input, NULL); if (code == NULL) return mkfailure(string("Couldn't compile script: ") + path + " : " + lastError()); PyObject* mod = PyImport_ExecCodeModuleEx(const_cast(c_str(name)), code, const_cast(c_str(path))); if (mod == NULL) return mkfailure(string("Couldn't import module: ") + path + " : " + lastError()); return mod; } /** * Evaluate an expression against a script provided as an input stream. */ const failable evalScript(const value& expr, istream& is) { failable script = readScript("script", "script.py", is); if (!hasContent(script)) return mkfailure(reason(script)); return evalScript(expr, content(script)); } } } #endif /* tuscany_python_eval_hpp */