pyembed.cpp 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #include "platform.h"
  14. #include "Python.h"
  15. #include "jexcept.hpp"
  16. #include "jthread.hpp"
  17. #include "hqlplugins.hpp"
  18. #include "deftype.hpp"
  19. #include "eclhelper.hpp"
  20. #include "eclrtl.hpp"
  21. #include "eclrtl_imp.hpp"
  22. #include "rtlds_imp.hpp"
  23. #include "rtlfield_imp.hpp"
  24. #include "nbcd.hpp"
  25. #ifdef _WIN32
  26. #define EXPORT __declspec(dllexport)
  27. #else
  28. #define EXPORT
  29. #endif
  30. static const char * compatibleVersions[] = {
  31. "Python2.7 Embed Helper 1.0.0",
  32. NULL };
  33. static const char *version = "Python2.7 Embed Helper 1.0.0";
  34. extern "C" EXPORT bool getECLPluginDefinition(ECLPluginDefinitionBlock *pb)
  35. {
  36. if (pb->size == sizeof(ECLPluginDefinitionBlockEx))
  37. {
  38. ECLPluginDefinitionBlockEx * pbx = (ECLPluginDefinitionBlockEx *) pb;
  39. pbx->compatibleVersions = compatibleVersions;
  40. }
  41. else if (pb->size != sizeof(ECLPluginDefinitionBlock))
  42. return false;
  43. pb->magicVersion = PLUGIN_VERSION;
  44. pb->version = version;
  45. pb->moduleName = "python";
  46. pb->ECL = NULL;
  47. pb->flags = PLUGIN_MULTIPLE_VERSIONS;
  48. pb->description = "Python2.7 Embed Helper";
  49. return true;
  50. }
  51. namespace pyembed {
  52. // Use class OwnedPyObject for any objects that are not 'borrowed references'
  53. // so that the appropriate Py_DECREF call is made when the OwnedPyObject goes
  54. // out of scope, even if the function returns prematurely (such as via an exception).
  55. // In particular, checkPythonError is a lot easier to call safely if this is used.
  56. class OwnedPyObject
  57. {
  58. PyObject *ptr;
  59. public:
  60. inline OwnedPyObject() : ptr(NULL) {}
  61. inline OwnedPyObject(PyObject *_ptr) : ptr(_ptr) {}
  62. inline ~OwnedPyObject() { if (ptr) Py_DECREF(ptr); }
  63. inline PyObject * get() const { return ptr; }
  64. inline PyObject * getClear() { PyObject *ret = ptr; ptr = NULL; return ret; }
  65. inline PyObject * operator -> () const { return ptr; }
  66. inline operator PyObject *() const { return ptr; }
  67. inline void clear() { if (ptr) Py_DECREF(ptr); ptr = NULL; }
  68. inline void setown(PyObject *_ptr) { clear(); ptr = _ptr; }
  69. inline void set(PyObject *_ptr) { clear(); ptr = _ptr; if (ptr) Py_INCREF(ptr);}
  70. inline PyObject *getLink() { if (ptr) Py_INCREF(ptr); return ptr;}
  71. inline PyObject **ref() { return &ptr; }
  72. };
  73. // call checkPythonError to throw an exception if Python error state is set
  74. static void checkPythonError()
  75. {
  76. PyObject* err = PyErr_Occurred();
  77. if (err)
  78. {
  79. OwnedPyObject pType, pValue, pTraceBack;
  80. PyErr_Fetch(pType.ref(), pValue.ref(), pTraceBack.ref());
  81. OwnedPyObject valStr = PyObject_Str(pValue);
  82. PyErr_Clear();
  83. VStringBuffer errMessage("pyembed: %s", PyString_AsString(valStr));
  84. rtlFail(0, errMessage.str());
  85. }
  86. }
  87. // The Python Global Interpreter Lock (GIL) won't know about C++-created threads, so we need to
  88. // call PyGILState_Ensure() and PyGILState_Release at the start and end of every function.
  89. // Wrapping them in a class like this ensures that the release always happens even if
  90. // the function exists prematurely
  91. class GILstateWrapper
  92. {
  93. PyGILState_STATE gstate;
  94. public:
  95. GILstateWrapper()
  96. {
  97. gstate = PyGILState_Ensure();
  98. }
  99. ~GILstateWrapper()
  100. {
  101. PyGILState_Release(gstate);
  102. }
  103. };
  104. // There is a singleton PythonThreadContext per thread. This allows us to
  105. // ensure that we can make repeated calls to a Python function efficiently.
  106. class PythonThreadContext
  107. {
  108. public:
  109. PyThreadState *threadState;
  110. public:
  111. PythonThreadContext()
  112. {
  113. threadState = PyEval_SaveThread();
  114. }
  115. ~PythonThreadContext()
  116. {
  117. PyEval_RestoreThread(threadState);
  118. script.clear();
  119. }
  120. inline PyObject * importFunction(size32_t lenChars, const char *utf)
  121. {
  122. size32_t bytes = rtlUtf8Size(lenChars, utf);
  123. StringBuffer text(bytes, utf);
  124. if (!prevtext || strcmp(text, prevtext) != 0)
  125. {
  126. prevtext.clear();
  127. // Name should be in the form module.function
  128. const char *funcname = strrchr(text, '.');
  129. if (!funcname)
  130. rtlFail(0, "pyembed: Expected module.function");
  131. StringBuffer modname(funcname-text, text);
  132. funcname++; // skip the '.'
  133. // If the modname is preceded by a path, add it to the python path before importing
  134. const char *pathsep = strrchr(modname, PATHSEPCHAR);
  135. if (pathsep)
  136. {
  137. StringBuffer path(pathsep-modname, modname);
  138. modname.remove(0, 1+pathsep-modname);
  139. PyObject *sys_path = PySys_GetObject((char *) "path");
  140. OwnedPyObject new_path = PyString_FromString(path);
  141. if (sys_path)
  142. {
  143. PyList_Insert(sys_path, 0, new_path);
  144. checkPythonError();
  145. }
  146. }
  147. module.setown(PyImport_ImportModule(modname));
  148. checkPythonError();
  149. PyObject *dict = PyModule_GetDict(module); // this is a borrowed reference and does not need to be released
  150. script.set(PyDict_GetItemString(dict, funcname));
  151. checkPythonError();
  152. if (!script || !PyCallable_Check(script))
  153. rtlFail(0, "pyembed: Object is not callable");
  154. prevtext.set(text);
  155. }
  156. return script.getLink();
  157. }
  158. inline PyObject *compileEmbeddedScript(size32_t lenChars, const char *utf)
  159. {
  160. size32_t bytes = rtlUtf8Size(lenChars, utf);
  161. StringBuffer text(bytes, utf);
  162. if (!prevtext || strcmp(text, prevtext) != 0)
  163. {
  164. prevtext.clear();
  165. // Try compiling as a eval first... if that fails, try as a script.
  166. text.stripChar('\r');
  167. script.setown(Py_CompileString(text, "", Py_eval_input));
  168. if (!script)
  169. {
  170. PyErr_Clear();
  171. StringBuffer wrapped;
  172. wrapPythonText(wrapped, text);
  173. script.setown(Py_CompileString(wrapped, "<embed>", Py_file_input));
  174. }
  175. checkPythonError();
  176. prevtext.set(utf, bytes);
  177. }
  178. return script.getLink();
  179. }
  180. private:
  181. static StringBuffer &wrapPythonText(StringBuffer &out, const char *in)
  182. {
  183. out.append("def __user__():\n ");
  184. char c;
  185. while ((c = *in++) != '\0')
  186. {
  187. out.append(c);
  188. if (c=='\n')
  189. out.append(" ");
  190. }
  191. out.append("\n__result__ = __user__()\n");
  192. return out;
  193. }
  194. GILstateWrapper GILState;
  195. OwnedPyObject module;
  196. OwnedPyObject script;
  197. StringAttr prevtext;
  198. };
  199. static __thread PythonThreadContext* threadContext; // We reuse per thread, for speed
  200. static __thread ThreadTermFunc threadHookChain;
  201. static void releaseContext()
  202. {
  203. if (threadContext)
  204. {
  205. delete threadContext;
  206. threadContext = NULL;
  207. }
  208. if (threadHookChain)
  209. {
  210. (*threadHookChain)();
  211. threadHookChain = NULL;
  212. }
  213. }
  214. // Use a global object to ensure that the Python interpreter is initialized on main thread
  215. static class Python27GlobalState
  216. {
  217. public:
  218. Python27GlobalState()
  219. {
  220. pythonLibrary = (HINSTANCE) 0;
  221. #ifndef _WIN32
  222. // If Py_Initialize is called when stdin is set to a directory, it calls exit()
  223. // We don't want that to happen - just disable Python support in such situations
  224. struct stat sb;
  225. if (fstat(fileno(stdin), &sb) == 0 && S_ISDIR(sb.st_mode))
  226. {
  227. initialized = false;
  228. return;
  229. }
  230. #endif
  231. #ifndef _WIN32
  232. // We need to ensure all symbols in the python2.6 so are loaded - due to bugs in some distro's python installations
  233. FILE *diskfp = fopen("/proc/self/maps", "r");
  234. if (diskfp)
  235. {
  236. char ln[_MAX_PATH];
  237. while (fgets(ln, sizeof(ln), diskfp))
  238. {
  239. if (strstr(ln, "libpython2"))
  240. {
  241. const char *fullName = strchr(ln, '/');
  242. if (fullName)
  243. {
  244. char * lf = (char *) strchr(fullName, '\n');
  245. if (lf)
  246. {
  247. *lf = 0;
  248. pythonLibrary = dlopen((char *)fullName, RTLD_NOW|RTLD_GLOBAL);
  249. // DBGLOG("dlopen %s returns %"I64F"x", fullName, (__uint64) pythonLibrary);
  250. break;
  251. }
  252. }
  253. }
  254. }
  255. fclose(diskfp);
  256. }
  257. #endif
  258. // Initialize the Python Interpreter
  259. Py_Initialize();
  260. PyEval_InitThreads();
  261. tstate = PyEval_SaveThread();
  262. initialized = true;
  263. }
  264. ~Python27GlobalState()
  265. {
  266. if (threadContext)
  267. delete threadContext; // The one on the main thread won't get picked up by the thread hook mechanism
  268. threadContext = NULL;
  269. if (initialized)
  270. {
  271. PyEval_RestoreThread(tstate);
  272. // Finish the Python Interpreter
  273. Py_Finalize();
  274. }
  275. if (pythonLibrary)
  276. FreeSharedObject(pythonLibrary);
  277. }
  278. bool isInitialized()
  279. {
  280. return initialized;
  281. }
  282. protected:
  283. PyThreadState *tstate;
  284. bool initialized;
  285. HINSTANCE pythonLibrary;
  286. } globalState;
  287. static int countFields(const RtlFieldInfo * const * fields)
  288. {
  289. unsigned count = 0;
  290. loop
  291. {
  292. if (!*fields)
  293. break;
  294. fields++;
  295. count++;
  296. }
  297. return count;
  298. }
  299. // Conversions from Python objects to ECL data
  300. static void typeError(const char *expected, const RtlFieldInfo *field) __attribute__((noreturn));
  301. static void typeError(const char *expected, const RtlFieldInfo *field)
  302. {
  303. VStringBuffer msg("pyembed: type mismatch - %s expected", expected);
  304. if (field)
  305. msg.appendf(" for field %s", field->name->str());
  306. rtlFail(0, msg.str());
  307. }
  308. static bool getBooleanResult(const RtlFieldInfo *field, PyObject *obj)
  309. {
  310. assertex(obj && obj != Py_None);
  311. if (!PyBool_Check(obj))
  312. typeError("boolean", field);
  313. return obj == Py_True;
  314. }
  315. static void getDataResult(const RtlFieldInfo *field, PyObject *obj, size32_t &chars, void * &result)
  316. {
  317. assertex(obj && obj != Py_None);
  318. if (!PyByteArray_Check(obj))
  319. typeError("bytearray", field);
  320. rtlStrToDataX(chars, result, PyByteArray_Size(obj), PyByteArray_AsString(obj));
  321. }
  322. static double getRealResult(const RtlFieldInfo *field, PyObject *obj)
  323. {
  324. assertex(obj && obj != Py_None);
  325. if (!PyFloat_Check(obj))
  326. typeError("real", field);
  327. return PyFloat_AsDouble(obj);
  328. }
  329. static __int64 getSignedResult(const RtlFieldInfo *field, PyObject *obj)
  330. {
  331. assertex(obj && obj != Py_None);
  332. __int64 ret;
  333. if (PyInt_Check(obj))
  334. ret = PyInt_AsUnsignedLongLongMask(obj);
  335. else if (PyLong_Check(obj))
  336. ret = (__int64) PyLong_AsLongLong(obj);
  337. else
  338. typeError("integer", field);
  339. return ret;
  340. }
  341. static unsigned __int64 getUnsignedResult(const RtlFieldInfo *field, PyObject *obj)
  342. {
  343. assertex(obj && obj != Py_None);
  344. unsigned __int64 ret;
  345. if (PyInt_Check(obj))
  346. ret = PyInt_AsUnsignedLongLongMask(obj);
  347. else if (PyLong_Check(obj))
  348. ret = (unsigned __int64) PyLong_AsUnsignedLongLong(obj);
  349. else
  350. typeError("integer", field);
  351. return ret;
  352. }
  353. static void getStringResult(const RtlFieldInfo *field, PyObject *obj, size32_t &chars, char * &result)
  354. {
  355. assertex(obj && obj != Py_None);
  356. if (PyString_Check(obj))
  357. {
  358. const char * text = PyString_AsString(obj);
  359. checkPythonError();
  360. size_t lenBytes = PyString_Size(obj);
  361. rtlStrToStrX(chars, result, lenBytes, text);
  362. }
  363. else
  364. typeError("string", field);
  365. }
  366. static void getUTF8Result(const RtlFieldInfo *field, PyObject *obj, size32_t &chars, char * &result)
  367. {
  368. assertex(obj && obj != Py_None);
  369. if (PyUnicode_Check(obj))
  370. {
  371. OwnedPyObject utf8 = PyUnicode_AsUTF8String(obj);
  372. checkPythonError();
  373. size_t lenBytes = PyString_Size(utf8);
  374. const char * text = PyString_AsString(utf8);
  375. checkPythonError();
  376. size32_t numchars = rtlUtf8Length(lenBytes, text);
  377. rtlUtf8ToUtf8X(chars, result, numchars, text);
  378. }
  379. else
  380. typeError("unicode string", field);
  381. }
  382. static void getSetResult(PyObject *obj, bool & isAllResult, size32_t & resultBytes, void * & result, int elemType, size32_t elemSize)
  383. {
  384. // MORE - should probably recode to use the getResultDataset mechanism
  385. assertex(obj && obj != Py_None);
  386. if (!PyList_Check(obj) && !PySet_Check(obj))
  387. rtlFail(0, "pyembed: type mismatch - list or set expected");
  388. rtlRowBuilder out;
  389. size32_t outBytes = 0;
  390. byte *outData = NULL;
  391. OwnedPyObject iter = PyObject_GetIter(obj);
  392. OwnedPyObject elem;
  393. for (elem.setown(PyIter_Next(iter)); elem != NULL; elem.setown(PyIter_Next(iter)))
  394. {
  395. if (elemSize != UNKNOWN_LENGTH)
  396. {
  397. out.ensureAvailable(outBytes + elemSize);
  398. outData = out.getbytes() + outBytes;
  399. outBytes += elemSize;
  400. }
  401. switch ((type_t) elemType)
  402. {
  403. case type_int:
  404. rtlWriteInt(outData, pyembed::getSignedResult(NULL, elem), elemSize);
  405. break;
  406. case type_unsigned:
  407. rtlWriteInt(outData, pyembed::getUnsignedResult(NULL, elem), elemSize);
  408. break;
  409. case type_real:
  410. if (elemSize == sizeof(double))
  411. * (double *) outData = (double) pyembed::getRealResult(NULL, elem);
  412. else
  413. {
  414. assertex(elemSize == sizeof(float));
  415. * (float *) outData = (float) pyembed::getRealResult(NULL, elem);
  416. }
  417. break;
  418. case type_boolean:
  419. assertex(elemSize == sizeof(bool));
  420. * (bool *) outData = pyembed::getBooleanResult(NULL, elem);
  421. break;
  422. case type_string:
  423. case type_varstring:
  424. {
  425. if (!PyString_Check(elem))
  426. rtlFail(0, "pyembed: type mismatch - return value in list was not a STRING");
  427. const char * text = PyString_AsString(elem);
  428. checkPythonError();
  429. size_t lenBytes = PyString_Size(elem);
  430. if (elemSize == UNKNOWN_LENGTH)
  431. {
  432. if (elemType == type_string)
  433. {
  434. out.ensureAvailable(outBytes + lenBytes + sizeof(size32_t));
  435. outData = out.getbytes() + outBytes;
  436. * (size32_t *) outData = lenBytes;
  437. rtlStrToStr(lenBytes, outData+sizeof(size32_t), lenBytes, text);
  438. outBytes += lenBytes + sizeof(size32_t);
  439. }
  440. else
  441. {
  442. out.ensureAvailable(outBytes + lenBytes + 1);
  443. outData = out.getbytes() + outBytes;
  444. rtlStrToVStr(0, outData, lenBytes, text);
  445. outBytes += lenBytes + 1;
  446. }
  447. }
  448. else
  449. {
  450. if (elemType == type_string)
  451. rtlStrToStr(elemSize, outData, lenBytes, text);
  452. else
  453. rtlStrToVStr(elemSize, outData, lenBytes, text); // Fixed size null terminated strings... weird.
  454. }
  455. break;
  456. }
  457. case type_unicode:
  458. case type_utf8:
  459. {
  460. if (!PyUnicode_Check(elem))
  461. rtlFail(0, "pyembed: type mismatch - return value in list was not a unicode STRING");
  462. OwnedPyObject utf8 = PyUnicode_AsUTF8String(elem);
  463. checkPythonError();
  464. size_t lenBytes = PyString_Size(utf8);
  465. const char * text = PyString_AsString(utf8);
  466. checkPythonError();
  467. size32_t numchars = rtlUtf8Length(lenBytes, text);
  468. if (elemType == type_utf8)
  469. {
  470. assertex (elemSize == UNKNOWN_LENGTH);
  471. out.ensureAvailable(outBytes + lenBytes + sizeof(size32_t));
  472. outData = out.getbytes() + outBytes;
  473. * (size32_t *) outData = numchars;
  474. rtlStrToStr(lenBytes, outData+sizeof(size32_t), lenBytes, text);
  475. outBytes += lenBytes + sizeof(size32_t);
  476. }
  477. else
  478. {
  479. if (elemSize == UNKNOWN_LENGTH)
  480. {
  481. out.ensureAvailable(outBytes + numchars*sizeof(UChar) + sizeof(size32_t));
  482. outData = out.getbytes() + outBytes;
  483. // You can't assume that number of chars in utf8 matches number in unicode16 ...
  484. size32_t numchars16;
  485. rtlDataAttr unicode16;
  486. rtlUtf8ToUnicodeX(numchars16, unicode16.refustr(), numchars, text);
  487. * (size32_t *) outData = numchars16;
  488. rtlUnicodeToUnicode(numchars16, (UChar *) (outData+sizeof(size32_t)), numchars16, unicode16.getustr());
  489. outBytes += numchars16*sizeof(UChar) + sizeof(size32_t);
  490. }
  491. else
  492. rtlUtf8ToUnicode(elemSize / sizeof(UChar), (UChar *) outData, numchars, text);
  493. }
  494. break;
  495. }
  496. case type_data:
  497. {
  498. if (!PyByteArray_Check(elem))
  499. rtlFail(0, "pyembed: type mismatch - return value in list was not a bytearray");
  500. size_t lenBytes = PyByteArray_Size(elem); // Could check does not overflow size32_t
  501. const char *data = PyByteArray_AsString(elem);
  502. if (elemSize == UNKNOWN_LENGTH)
  503. {
  504. out.ensureAvailable(outBytes + lenBytes + sizeof(size32_t));
  505. outData = out.getbytes() + outBytes;
  506. * (size32_t *) outData = lenBytes;
  507. rtlStrToData(lenBytes, outData+sizeof(size32_t), lenBytes, data);
  508. outBytes += lenBytes + sizeof(size32_t);
  509. }
  510. else
  511. rtlStrToData(elemSize, outData, lenBytes, data);
  512. break;
  513. }
  514. default:
  515. rtlFail(0, "pyembed: type mismatch - unsupported return type");
  516. break;
  517. }
  518. checkPythonError();
  519. }
  520. isAllResult = false;
  521. resultBytes = outBytes;
  522. result = out.detachdata();
  523. }
  524. static void getUnicodeResult(const RtlFieldInfo *field, PyObject *obj, size32_t &chars, UChar * &result)
  525. {
  526. assertex(obj && obj != Py_None);
  527. if (PyUnicode_Check(obj))
  528. {
  529. OwnedPyObject utf8 = PyUnicode_AsUTF8String(obj);
  530. checkPythonError();
  531. size_t lenBytes = PyString_Size(utf8);
  532. const char * text = PyString_AsString(utf8);
  533. checkPythonError();
  534. size32_t numchars = rtlUtf8Length(lenBytes, text);
  535. rtlUtf8ToUnicodeX(chars, result, numchars, text);
  536. }
  537. else
  538. typeError("unicode string", field);
  539. }
  540. // A PythonRowBuilder object is used to construct an ECL row from a python object
  541. class PythonRowBuilder : public CInterfaceOf<IFieldSource>
  542. {
  543. public:
  544. PythonRowBuilder(PyObject *_row)
  545. : iter(NULL), elem(NULL), pushback(_row), named(false)
  546. {
  547. }
  548. virtual bool getBooleanResult(const RtlFieldInfo *field)
  549. {
  550. nextField(field);
  551. return pyembed::getBooleanResult(field, elem);
  552. }
  553. virtual void getDataResult(const RtlFieldInfo *field, size32_t &len, void * &result)
  554. {
  555. nextField(field);
  556. pyembed::getDataResult(field, elem, len, result);
  557. }
  558. virtual double getRealResult(const RtlFieldInfo *field)
  559. {
  560. nextField(field);
  561. return pyembed::getRealResult(field, elem);
  562. }
  563. virtual __int64 getSignedResult(const RtlFieldInfo *field)
  564. {
  565. nextField(field);
  566. return pyembed::getSignedResult(field, elem);
  567. }
  568. virtual unsigned __int64 getUnsignedResult(const RtlFieldInfo *field)
  569. {
  570. nextField(field);
  571. return pyembed::getUnsignedResult(field, elem);
  572. }
  573. virtual void getStringResult(const RtlFieldInfo *field, size32_t &chars, char * &result)
  574. {
  575. nextField(field);
  576. pyembed::getStringResult(field, elem, chars, result);
  577. }
  578. virtual void getUTF8Result(const RtlFieldInfo *field, size32_t &chars, char * &result)
  579. {
  580. nextField(field);
  581. pyembed::getUTF8Result(field, elem, chars, result);
  582. }
  583. virtual void getUnicodeResult(const RtlFieldInfo *field, size32_t &chars, UChar * &result)
  584. {
  585. nextField(field);
  586. pyembed::getUnicodeResult(field, elem, chars, result);
  587. }
  588. virtual void getDecimalResult(const RtlFieldInfo *field, Decimal &value)
  589. {
  590. nextField(field);
  591. double ret = pyembed::getRealResult(field, elem);
  592. value.setReal(ret);
  593. }
  594. virtual void processBeginSet(const RtlFieldInfo * field, bool &isAll)
  595. {
  596. nextField(field);
  597. isAll = false; // No concept of an 'all' set in Python
  598. assertex(elem && elem != Py_None);
  599. if (!PyList_Check(elem) && !PySet_Check(elem))
  600. typeError("list or set", field);
  601. push();
  602. }
  603. virtual bool processNextSet(const RtlFieldInfo * field)
  604. {
  605. nextField(NULL);
  606. pushback.setown(elem.getClear());
  607. return pushback != NULL;
  608. }
  609. virtual void processBeginDataset(const RtlFieldInfo * field)
  610. {
  611. nextField(field);
  612. if (!PyList_Check(elem))
  613. typeError("list", field);
  614. push();
  615. }
  616. virtual void processBeginRow(const RtlFieldInfo * field)
  617. {
  618. // Expect to see a tuple here, or possibly (if the ECL record has a single field), an arbitrary scalar object
  619. // If it's a tuple, we push it onto our stack as the active object
  620. nextField(NULL); // MORE - should it be passing field?
  621. if (!PyTuple_Check(elem))
  622. {
  623. if (countFields(field->type->queryFields())==1)
  624. {
  625. // Python doesn't seem to support the concept of a tuple containing a single element.
  626. // If we are expecting a single field in our row, then the 'tuple' layer will be missing
  627. elem.setown(PyTuple_Pack(1, elem.get()));
  628. }
  629. else
  630. typeError("tuple", field);
  631. }
  632. push();
  633. }
  634. virtual bool processNextRow(const RtlFieldInfo * field)
  635. {
  636. nextField(NULL);
  637. pushback.setown(elem.getClear());
  638. return pushback != NULL;
  639. }
  640. virtual void processEndSet(const RtlFieldInfo * field)
  641. {
  642. pop();
  643. }
  644. virtual void processEndDataset(const RtlFieldInfo * field)
  645. {
  646. pop();
  647. }
  648. virtual void processEndRow(const RtlFieldInfo * field)
  649. {
  650. pop();
  651. }
  652. protected:
  653. void pop()
  654. {
  655. iter.setown((PyObject *) iterStack.pop());
  656. parent.setown((PyObject *) parentStack.pop());
  657. named = namedStack.pop();
  658. elem.clear();
  659. }
  660. void push()
  661. {
  662. iterStack.append(iter.getClear());
  663. parentStack.append(parent.getClear());
  664. namedStack.append(named);
  665. parent.set(elem);
  666. iter.setown(PyObject_GetIter(elem));
  667. named = isNamedTuple(elem);
  668. }
  669. bool isNamedTuple(PyObject *obj)
  670. {
  671. return PyObject_HasAttrString((PyObject *) obj->ob_type, "_fields");
  672. }
  673. void nextField(const RtlFieldInfo * field)
  674. {
  675. if (pushback)
  676. elem.setown(pushback.getClear());
  677. else if (field && named) // If it's named tuple, expect to always resolve fields by name, not position
  678. {
  679. elem.setown(PyObject_GetAttrString(parent, field->name->str()));
  680. }
  681. else if (iter)
  682. elem.setown(PyIter_Next(iter));
  683. else
  684. elem = NULL;
  685. checkPythonError();
  686. }
  687. OwnedPyObject iter;
  688. OwnedPyObject pushback;
  689. OwnedPyObject elem;
  690. OwnedPyObject parent;
  691. bool named;
  692. PointerArray iterStack;
  693. PointerArray parentStack;
  694. BoolArray namedStack;
  695. };
  696. // GILBlock ensures the we hold the Python "Global interpreter lock" for the appropriate duration
  697. class GILBlock
  698. {
  699. public:
  700. GILBlock(PyThreadState * &_state) : state(_state)
  701. {
  702. PyEval_RestoreThread(state);
  703. }
  704. ~GILBlock()
  705. {
  706. state = PyEval_SaveThread();
  707. }
  708. private:
  709. PyThreadState * &state;
  710. };
  711. // A Python function that returns a dataset will return a PythonRowStream object that can be
  712. // interrogated to return each row of the result in turn
  713. class PythonRowStream : public CInterfaceOf<IRowStream>
  714. {
  715. public:
  716. PythonRowStream(PythonThreadContext *_sharedCtx, PyObject *result, IEngineRowAllocator *_resultAllocator)
  717. : sharedCtx(_sharedCtx), resultIterator(NULL)
  718. {
  719. // NOTE - the caller should already have the GIL lock before creating me
  720. if (!result || result == Py_None)
  721. typeError("list or generator", NULL);
  722. if (!PyList_Check(result) && !PyGen_Check(result)) // MORE - should I remove this check, and just say if it is iterable, it's good?
  723. typeError("list or generator", NULL);
  724. resultIterator.setown(PyObject_GetIter(result));
  725. checkPythonError();
  726. resultAllocator.set(_resultAllocator);
  727. }
  728. virtual const void *nextRow()
  729. {
  730. GILBlock b(sharedCtx->threadState);
  731. if (!resultIterator)
  732. return NULL;
  733. OwnedPyObject row = PyIter_Next(resultIterator);
  734. if (!row)
  735. return NULL;
  736. RtlDynamicRowBuilder rowBuilder(resultAllocator);
  737. PythonRowBuilder pyRowBuilder(row);
  738. const RtlTypeInfo *typeInfo = resultAllocator->queryOutputMeta()->queryTypeInfo();
  739. assertex(typeInfo);
  740. RtlFieldStrInfo dummyField("<row>", NULL, typeInfo);
  741. size32_t len = typeInfo->build(rowBuilder, 0, &dummyField, pyRowBuilder);
  742. return rowBuilder.finalizeRowClear(len);
  743. }
  744. virtual void stop()
  745. {
  746. resultAllocator.clear();
  747. resultIterator.clear();
  748. }
  749. protected:
  750. PythonThreadContext *sharedCtx;
  751. Linked<IEngineRowAllocator> resultAllocator;
  752. OwnedPyObject resultIterator;
  753. };
  754. // Each call to a Python function will use a new Python27EmbedFunctionContext object
  755. // This takes care of ensuring that the Python GIL is locked while we are executing python code,
  756. // and released when we are not
  757. class Python27EmbedContextBase : public CInterfaceOf<IEmbedFunctionContext>
  758. {
  759. public:
  760. Python27EmbedContextBase(PythonThreadContext *_sharedCtx)
  761. : sharedCtx(_sharedCtx)
  762. {
  763. PyEval_RestoreThread(sharedCtx->threadState);
  764. locals.setown(PyDict_New());
  765. globals.setown(PyDict_New());
  766. PyDict_SetItemString(locals, "__builtins__", PyEval_GetBuiltins()); // required for import to work
  767. }
  768. ~Python27EmbedContextBase()
  769. {
  770. // We need to clear these before calling savethread, or we won't own the GIL
  771. locals.clear();
  772. globals.clear();
  773. result.clear();
  774. script.clear();
  775. sharedCtx->threadState = PyEval_SaveThread();
  776. }
  777. virtual bool getBooleanResult()
  778. {
  779. return pyembed::getBooleanResult(NULL, result);
  780. }
  781. virtual void getDataResult(size32_t &__chars, void * &__result)
  782. {
  783. pyembed::getDataResult(NULL, result, __chars, __result);
  784. }
  785. virtual double getRealResult()
  786. {
  787. return pyembed::getRealResult(NULL, result);
  788. }
  789. virtual __int64 getSignedResult()
  790. {
  791. return pyembed::getSignedResult(NULL, result);
  792. }
  793. virtual unsigned __int64 getUnsignedResult()
  794. {
  795. return pyembed::getUnsignedResult(NULL, result);
  796. }
  797. virtual void getStringResult(size32_t &__chars, char * &__result)
  798. {
  799. pyembed::getStringResult(NULL, result, __chars, __result);
  800. }
  801. virtual void getUTF8Result(size32_t &__chars, char * &__result)
  802. {
  803. pyembed::getUTF8Result(NULL, result, __chars, __result);
  804. }
  805. virtual void getUnicodeResult(size32_t &__chars, UChar * &__result)
  806. {
  807. pyembed::getUnicodeResult(NULL, result, __chars, __result);
  808. }
  809. virtual void getSetResult(bool & __isAllResult, size32_t & __resultBytes, void * & __result, int elemType, size32_t elemSize)
  810. {
  811. pyembed::getSetResult(result, __isAllResult, __resultBytes, __result, elemType, elemSize);
  812. }
  813. virtual IRowStream *getDatasetResult(IEngineRowAllocator * _resultAllocator)
  814. {
  815. return new PythonRowStream(sharedCtx, result, _resultAllocator);
  816. }
  817. virtual void bindBooleanParam(const char *name, bool val)
  818. {
  819. addArg(name, PyBool_FromLong(val ? 1 : 0));
  820. }
  821. virtual void bindDataParam(const char *name, size32_t len, const void *val)
  822. {
  823. addArg(name, PyByteArray_FromStringAndSize((const char *) val, len));
  824. }
  825. virtual void bindRealParam(const char *name, double val)
  826. {
  827. addArg(name, PyFloat_FromDouble(val));
  828. }
  829. virtual void bindSignedParam(const char *name, __int64 val)
  830. {
  831. addArg(name, PyLong_FromLongLong(val));
  832. }
  833. virtual void bindUnsignedParam(const char *name, unsigned __int64 val)
  834. {
  835. addArg(name, PyLong_FromUnsignedLongLong(val));
  836. }
  837. virtual void bindStringParam(const char *name, size32_t len, const char *val)
  838. {
  839. addArg(name, PyString_FromStringAndSize(val, len));
  840. }
  841. virtual void bindVStringParam(const char *name, const char *val)
  842. {
  843. addArg(name, PyString_FromString(val));
  844. }
  845. virtual void bindUTF8Param(const char *name, size32_t chars, const char *val)
  846. {
  847. size32_t sizeBytes = rtlUtf8Size(chars, val);
  848. PyObject *vval = PyUnicode_FromStringAndSize(val, sizeBytes); // NOTE - requires size in bytes not chars
  849. checkPythonError();
  850. addArg(name, vval);
  851. }
  852. virtual void bindUnicodeParam(const char *name, size32_t chars, const UChar *val)
  853. {
  854. // You don't really know what size Py_UNICODE is (varies from system to system), so go via utf8
  855. unsigned unicodeChars;
  856. char *unicode;
  857. rtlUnicodeToUtf8X(unicodeChars, unicode, chars, val);
  858. size32_t sizeBytes = rtlUtf8Size(unicodeChars, unicode);
  859. PyObject *vval = PyUnicode_FromStringAndSize(unicode, sizeBytes); // NOTE - requires size in bytes not chars
  860. checkPythonError();
  861. addArg(name, vval);
  862. rtlFree(unicode);
  863. }
  864. virtual void bindSetParam(const char *name, int elemType, size32_t elemSize, bool isAll, size32_t totalBytes, void *setData)
  865. {
  866. if (isAll)
  867. rtlFail(0, "pyembed: Cannot pass ALL");
  868. type_t typecode = (type_t) elemType;
  869. const byte *inData = (const byte *) setData;
  870. const byte *endData = inData + totalBytes;
  871. OwnedPyObject vval = PyList_New(0);
  872. while (inData < endData)
  873. {
  874. OwnedPyObject thisElem;
  875. size32_t thisSize = elemSize;
  876. switch (typecode)
  877. {
  878. case type_int:
  879. thisElem.setown(PyLong_FromLongLong(rtlReadInt(inData, elemSize)));
  880. break;
  881. case type_unsigned:
  882. thisElem.setown(PyLong_FromUnsignedLongLong(rtlReadUInt(inData, elemSize)));
  883. break;
  884. case type_varstring:
  885. {
  886. size32_t numChars = strlen((const char *) inData);
  887. thisElem.setown(PyString_FromStringAndSize((const char *) inData, numChars));
  888. if (elemSize == UNKNOWN_LENGTH)
  889. thisSize = numChars + 1;
  890. break;
  891. }
  892. case type_string:
  893. if (elemSize == UNKNOWN_LENGTH)
  894. {
  895. thisSize = * (size32_t *) inData;
  896. inData += sizeof(size32_t);
  897. }
  898. thisElem.setown(PyString_FromStringAndSize((const char *) inData, thisSize));
  899. break;
  900. case type_real:
  901. if (elemSize == sizeof(double))
  902. thisElem.setown(PyFloat_FromDouble(* (double *) inData));
  903. else
  904. thisElem.setown(PyFloat_FromDouble(* (float *) inData));
  905. break;
  906. case type_boolean:
  907. assertex(elemSize == sizeof(bool));
  908. thisElem.setown(PyBool_FromLong(*(bool*)inData ? 1 : 0));
  909. break;
  910. case type_unicode:
  911. {
  912. if (elemSize == UNKNOWN_LENGTH)
  913. {
  914. thisSize = (* (size32_t *) inData) * sizeof(UChar); // NOTE - it's in chars...
  915. inData += sizeof(size32_t);
  916. }
  917. unsigned unicodeChars;
  918. rtlDataAttr unicode;
  919. rtlUnicodeToUtf8X(unicodeChars, unicode.refstr(), thisSize / sizeof(UChar), (const UChar *) inData);
  920. size32_t sizeBytes = rtlUtf8Size(unicodeChars, unicode.getstr());
  921. thisElem.setown(PyUnicode_FromStringAndSize(unicode.getstr(), sizeBytes)); // NOTE - requires size in bytes not chars
  922. checkPythonError();
  923. break;
  924. }
  925. case type_utf8:
  926. {
  927. assertex (elemSize == UNKNOWN_LENGTH);
  928. size32_t numChars = * (size32_t *) inData;
  929. inData += sizeof(size32_t);
  930. thisSize = rtlUtf8Size(numChars, inData);
  931. thisElem.setown(PyUnicode_FromStringAndSize((const char *) inData, thisSize)); // NOTE - requires size in bytes not chars
  932. break;
  933. }
  934. case type_data:
  935. if (elemSize == UNKNOWN_LENGTH)
  936. {
  937. thisSize = * (size32_t *) inData;
  938. inData += sizeof(size32_t);
  939. }
  940. thisElem.setown(PyByteArray_FromStringAndSize((const char *) inData, thisSize));
  941. break;
  942. }
  943. checkPythonError();
  944. inData += thisSize;
  945. PyList_Append(vval, thisElem);
  946. }
  947. addArg(name, vval.getLink());
  948. }
  949. protected:
  950. virtual void addArg(const char *name, PyObject *arg) = 0;
  951. PythonThreadContext *sharedCtx;
  952. OwnedPyObject locals;
  953. OwnedPyObject globals;
  954. OwnedPyObject result;
  955. OwnedPyObject script;
  956. };
  957. class Python27EmbedScriptContext : public Python27EmbedContextBase
  958. {
  959. public:
  960. Python27EmbedScriptContext(PythonThreadContext *_sharedCtx, const char *options)
  961. : Python27EmbedContextBase(_sharedCtx)
  962. {
  963. }
  964. ~Python27EmbedScriptContext()
  965. {
  966. }
  967. virtual void importFunction(size32_t lenChars, const char *text)
  968. {
  969. throwUnexpected();
  970. }
  971. virtual void compileEmbeddedScript(size32_t lenChars, const char *utf)
  972. {
  973. script.setown(sharedCtx->compileEmbeddedScript(lenChars, utf));
  974. }
  975. virtual void callFunction()
  976. {
  977. result.setown(PyEval_EvalCode((PyCodeObject *) script.get(), locals, globals));
  978. checkPythonError();
  979. if (!result || result == Py_None)
  980. result.set(PyDict_GetItemString(locals, "__result__"));
  981. if (!result || result == Py_None)
  982. result.set(PyDict_GetItemString(globals, "__result__"));
  983. }
  984. protected:
  985. virtual void addArg(const char *name, PyObject *arg)
  986. {
  987. assertex(arg);
  988. PyDict_SetItemString(locals, name, arg);
  989. Py_DECREF(arg);
  990. checkPythonError();
  991. }
  992. };
  993. class Python27EmbedImportContext : public Python27EmbedContextBase
  994. {
  995. public:
  996. Python27EmbedImportContext(PythonThreadContext *_sharedCtx, const char *options)
  997. : Python27EmbedContextBase(_sharedCtx)
  998. {
  999. argcount = 0;
  1000. }
  1001. ~Python27EmbedImportContext()
  1002. {
  1003. }
  1004. virtual void importFunction(size32_t lenChars, const char *utf)
  1005. {
  1006. script.setown(sharedCtx->importFunction(lenChars, utf));
  1007. }
  1008. virtual void compileEmbeddedScript(size32_t len, const char *text)
  1009. {
  1010. throwUnexpected();
  1011. }
  1012. virtual void callFunction()
  1013. {
  1014. result.setown(PyObject_CallObject(script, args));
  1015. checkPythonError();
  1016. }
  1017. private:
  1018. virtual void addArg(const char *name, PyObject *arg)
  1019. {
  1020. if (argcount)
  1021. _PyTuple_Resize(args.ref(), argcount+1);
  1022. else
  1023. args.setown(PyTuple_New(1));
  1024. PyTuple_SET_ITEM((PyTupleObject *) args.get(), argcount++, arg); // Note - 'steals' the arg reference
  1025. }
  1026. int argcount;
  1027. OwnedPyObject args;
  1028. };
  1029. class Python27EmbedContext : public CInterfaceOf<IEmbedContext>
  1030. {
  1031. public:
  1032. virtual IEmbedFunctionContext *createFunctionContext(bool isImport, const char *options)
  1033. {
  1034. if (!threadContext)
  1035. {
  1036. if (!globalState.isInitialized())
  1037. rtlFail(0, "Python not initialized");
  1038. threadContext = new PythonThreadContext;
  1039. threadHookChain = addThreadTermFunc(releaseContext);
  1040. }
  1041. if (isImport)
  1042. return new Python27EmbedImportContext(threadContext, options);
  1043. else
  1044. return new Python27EmbedScriptContext(threadContext, options);
  1045. }
  1046. };
  1047. extern IEmbedContext* getEmbedContext()
  1048. {
  1049. return new Python27EmbedContext;
  1050. }
  1051. extern bool syntaxCheck(const char *script)
  1052. {
  1053. return true; // MORE
  1054. }
  1055. } // namespace