pythonutils.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * *
3  * Copyright (C) 2007-2013 by Johan De Taeye, frePPLe bvba *
4  * *
5  * This library is free software; you can redistribute it and/or modify it *
6  * under the terms of the GNU Affero General Public License as published *
7  * by the Free Software Foundation; either version 3 of the License, or *
8  * (at your option) any later version. *
9  * *
10  * This library is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU Affero General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU Affero General Public *
16  * License along with this program. *
17  * If not, see <http://www.gnu.org/licenses/>. *
18  * *
19  ***************************************************************************/
20 
21 /** @file pythonutils.cpp
22  * @brief Reusable functions for python functionality.
23  *
24  * The structure of the C++ wrappers around the C Python API is heavily
25  * inspired on the design of PyCXX.<br>
26  * More information can be found on http://cxx.sourceforge.net
27  */
28 
29 #define FREPPLE_CORE
30 #include "frepple/utils.h"
31 
32 #if PY_MAJOR_VERSION >= 3
33 extern PyTypeObject PyIOBase_Type;
34 #endif
35 
36 namespace frepple
37 {
38 namespace utils
39 {
40 
44 
45 DECLARE_EXPORT PyObject *PythonInterpreter::module = NULL;
46 DECLARE_EXPORT PyThreadState* PythonInterpreter::mainThreadState = NULL;
47 
48 
49 #if PY_MAJOR_VERSION >= 3
50 PyObject* PythonInterpreter::createModule()
51 {
52  static PyMethodDef freppleMethods[] = {
53  {NULL, NULL, 0, NULL}
54  };
55  static struct PyModuleDef frepplemodule = {
56  PyModuleDef_HEAD_INIT,
57  "frepple",
58  "Access to the frePPLe library",
59  -1, freppleMethods,
60  NULL, NULL, NULL, NULL
61  };
62  module = PyModule_Create(&frepplemodule);
63  return module;
64 }
65 #endif
66 
67 
69 {
70  // Initialize the Python interpreter in case we are embedding it in frePPLe.
71  if(!Py_IsInitialized())
72  {
73 #if PY_MAJOR_VERSION >= 3
74  // This method needs to be called before the initialization
75  PyImport_AppendInittab("frepple", &PythonInterpreter::createModule);
76 #endif
77  // The arg 0 indicates that the interpreter doesn't
78  // implement its own signal handler
79  Py_InitializeEx(0);
80  // Initializes threads
81  PyEval_InitThreads();
82  mainThreadState = PyEval_SaveThread();
83  }
84 
85  // Capture global lock
86  PyGILState_STATE state = PyGILState_Ensure();
87 
88 #if PY_MAJOR_VERSION < 3
89  // Create the frePPLe extension module
90  module = Py_InitModule3("frepple", NULL, "Access to the frePPLe library");
91 #endif
92 
93  // Create the logging function.
94  // In Python3 this also creates the frepple module, by calling the createModule callback.
95  PyRun_SimpleString(
96  "import frepple, sys\n"
97  "class redirect:\n"
98  "\tdef write(self,str):\n"
99  "\t\tfrepple.log(str)\n"
100  "sys.stdout = redirect()\n"
101  "sys.stderr = redirect()"
102  );
103 
104  if (!module)
105  {
106  PyGILState_Release(state);
107  throw RuntimeException("Can't initialize Python interpreter");
108  }
109 
110  // Make the datetime types available
111  PyDateTime_IMPORT;
112 
113  // Create python exception types
114  int nok = 0;
115  PythonLogicException = PyErr_NewException((char*)"frepple.LogicException", NULL, NULL);
116  Py_IncRef(PythonLogicException);
117  nok += PyModule_AddObject(module, "LogicException", PythonLogicException);
118  PythonDataException = PyErr_NewException((char*)"frepple.DataException", NULL, NULL);
119  Py_IncRef(PythonDataException);
120  nok += PyModule_AddObject(module, "DataException", PythonDataException);
121  PythonRuntimeException = PyErr_NewException((char*)"frepple.RuntimeException", NULL, NULL);
122  Py_IncRef(PythonRuntimeException);
123  nok += PyModule_AddObject(module, "RuntimeException", PythonRuntimeException);
124 
125  // Add a string constant for the version
126  nok += PyModule_AddStringConstant(module, "version", PACKAGE_VERSION);
127 
128  // Redirect the stderr and stdout streams of Python
129  registerGlobalMethod("log", python_log, METH_VARARGS,
130  "Prints a string to the frePPLe log file.", false);
131 
132  // Release the lock
133  PyGILState_Release(state);
134 
135  // A final check...
136  if (nok) throw RuntimeException("Can't initialize Python interpreter");
137 }
138 
139 
141 {
142  // Only valid if this is an embedded interpreter
143  if (!mainThreadState) return;
144 
145  // Swap to the main thread and exit
146  PyEval_AcquireLock();
147  PyEval_RestoreThread(mainThreadState);
148  Py_Finalize();
149 }
150 
151 
153 {
154  // Check whether the thread already has a Python state
155  PyThreadState * myThreadState = PyGILState_GetThisThreadState();
156  if (myThreadState) return;
157 
158  // Create a new state
159  PyThreadState *tcur = PyThreadState_New(PyInterpreterState_Head());
160  if (!tcur) throw RuntimeException("Can't create new thread state");
161 
162  // Make the new state current
163  PyEval_RestoreThread(tcur);
164  PyEval_ReleaseLock();
165 }
166 
167 
169 {
170  // Check whether the thread already has a Python state
171  PyThreadState * tcur = PyGILState_GetThisThreadState();
172  if (!tcur) return;
173 
174  // Delete the current Python thread state
175  PyEval_RestoreThread(tcur);
176  PyThreadState_Clear(tcur);
177  PyThreadState_DeleteCurrent(); // This releases the GIL too!
178 }
179 
180 
182 {
183  // Capture global lock
184  PyGILState_STATE state = PyGILState_Ensure();
185 
186  // Execute the command
187  PyObject *m = PyImport_AddModule("__main__");
188  if (!m)
189  {
190  // Release the global Python lock
191  PyGILState_Release(state);
192  throw RuntimeException("Can't initialize Python interpreter");
193  }
194  PyObject *d = PyModule_GetDict(m);
195  if (!d)
196  {
197  // Release the global Python lock
198  PyGILState_Release(state);
199  throw RuntimeException("Can't initialize Python interpreter");
200  }
201 
202  // Execute the Python code. Note that during the call the Python lock can be
203  // temporarily released.
204  PyObject *v = PyRun_String(cmd, Py_file_input, d, d);
205  if (!v)
206  {
207  // Print the error message
208  PyErr_Print();
209  // Release the global Python lock
210  PyGILState_Release(state);
211  throw RuntimeException("Error executing Python command");
212  }
213  Py_DECREF(v);
214 #if PY_MAJOR_VERSION >= 3
215  PyErr_Clear();
216 #else
217  if (Py_FlushLine()) PyErr_Clear(); // TODO PYTHON3: verify this is ok to skip
218 #endif
219  // Release the global Python lock
220  PyGILState_Release(state);
221 }
222 
223 
225 {
226  // A file to be executed.
227  // We build an equivalent python command rather than using the
228  // PyRun_File function. On windows different versions of the
229  // VC compiler have a different structure for FILE, thus making it
230  // impossible to use a lib compiled in python version x when compiling
231  // under version y. Quite ugly... :-( :-( :-(
232  for (string::size_type pos = filename.find_first_of("'", 0);
233  pos < string::npos;
234  pos = filename.find_first_of("'", pos))
235  {
236  filename.replace(pos,1,"\\'",2); // Replacing ' with \'
237  pos+=2;
238  }
239 #if PY_MAJOR_VERSION >= 3
240  string cmd = "exec(compile(open(r'" + filename + "').read(), r'" + filename + "', 'exec'))";
241 #else
242  string cmd = "execfile(r'" + filename + "')\n";
243 #endif
244  execute(cmd.c_str());
245 }
246 
247 
249  const char* name, PyCFunction method, int flags, const char* doc, bool lock
250 )
251 {
252  // Define a new method object.
253  // We need are leaking the memory allocated for it to assure the data
254  // are available at all times to Python.
255  string *leakingName = new string(name);
256  string *leakingDoc = new string(doc);
257  PyMethodDef *newMethod = new PyMethodDef;
258  newMethod->ml_name = leakingName->c_str();
259  newMethod->ml_meth = method;
260  newMethod->ml_flags = flags;
261  newMethod->ml_doc = leakingDoc->c_str();
262 
263  // Lock the interpreter
264  PyGILState_STATE state;
265  if (lock) state = PyGILState_Ensure();
266 
267  // Register a new C function in Python
268  PyObject* mod = PyUnicode_FromString("frepple");
269  if (!mod)
270  {
271  if (lock) PyGILState_Release(state);;
272  throw RuntimeException("Error registering a new Python method");
273  }
274  PyObject* func = PyCFunction_NewEx(newMethod, NULL, mod);
275  Py_DECREF(mod);
276  if (!func)
277  {
278  if (lock) PyGILState_Release(state);
279  throw RuntimeException("Error registering a new Python method");
280  }
281 
282  // Add the method to the module dictionary
283  PyObject* moduledict = PyModule_GetDict(module);
284  if (!moduledict)
285  {
286  Py_DECREF(func);
287  if (lock) PyGILState_Release(state);
288  throw RuntimeException("Error registering a new Python method");
289  }
290  if (PyDict_SetItemString(moduledict ,leakingName->c_str(), func) < 0)
291  {
292  Py_DECREF(func);
293  if (lock) PyGILState_Release(state);
294  throw RuntimeException("Error registering a new Python method");
295  }
296  Py_DECREF(func);
297 
298  // Release the interpeter
299  if (lock) PyGILState_Release(state);
300 }
301 
302 
304 (const char* c, PyCFunctionWithKeywords f, int i, const char* d, bool b)
305 {
306  registerGlobalMethod(c, reinterpret_cast<PyCFunction>(f), i | METH_KEYWORDS, d, b);
307 }
308 
309 
311 (const char* name, PyObject *obj, bool lock)
312 {
313  PyGILState_STATE state;
314  if (lock) state = PyGILState_Ensure();
315  PyModule_AddObject(module, name, obj);
316  if (lock) PyGILState_Release(state);
317 }
318 
319 
320 PyObject* PythonInterpreter::python_log(PyObject *self, PyObject *args)
321 {
322  // Pick up arguments
323  char *data;
324  int ok = PyArg_ParseTuple(args, "s:log", &data);
325  if (!ok) return NULL;
326 
327  // Print and flush the output stream
328  logger << data;
329  logger.flush();
330 
331  // Return code
332  return Py_BuildValue(""); // Safer than using Py_None, which is not
333  // portable across compilers
334 }
335 
336 
337 const PyTypeObject PythonType::PyTypeObjectTemplate =
338 {
339  PyVarObject_HEAD_INIT(NULL, 0)
340  "frepple.unspecified", /* WILL BE UPDATED tp_name */
341  0, /* WILL BE UPDATED tp_basicsize */
342  0, /* tp_itemsize */
343  0, /* CAN BE UPDATED tp_dealloc */
344  0, /* tp_print */
345  0, /* tp_getattr */
346  0, /* tp_setattr */
347  0, /* tp_compare */
348  0, /* tp_repr */
349  0, /* tp_as_number */
350  0, /* tp_as_sequence */
351  0, /* tp_as_mapping */
352  reinterpret_cast<hashfunc>(_Py_HashPointer), /* tp_hash */
353  0, /* CAN BE UPDATED tp_call */
354  0, /* CAN BE UPDATED tp_str */
355  0, /* CAN BE UPDATED tp_getattro */
356  0, /* CAN BE UPDATED tp_setattro */
357  0, /* tp_as_buffer */
358  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
359  "std doc", /* CAN BE UPDATED tp_doc */
360  0, /* tp_traverse */
361  0, /* tp_clear */
362  0, /* CAN BE UPDATED tp_richcompare */
363  0, /* tp_weaklistoffset */
364  0, /* CAN BE UPDATED tp_iter */
365  0, /* CAN BE UPDATED tp_iternext */
366  0, /* tp_methods */
367  0, /* tp_members */
368  0, /* tp_getset */
369  0, /* tp_base */
370  0, /* tp_dict */
371  0, /* tp_descr_get */
372  0, /* tp_descr_set */
373  0, /* tp_dictoffset */
374  0, /* tp_init */
375  0, /* tp_alloc */
376  0, /* CAN BE UPDATED tp_new */
377  0, /* tp_free */
378 #if PY_MAJOR_VERSION > 3
379  0, /* tp_is_gc */
380  0, /* tp_bases */
381  0, /* tp_mro */
382  0, /* tp_cache */
383  0, /* tp_subclasses */
384  0, /* tp_weaklist */
385  0, /* tp_del */
386 #endif
387  0 /* tp_version_tag */
388 };
389 
390 
392 {
393  PyDateTime_IMPORT;
394  // The standard library function localtime() is not re-entrant: the same
395  // static structure is used for all calls. In a multi-threaded environment
396  // the function is not to be used.
397  // The POSIX standard defines a re-entrant version of the function:
398  // localtime_r.
399  // Visual C++ 6.0 and Borland 5.5 are missing it, but provide a thread-safe
400  // variant without changing the function semantics.
401  time_t ticks = d.getTicks();
402 #ifdef HAVE_LOCALTIME_R
403  struct tm t;
404  localtime_r(&ticks, &t);
405 #else
406  struct tm t = *localtime(&ticks);
407 #endif
408  obj = PyDateTime_FromDateAndTime(t.tm_year+1900, t.tm_mon+1, t.tm_mday,
409  t.tm_hour, t.tm_min, t.tm_sec, 0);
410 }
411 
412 
414 {
415  PyDateTime_IMPORT;
416  if (PyDateTime_Check(obj))
417  return Date(
418  PyDateTime_GET_YEAR(obj),
419  PyDateTime_GET_MONTH(obj),
420  PyDateTime_GET_DAY(obj),
421  PyDateTime_DATE_GET_HOUR(obj),
422  PyDateTime_DATE_GET_MINUTE(obj),
423  PyDateTime_DATE_GET_SECOND(obj)
424  );
425  else if (PyDate_Check(obj))
426  return Date(
427  PyDateTime_GET_YEAR(obj),
428  PyDateTime_GET_MONTH(obj),
429  PyDateTime_GET_DAY(obj)
430  );
431 #if PY_MAJOR_VERSION >= 3
432  else if (!PyUnicode_Check(obj))
433 #else
434  else if (PyString_Check(obj))
435 #endif
436  {
437  if (PyUnicode_Check(obj))
438  {
439  // Replace the unicode object with a string encoded in the correct locale
440  const_cast<PyObject*&>(obj) =
441  PyUnicode_AsEncodedString(obj, "UTF-8", "ignore");
442  }
443 #if PY_MAJOR_VERSION >= 3
444  return Date(PyBytes_AsString(PyObject_Str(obj)));
445 #else
446  return Date(PyString_AsString(PyObject_Str(obj)));
447 #endif
448  }
449  else
450  throw DataException(
451  "Invalid data type. Expecting datetime.date, datetime.datetime or string"
452  );
453 }
454 
455 
457 {
458  obj = p ? static_cast<PyObject*>(p) : Py_None;
459  Py_INCREF(obj);
460 }
461 
462 
463 DECLARE_EXPORT PythonType::PythonType(size_t base_size, const type_info* tp)
464  : cppClass(tp)
465 {
466  // Allocate a new type object if it doesn't exist yet.
467  // We copy from a template type definition.
468  table = new PyTypeObject(PyTypeObjectTemplate);
469  table->tp_basicsize = base_size;
470 }
471 
472 
474 {
475  // Scan the types already registered
476  for (vector<PythonType*>::const_iterator i = table.begin(); i != table.end(); ++i)
477  if (**i==*t) return *i;
478 
479  // Not found in the vector, so create a new one
480  PythonType *cachedTypePtr = new PythonType(size, t);
481  table.push_back(cachedTypePtr);
482  return cachedTypePtr;
483 }
484 
485 
486 DECLARE_EXPORT PyObject* Object::toXML(PyObject* self, PyObject* args)
487 {
488  try
489  {
490  // Parse the argument
491  PyObject *filearg = NULL;
492  char *mode = NULL;
493  if (!PyArg_ParseTuple(args, "|sO:toXML", &mode, &filearg))
494  return NULL;
495 
496  // Create the XML string.
497  ostringstream ch;
498  XMLOutput x(ch);
499  x.setReferencesOnly(true);
500  if (!mode || mode[0] == 'S')
502  else if (mode[0] == 'P')
504  else if (mode[0] == 'D')
506  else
507  throw DataException("Invalid output mode");
508 
509  // The next call assumes the self argument is an instance of the Object
510  // base class. We don't need to check this explicitly since we expose
511  // this method only on subclasses.
512  static_cast<Object*>(self)->writeElement
513  (&x, *(static_cast<Object*>(self)->getType().category->typetag));
514  // Write the output...
515  if (filearg)
516  {
517 #if PY_MAJOR_VERSION >= 3
518  if (PyObject_IsInstance(filearg, (PyObject*)&PyIOBase_Type))
519 #else
520  if (PyFile_Check(filearg))
521 #endif
522  {
523  // ... to a file
524  return PyFile_WriteString(ch.str().c_str(), filearg) ?
525  NULL : // Error writing to the file
526  Py_BuildValue("");
527  }
528  else
529  // The argument is not a file
530  throw LogicException("Expecting a file argument");
531  }
532  else
533  // ... to a string
534  return PythonObject(ch.str());
535  }
536  catch(...)
537  {
539  return NULL;
540  }
541  throw LogicException("Unreachable code reached");
542 }
543 
544 
546 (const char* method_name, PyCFunction f, int flags, const char* doc )
547 {
548  unsigned short i = 0;
549 
550  // Create a method table array
551  if (!table->tp_methods)
552  // Allocate a first block
553  table->tp_methods = new PyMethodDef[methodArraySize];
554  else
555  {
556  // Find the first non-empty method record
557  while (table->tp_methods[i].ml_name) i++;
558  if (i % methodArraySize == methodArraySize - 1)
559  {
560  // Allocation of a bigger buffer is required
561  PyMethodDef* tmp = new PyMethodDef[i + 1 + methodArraySize];
562  for(unsigned short j = 0; j < i; j++)
563  tmp[j] = table->tp_methods[j];
564  delete [] table->tp_methods;
565  table->tp_methods = tmp;
566  }
567  }
568 
569  // Populate a method definition struct
570  table->tp_methods[i].ml_name = method_name;
571  table->tp_methods[i].ml_meth = f;
572  table->tp_methods[i].ml_flags = flags;
573  table->tp_methods[i].ml_doc = doc;
574 
575  // Append an empty terminator record
576  table->tp_methods[++i].ml_name = NULL;
577  table->tp_methods[i].ml_meth = NULL;
578  table->tp_methods[i].ml_flags = 0;
579  table->tp_methods[i].ml_doc = NULL;
580 }
581 
582 
584 (const char* c, PyCFunctionWithKeywords f, int i, const char* d)
585 {
586  addMethod(c, reinterpret_cast<PyCFunction>(f), i | METH_KEYWORDS, d);
587 }
588 
589 
591 {
592  // Register the new type in the module
593  PyGILState_STATE state = PyGILState_Ensure();
594  if (PyType_Ready(table) < 0)
595  {
596  PyGILState_Release(state);
597  throw RuntimeException(string("Can't register python type ") + table->tp_name);
598  }
599  Py_INCREF(table);
600  int result = PyModule_AddObject(
602  table->tp_name + 8, // Note: +8 is to skip the "frepple." characters in the name
603  reinterpret_cast<PyObject*>(table)
604  );
605  PyGILState_Release(state);
606  return result;
607 }
608 
609 
611 {
612  // Rethrowing the exception to catch its type better
613  try {throw;}
614  catch (const DataException& e)
615  {PyErr_SetString(PythonDataException, e.what());}
616  catch (const LogicException& e)
617  {PyErr_SetString(PythonLogicException, e.what());}
618  catch (const RuntimeException& e)
619  {PyErr_SetString(PythonRuntimeException, e.what());}
620  catch (const exception& e)
621  {PyErr_SetString(PyExc_Exception, e.what());}
622  catch (...)
623  {PyErr_SetString(PyExc_Exception, "Unidentified exception");}
624 }
625 
626 
628 {
629  if (n.empty())
630  {
631  // Resetting to NULL when the string is empty
632  func = NULL;
633  return;
634  }
635 
636  // Find the Python function
637  PyGILState_STATE pythonstate = PyGILState_Ensure();
638  func = PyRun_String(n.c_str(), Py_eval_input,
639  PyEval_GetGlobals(), PyEval_GetLocals() );
640  if (!func)
641  {
642  PyGILState_Release(pythonstate);
643  throw DataException("Python function '" + n + "' not defined");
644  }
645  if (!PyCallable_Check(func))
646  {
647  PyGILState_Release(pythonstate);
648  throw DataException("Python object '" + n + "' is not a function");
649  }
650  Py_INCREF(func);
651 
652  // Store the Python function
653  PyGILState_Release(pythonstate);
654 }
655 
656 
658 {
659  if (!p || p == Py_None)
660  {
661  // Resetting to null
662  func = NULL;
663  return;
664  }
665 
666  if (!PyCallable_Check(p))
667  {
668  // It's not a callable object. Interprete it as a function name and
669  // look it up.
670  PyGILState_STATE pythonstate = PyGILState_Ensure();
671  string n = PythonObject(p).getString();
672  p = PyRun_String(n.c_str(), Py_eval_input,
673  PyEval_GetGlobals(), PyEval_GetLocals() );
674  if (!p)
675  {
676  PyGILState_Release(pythonstate);
677  throw DataException("Python function '" + n + "' not defined");
678  }
679  if (!PyCallable_Check(p))
680  {
681  PyGILState_Release(pythonstate);
682  throw DataException("Python object '" + n + "' is not a function");
683  }
684  PyGILState_Release(pythonstate);
685  }
686 
687  // Store the Python function
688  func = p;
689  Py_INCREF(func);
690 }
691 
692 
694 {
695  if (!func) return PythonObject();
696  PyGILState_STATE pythonstate = PyGILState_Ensure();
697  PyObject* result = PyEval_CallFunction(func, "()");
698  if (!result)
699  {
700  logger << "Error: Exception caught when calling Python function '"
701  << (func ? PyEval_GetFuncName(func) : "NULL") << "'" << endl;
702  if (PyErr_Occurred()) PyErr_PrintEx(0);
703  }
704  PyGILState_Release(pythonstate);
705  return PythonObject(result);
706 }
707 
708 
710 {
711  if (!func) return PythonObject();
712  PyGILState_STATE pythonstate = PyGILState_Ensure();
713  PyObject* result = PyEval_CallFunction(func, "(O)", p);
714  if (!result)
715  {
716  logger << "Error: Exception caught when calling Python function '"
717  << (func ? PyEval_GetFuncName(func) : "NULL") << "'" << endl;
718  if (PyErr_Occurred()) PyErr_PrintEx(0);
719  }
720  PyGILState_Release(pythonstate);
721  return PythonObject(result);
722 }
723 
724 
725 DECLARE_EXPORT PythonObject PythonFunction::call(const PyObject* p, const PyObject* q) const
726 {
727  if (!func) return PythonObject();
728  PyGILState_STATE pythonstate = PyGILState_Ensure();
729  PyObject* result = PyEval_CallFunction(func, "(OO)", p, q);
730  if (!result)
731  {
732  logger << "Error: Exception caught when calling Python function '"
733  << (func ? PyEval_GetFuncName(func) : "NULL") << "'" << endl;
734  if (PyErr_Occurred()) PyErr_PrintEx(0);
735  }
736  PyGILState_Release(pythonstate);
737  return PythonObject(result);
738 }
739 
740 
741 extern "C" DECLARE_EXPORT PyObject* getattro_handler(PyObject *self, PyObject *name)
742 {
743  try
744  {
745 #if PY_MAJOR_VERSION >= 3
746  if (!PyUnicode_Check(name))
747 #else
748  if (!PyString_Check(name))
749 #endif
750  {
751  PyErr_Format(PyExc_TypeError,
752 #if PY_MAJOR_VERSION >= 3
753  "attribute name must be string, not '%S'",
754  Py_TYPE(name)->tp_name);
755 #else
756  "attribute name must be string, not '%s'",
757  PyString_AsString(name));
758 #endif
759  return NULL;
760  }
761 #if PY_MAJOR_VERSION >= 3
762  PyObject* name_utf8 = PyUnicode_AsUTF8String(name);
763  PyObject* result = static_cast<PythonExtensionBase*>(self)->getattro(Attribute(PyBytes_AsString(name_utf8)));
764  Py_DECREF(name_utf8);
765 #else
766  PyObject* result = static_cast<PythonExtensionBase*>(self)->getattro(Attribute(PyString_AsString(name)));
767 #endif
768  // Exit 1: Normal
769  if (result) return result;
770  // Exit 2: Exception occurred
771  if (PyErr_Occurred()) return NULL;
772  // Exit 3: No error occurred but the attribute was not found.
773  // Use the standard generic function to pick up standard attributes
774  // (such as __class__, __doc__, ...)
775  // Note that this function also picks up attributes from base classes, but
776  // we can't rely on that: any C++ exceptions are lost along the way...
777  return PyObject_GenericGetAttr(self,name);
778  }
779  catch (...)
780  {
782  return NULL;
783  }
784 }
785 
786 
787 extern "C" DECLARE_EXPORT int setattro_handler(PyObject *self, PyObject *name, PyObject *value)
788 {
789  try
790  {
791  // Pick up the field name
792 #if PY_MAJOR_VERSION >= 3
793  if (!PyUnicode_Check(name))
794 #else
795  if (!PyString_Check(name))
796 #endif
797  {
798  PyErr_Format(PyExc_TypeError,
799 #if PY_MAJOR_VERSION >= 3
800  "attribute name must be string, not '%S'",
801  Py_TYPE(name)->tp_name);
802 #else
803  "attribute name must be string, not '%s'",
804  PyString_AsString(name));
805 #endif
806  return -1;
807  }
808  PythonObject field(value);
809 
810  // Call the object to update the attribute
811 #if PY_MAJOR_VERSION >= 3
812  PyObject* name_utf8 = PyUnicode_AsUTF8String(name);
813  int result = static_cast<PythonExtensionBase*>(self)->setattro(Attribute(PyBytes_AsString(name_utf8)), field);
814  Py_DECREF(name_utf8);
815 #else
816  int result = static_cast<PythonExtensionBase*>(self)->setattro(Attribute(PyBytes_AsString(name)), field);
817 #endif
818  // Process 'OK' result
819  if (!result) return 0;
820 
821  // Process 'not OK' result - set python error string if it isn't set yet
822  if (!PyErr_Occurred())
823  PyErr_Format(PyExc_AttributeError,
824 #if PY_MAJOR_VERSION >= 3
825  "attribute '%S' on '%s' can't be updated",
826  name, Py_TYPE(self)->tp_name);
827 #else
828  "attribute '%s' on '%s' can't be updated",
829  PyString_AsString(name), Py_TYPE(self)->tp_name);
830 #endif
831  return -1;
832  }
833  catch (...)
834  {
836  return -1;
837  }
838 }
839 
840 
841 extern "C" DECLARE_EXPORT PyObject* compare_handler(PyObject *self, PyObject *other, int op)
842 {
843  try
844  {
845  if (Py_TYPE(self) != Py_TYPE(other)
846  && Py_TYPE(self)->tp_base != Py_TYPE(other)->tp_base)
847  {
848  // Can't compare these objects.
849  Py_INCREF(Py_NotImplemented);
850  return Py_NotImplemented;
851  }
852  int result = static_cast<PythonExtensionBase*>(self)->compare(other);
853  switch(op)
854  {
855  case Py_LT: return PythonObject(result > 0);
856  case Py_LE: return PythonObject(result >= 0);
857  case Py_EQ: return PythonObject(result == 0);
858  case Py_NE: return PythonObject(result != 0);
859  case Py_GT: return PythonObject(result < 0);
860  case Py_GE: return PythonObject(result <= 0);
861  default: throw LogicException("Unknown operator in comparison");
862  }
863  }
864  catch (...)
865  {
867  return NULL;
868  }
869 }
870 
871 
872 extern "C" DECLARE_EXPORT PyObject* iternext_handler(PyObject *self)
873 {
874  try
875  {
876  return static_cast<PythonExtensionBase*>(self)->iternext();
877  }
878  catch (...)
879  {
881  return NULL;
882  }
883 }
884 
885 
886 extern "C" DECLARE_EXPORT PyObject* call_handler(PyObject* self, PyObject* args, PyObject* kwds)
887 {
888  try
889  {
890  return static_cast<PythonExtensionBase*>(self)->call(args, kwds);
891  }
892  catch (...)
893  {
895  return NULL;
896  }
897 }
898 
899 
900 extern "C" DECLARE_EXPORT PyObject* str_handler(PyObject* self)
901 {
902  try
903  {
904  return static_cast<PythonExtensionBase*>(self)->str();
905  }
906  catch (...)
907  {
909  return NULL;
910  }
911 }
912 
913 } // end namespace
914 } // end namespace