Logo ROOT   6.12/07
Reference Guide
MethodProxy.cxx
Go to the documentation of this file.
1 // @(#)root/pyroot:$Id$
2 // Author: Wim Lavrijsen, Jan 2005
3 
4 // Bindings
5 #include "PyROOT.h"
6 #include "structmember.h" // from Python
7 #if PY_VERSION_HEX >= 0x02050000
8 #include "code.h" // from Python
9 #else
10 #include "compile.h" // from Python
11 #endif
12 #ifndef CO_NOFREE
13 // python2.2 does not have CO_NOFREE defined
14 #define CO_NOFREE 0x0040
15 #endif
16 #include "MethodProxy.h"
17 #include "ObjectProxy.h"
18 #include "TCallContext.h"
19 #include "TPyException.h"
20 #include "PyStrings.h"
21 
22 // Standard
23 #include <algorithm>
24 #include <vector>
25 
26 
27 namespace PyROOT {
28 
29 // TODO: only used here, but may be better off integrated with Pythonize.cxx callbacks
30  class TPythonCallback : public PyCallable {
31  public:
32  PyObject* fCallable;
33 
34  TPythonCallback( PyObject* callable ):
35  fCallable(nullptr)
36  {
37  if ( !PyCallable_Check( callable ) ) {
38  PyErr_SetString(PyExc_TypeError, "parameter must be callable");
39  return;
40  }
41  fCallable = callable;
42  Py_INCREF( fCallable );
43  }
44 
45  virtual ~TPythonCallback() {
46  Py_DECREF( fCallable );
47  fCallable = 0;
48  }
49 
50  virtual PyObject* GetSignature() { return PyROOT_PyUnicode_FromString( "*args, **kwargs" ); } ;
51  virtual PyObject* GetPrototype() { return PyROOT_PyUnicode_FromString( "<callback>" ); } ;
52  virtual PyObject* GetDocString() {
53  if ( PyObject_HasAttrString( fCallable, "__doc__" )) {
54  return PyObject_GetAttrString( fCallable, "__doc__" );
55  } else {
56  return GetPrototype();
57  }
58  }
59 
60  virtual Int_t GetPriority() { return 100; };
61 
62  virtual Int_t GetMaxArgs() { return 100; };
63  virtual PyObject* GetCoVarNames() { // TODO: pick these up from the callable
64  Py_INCREF( Py_None );
65  return Py_None;
66  }
67  virtual PyObject* GetArgDefault( Int_t /* iarg */ ) { // TODO: pick these up from the callable
68  Py_INCREF( Py_None );
69  return Py_None;
70  }
71 
72  virtual PyObject* GetScopeProxy() { // should this be the module ??
73  Py_INCREF( Py_None );
74  return Py_None;
75  }
76 
77  virtual PyCallable* Clone() { return new TPythonCallback( *this ); }
78 
79  virtual PyObject* Call(
80  ObjectProxy*& self, PyObject* args, PyObject* kwds, TCallContext* /* ctxt = 0 */ ) {
81 
82  PyObject* newArgs = nullptr;
83  if ( self ) {
84  Py_ssize_t nargs = PyTuple_Size( args );
85  newArgs = PyTuple_New( nargs+1 );
86  Py_INCREF( self );
87  PyTuple_SET_ITEM( newArgs, 0, (PyObject*)self );
88  for ( Py_ssize_t iarg = 0; iarg < nargs; ++iarg ) {
89  PyObject* pyarg = PyTuple_GET_ITEM( args, iarg );
90  Py_INCREF( pyarg );
91  PyTuple_SET_ITEM( newArgs, iarg+1, pyarg );
92  }
93  } else {
94  Py_INCREF( args );
95  newArgs = args;
96  }
97  return PyObject_Call( fCallable, newArgs, kwds );
98  }
99  };
100 
101 namespace {
102 
103 // helper to test whether a method is used in a pseudo-function modus
104  Bool_t inline IsPseudoFunc( MethodProxy* pymeth )
105  {
106  return (void*)pymeth == (void*)pymeth->fSelf;
107  }
108 
109 // helper for collecting/maintaining exception data in overload dispatch
110  struct PyError_t {
111  PyError_t() { fType = fValue = fTrace = 0; }
112 
113  static void Clear( PyError_t& e )
114  {
115  // Remove exception information.
116  Py_XDECREF( e.fType ); Py_XDECREF( e.fValue ); Py_XDECREF( e.fTrace );
117  e.fType = e.fValue = e.fTrace = 0;
118  }
119 
121  };
122 
123 // helper to hash tuple (using tuple hash would cause self-tailing loops)
124  inline Long_t HashSignature( PyObject* args )
125  {
126  // Build a hash from the types of the given python function arguments.
127  ULong_t hash = 0;
128 
129  Int_t nargs = PyTuple_GET_SIZE( args );
130  for ( Int_t i = 0; i < nargs; ++i ) {
131  hash += (ULong_t) Py_TYPE( PyTuple_GET_ITEM( args, i ) );
132  hash += (hash << 10); hash ^= (hash >> 6);
133  }
134 
135  hash += (hash << 3); hash ^= (hash >> 11); hash += (hash << 15);
136 
137  return hash;
138  }
139 
140 // helper to sort on method priority
141  int PriorityCmp( PyCallable* left, PyCallable* right )
142  {
143  return left->GetPriority() > right->GetPriority();
144  }
145 
146 // return helper
147  inline void ResetCallState( ObjectProxy*& selfnew, ObjectProxy* selfold, Bool_t clear ) {
148  if ( selfnew != selfold ) {
149  Py_XDECREF( selfnew );
150  selfnew = selfold;
151  }
152 
153  if ( clear )
154  PyErr_Clear();
155  }
156 
157 // helper to factor out return logic of mp_call
158  inline PyObject* HandleReturn( MethodProxy* pymeth, ObjectProxy* oldSelf, PyObject* result ) {
159 
160  // special case for python exceptions, propagated through C++ layer
161  if ( result ) {
162 
163  // if this method creates new objects, always take ownership
164  if ( IsCreator( pymeth->fMethodInfo->fFlags ) ) {
165 
166  // either be a constructor with a fresh object proxy self ...
167  if ( IsConstructor( pymeth->fMethodInfo->fFlags ) ) {
168  if ( pymeth->fSelf )
169  pymeth->fSelf->HoldOn();
170  }
171 
172  // ... or be a method with an object proxy return value
173  else if ( ObjectProxy_Check( result ) )
174  ((ObjectProxy*)result)->HoldOn();
175  }
176 
177  // if this new object falls inside self, make sure its lifetime is proper
178  if ( ObjectProxy_Check( pymeth->fSelf ) && ObjectProxy_Check( result ) ) {
179  Long_t ptrdiff = (Long_t)((ObjectProxy*)result)->GetObject() - (Long_t)pymeth->fSelf->GetObject();
180  if ( 0 <= ptrdiff && ptrdiff < (Long_t)Cppyy::SizeOf( pymeth->fSelf->ObjectIsA() ) ) {
181  if ( PyObject_SetAttr( result, PyStrings::gLifeLine, (PyObject*)pymeth->fSelf ) == -1 )
182  PyErr_Clear(); // ignored
183  }
184  }
185  }
186 
187  // reset self as necessary to allow re-use of the MethodProxy
188  ResetCallState( pymeth->fSelf, oldSelf, kFALSE );
189 
190  return result;
191  }
192 
193 
194 //= PyROOT method proxy object behaviour =====================================
195  PyObject* mp_name( MethodProxy* pymeth, void* )
196  {
197  return PyROOT_PyUnicode_FromString( pymeth->GetName().c_str() );
198  }
199 
200 ////////////////////////////////////////////////////////////////////////////////
201 
202  PyObject* mp_module( MethodProxy* /* pymeth */, void* )
203  {
204  Py_INCREF( PyStrings::gROOTns );
205  return PyStrings::gROOTns;
206  }
207 
208 ////////////////////////////////////////////////////////////////////////////////
209 /// Build python document string ('__doc__') from all C++-side overloads.
210 
211  PyObject* mp_doc( MethodProxy* pymeth, void* )
212  {
213  MethodProxy::Methods_t& methods = pymeth->fMethodInfo->fMethods;
214 
215  // collect doc strings
216  Int_t nMethods = methods.size();
217 
218  // from template proxy with no instantiations
219  if ( nMethods == 0 )
220  return NULL;
221 
222  PyObject* doc = methods[0]->GetDocString();
223 
224  // simple case
225  if ( nMethods == 1 )
226  return doc;
227 
228  // overloaded method
229  PyObject* separator = PyROOT_PyUnicode_FromString( "\n" );
230  for ( Int_t i = 1; i < nMethods; ++i ) {
231  PyROOT_PyUnicode_Append( &doc, separator );
232  PyROOT_PyUnicode_AppendAndDel( &doc, methods[i]->GetDocString() );
233  }
234  Py_DECREF( separator );
235 
236  return doc;
237  }
238 
239 ////////////////////////////////////////////////////////////////////////////////
240 /// Create a new method proxy to be returned.
241 
242  PyObject* mp_meth_func( MethodProxy* pymeth, void* )
243  {
244  MethodProxy* newPyMeth = (MethodProxy*)MethodProxy_Type.tp_alloc( &MethodProxy_Type, 0 );
245 
246  // method info is shared, as it contains the collected overload knowledge
247  *pymeth->fMethodInfo->fRefCount += 1;
248  newPyMeth->fMethodInfo = pymeth->fMethodInfo;
249 
250  // new method is unbound, use of 'meth' is for keeping track whether this
251  // proxy is used in the capacity of a method or a function
252  newPyMeth->fSelf = (ObjectProxy*)newPyMeth;
253 
254  return (PyObject*)newPyMeth;
255  }
256 
257 ////////////////////////////////////////////////////////////////////////////////
258 /// Return the bound self, if any; in case of pseudo-function role, pretend
259 /// that the data member im_self does not exist.
260 
261  PyObject* mp_meth_self( MethodProxy* pymeth, void* )
262  {
263  if ( IsPseudoFunc( pymeth ) ) {
264  PyErr_Format( PyExc_AttributeError,
265  "function %s has no attribute \'im_self\'", pymeth->fMethodInfo->fName.c_str() );
266  return 0;
267  } else if ( pymeth->fSelf != 0 ) {
268  Py_INCREF( (PyObject*)pymeth->fSelf );
269  return (PyObject*)pymeth->fSelf;
270  }
271 
272  Py_INCREF( Py_None );
273  return Py_None;
274  }
275 
276 ////////////////////////////////////////////////////////////////////////////////
277 /// Return scoping class; in case of pseudo-function role, pretend that there
278 /// is no encompassing class (i.e. global scope).
279 
280  PyObject* mp_meth_class( MethodProxy* pymeth, void* )
281  {
282  if ( ! IsPseudoFunc( pymeth ) ) {
283  PyObject* pyclass = pymeth->fMethodInfo->fMethods[0]->GetScopeProxy();
284  if ( ! pyclass )
285  PyErr_Format( PyExc_AttributeError,
286  "function %s has no attribute \'im_class\'", pymeth->fMethodInfo->fName.c_str() );
287  return pyclass;
288  }
289 
290  Py_INCREF( Py_None );
291  return Py_None;
292  }
293 
294 ////////////////////////////////////////////////////////////////////////////////
295 /// Stub only, to fill out the python function interface.
296 
297  PyObject* mp_func_closure( MethodProxy* /* pymeth */, void* )
298  {
299  Py_INCREF( Py_None );
300  return Py_None;
301  }
302 
303 ////////////////////////////////////////////////////////////////////////////////
304 /// Code details are used in module inspect to fill out interactive help()
305 
306  PyObject* mp_func_code( MethodProxy* pymeth, void* )
307  {
308 #if PY_VERSION_HEX < 0x03000000
309  MethodProxy::Methods_t& methods = pymeth->fMethodInfo->fMethods;
310 
311  // collect arguments only if there is just 1 overload, otherwise put in a
312  // fake *args (see below for co_varnames)
313  PyObject* co_varnames = methods.size() == 1 ? methods[0]->GetCoVarNames() : NULL;
314  if ( !co_varnames ) {
315  // TODO: static methods need no 'self' (but is harmless otherwise)
316  co_varnames = PyTuple_New( 1 /* self */ + 1 /* fake */ );
317  PyTuple_SET_ITEM( co_varnames, 0, PyROOT_PyUnicode_FromString( "self" ) );
318  PyTuple_SET_ITEM( co_varnames, 1, PyROOT_PyUnicode_FromString( "*args" ) );
319  }
320 
321  int co_argcount = PyTuple_Size( co_varnames );
322 
323  // for now, code object representing the statement 'pass'
324  PyObject* co_code = PyString_FromStringAndSize( "d\x00\x00S", 4 );
325 
326  // tuples with all the const literals used in the function
327  PyObject* co_consts = PyTuple_New( 0 );
328  PyObject* co_names = PyTuple_New( 0 );
329 
330  // names, freevars, and cellvars go unused
331  PyObject* co_unused = PyTuple_New( 0 );
332 
333  // filename is made-up
334  PyObject* co_filename = PyString_FromString( "ROOT.py" );
335 
336  // name is the function name, also through __name__ on the function itself
337  PyObject* co_name = PyString_FromString( pymeth->GetName().c_str() );
338 
339  // firstlineno is the line number of first function code in the containing scope
340 
341  // lnotab is a packed table that maps instruction count and line number
342  PyObject* co_lnotab = PyString_FromString( "\x00\x01\x0c\x01" );
343 
344  PyObject* code = (PyObject*)PyCode_New(
345  co_argcount, // argcount
346  co_argcount + 1, // nlocals
347  2, // stacksize
348  CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE, // flags
349  co_code, // code
350  co_consts, // consts
351  co_names, // names
352  co_varnames, // varnames
353  co_unused, // freevars
354  co_unused, // cellvars
355  co_filename, // filename
356  co_name, // name
357  1, // firstlineno
358  co_lnotab ); // lnotab
359 
360  Py_DECREF( co_lnotab );
361  Py_DECREF( co_name );
362  Py_DECREF( co_unused );
363  Py_DECREF( co_filename );
364  Py_DECREF( co_varnames );
365  Py_DECREF( co_names );
366  Py_DECREF( co_consts );
367  Py_DECREF( co_code );
368 
369  return code;
370 #else
371 // not important for functioning of most code, so not implemented for p3 for now (TODO)
372  pymeth = 0;
373  if ( pymeth || !pymeth) Py_INCREF( Py_None );
374  return Py_None;
375 #endif
376  }
377 
378 ////////////////////////////////////////////////////////////////////////////////
379 /// Create a tuple of default values, if there is only one method (otherwise
380 /// leave undefined: this is only used by inspect for interactive help())
381 
382  PyObject* mp_func_defaults( MethodProxy* pymeth, void* )
383  {
384  MethodProxy::Methods_t& methods = pymeth->fMethodInfo->fMethods;
385 
386  if ( methods.size() != 1 )
387  return PyTuple_New( 0 );
388 
389  int maxarg = methods[0]->GetMaxArgs();
390 
391  PyObject* defaults = PyTuple_New( maxarg );
392 
393  int itup = 0;
394  for ( int iarg = 0; iarg < maxarg; ++iarg ) {
395  PyObject* defvalue = methods[0]->GetArgDefault( iarg );
396  if ( defvalue )
397  PyTuple_SET_ITEM( defaults, itup++, defvalue );
398  }
399  _PyTuple_Resize( &defaults, itup );
400 
401  return defaults;
402  }
403 
404 ////////////////////////////////////////////////////////////////////////////////
405 /// Return this function's global dict (hard-wired to be the ROOT module); used
406 /// for lookup of names from co_code indexing into co_names.
407 
408  PyObject* mp_func_globals( MethodProxy* /* pymeth */, void* )
409  {
410  PyObject* pyglobal = PyModule_GetDict( PyImport_AddModule( (char*)"ROOT" ) );
411  Py_XINCREF( pyglobal );
412  return pyglobal;
413  }
414 
415 ////////////////////////////////////////////////////////////////////////////////
416 /// Get '_creates' boolean, which determines ownership of return values.
417 
418  PyObject* mp_getcreates( MethodProxy* pymeth, void* )
419  {
420  return PyInt_FromLong( (Bool_t)IsCreator( pymeth->fMethodInfo->fFlags ) );
421  }
422 
423 ////////////////////////////////////////////////////////////////////////////////
424 /// Set '_creates' boolean, which determines ownership of return values.
425 
426  int mp_setcreates( MethodProxy* pymeth, PyObject* value, void* )
427  {
428  if ( ! value ) { // means that _creates is being deleted
429  pymeth->fMethodInfo->fFlags &= ~TCallContext::kIsCreator;
430  return 0;
431  }
432 
433  Long_t iscreator = PyLong_AsLong( value );
434  if ( iscreator == -1 && PyErr_Occurred() ) {
435  PyErr_SetString( PyExc_ValueError, "a boolean 1 or 0 is required for _creates" );
436  return -1;
437  }
438 
439  if ( iscreator )
440  pymeth->fMethodInfo->fFlags |= TCallContext::kIsCreator;
441  else
442  pymeth->fMethodInfo->fFlags &= ~TCallContext::kIsCreator;
443 
444  return 0;
445  }
446 
447 ////////////////////////////////////////////////////////////////////////////////
448 /// Get '_mempolicy' enum, which determines ownership of call arguments.
449 
450  PyObject* mp_getmempolicy( MethodProxy* pymeth, void* )
451  {
452  if ( (Bool_t)(pymeth->fMethodInfo->fFlags & TCallContext::kUseHeuristics ) )
453  return PyInt_FromLong( TCallContext::kUseHeuristics );
454 
455  if ( (Bool_t)(pymeth->fMethodInfo->fFlags & TCallContext::kUseStrict ) )
456  return PyInt_FromLong( TCallContext::kUseStrict );
457 
458  return PyInt_FromLong( -1 );
459  }
460 
461 ////////////////////////////////////////////////////////////////////////////////
462 /// Set '_mempolicy' enum, which determines ownership of call arguments.
463 
464  int mp_setmempolicy( MethodProxy* pymeth, PyObject* value, void* )
465  {
466  Long_t mempolicy = PyLong_AsLong( value );
467  if ( mempolicy == TCallContext::kUseHeuristics ) {
468  pymeth->fMethodInfo->fFlags |= TCallContext::kUseHeuristics;
469  pymeth->fMethodInfo->fFlags &= ~TCallContext::kUseStrict;
470  } else if ( mempolicy == TCallContext::kUseStrict ) {
471  pymeth->fMethodInfo->fFlags |= TCallContext::kUseStrict;
472  pymeth->fMethodInfo->fFlags &= ~TCallContext::kUseHeuristics;
473  } else {
474  PyErr_SetString( PyExc_ValueError,
475  "expected kMemoryStrict or kMemoryHeuristics as value for _mempolicy" );
476  return -1;
477  }
478 
479  return 0;
480  }
481 
482 ////////////////////////////////////////////////////////////////////////////////
483 /// Get '_manage_smart_ptr' boolean, which determines whether or not to
484 /// manage returned smart pointers intelligently.
485 
486  PyObject* mp_get_manage_smart_ptr( MethodProxy* pymeth, void* )
487  {
488  return PyInt_FromLong(
489  (Bool_t)(pymeth->fMethodInfo->fFlags & TCallContext::kManageSmartPtr) );
490  }
491 
492 ////////////////////////////////////////////////////////////////////////////////
493 /// Set '_manage_smart_ptr' boolean, which determines whether or not to
494 /// manage returned smart pointers intelligently.
495 
496  int mp_set_manage_smart_ptr( MethodProxy* pymeth, PyObject* value, void* )
497  {
498  Long_t policy = PyLong_AsLong( value );
499  if ( policy == -1 && PyErr_Occurred() ) {
500  PyErr_SetString( PyExc_ValueError, "a boolean 1 or 0 is required for _manage_smart_ptr" );
501  return -1;
502  }
503 
504  pymeth->fMethodInfo->fFlags |= TCallContext::kManageSmartPtr;
505 
506  return 0;
507  }
508 
509 ////////////////////////////////////////////////////////////////////////////////
510 /// Get '_threaded' boolean, which determines whether the GIL will be released.
511 
512  PyObject* mp_getthreaded( MethodProxy* pymeth, void* )
513  {
514  return PyInt_FromLong(
515  (Bool_t)(pymeth->fMethodInfo->fFlags & TCallContext::kReleaseGIL) );
516  }
517 
518 ////////////////////////////////////////////////////////////////////////////////
519 /// Set '_threaded' boolean, which determines whether the GIL will be released.
520 
521  int mp_setthreaded( MethodProxy* pymeth, PyObject* value, void* )
522  {
523  Long_t isthreaded = PyLong_AsLong( value );
524  if ( isthreaded == -1 && PyErr_Occurred() ) {
525  PyErr_SetString( PyExc_ValueError, "a boolean 1 or 0 is required for _creates" );
526  return -1;
527  }
528 
529  if ( isthreaded )
530  pymeth->fMethodInfo->fFlags |= TCallContext::kReleaseGIL;
531  else
532  pymeth->fMethodInfo->fFlags &= ~TCallContext::kReleaseGIL;
533 
534  return 0;
535  }
536 
537 ////////////////////////////////////////////////////////////////////////////////
538 
539  PyGetSetDef mp_getset[] = {
540  { (char*)"__name__", (getter)mp_name, NULL, NULL, NULL },
541  { (char*)"__module__", (getter)mp_module, NULL, NULL, NULL },
542  { (char*)"__doc__", (getter)mp_doc, NULL, NULL, NULL },
543 
544  // to be more python-like, where these are duplicated as well; to actually
545  // derive from the python method or function type is too memory-expensive,
546  // given that most of the members of those types would not be used
547  { (char*)"im_func", (getter)mp_meth_func, NULL, NULL, NULL },
548  { (char*)"im_self", (getter)mp_meth_self, NULL, NULL, NULL },
549  { (char*)"im_class", (getter)mp_meth_class, NULL, NULL, NULL },
550 
551  { (char*)"func_closure", (getter)mp_func_closure, NULL, NULL, NULL },
552  { (char*)"func_code", (getter)mp_func_code, NULL, NULL, NULL },
553  { (char*)"func_defaults", (getter)mp_func_defaults, NULL, NULL, NULL },
554  { (char*)"func_globals", (getter)mp_func_globals, NULL, NULL, NULL },
555  { (char*)"func_doc", (getter)mp_doc, NULL, NULL, NULL },
556  { (char*)"func_name", (getter)mp_name, NULL, NULL, NULL },
557 
558  { (char*)"_creates", (getter)mp_getcreates, (setter)mp_setcreates,
559  (char*)"For ownership rules of result: if true, objects are python-owned", NULL },
560  { (char*)"_mempolicy", (getter)mp_getmempolicy, (setter)mp_setmempolicy,
561  (char*)"For argument ownership rules: like global, either heuristic or strict", NULL },
562  { (char*)"_manage_smart_ptr", (getter)mp_get_manage_smart_ptr, (setter)mp_set_manage_smart_ptr,
563  (char*)"If a smart pointer is returned, determines management policy.", NULL },
564  { (char*)"_threaded", (getter)mp_getthreaded, (setter)mp_setthreaded,
565  (char*)"If true, releases GIL on call into C++", NULL },
566  { (char*)NULL, NULL, NULL, NULL, NULL }
567  };
568 
569 //= PyROOT method proxy function behavior ====================================
570  PyObject* mp_call( MethodProxy* pymeth, PyObject* args, PyObject* kwds )
571  {
572  // Call the appropriate overload of this method.
573 
574  // if called through im_func pseudo-representation (this can be gamed if the
575  // user really wants to ... )
576  if ( IsPseudoFunc( pymeth ) )
577  pymeth->fSelf = NULL;
578 
579  ObjectProxy* oldSelf = pymeth->fSelf;
580 
581  // get local handles to proxy internals
582  auto& methods = pymeth->fMethodInfo->fMethods;
583  auto& dispatchMap = pymeth->fMethodInfo->fDispatchMap;
584  auto& mflags = pymeth->fMethodInfo->fFlags;
585 
586  Int_t nMethods = methods.size();
587 
588  TCallContext ctxt = { 0 };
589  ctxt.fFlags |= (mflags & TCallContext::kUseHeuristics);
590  ctxt.fFlags |= (mflags & TCallContext::kUseStrict);
591  ctxt.fFlags |= (mflags & TCallContext::kManageSmartPtr);
592  if ( ! ctxt.fFlags ) ctxt.fFlags |= TCallContext::sMemoryPolicy;
593  ctxt.fFlags |= (mflags & TCallContext::kReleaseGIL);
594 
595  // simple case
596  if ( nMethods == 1 ) {
597  PyObject* result = methods[0]->Call( pymeth->fSelf, args, kwds, &ctxt );
598  return HandleReturn( pymeth, oldSelf, result );
599  }
600 
601  // otherwise, handle overloading
602  Long_t sighash = HashSignature( args );
603 
604  // look for known signatures ...
605  MethodProxy::DispatchMap_t::iterator m = dispatchMap.find( sighash );
606  if ( m != dispatchMap.end() ) {
607  Int_t index = m->second;
608  PyObject* result = methods[ index ]->Call( pymeth->fSelf, args, kwds, &ctxt );
609  result = HandleReturn( pymeth, oldSelf, result );
610 
611  if ( result != 0 )
612  return result;
613 
614  // fall through: python is dynamic, and so, the hashing isn't infallible
615  ResetCallState( pymeth->fSelf, oldSelf, kTRUE );
616  }
617 
618  // ... otherwise loop over all methods and find the one that does not fail
619  if ( ! IsSorted( mflags ) ) {
620  std::stable_sort( methods.begin(), methods.end(), PriorityCmp );
621  mflags |= TCallContext::kIsSorted;
622  }
623 
624  std::vector< PyError_t > errors;
625  for ( Int_t i = 0; i < nMethods; ++i ) {
626  PyObject* result = methods[i]->Call( pymeth->fSelf, args, kwds, &ctxt );
627 
628  if ( result != 0 ) {
629  // success: update the dispatch map for subsequent calls
630  dispatchMap[ sighash ] = i;
631  std::for_each( errors.begin(), errors.end(), PyError_t::Clear );
632  return HandleReturn( pymeth, oldSelf, result );
633  }
634 
635  // failure: collect error message/trace (automatically clears exception, too)
636  if ( ! PyErr_Occurred() ) {
637  // this should not happen; set an error to prevent core dump and report
638  PyObject* sig = methods[i]->GetPrototype();
639  PyErr_Format( PyExc_SystemError, "%s =>\n %s",
640  PyROOT_PyUnicode_AsString( sig ), (char*)"NULL result without error in mp_call" );
641  Py_DECREF( sig );
642  }
643  PyError_t e;
644  PyErr_Fetch( &e.fType, &e.fValue, &e.fTrace );
645  errors.push_back( e );
646  ResetCallState( pymeth->fSelf, oldSelf, kFALSE );
647  }
648 
649  // first summarize, then add details
651  "none of the %d overloaded methods succeeded. Full details:", nMethods );
652  PyObject* separator = PyROOT_PyUnicode_FromString( "\n " );
653 
654  // if this point is reached, none of the overloads succeeded: notify user
655  PyObject* exc_type = NULL;
656  for ( std::vector< PyError_t >::iterator e = errors.begin(); e != errors.end(); ++e ) {
657  if ( e->fType != PyExc_NotImplementedError ) {
658  if ( ! exc_type ) exc_type = e->fType;
659  else if ( exc_type != e->fType ) exc_type = PyExc_TypeError;
660  }
661  PyROOT_PyUnicode_Append( &value, separator );
662  PyROOT_PyUnicode_Append( &value, e->fValue );
663  }
664 
665  Py_DECREF( separator );
666  std::for_each( errors.begin(), errors.end(), PyError_t::Clear );
667 
668  // report failure
669  PyErr_SetObject( exc_type ? exc_type : PyExc_TypeError, value );
670  Py_DECREF( value );
671  return 0;
672  }
673 
674 ////////////////////////////////////////////////////////////////////////////////
675 /// Descriptor; create and return a new bound method proxy (language requirement).
676 
677  MethodProxy* mp_descrget( MethodProxy* pymeth, ObjectProxy* pyobj, PyObject* )
678  {
679  MethodProxy* newPyMeth = (MethodProxy*)MethodProxy_Type.tp_alloc( &MethodProxy_Type, 0 );
680 
681  // method info is shared, as it contains the collected overload knowledge
682  *pymeth->fMethodInfo->fRefCount += 1;
683  newPyMeth->fMethodInfo = pymeth->fMethodInfo;
684 
685  // new method is to be bound to current object (may be NULL)
686  Py_XINCREF( (PyObject*)pyobj );
687  newPyMeth->fSelf = pyobj;
688 
689  return newPyMeth;
690  }
691 
692 
693 //= PyROOT method proxy construction/destruction =================================
694  MethodProxy* mp_new( PyTypeObject*, PyObject*, PyObject* )
695  {
696  // Create a new method proxy object.
697  MethodProxy* pymeth = PyObject_GC_New( MethodProxy, &MethodProxy_Type );
698  pymeth->fSelf = NULL;
699  pymeth->fMethodInfo = new MethodProxy::MethodInfo_t;
700 
701  PyObject_GC_Track( pymeth );
702  return pymeth;
703  }
704 
705 ////////////////////////////////////////////////////////////////////////////////
706 /// Deallocate memory held by method proxy object.
707 
708  void mp_dealloc( MethodProxy* pymeth )
709  {
710  PyObject_GC_UnTrack( pymeth );
711 
712  if ( ! IsPseudoFunc( pymeth ) )
713  Py_CLEAR( pymeth->fSelf );
714  pymeth->fSelf = NULL;
715 
716  if ( --(*pymeth->fMethodInfo->fRefCount) <= 0 ) {
717  delete pymeth->fMethodInfo;
718  }
719 
720  PyObject_GC_Del( pymeth );
721  }
722 
723 
724 ////////////////////////////////////////////////////////////////////////////////
725 /// Hash of method proxy object for insertion into dictionaries; with actual
726 /// method (fMethodInfo) shared, its address is best suited.
727 
728  Long_t mp_hash( MethodProxy* pymeth )
729  {
730  return _Py_HashPointer( pymeth->fMethodInfo );
731  }
732 
733 ////////////////////////////////////////////////////////////////////////////////
734 /// Garbage collector traverse of held python member objects.
735 
736  int mp_traverse( MethodProxy* pymeth, visitproc visit, void* args )
737  {
738  if ( pymeth->fSelf && ! IsPseudoFunc( pymeth ) )
739  return visit( (PyObject*)pymeth->fSelf, args );
740 
741  return 0;
742  }
743 
744 ////////////////////////////////////////////////////////////////////////////////
745 /// Garbage collector clear of held python member objects.
746 
747  int mp_clear( MethodProxy* pymeth )
748  {
749  if ( ! IsPseudoFunc( pymeth ) )
750  Py_CLEAR( pymeth->fSelf );
751  pymeth->fSelf = NULL;
752 
753  return 0;
754  }
755 
756 ////////////////////////////////////////////////////////////////////////////////
757 /// Rich set of comparison objects; only equals is defined.
758 
759  PyObject* mp_richcompare( MethodProxy* self, MethodProxy* other, int op )
760  {
761  if ( op != Py_EQ )
762  return PyType_Type.tp_richcompare( (PyObject*)self, (PyObject*)other, op );
763 
764  // defined by type + (shared) MethodInfo + bound self, with special case for fSelf (i.e. pseudo-function)
765  if ( ( Py_TYPE(self) == Py_TYPE(other) && self->fMethodInfo == other->fMethodInfo ) && \
766  ( ( IsPseudoFunc( self ) && IsPseudoFunc( other ) ) || self->fSelf == other->fSelf ) ) {
767  Py_INCREF( Py_True );
768  return Py_True;
769  }
770  Py_INCREF( Py_False );
771  return Py_False;
772  }
773 
774 
775 //= PyROOT method proxy access to internals =================================
776  PyObject* mp_disp( MethodProxy* pymeth, PyObject* sigarg )
777  {
778  // Select and call a specific C++ overload, based on its signature.
779  if ( ! PyROOT_PyUnicode_Check( sigarg ) ) {
780  PyErr_Format( PyExc_TypeError, "disp() argument 1 must be string, not %.50s",
781  sigarg == Py_None ? "None" : Py_TYPE(sigarg)->tp_name );
782  return 0;
783  }
784 
786 
787  MethodProxy::Methods_t& methods = pymeth->fMethodInfo->fMethods;
788  for ( Int_t i = 0; i < (Int_t)methods.size(); ++i ) {
789 
790  PyObject* sig2 = methods[ i ]->GetSignature();
791  if ( PyObject_RichCompareBool( sig1, sig2, Py_EQ ) ) {
792  Py_DECREF( sig2 );
793 
794  MethodProxy* newmeth = mp_new( NULL, NULL, NULL );
795  MethodProxy::Methods_t vec; vec.push_back( methods[ i ]->Clone() );
796  newmeth->Set( pymeth->fMethodInfo->fName, vec );
797 
798  if ( pymeth->fSelf && ! IsPseudoFunc( pymeth ) ) {
799  Py_INCREF( pymeth->fSelf );
800  newmeth->fSelf = pymeth->fSelf;
801  }
802 
803  Py_DECREF( sig1 );
804  return (PyObject*)newmeth;
805  }
806 
807  Py_DECREF( sig2 );
808  }
809 
810  Py_DECREF( sig1 );
811  PyErr_Format( PyExc_LookupError, "signature \"%s\" not found", PyROOT_PyUnicode_AsString( sigarg ) );
812  return 0;
813  }
814 
815 //= PyROOT method proxy access to internals =================================
816  PyObject* mp_add_overload( MethodProxy* pymeth, PyObject* new_overload )
817  {
818  TPythonCallback* cb = new TPythonCallback(new_overload);
819  pymeth->AddMethod( cb );
820  Py_INCREF( Py_None );
821  return Py_None;
822  }
823 
824  PyMethodDef mp_methods[] = {
825  { (char*)"disp", (PyCFunction)mp_disp, METH_O, (char*)"select overload for dispatch" },
826  { (char*)"__add_overload__", (PyCFunction)mp_add_overload, METH_O, (char*)"add a new overload" },
827  { (char*)NULL, NULL, 0, NULL }
828  };
829 
830 } // unnamed namespace
831 
832 ////////////////////////////////////////////////////////////////////////////////
833 
834 
835 //= PyROOT method proxy type =================================================
836 PyTypeObject MethodProxy_Type = {
837  PyVarObject_HEAD_INIT( &PyType_Type, 0 )
838  (char*)"ROOT.MethodProxy", // tp_name
839  sizeof(MethodProxy), // tp_basicsize
840  0, // tp_itemsize
841  (destructor)mp_dealloc, // tp_dealloc
842  0, // tp_print
843  0, // tp_getattr
844  0, // tp_setattr
845  0, // tp_compare
846  0, // tp_repr
847  0, // tp_as_number
848  0, // tp_as_sequence
849  0, // tp_as_mapping
850  (hashfunc)mp_hash, // tp_hash
851  (ternaryfunc)mp_call, // tp_call
852  0, // tp_str
853  0, // tp_getattro
854  0, // tp_setattro
855  0, // tp_as_buffer
856  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, // tp_flags
857  (char*)"PyROOT method proxy (internal)", // tp_doc
858  (traverseproc)mp_traverse, // tp_traverse
859  (inquiry)mp_clear, // tp_clear
860  (richcmpfunc)mp_richcompare, // tp_richcompare
861  0, // tp_weaklistoffset
862  0, // tp_iter
863  0, // tp_iternext
864  mp_methods, // tp_methods
865  0, // tp_members
866  mp_getset, // tp_getset
867  0, // tp_base
868  0, // tp_dict
869  (descrgetfunc)mp_descrget, // tp_descr_get
870  0, // tp_descr_set
871  0, // tp_dictoffset
872  0, // tp_init
873  0, // tp_alloc
874  (newfunc)mp_new, // tp_new
875  0, // tp_free
876  0, // tp_is_gc
877  0, // tp_bases
878  0, // tp_mro
879  0, // tp_cache
880  0, // tp_subclasses
881  0 // tp_weaklist
882 #if PY_VERSION_HEX >= 0x02030000
883  , 0 // tp_del
884 #endif
885 #if PY_VERSION_HEX >= 0x02060000
886  , 0 // tp_version_tag
887 #endif
888 #if PY_VERSION_HEX >= 0x03040000
889  , 0 // tp_finalize
890 #endif
891 };
892 
893 } // namespace PyROOT
894 
895 
896 //- public members -----------------------------------------------------------
897 void PyROOT::MethodProxy::Set( const std::string& name, std::vector< PyCallable* >& methods )
898 {
899 // Fill in the data of a freshly created method proxy.
900  fMethodInfo->fName = name;
901  fMethodInfo->fMethods.swap( methods );
902  fMethodInfo->fFlags &= ~TCallContext::kIsSorted;
903  fMethodInfo->fFlags |= TCallContext::kManageSmartPtr;
904 
905 // special case: all constructors are considered creators by default
906  if ( name == "__init__" )
907  fMethodInfo->fFlags |= (TCallContext::kIsCreator | TCallContext::kIsConstructor);
908 
909 // special case, in heuristics mode also tag *Clone* methods as creators
911  name.find( "Clone" ) != std::string::npos )
912  fMethodInfo->fFlags |= TCallContext::kIsCreator;
913 }
914 
915 ////////////////////////////////////////////////////////////////////////////////
916 /// Fill in the data of a freshly created method proxy.
917 
919 {
920  fMethodInfo->fMethods.push_back( pc );
921  fMethodInfo->fFlags &= ~TCallContext::kIsSorted;
922 }
923 
924 ////////////////////////////////////////////////////////////////////////////////
925 
927 {
928  fMethodInfo->fMethods.insert( fMethodInfo->fMethods.end(),
929  meth->fMethodInfo->fMethods.begin(), meth->fMethodInfo->fMethods.end() );
930  fMethodInfo->fFlags &= ~TCallContext::kIsSorted;
931 }
932 
933 ////////////////////////////////////////////////////////////////////////////////
934 /// Destructor (this object is reference counted).
935 
937 {
938  for ( Methods_t::iterator it = fMethods.begin(); it != fMethods.end(); ++it ) {
939  delete *it;
940  }
941  fMethods.clear();
942  delete fRefCount;
943 }
#define PyROOT_PyUnicode_FromString
Definition: PyROOT.h:71
auto * m
Definition: textangle.C:8
#define CO_NOFREE
Definition: MethodProxy.cxx:14
PyObject * fTrace
void Set(const std::string &name, std::vector< PyCallable * > &methods)
std::vector< PyCallable *> Methods_t
Definition: MethodProxy.h:24
int Int_t
Definition: RtypesCore.h:41
bool Bool_t
Definition: RtypesCore.h:59
#define PyVarObject_HEAD_INIT(type, size)
Definition: PyROOT.h:149
Bool_t IsSorted(UInt_t flags)
Definition: TCallContext.h:61
#define PyROOT_PyUnicode_FromFormat
Definition: PyROOT.h:70
#define PyROOT_PyUnicode_Append
Definition: PyROOT.h:73
MethodProxy::Methods_t fMethods
Definition: MethodProxy.h:32
#define PyROOT_PyUnicode_AsString
Definition: PyROOT.h:66
Bool_t IsConstructor(UInt_t flags)
Definition: TCallContext.h:69
Bool_t ObjectProxy_Check(T *object)
Definition: ObjectProxy.h:91
R__EXTERN PyObject * gROOTns
Definition: PyStrings.h:56
PyObject * fValue
PyTypeObject MethodProxy_Type
const Bool_t kFALSE
Definition: RtypesCore.h:88
PyObject * fType
long Long_t
Definition: RtypesCore.h:50
MethodInfo_t * fMethodInfo
Definition: MethodProxy.h:52
#define PyROOT_PyUnicode_AppendAndDel
Definition: PyROOT.h:74
void AddMethod(PyCallable *pc)
Fill in the data of a freshly created method proxy.
R__EXTERN PyObject * gLifeLine
Definition: PyStrings.h:30
unsigned long ULong_t
Definition: RtypesCore.h:51
#define PyROOT_PyUnicode_Check
Definition: PyROOT.h:64
you should not use this method at all Int_t Int_t Double_t Double_t Double_t e
Definition: TRolke.cxx:630
~MethodInfo_t()
Destructor (this object is reference counted).
size_t SizeOf(TCppType_t klass)
Definition: Cppyy.cxx:218
#define Py_TYPE(ob)
Definition: PyROOT.h:151
int Py_ssize_t
Definition: PyROOT.h:156
Bool_t IsCreator(UInt_t flags)
Definition: TCallContext.h:65
PyObject * GetScopeProxy(Cppyy::TCppScope_t)
Retrieve scope proxy from the known ones.
static constexpr double pc
const Bool_t kTRUE
Definition: RtypesCore.h:87
static ECallFlags sMemoryPolicy
Definition: TCallContext.h:49
char name[80]
Definition: TGX11.cxx:109
_object PyObject
Definition: TPyArg.h:20