00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036 #define FREPPLE_CORE
00037 #include "frepple/utils.h"
00038
00039 namespace frepple
00040 {
00041 namespace utils
00042 {
00043
00044 DECLARE_EXPORT PyObject* PythonLogicException = NULL;
00045 DECLARE_EXPORT PyObject* PythonDataException = NULL;
00046 DECLARE_EXPORT PyObject* PythonRuntimeException = NULL;
00047
00048 const MetaClass* CommandPython::metadata2;
00049
00050 DECLARE_EXPORT PyObject *PythonInterpreter::module = NULL;
00051 DECLARE_EXPORT string PythonInterpreter::encoding;
00052
00053
00054 void CommandPython::execute()
00055 {
00056
00057 if (getVerbose())
00058 {
00059 logger << "Start executing python ";
00060 if (!cmd.empty()) logger << "command";
00061 if (!filename.empty()) logger << "file";
00062 logger << " at " << Date::now() << endl;
00063 }
00064 Timer t;
00065
00066
00067 string c;
00068 if (!cmd.empty())
00069
00070 c = cmd + "\n";
00071 else if (!filename.empty())
00072 {
00073
00074
00075
00076
00077
00078
00079 c = filename;
00080 for (string::size_type pos = c.find_first_of("'", 0);
00081 pos < string::npos;
00082 pos = c.find_first_of("'", pos))
00083 {
00084 c.replace(pos,1,"\\'",2);
00085 pos+=2;
00086 }
00087 c = "execfile(ur'" + c + "')\n";
00088 }
00089 else throw DataException("Python command without statement or filename");
00090
00091
00092 PythonInterpreter::execute(c.c_str());
00093
00094
00095 if (getVerbose()) logger << "Finished executing python at "
00096 << Date::now() << " : " << t << endl;
00097 }
00098
00099
00100 void PythonInterpreter::initialize()
00101 {
00102
00103 Py_InitializeEx(0);
00104
00105 PyEval_InitThreads();
00106 module = Py_InitModule3("frepple", NULL, "Access to the frePPLe library");
00107 if (!module)
00108 {
00109 PyEval_ReleaseLock();
00110 throw RuntimeException("Can't initialize Python interpreter");
00111 }
00112
00113
00114 PyDateTime_IMPORT;
00115
00116
00117 int nok = 0;
00118 PythonLogicException = PyErr_NewException("frepple.LogicException", NULL, NULL);
00119 Py_IncRef(PythonLogicException);
00120 nok += PyModule_AddObject(module, "LogicException", PythonLogicException);
00121 PythonDataException = PyErr_NewException("frepple.DataException", NULL, NULL);
00122 Py_IncRef(PythonDataException);
00123 nok += PyModule_AddObject(module, "DataException", PythonDataException);
00124 PythonRuntimeException = PyErr_NewException("frepple.RuntimeException", NULL, NULL);
00125 Py_IncRef(PythonRuntimeException);
00126 nok += PyModule_AddObject(module, "RuntimeException", PythonRuntimeException);
00127
00128
00129 nok += PyModule_AddStringConstant(module, "version", PACKAGE_VERSION);
00130
00131
00132 registerGlobalMethod("log", python_log, METH_VARARGS,
00133 "Prints a string to the frePPLe log file.", false);
00134 PyRun_SimpleString(
00135 "import frepple, sys\n"
00136 "class redirect:\n"
00137 "\tdef write(self,str):\n"
00138 "\t\tfrepple.log(str)\n"
00139 "sys.stdout = redirect()\n"
00140 "sys.stderr = redirect()"
00141 );
00142
00143
00144 PyObject* localemodule = PyImport_ImportModule("locale");
00145 if (!localemodule)
00146 throw RuntimeException("Can't import 'locale' Python module");
00147 else
00148 {
00149 PyObject* moduledict = PyModule_GetDict(localemodule);
00150 PyObject* func = PyDict_GetItemString(moduledict, "getpreferredencoding");
00151 if (!func)
00152 throw RuntimeException("Can't find 'getpreferredencoding' Python function");
00153 PyObject* retval = PyEval_CallObject(func, NULL);
00154 if (retval)
00155 {
00156 encoding = PyString_AsString(retval);
00157 Py_XDECREF(retval);
00158 }
00159 Py_XDECREF(localemodule);
00160 }
00161
00162
00163 PyEval_ReleaseLock();
00164
00165
00166 if (nok) throw RuntimeException("Can't initialize Python interpreter");
00167 }
00168
00169
00170 DECLARE_EXPORT void PythonInterpreter::addThread()
00171 {
00172
00173 PyThreadState * myThreadState = PyGILState_GetThisThreadState();
00174 if (myThreadState) return;
00175
00176
00177 PyThreadState *tcur = PyThreadState_New(PyInterpreterState_Head());
00178 if (!tcur) throw RuntimeException("Can't create new thread state");
00179
00180
00181 PyEval_RestoreThread(tcur);
00182 PyEval_ReleaseLock();
00183 }
00184
00185
00186 DECLARE_EXPORT void PythonInterpreter::deleteThread()
00187 {
00188
00189 PyThreadState * tcur = PyGILState_GetThisThreadState();
00190 if (!tcur) return;
00191
00192
00193 PyEval_RestoreThread(tcur);
00194 PyThreadState_Clear(tcur);
00195 PyThreadState_DeleteCurrent();
00196 }
00197
00198
00199 DECLARE_EXPORT void PythonInterpreter::execute(const char* cmd)
00200 {
00201
00202
00203 PyThreadState *myThreadState = PyGILState_GetThisThreadState();
00204 if (!myThreadState)
00205 throw RuntimeException("No Python thread state for this thread");
00206 PyEval_RestoreThread(myThreadState);
00207
00208
00209 PyObject *m = PyImport_AddModule("__main__");
00210 if (!m)
00211 {
00212
00213 PyEval_ReleaseLock();
00214 throw RuntimeException("Can't initialize Python interpreter");
00215 }
00216 PyObject *d = PyModule_GetDict(m);
00217 if (!d)
00218 {
00219
00220 PyEval_ReleaseLock();
00221 throw RuntimeException("Can't initialize Python interpreter");
00222 }
00223
00224
00225
00226 PyObject *v = PyRun_String(cmd, Py_file_input, d, d);
00227 if (!v)
00228 {
00229
00230 PyErr_Print();
00231
00232 PyEval_ReleaseLock();
00233 throw RuntimeException("Error executing Python command");
00234 }
00235 Py_DECREF(v);
00236 if (Py_FlushLine()) PyErr_Clear();
00237
00238
00239 PyEval_ReleaseLock();
00240 }
00241
00242
00243 DECLARE_EXPORT void PythonInterpreter::registerGlobalMethod(
00244 const char* name, PyCFunction method, int flags, const char* doc, bool lock
00245 )
00246 {
00247
00248
00249
00250 string *leakingName = new string(name);
00251 string *leakingDoc = new string(doc);
00252 PyMethodDef *newMethod = new PyMethodDef;
00253 newMethod->ml_name = leakingName->c_str();
00254 newMethod->ml_meth = method;
00255 newMethod->ml_flags = flags;
00256 newMethod->ml_doc = leakingDoc->c_str();
00257
00258
00259 if (lock)
00260 {
00261 PyThreadState *myThreadState = PyGILState_GetThisThreadState();
00262 if (!Py_IsInitialized() || !myThreadState)
00263 throw RuntimeException("Python isn't initialized correctly");
00264 PyEval_RestoreThread(myThreadState);
00265 }
00266
00267
00268 PyObject* mod = PyString_FromString("frepple");
00269 if (!mod)
00270 {
00271 if (lock) PyEval_ReleaseLock();
00272 throw RuntimeException("Error registering a new Python method");
00273 }
00274 PyObject* func = PyCFunction_NewEx(newMethod, NULL, mod);
00275 Py_DECREF(mod);
00276 if (!func)
00277 {
00278 if (lock) PyEval_ReleaseLock();
00279 throw RuntimeException("Error registering a new Python method");
00280 }
00281
00282
00283 PyObject* moduledict = PyModule_GetDict(module);
00284 if (!moduledict)
00285 {
00286 Py_DECREF(func);
00287 if (lock) PyEval_ReleaseLock();
00288 throw RuntimeException("Error registering a new Python method");
00289 }
00290 if (PyDict_SetItemString(moduledict ,leakingName->c_str(), func) < 0)
00291 {
00292 Py_DECREF(func);
00293 if (lock) PyEval_ReleaseLock();
00294 throw RuntimeException("Error registering a new Python method");
00295 }
00296 Py_DECREF(func);
00297
00298
00299 if (lock) PyEval_ReleaseLock();
00300 }
00301
00302
00303 DECLARE_EXPORT void PythonInterpreter::registerGlobalMethod
00304 (const char* c, PyCFunctionWithKeywords f, int i, const char* d)
00305 {
00306 registerGlobalMethod(c, reinterpret_cast<PyCFunction>(f), i | METH_KEYWORDS, d);
00307 }
00308
00309
00310 PyObject* PythonInterpreter::python_log(PyObject *self, PyObject *args)
00311 {
00312
00313 char *data;
00314 int ok = PyArg_ParseTuple(args, "s:log", &data);
00315 if (!ok) return NULL;
00316
00317
00318 logger << data;
00319 logger.flush();
00320
00321
00322 return Py_BuildValue("");
00323
00324 }
00325
00326
00327 const PyTypeObject PythonType::PyTypeObjectTemplate =
00328 {
00329 PyObject_HEAD_INIT(NULL)
00330 0,
00331 "frepple.unspecified",
00332 0,
00333 0,
00334 0,
00335 0,
00336 0,
00337 0,
00338 0,
00339 0,
00340 0,
00341 0,
00342 0,
00343 0,
00344 0,
00345 0,
00346 0,
00347 0,
00348 0,
00349 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
00350 "std doc",
00351 0,
00352 0,
00353 0,
00354 0,
00355 0,
00356 0,
00357 0,
00358 0,
00359 0,
00360 0,
00361 0,
00362 0,
00363 0,
00364 0,
00365 0,
00366 0,
00367 0,
00368 0,
00369 };
00370
00371
00372 DECLARE_EXPORT PythonObject::PythonObject(const Date& d)
00373 {
00374 PyDateTime_IMPORT;
00375
00376
00377
00378
00379
00380
00381
00382 time_t ticks = d.getTicks();
00383 #ifdef HAVE_LOCALTIME_R
00384 struct tm t;
00385 localtime_r(&ticks, &t);
00386 #else
00387 struct tm t = *localtime(&ticks);
00388 #endif
00389 obj = PyDateTime_FromDateAndTime(t.tm_year+1900, t.tm_mon+1, t.tm_mday,
00390 t.tm_hour, t.tm_min, t.tm_sec, 0);
00391 }
00392
00393
00394 DECLARE_EXPORT Date PythonObject::getDate() const
00395 {
00396 PyDateTime_IMPORT;
00397 if (PyDateTime_Check(obj))
00398 return Date(
00399 PyDateTime_GET_YEAR(obj),
00400 PyDateTime_GET_MONTH(obj),
00401 PyDateTime_GET_DAY(obj),
00402 PyDateTime_DATE_GET_HOUR(obj),
00403 PyDateTime_DATE_GET_MINUTE(obj),
00404 PyDateTime_DATE_GET_SECOND(obj)
00405 );
00406 else if (PyDate_Check(obj))
00407 return Date(
00408 PyDateTime_GET_YEAR(obj),
00409 PyDateTime_GET_MONTH(obj),
00410 PyDateTime_GET_DAY(obj)
00411 );
00412 else if (PyString_Check(obj))
00413 {
00414 if (PyUnicode_Check(obj))
00415 {
00416
00417 const_cast<PyObject*&>(obj) =
00418 PyUnicode_AsEncodedString(obj, PythonInterpreter::getPythonEncoding(), "ignore");
00419 }
00420 return Date(PyString_AsString(PyObject_Str(obj)));
00421 }
00422 else
00423 throw DataException(
00424 "Invalid data type. Expecting datetime.date, datetime.datetime or string"
00425 );
00426 }
00427
00428
00429 DECLARE_EXPORT PythonObject::PythonObject(Object* p)
00430 {
00431 obj = p ? static_cast<PyObject*>(p) : Py_None;
00432 Py_INCREF(obj);
00433 }
00434
00435
00436 DECLARE_EXPORT PythonType::PythonType(size_t base_size, const type_info* tp)
00437 : cppClass(tp)
00438 {
00439
00440
00441 table = new PyTypeObject(PyTypeObjectTemplate);
00442 table->tp_basicsize = base_size;
00443 }
00444
00445
00446 DECLARE_EXPORT PythonType* PythonExtensionBase::registerPythonType(int size, const type_info *t)
00447 {
00448
00449 for (vector<PythonType*>::const_iterator i = table.begin(); i != table.end(); ++i)
00450 if (**i==*t) return *i;
00451
00452
00453 PythonType *cachedTypePtr = new PythonType(size, t);
00454 table.push_back(cachedTypePtr);
00455 return cachedTypePtr;
00456 }
00457
00458
00459 DECLARE_EXPORT PyObject* Object::toXML(PyObject* self, PyObject* args)
00460 {
00461 try
00462 {
00463
00464 PyObject *filearg = NULL;
00465 if (PyArg_UnpackTuple(args, "toXML", 0, 1, &filearg))
00466 {
00467 ostringstream ch;
00468 XMLOutput x(ch);
00469
00470
00471
00472 static_cast<Object*>(self)->writeElement
00473 (&x, *(static_cast<Object*>(self)->getType().category->typetag));
00474
00475 if (filearg)
00476 {
00477 if (PyFile_Check(filearg))
00478 {
00479
00480 return PyFile_WriteString(ch.str().c_str(), filearg) ?
00481 NULL :
00482 Py_BuildValue("");
00483 }
00484 else
00485
00486 throw LogicException("Expecting a file argument");
00487 }
00488 else
00489
00490 return PythonObject(ch.str());
00491 }
00492 }
00493 catch(...)
00494 {
00495 PythonType::evalException();
00496 return NULL;
00497 }
00498 throw LogicException("Unreachable code reached");
00499 }
00500
00501
00502 DECLARE_EXPORT void PythonType::addMethod
00503 (const char* method_name, PyCFunction f, int flags, const char* doc )
00504 {
00505 unsigned short i = 0;
00506
00507
00508 if (!table->tp_methods)
00509
00510 table->tp_methods = new PyMethodDef[methodArraySize];
00511 else
00512 {
00513
00514 while (table->tp_methods[i].ml_name) i++;
00515 if (i % methodArraySize == methodArraySize - 1)
00516 {
00517
00518 PyMethodDef* tmp = new PyMethodDef[i + 1 + methodArraySize];
00519 for(unsigned short j = 0; j < i; j++)
00520 tmp[j] = table->tp_methods[j];
00521 delete [] table->tp_methods;
00522 table->tp_methods = tmp;
00523 }
00524 }
00525
00526
00527 table->tp_methods[i].ml_name = method_name;
00528 table->tp_methods[i].ml_meth = f;
00529 table->tp_methods[i].ml_flags = flags;
00530 table->tp_methods[i].ml_doc = doc;
00531
00532
00533 table->tp_methods[++i].ml_name = NULL;
00534 table->tp_methods[i].ml_meth = NULL;
00535 table->tp_methods[i].ml_flags = 0;
00536 table->tp_methods[i].ml_doc = NULL;
00537 }
00538
00539
00540 DECLARE_EXPORT void PythonType::addMethod
00541 (const char* c, PyCFunctionWithKeywords f, int i, const char* d)
00542 {
00543 addMethod(c, reinterpret_cast<PyCFunction>(f), i | METH_KEYWORDS, d);
00544 }
00545
00546
00547 DECLARE_EXPORT int PythonType::typeReady()
00548 {
00549
00550 if (PyType_Ready(table) < 0)
00551 throw RuntimeException(string("Can't register python type ") + table->tp_name);
00552 Py_INCREF(table);
00553 return PyModule_AddObject(
00554 PythonInterpreter::getModule(),
00555 table->tp_name + 8,
00556 reinterpret_cast<PyObject*>(table)
00557 );
00558 }
00559
00560
00561 DECLARE_EXPORT void PythonType::evalException()
00562 {
00563
00564 try {throw;}
00565 catch (DataException e)
00566 {PyErr_SetString(PythonDataException, e.what());}
00567 catch (LogicException e)
00568 {PyErr_SetString(PythonLogicException, e.what());}
00569 catch (RuntimeException e)
00570 {PyErr_SetString(PythonRuntimeException, e.what());}
00571 catch (exception e)
00572 {PyErr_SetString(PyExc_Exception, e.what());}
00573 catch (...)
00574 {PyErr_SetString(PyExc_Exception, "Unidentified exception");}
00575 }
00576
00577
00578 DECLARE_EXPORT PythonFunction::PythonFunction(const string& n)
00579 {
00580 if (n.empty())
00581 {
00582
00583 func = NULL;
00584 return;
00585 }
00586
00587
00588 PyGILState_STATE pythonstate = PyGILState_Ensure();
00589 func = PyRun_String(n.c_str(), Py_eval_input,
00590 PyEval_GetGlobals(), PyEval_GetLocals() );
00591 if (!func)
00592 {
00593 PyGILState_Release(pythonstate);
00594 throw DataException("Python function '" + n + "' not defined");
00595 }
00596 if (!PyCallable_Check(func))
00597 {
00598 PyGILState_Release(pythonstate);
00599 throw DataException("Python object '" + n + "' is not a function");
00600 }
00601 Py_INCREF(func);
00602
00603
00604 PyGILState_Release(pythonstate);
00605 }
00606
00607
00608 DECLARE_EXPORT PythonFunction::PythonFunction(PyObject* p)
00609 {
00610 if (!p || p == Py_None)
00611 {
00612
00613 func = NULL;
00614 return;
00615 }
00616
00617 if (!PyCallable_Check(p))
00618 {
00619
00620
00621 string n = PythonObject(p).getString();
00622 PyGILState_STATE pythonstate = PyGILState_Ensure();
00623 p = PyRun_String(n.c_str(), Py_eval_input,
00624 PyEval_GetGlobals(), PyEval_GetLocals() );
00625 if (!p)
00626 {
00627 PyGILState_Release(pythonstate);
00628 throw DataException("Python function '" + n + "' not defined");
00629 }
00630 if (!PyCallable_Check(p))
00631 {
00632 PyGILState_Release(pythonstate);
00633 throw DataException("Python object '" + n + "' is not a function");
00634 }
00635 PyGILState_Release(pythonstate);
00636 }
00637
00638
00639 func = p;
00640 Py_INCREF(func);
00641 }
00642
00643
00644 DECLARE_EXPORT PythonObject PythonFunction::call() const
00645 {
00646 if (!func) return PythonObject();
00647 PyGILState_STATE pythonstate = PyGILState_Ensure();
00648 PyObject* result = PyEval_CallFunction(func, "()");
00649 if (!result)
00650 {
00651 logger << "Error: Exception caught when calling Python function '"
00652 << (func ? PyEval_GetFuncName(func) : "NULL") << "'" << endl;
00653 if (PyErr_Occurred()) PyErr_PrintEx(0);
00654 }
00655 PyGILState_Release(pythonstate);
00656 return PythonObject(result);
00657 }
00658
00659
00660 DECLARE_EXPORT PythonObject PythonFunction::call(const PyObject* p) const
00661 {
00662 if (!func) return PythonObject();
00663 PyGILState_STATE pythonstate = PyGILState_Ensure();
00664 PyObject* result = PyEval_CallFunction(func, "(O)", p);
00665 if (!result)
00666 {
00667 logger << "Error: Exception caught when calling Python function '"
00668 << (func ? PyEval_GetFuncName(func) : "NULL") << "'" << endl;
00669 if (PyErr_Occurred()) PyErr_PrintEx(0);
00670 }
00671 PyGILState_Release(pythonstate);
00672 return PythonObject(result);
00673 }
00674
00675
00676 DECLARE_EXPORT PythonObject PythonFunction::call(const PyObject* p, const PyObject* q) const
00677 {
00678 if (!func) return PythonObject();
00679 PyGILState_STATE pythonstate = PyGILState_Ensure();
00680 PyObject* result = PyEval_CallFunction(func, "(OO)", p, q);
00681 if (!result)
00682 {
00683 logger << "Error: Exception caught when calling Python function '"
00684 << (func ? PyEval_GetFuncName(func) : "NULL") << "'" << endl;
00685 if (PyErr_Occurred()) PyErr_PrintEx(0);
00686 }
00687 PyGILState_Release(pythonstate);
00688 return PythonObject(result);
00689 }
00690
00691
00692 extern "C" DECLARE_EXPORT PyObject* getattro_handler(PyObject *self, PyObject *name)
00693 {
00694 try
00695 {
00696 if (!PyString_Check(name))
00697 {
00698 PyErr_Format(PyExc_TypeError,
00699 "attribute name must be string, not '%s'",
00700 name->ob_type->tp_name);
00701 return NULL;
00702 }
00703 PyObject* result = static_cast<PythonExtensionBase*>(self)->getattro(Attribute(PyString_AsString(name)));
00704
00705 if (result) return result;
00706
00707 if (PyErr_Occurred()) return NULL;
00708
00709
00710
00711
00712
00713 return PyObject_GenericGetAttr(self,name);
00714 }
00715 catch (...)
00716 {
00717 PythonType::evalException();
00718 return NULL;
00719 }
00720 }
00721
00722
00723 extern "C" DECLARE_EXPORT int setattro_handler(PyObject *self, PyObject *name, PyObject *value)
00724 {
00725 try
00726 {
00727
00728 if (!PyString_Check(name))
00729 {
00730 PyErr_Format(PyExc_TypeError,
00731 "attribute name must be string, not '%s'",
00732 name->ob_type->tp_name);
00733 return -1;
00734 }
00735 PythonObject field(value);
00736
00737
00738 int result = static_cast<PythonExtensionBase*>(self)->setattro(Attribute(PyString_AsString(name)), field);
00739
00740
00741 if (!result) return 0;
00742
00743
00744 if (!PyErr_Occurred())
00745 PyErr_Format(PyExc_AttributeError,
00746 "attribute '%s' on '%s' can't be updated",
00747 PyString_AsString(name), self->ob_type->tp_name);
00748 return -1;
00749 }
00750 catch (...)
00751 {
00752 PythonType::evalException();
00753 return -1;
00754 }
00755 }
00756
00757
00758 extern "C" DECLARE_EXPORT int compare_handler(PyObject *self, PyObject *other)
00759 {
00760 try
00761 {
00762 return static_cast<PythonExtensionBase*>(self)->compare(other);
00763 }
00764 catch (...)
00765 {
00766 PythonType::evalException();
00767 return -1;
00768 }
00769 }
00770
00771
00772 extern "C" DECLARE_EXPORT PyObject* iternext_handler(PyObject *self)
00773 {
00774 try
00775 {
00776 return static_cast<PythonExtensionBase*>(self)->iternext();
00777 }
00778 catch (...)
00779 {
00780 PythonType::evalException();
00781 return NULL;
00782 }
00783 }
00784
00785
00786 extern "C" DECLARE_EXPORT PyObject* call_handler(PyObject* self, PyObject* args, PyObject* kwds)
00787 {
00788 try
00789 {
00790 return static_cast<PythonExtensionBase*>(self)->call(args, kwds);
00791 }
00792 catch (...)
00793 {
00794 PythonType::evalException();
00795 return NULL;
00796 }
00797 }
00798
00799
00800 extern "C" DECLARE_EXPORT PyObject* str_handler(PyObject* self)
00801 {
00802 try
00803 {
00804 return static_cast<PythonExtensionBase*>(self)->str();
00805 }
00806 catch (...)
00807 {
00808 PythonType::evalException();
00809 return NULL;
00810 }
00811 }
00812
00813 }
00814 }