speech-tools/siod/slib_python.cc
2015-09-19 10:52:26 +02:00

380 lines
11 KiB
C++

/*************************************************************************/
/* */
/* Language Technologies Institute */
/* Carnegie Mellon University */
/* Copyright (c) 2013 */
/* All Rights Reserved. */
/* */
/* Permission is hereby granted, free of charge, to use and distribute */
/* this software and its documentation without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of this work, and to */
/* permit persons to whom this work is furnished to do so, subject to */
/* the following conditions: */
/* 1. The code must retain the above copyright notice, this list of */
/* conditions and the following disclaimer. */
/* 2. Any modifications must be clearly marked as such. */
/* 3. Original authors' names are not deleted. */
/* 4. The authors' names are not used to endorse or promote products */
/* derived from this software without specific prior written */
/* permission. */
/* */
/* CARNEGIE MELLON UNIVERSITY AND THE CONTRIBUTORS TO THIS WORK */
/* DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING */
/* ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT */
/* SHALL CARNEGIE MELLON UNIVERSITY NOR THE CONTRIBUTORS BE LIABLE */
/* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES */
/* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN */
/* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, */
/* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF */
/* THIS SOFTWARE. */
/* */
/*************************************************************************/
/* Author: Alok Parlikar (aup@cs.cmu.edu) */
/* Date: April 2013 */
/*************************************************************************/
/*
Support to call Python Functions from SIOD
*/
#ifdef EST_SIOD_ENABLE_PYTHON
#include "slib_python.h"
#include "siod.h"
#include "Python.h"
// The following are the types for Python objects in LISP, they are
// set when the objects are registered. These are not required
// outside this file, hence static.
static int tc_pyobject = -1;
// Check if a LISP object stores reference to PyObject
int pyobject_p(LISP x) {
if (TYPEP(x, tc_pyobject))
return TRUE;
return FALSE;
}
LISP pyobjectp(LISP x) {
if (pyobject_p(x))
return truth;
return NIL;
}
// This always returns a new reference to PyObject
// If it already stores a reference to a PyObject
// This increments its count.
static PyObject *get_c_pyobject(LISP x) {
if (TYPEP(x, tc_pyobject)) {
PyObject *p = reinterpret_cast<PyObject *>(USERVAL(x));
Py_XINCREF(p);
return p;
}
if (NULLP(x))
Py_RETURN_NONE;
if (numberp(x))
return PyFloat_FromDouble(get_c_double(x));
if (TYPEP(x, tc_string))
return PyUnicode_FromString(get_c_string(x));
if (consp(x)) {
int num_items = siod_llength(x);
PyObject *pList = PyList_New(num_items);
LISP ptr;
int i;
for (ptr = x, i = 0;
NNULLP(ptr);
ptr = cdr(ptr), i++) {
PyList_SetItem(pList, i, get_c_pyobject(car(ptr)));
}
return pList;
}
err("wrong type of argument to get_c_pyobject", x);
return NULL; // err doesn't return but compilers don't know that
}
static LISP siod_make_pyobject(PyObject *pyobj) {
if (pyobj == NULL || pyobj == Py_None)
return NIL;
if (PyLong_Check(pyobj) || PyFloat_Check(pyobj))
return flocons(PyFloat_AsDouble(pyobj));
if (PyBool_Check(pyobj))
return PyObject_IsTrue(pyobj)? truth : NIL;
if (PyUnicode_Check(pyobj)) {
PyObject *pBytes;
LISP ret;
pBytes = PyUnicode_AsUTF8String(pyobj);
if (pBytes == NULL)
return NIL;
ret = strcons(PyBytes_Size(pBytes),
PyBytes_AsString(pBytes));
Py_DECREF(pBytes);
return ret;
}
if (PyTuple_Check(pyobj) || PyList_Check(pyobj)) {
LISP ret = NIL;
int size = PySequence_Size(pyobj);
if (size <= 0)
return NIL;
for (int i = size - 1; i >= 0; i--)
ret = cons(siod_make_pyobject(PySequence_GetItem(pyobj, i)),
ret);
return ret;
}
// Bytes, Dict, or Other Objects are stored as Python Objects.
Py_XINCREF(pyobj);
return siod_make_typed_cell(tc_pyobject, pyobj);
}
static void pyobject_free(LISP x) {
// Decrement refcount if x stores a PyObject;
if (TYPEP(x, tc_pyobject)) {
PyObject *p = reinterpret_cast<PyObject *>(USERVAL(x));
Py_XDECREF(p);
}
}
static void pyobject_prin1(LISP v, FILE *fp) {
if (TYPEP(v, tc_pyobject)) {
PyObject *p = reinterpret_cast<PyObject *>(USERVAL(v));
PyObject_Print(p, fp, Py_PRINT_RAW);
}
}
static void pyobject_print_string(LISP v, char *tkbuffer) {
if (TYPEP(v, tc_pyobject)) {
PyObject *p = reinterpret_cast<PyObject *>(USERVAL(v));
PyObject *pRepr = PyObject_Str(p);
if (pRepr == NULL) {
snprintf(tkbuffer, 1024, "#<UnknownPythonObject %p>", p); // NOLINT
return;
}
LISP repr = siod_make_pyobject(pRepr);
snprintf(tkbuffer, 1024, "PyObject %s", get_c_string(repr)); // NOLINT
Py_DECREF(pRepr);
return;
}
snprintf(tkbuffer, 1024, "#<UnknownObject>"); // NOLINT
}
static LISP python_syspath_append(LISP path) {
if (!TYPEP(path, tc_string)) {
err("Invalid Path", path);
return NIL;
}
PyObject* sysPath = PySys_GetObject("path");
int ret = PyList_Append(sysPath, PyUnicode_FromString(get_c_string(path)));
if (ret == 0) {
// Success
return truth;
}
return NIL;
}
static LISP python_import(LISP modulename) {
PyObject *pName, *pModule;
LISP ret;
if (!TYPEP(modulename, tc_string)) {
err("Invalid module name (expecting string)", modulename);
return NIL;
}
pName = PyUnicode_FromString(get_c_string(modulename));
pModule = PyImport_Import(pName);
Py_XDECREF(pName);
if (pModule == NULL) {
if (PyErr_Occurred()) {
PyErr_Print();
PyErr_Clear();
}
err("Failed to load module", modulename);
return NIL;
}
ret = siod_make_pyobject(pModule);
Py_DECREF(pModule);
return ret;
}
static LISP python_attr_get(LISP lpobj, LISP attrname) {
if (!TYPEP(lpobj, tc_pyobject)) {
err("Invalid Object for python_attr_get", lpobj);
return NIL;
}
PyObject *p = reinterpret_cast<PyObject *>(USERVAL(lpobj));
if (!TYPEP(attrname, tc_string)) {
err("Invalid Attribute Name (expecting string)", attrname);
return NIL;
}
PyObject *pAttr = PyObject_GetAttrString(p, get_c_string(attrname));
if (pAttr == NULL) {
if (PyErr_Occurred()) {
PyErr_Print();
PyErr_Clear();
}
}
LISP ret = siod_make_pyobject(pAttr);
Py_XDECREF(pAttr);
return ret;
}
static LISP python_attr_set(LISP lpobj, LISP attrname, LISP value) {
if (!TYPEP(lpobj, tc_pyobject)) {
err("Invalid Object for python_attr_set", lpobj);
return NIL;
}
PyObject *p = reinterpret_cast<PyObject *>(USERVAL(lpobj));
if (!TYPEP(attrname, tc_string)) {
err("Invalid Attribute Name (expecting string)", attrname);
return NIL;
}
PyObject *pValue = get_c_pyobject(value);
if (pValue == NULL) {
if (PyErr_Occurred()) {
PyErr_Print();
PyErr_Clear();
}
err("Invalid Value for python_attr_set", value);
return NIL;
}
int result = PyObject_SetAttrString(p, get_c_string(attrname), pValue);
Py_DECREF(pValue);
if (result == -1) {
err("Failed to set value", value);
return NIL;
}
return truth;
}
static LISP python_call_object(LISP fpobj, LISP args) {
if (!TYPEP(fpobj, tc_pyobject)) {
err("Invalid Object for python_callfunction", fpobj);
return NIL;
}
PyObject *p = reinterpret_cast<PyObject *>(USERVAL(fpobj));
if (p == NULL || !PyCallable_Check(p)) {
err("Not a callable object", fpobj);
return NIL;
}
PyObject *pArgs;
if (args == NIL) {
pArgs = NULL;
} else {
if (!consp(args)) {
err("Invalid argument (expecting list)", args);
return NIL;
}
pArgs = get_c_pyobject(args);
if (pArgs == NULL) {
err("Could not convert arguments", args);
return NIL;
}
}
PyObject *pArgsTuple = NULL;
if (pArgs != NULL) {
pArgsTuple = PyList_AsTuple(pArgs);
Py_XDECREF(pArgs);
}
PyObject *pValue = PyObject_CallObject(p, pArgsTuple);
Py_XDECREF(pArgsTuple);
if (pValue == NULL) {
if (PyErr_Occurred()) {
PyErr_Print();
PyErr_Clear();
}
err("Could not call object", fpobj);
return NIL;
}
LISP ret = siod_make_pyobject(pValue);
Py_DECREF(pValue);
return ret;
}
static LISP python_call_method(LISP lpobj, LISP methodname, LISP args) {
LISP callable = python_attr_get(lpobj, methodname);
return python_call_object(callable, args);
}
void init_subrs_python(void) {
Py_Initialize();
long kind; // NOLINT
tc_pyobject = siod_register_user_type("PyObject");
set_gc_hooks(tc_pyobject, 0, NULL, NULL, NULL, pyobject_free, NULL, &kind);
set_print_hooks(tc_pyobject, pyobject_prin1, pyobject_print_string);
// Add CWD to PythonPath
PyObject* sysPath = PySys_GetObject("path");
PyList_Append(sysPath, PyUnicode_FromString("."));
init_subr_1("pyobjectp", pyobjectp,
"(pyobjectp obj)\n"
"Checks if obj is a Python Object");
init_subr_1("python_syspath_append", python_syspath_append,
"(python_addpath path)\n"
"Appends path (string) to sys.path");
init_subr_1("python_import", python_import,
"(python_import modulename)\n"
"Imports specified module and returns it");
init_subr_2("python_attr_get", python_attr_get,
"(python_attr_get object attrname)\n"
"Returns the specified attribute of the given PyObject");
init_subr_3("python_attr_set", python_attr_set,
"(python_attr_set object attrname value)\n"
"Set value of the given attribute of the given PyObject");
init_subr_3("python_call_method", python_call_method,
"(python_call_method object methodname args)\n"
"Calls object.methodname(args)\n"
"object is a PyObject, methodname is string. args is a list.");
init_subr_2("python_call_object", python_call_object,
"(python_call_object object args)\n"
"Calls object(args)\n"
"object is a callable PyObject, args is a list");
}
void python_tidy_up(void) {
Py_Finalize();
}
#else // No python support
/* So there is a symbol in here even if there is no python support */
static int est_no_python_support = 1;
#endif // EST_SIOD_ENABLE_PYTHON