Edinburgh Speech Tools  2.4-release
 All Classes Functions Variables Typedefs Enumerations Enumerator Friends Pages
slib_python.cc
1 /*************************************************************************/
2 /* */
3 /* Language Technologies Institute */
4 /* Carnegie Mellon University */
5 /* Copyright (c) 2013 */
6 /* All Rights Reserved. */
7 /* */
8 /* Permission is hereby granted, free of charge, to use and distribute */
9 /* this software and its documentation without restriction, including */
10 /* without limitation the rights to use, copy, modify, merge, publish, */
11 /* distribute, sublicense, and/or sell copies of this work, and to */
12 /* permit persons to whom this work is furnished to do so, subject to */
13 /* the following conditions: */
14 /* 1. The code must retain the above copyright notice, this list of */
15 /* conditions and the following disclaimer. */
16 /* 2. Any modifications must be clearly marked as such. */
17 /* 3. Original authors' names are not deleted. */
18 /* 4. The authors' names are not used to endorse or promote products */
19 /* derived from this software without specific prior written */
20 /* permission. */
21 /* */
22 /* CARNEGIE MELLON UNIVERSITY AND THE CONTRIBUTORS TO THIS WORK */
23 /* DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING */
24 /* ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT */
25 /* SHALL CARNEGIE MELLON UNIVERSITY NOR THE CONTRIBUTORS BE LIABLE */
26 /* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES */
27 /* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN */
28 /* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, */
29 /* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF */
30 /* THIS SOFTWARE. */
31 /* */
32 /*************************************************************************/
33 /* Author: Alok Parlikar (aup@cs.cmu.edu) */
34 /* Date: April 2013 */
35 /*************************************************************************/
36 /*
37  Support to call Python Functions from SIOD
38 */
39 
40 #ifdef EST_SIOD_ENABLE_PYTHON
41 #include "slib_python.h"
42 #include "siod.h"
43 
44 #include "Python.h"
45 
46 // The following are the types for Python objects in LISP, they are
47 // set when the objects are registered. These are not required
48 // outside this file, hence static.
49 static int tc_pyobject = -1;
50 
51 // Check if a LISP object stores reference to PyObject
52 int pyobject_p(LISP x) {
53  if (TYPEP(x, tc_pyobject))
54  return TRUE;
55  return FALSE;
56 }
57 
58 LISP pyobjectp(LISP x) {
59  if (pyobject_p(x))
60  return truth;
61  return NIL;
62 }
63 
64 // This always returns a new reference to PyObject
65 // If it already stores a reference to a PyObject
66 // This increments its count.
67 static PyObject *get_c_pyobject(LISP x) {
68  if (TYPEP(x, tc_pyobject)) {
69  PyObject *p = reinterpret_cast<PyObject *>(USERVAL(x));
70  Py_XINCREF(p);
71  return p;
72  }
73 
74  if (NULLP(x))
75  Py_RETURN_NONE;
76 
77  if (numberp(x))
78  return PyFloat_FromDouble(get_c_double(x));
79 
80  if (TYPEP(x, tc_string))
81  return PyUnicode_FromString(get_c_string(x));
82 
83  if (consp(x)) {
84  int num_items = siod_llength(x);
85  PyObject *pList = PyList_New(num_items);
86  LISP ptr;
87  int i;
88  for (ptr = x, i = 0;
89  NNULLP(ptr);
90  ptr = cdr(ptr), i++) {
91  PyList_SetItem(pList, i, get_c_pyobject(car(ptr)));
92  }
93  return pList;
94  }
95 
96  err("wrong type of argument to get_c_pyobject", x);
97  return NULL; // err doesn't return but compilers don't know that
98 }
99 
100 static LISP siod_make_pyobject(PyObject *pyobj) {
101  if (pyobj == NULL || pyobj == Py_None)
102  return NIL;
103 
104  if (PyLong_Check(pyobj) || PyFloat_Check(pyobj))
105  return flocons(PyFloat_AsDouble(pyobj));
106 
107  if (PyBool_Check(pyobj))
108  return PyObject_IsTrue(pyobj)? truth : NIL;
109 
110  if (PyUnicode_Check(pyobj)) {
111  PyObject *pBytes;
112  LISP ret;
113  pBytes = PyUnicode_AsUTF8String(pyobj);
114  if (pBytes == NULL)
115  return NIL;
116 
117  ret = strcons(PyBytes_Size(pBytes),
118  PyBytes_AsString(pBytes));
119  Py_DECREF(pBytes);
120  return ret;
121  }
122 
123  if (PyTuple_Check(pyobj) || PyList_Check(pyobj)) {
124  LISP ret = NIL;
125  int size = PySequence_Size(pyobj);
126  if (size <= 0)
127  return NIL;
128  for (int i = size - 1; i >= 0; i--)
129  ret = cons(siod_make_pyobject(PySequence_GetItem(pyobj, i)),
130  ret);
131  return ret;
132  }
133 
134  // Bytes, Dict, or Other Objects are stored as Python Objects.
135  Py_XINCREF(pyobj);
136  return siod_make_typed_cell(tc_pyobject, pyobj);
137 }
138 
139 static void pyobject_free(LISP x) {
140  // Decrement refcount if x stores a PyObject;
141  if (TYPEP(x, tc_pyobject)) {
142  PyObject *p = reinterpret_cast<PyObject *>(USERVAL(x));
143  Py_XDECREF(p);
144  }
145 }
146 
147 static void pyobject_prin1(LISP v, FILE *fp) {
148  if (TYPEP(v, tc_pyobject)) {
149  PyObject *p = reinterpret_cast<PyObject *>(USERVAL(v));
150  PyObject_Print(p, fp, Py_PRINT_RAW);
151  }
152 }
153 
154 static void pyobject_print_string(LISP v, char *tkbuffer) {
155  if (TYPEP(v, tc_pyobject)) {
156  PyObject *p = reinterpret_cast<PyObject *>(USERVAL(v));
157  PyObject *pRepr = PyObject_Str(p);
158  if (pRepr == NULL) {
159  snprintf(tkbuffer, 1024, "#<UnknownPythonObject %p>", p); // NOLINT
160  return;
161  }
162 
163  LISP repr = siod_make_pyobject(pRepr);
164  snprintf(tkbuffer, 1024, "PyObject %s", get_c_string(repr)); // NOLINT
165  Py_DECREF(pRepr);
166  return;
167  }
168  snprintf(tkbuffer, 1024, "#<UnknownObject>"); // NOLINT
169 }
170 
171 static LISP python_syspath_append(LISP path) {
172  if (!TYPEP(path, tc_string)) {
173  err("Invalid Path", path);
174  return NIL;
175  }
176 
177  PyObject* sysPath = PySys_GetObject("path");
178  int ret = PyList_Append(sysPath, PyUnicode_FromString(get_c_string(path)));
179  if (ret == 0) {
180  // Success
181  return truth;
182  }
183  return NIL;
184 }
185 
186 static LISP python_import(LISP modulename) {
187  PyObject *pName, *pModule;
188  LISP ret;
189 
190  if (!TYPEP(modulename, tc_string)) {
191  err("Invalid module name (expecting string)", modulename);
192  return NIL;
193  }
194 
195  pName = PyUnicode_FromString(get_c_string(modulename));
196  pModule = PyImport_Import(pName);
197  Py_XDECREF(pName);
198 
199  if (pModule == NULL) {
200  if (PyErr_Occurred()) {
201  PyErr_Print();
202  PyErr_Clear();
203  }
204  err("Failed to load module", modulename);
205  return NIL;
206  }
207  ret = siod_make_pyobject(pModule);
208  Py_DECREF(pModule);
209  return ret;
210 }
211 
212 static LISP python_attr_get(LISP lpobj, LISP attrname) {
213  if (!TYPEP(lpobj, tc_pyobject)) {
214  err("Invalid Object for python_attr_get", lpobj);
215  return NIL;
216  }
217 
218  PyObject *p = reinterpret_cast<PyObject *>(USERVAL(lpobj));
219 
220  if (!TYPEP(attrname, tc_string)) {
221  err("Invalid Attribute Name (expecting string)", attrname);
222  return NIL;
223  }
224 
225  PyObject *pAttr = PyObject_GetAttrString(p, get_c_string(attrname));
226  if (pAttr == NULL) {
227  if (PyErr_Occurred()) {
228  PyErr_Print();
229  PyErr_Clear();
230  }
231  }
232  LISP ret = siod_make_pyobject(pAttr);
233  Py_XDECREF(pAttr);
234  return ret;
235 }
236 
237 static LISP python_attr_set(LISP lpobj, LISP attrname, LISP value) {
238  if (!TYPEP(lpobj, tc_pyobject)) {
239  err("Invalid Object for python_attr_set", lpobj);
240  return NIL;
241  }
242 
243  PyObject *p = reinterpret_cast<PyObject *>(USERVAL(lpobj));
244 
245  if (!TYPEP(attrname, tc_string)) {
246  err("Invalid Attribute Name (expecting string)", attrname);
247  return NIL;
248  }
249 
250  PyObject *pValue = get_c_pyobject(value);
251  if (pValue == NULL) {
252  if (PyErr_Occurred()) {
253  PyErr_Print();
254  PyErr_Clear();
255  }
256  err("Invalid Value for python_attr_set", value);
257  return NIL;
258  }
259 
260  int result = PyObject_SetAttrString(p, get_c_string(attrname), pValue);
261  Py_DECREF(pValue);
262 
263  if (result == -1) {
264  err("Failed to set value", value);
265  return NIL;
266  }
267  return truth;
268 }
269 
270 static LISP python_call_object(LISP fpobj, LISP args) {
271  if (!TYPEP(fpobj, tc_pyobject)) {
272  err("Invalid Object for python_callfunction", fpobj);
273  return NIL;
274  }
275 
276  PyObject *p = reinterpret_cast<PyObject *>(USERVAL(fpobj));
277 
278  if (p == NULL || !PyCallable_Check(p)) {
279  err("Not a callable object", fpobj);
280  return NIL;
281  }
282 
283  PyObject *pArgs;
284  if (args == NIL) {
285  pArgs = NULL;
286  } else {
287  if (!consp(args)) {
288  err("Invalid argument (expecting list)", args);
289  return NIL;
290  }
291 
292  pArgs = get_c_pyobject(args);
293  if (pArgs == NULL) {
294  err("Could not convert arguments", args);
295  return NIL;
296  }
297  }
298 
299  PyObject *pArgsTuple = NULL;
300  if (pArgs != NULL) {
301  pArgsTuple = PyList_AsTuple(pArgs);
302  Py_XDECREF(pArgs);
303  }
304 
305  PyObject *pValue = PyObject_CallObject(p, pArgsTuple);
306  Py_XDECREF(pArgsTuple);
307 
308  if (pValue == NULL) {
309  if (PyErr_Occurred()) {
310  PyErr_Print();
311  PyErr_Clear();
312  }
313  err("Could not call object", fpobj);
314  return NIL;
315  }
316  LISP ret = siod_make_pyobject(pValue);
317  Py_DECREF(pValue);
318  return ret;
319 }
320 
321 static LISP python_call_method(LISP lpobj, LISP methodname, LISP args) {
322  LISP callable = python_attr_get(lpobj, methodname);
323  return python_call_object(callable, args);
324 }
325 
326 void init_subrs_python(void) {
327  Py_Initialize();
328 
329  long kind; // NOLINT
330 
331  tc_pyobject = siod_register_user_type("PyObject");
332  set_gc_hooks(tc_pyobject, 0, NULL, NULL, NULL, pyobject_free, NULL, &kind);
333  set_print_hooks(tc_pyobject, pyobject_prin1, pyobject_print_string);
334 
335  // Add CWD to PythonPath
336  PyObject* sysPath = PySys_GetObject("path");
337  PyList_Append(sysPath, PyUnicode_FromString("."));
338 
339  init_subr_1("pyobjectp", pyobjectp,
340  "(pyobjectp obj)\n"
341  "Checks if obj is a Python Object");
342 
343 
344  init_subr_1("python_syspath_append", python_syspath_append,
345  "(python_addpath path)\n"
346  "Appends path (string) to sys.path");
347 
348  init_subr_1("python_import", python_import,
349  "(python_import modulename)\n"
350  "Imports specified module and returns it");
351 
352  init_subr_2("python_attr_get", python_attr_get,
353  "(python_attr_get object attrname)\n"
354  "Returns the specified attribute of the given PyObject");
355 
356  init_subr_3("python_attr_set", python_attr_set,
357  "(python_attr_set object attrname value)\n"
358  "Set value of the given attribute of the given PyObject");
359 
360  init_subr_3("python_call_method", python_call_method,
361  "(python_call_method object methodname args)\n"
362  "Calls object.methodname(args)\n"
363  "object is a PyObject, methodname is string. args is a list.");
364 
365  init_subr_2("python_call_object", python_call_object,
366  "(python_call_object object args)\n"
367  "Calls object(args)\n"
368  "object is a callable PyObject, args is a list");
369 }
370 
371 void python_tidy_up(void) {
372  Py_Finalize();
373 }
374 #else // No python support
375 
376 /* So there is a symbol in here even if there is no python support */
377 static int est_no_python_support = 1;
378 
379 #endif // EST_SIOD_ENABLE_PYTHON