python_embed.ecl 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. /* Example of calling Python from ECL code via embedded C++
  2. *
  3. * This example evalues a python expression within an ECL transform
  4. * Because the python will be compiled every time the expression is evaluated, it
  5. * is likely to be less efficient than calling external python code as in the example
  6. * python_from_ecl
  7. *
  8. */
  9. #option('compileOptions', '-I/usr/include/python2.7/')
  10. #option('linkOptions', '-lpython2.7')
  11. // Embedded C++ that makes a call to a Python function
  12. string pythonCat(varstring a, varstring b) := BEGINC++
  13. // This section of the code should probably move to a plugin, or somesuch
  14. #include <Python.h>
  15. #include <assert.h>
  16. #include <pthread.h>
  17. static PyObject *pFunc_cat;
  18. class PythonInitializer
  19. {
  20. PyObject *pModule;
  21. PyThreadState *tstate;
  22. bool pythonInitialized;
  23. public:
  24. PythonInitializer()
  25. {
  26. pModule = NULL;
  27. tstate = NULL;
  28. pythonInitialized = false;
  29. // Initialize the Python Interpreter
  30. Py_Initialize();
  31. PyEval_InitThreads();
  32. pythonInitialized = true;
  33. tstate = PyEval_SaveThread();
  34. }
  35. ~PythonInitializer()
  36. {
  37. PyEval_RestoreThread(tstate);
  38. // Clean up
  39. if (pModule)
  40. Py_DECREF(pModule);
  41. // Finish the Python Interpreter
  42. if (pythonInitialized)
  43. Py_Finalize();
  44. }
  45. };
  46. PythonInitializer __initializer;
  47. // Use class OwnedPyObject for any objects that are not 'borrowed references'
  48. // so that the appropriate Py_DECREF call is made when the OwnedPyObject goes
  49. // out of scope, even if the function returns prematurely (such as via an exception).
  50. // In particular, checkPythonError is a lot easier to call safely if this is used.
  51. class OwnedPyObject
  52. {
  53. PyObject *ptr;
  54. public:
  55. inline OwnedPyObject(PyObject *_ptr) : ptr(_ptr) {}
  56. inline ~OwnedPyObject() { if (ptr) Py_DECREF(ptr); }
  57. inline PyObject * get() const { return ptr; }
  58. inline PyObject * operator -> () const { return ptr; }
  59. inline operator PyObject *() const { return ptr; }
  60. };
  61. // call checkPythonError to throw an exception if Python error state is set
  62. static void checkPythonError()
  63. {
  64. PyObject* err = PyErr_Occurred();
  65. if (err)
  66. {
  67. OwnedPyObject errStr = PyObject_Str(err);
  68. PyErr_Clear();
  69. rtlFail(0, PyString_AsString(errStr));
  70. }
  71. }
  72. // The Python Global Interpreter Lock (GIL) won't know about C++-created threads, so we need to
  73. // call PyGILState_Ensure() and PyGILState_Release at the start and end of every function.
  74. // Wrapping them in a class like this ensures that the release always happens even if
  75. // the function exists prematurely
  76. class GILstateWrapper
  77. {
  78. PyGILState_STATE gstate;
  79. public:
  80. GILstateWrapper()
  81. {
  82. gstate = PyGILState_Ensure();
  83. }
  84. ~GILstateWrapper()
  85. {
  86. PyGILState_Release(gstate);
  87. }
  88. };
  89. //--------------------------------------------------------
  90. #body
  91. // extern void user1(size32_t & __lenResult,char * & __result, const char *a, const char *b) {
  92. {
  93. GILstateWrapper gstate;
  94. static OwnedPyObject pythonCode = Py_CompileString("a+b", "user1", Py_eval_input);
  95. OwnedPyObject locals = PyDict_New ();
  96. OwnedPyObject globals = PyDict_New ();
  97. PyDict_SetItemString(locals, "a", PyString_FromString(a));
  98. PyDict_SetItemString(locals, "b", PyString_FromString(b));
  99. OwnedPyObject pResult = PyEval_EvalCode((PyCodeObject *) pythonCode.get(), locals, globals);
  100. checkPythonError();
  101. __lenResult = PyString_Size(pResult);
  102. const char * chars = PyString_AsString(pResult);
  103. checkPythonError();
  104. __result = (char *)rtlMalloc(__lenResult);
  105. memcpy(__result, chars, __lenResult);
  106. }
  107. ENDC++;
  108. //--------------------------------------------------------
  109. // ECL code - an input dataset with 2 records, each containing 2 strings
  110. inrec := RECORD
  111. string f1;
  112. string f2;
  113. END;
  114. infile1 := DATASET([{'a', 'b'}, {'c', 'd'}], inrec);
  115. infile2 := DATASET([{'e', 'f'}, {'g', 'h'}], inrec);
  116. // Output record has just one string, filled in from the result of the python function
  117. outrec := RECORD
  118. string c;
  119. END;
  120. outrec t(inrec L) := TRANSFORM
  121. SELF.c := pythonCat(L.f1, L.f2) // Evaluates python expression to concatenate strings
  122. END;
  123. outfile := project(infile1, t(LEFT))+project(infile2, t(LEFT)); // threaded concat operation
  124. outfile;