Logo ROOT  
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
27namespace 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
101namespace {
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*)"__creates__", (getter)mp_getcreates, (setter)mp_setcreates,
561 (char*)"For ownership rules of result: if true, objects are python-owned", NULL },
562 { (char*)"_mempolicy", (getter)mp_getmempolicy, (setter)mp_setmempolicy,
563 (char*)"For argument ownership rules: like global, either heuristic or strict", NULL },
564 { (char*)"__mempolicy__", (getter)mp_getmempolicy, (setter)mp_setmempolicy,
565 (char*)"For argument ownership rules: like global, either heuristic or strict", NULL },
566 { (char*)"_manage_smart_ptr", (getter)mp_get_manage_smart_ptr, (setter)mp_set_manage_smart_ptr,
567 (char*)"If a smart pointer is returned, determines management policy.", NULL },
568 { (char*)"_threaded", (getter)mp_getthreaded, (setter)mp_setthreaded,
569 (char*)"If true, releases GIL on call into C++", NULL },
570 { (char*)"__release_gil__", (getter)mp_getthreaded, (setter)mp_setthreaded,
571 (char*)"If true, releases GIL on call into C++", NULL },
572 { (char*)NULL, NULL, NULL, NULL, NULL }
573 };
574
575//= PyROOT method proxy function behavior ====================================
576 PyObject* mp_call( MethodProxy* pymeth, PyObject* args, PyObject* kwds )
577 {
578 // Call the appropriate overload of this method.
579
580 // if called through im_func pseudo-representation (this can be gamed if the
581 // user really wants to ... )
582 if ( IsPseudoFunc( pymeth ) )
583 pymeth->fSelf = NULL;
584
585 ObjectProxy* oldSelf = pymeth->fSelf;
586
587 // get local handles to proxy internals
588 auto& methods = pymeth->fMethodInfo->fMethods;
589 auto& dispatchMap = pymeth->fMethodInfo->fDispatchMap;
590 auto& mflags = pymeth->fMethodInfo->fFlags;
591
592 Int_t nMethods = methods.size();
593
594 TCallContext ctxt = { 0 };
595 ctxt.fFlags |= (mflags & TCallContext::kUseHeuristics);
596 ctxt.fFlags |= (mflags & TCallContext::kUseStrict);
597 ctxt.fFlags |= (mflags & TCallContext::kManageSmartPtr);
598 if ( ! ctxt.fFlags ) ctxt.fFlags |= TCallContext::sMemoryPolicy;
599 ctxt.fFlags |= (mflags & TCallContext::kReleaseGIL);
600
601 // simple case
602 if ( nMethods == 1 ) {
603 PyObject* result = methods[0]->Call( pymeth->fSelf, args, kwds, &ctxt );
604 return HandleReturn( pymeth, oldSelf, result );
605 }
606
607 // otherwise, handle overloading
608 Long_t sighash = HashSignature( args );
609
610 // look for known signatures ...
611 MethodProxy::DispatchMap_t::iterator m = dispatchMap.find( sighash );
612 if ( m != dispatchMap.end() ) {
613 Int_t index = m->second;
614 PyObject* result = methods[ index ]->Call( pymeth->fSelf, args, kwds, &ctxt );
615 result = HandleReturn( pymeth, oldSelf, result );
616
617 if ( result != 0 )
618 return result;
619
620 // fall through: python is dynamic, and so, the hashing isn't infallible
621 ResetCallState( pymeth->fSelf, oldSelf, kTRUE );
622 }
623
624 // ... otherwise loop over all methods and find the one that does not fail
625 if ( ! IsSorted( mflags ) ) {
626 std::stable_sort( methods.begin(), methods.end(), PriorityCmp );
627 mflags |= TCallContext::kIsSorted;
628 }
629
630 std::vector< PyError_t > errors;
631 for ( Int_t i = 0; i < nMethods; ++i ) {
632 PyObject* result = methods[i]->Call( pymeth->fSelf, args, kwds, &ctxt );
633
634 if ( result != 0 ) {
635 // success: update the dispatch map for subsequent calls
636 dispatchMap[ sighash ] = i;
637 std::for_each( errors.begin(), errors.end(), PyError_t::Clear );
638 return HandleReturn( pymeth, oldSelf, result );
639 }
640
641 // failure: collect error message/trace (automatically clears exception, too)
642 if ( ! PyErr_Occurred() ) {
643 // this should not happen; set an error to prevent core dump and report
644 PyObject* sig = methods[i]->GetPrototype();
645 PyErr_Format( PyExc_SystemError, "%s =>\n %s",
646 PyROOT_PyUnicode_AsString( sig ), (char*)"NULL result without error in mp_call" );
647 Py_DECREF( sig );
648 }
649 PyError_t e;
650 PyErr_Fetch( &e.fType, &e.fValue, &e.fTrace );
651 errors.push_back( e );
652 ResetCallState( pymeth->fSelf, oldSelf, kFALSE );
653 }
654
655 // first summarize, then add details
657 "none of the %d overloaded methods succeeded. Full details:", nMethods );
658 PyObject* separator = PyROOT_PyUnicode_FromString( "\n " );
659
660 // if this point is reached, none of the overloads succeeded: notify user
661 PyObject* exc_type = NULL;
662 for ( std::vector< PyError_t >::iterator e = errors.begin(); e != errors.end(); ++e ) {
663 if ( e->fType != PyExc_NotImplementedError ) {
664 if ( ! exc_type ) exc_type = e->fType;
665 else if ( exc_type != e->fType ) exc_type = PyExc_TypeError;
666 }
667 PyROOT_PyUnicode_Append( &value, separator );
668 PyROOT_PyUnicode_Append( &value, e->fValue );
669 }
670
671 Py_DECREF( separator );
672 std::for_each( errors.begin(), errors.end(), PyError_t::Clear );
673
674 // report failure
675 PyErr_SetObject( exc_type ? exc_type : PyExc_TypeError, value );
676 Py_DECREF( value );
677 return 0;
678 }
679
680////////////////////////////////////////////////////////////////////////////////
681/// Descriptor; create and return a new bound method proxy (language requirement).
682
683 MethodProxy* mp_descrget( MethodProxy* pymeth, ObjectProxy* pyobj, PyObject* )
684 {
685 MethodProxy* newPyMeth = (MethodProxy*)MethodProxy_Type.tp_alloc( &MethodProxy_Type, 0 );
686
687 // method info is shared, as it contains the collected overload knowledge
688 *pymeth->fMethodInfo->fRefCount += 1;
689 newPyMeth->fMethodInfo = pymeth->fMethodInfo;
690
691 // new method is to be bound to current object (may be NULL)
692 Py_XINCREF( (PyObject*)pyobj );
693 newPyMeth->fSelf = pyobj;
694
695 return newPyMeth;
696 }
697
698
699//= PyROOT method proxy construction/destruction =================================
700 MethodProxy* mp_new( PyTypeObject*, PyObject*, PyObject* )
701 {
702 // Create a new method proxy object.
703 MethodProxy* pymeth = PyObject_GC_New( MethodProxy, &MethodProxy_Type );
704 pymeth->fSelf = NULL;
705 pymeth->fMethodInfo = new MethodProxy::MethodInfo_t;
706
707 PyObject_GC_Track( pymeth );
708 return pymeth;
709 }
710
711////////////////////////////////////////////////////////////////////////////////
712/// Deallocate memory held by method proxy object.
713
714 void mp_dealloc( MethodProxy* pymeth )
715 {
716 PyObject_GC_UnTrack( pymeth );
717
718 if ( ! IsPseudoFunc( pymeth ) )
719 Py_CLEAR( pymeth->fSelf );
720 pymeth->fSelf = NULL;
721
722 if ( --(*pymeth->fMethodInfo->fRefCount) <= 0 ) {
723 delete pymeth->fMethodInfo;
724 }
725
726 PyObject_GC_Del( pymeth );
727 }
728
729
730////////////////////////////////////////////////////////////////////////////////
731/// Hash of method proxy object for insertion into dictionaries; with actual
732/// method (fMethodInfo) shared, its address is best suited.
733
734 Long_t mp_hash( MethodProxy* pymeth )
735 {
736 return _Py_HashPointer( pymeth->fMethodInfo );
737 }
738
739////////////////////////////////////////////////////////////////////////////////
740/// Garbage collector traverse of held python member objects.
741
742 int mp_traverse( MethodProxy* pymeth, visitproc visit, void* args )
743 {
744 if ( pymeth->fSelf && ! IsPseudoFunc( pymeth ) )
745 return visit( (PyObject*)pymeth->fSelf, args );
746
747 return 0;
748 }
749
750////////////////////////////////////////////////////////////////////////////////
751/// Garbage collector clear of held python member objects.
752
753 int mp_clear( MethodProxy* pymeth )
754 {
755 if ( ! IsPseudoFunc( pymeth ) )
756 Py_CLEAR( pymeth->fSelf );
757 pymeth->fSelf = NULL;
758
759 return 0;
760 }
761
762////////////////////////////////////////////////////////////////////////////////
763/// Rich set of comparison objects; only equals is defined.
764
765 PyObject* mp_richcompare( MethodProxy* self, MethodProxy* other, int op )
766 {
767 if ( op != Py_EQ )
768 return PyType_Type.tp_richcompare( (PyObject*)self, (PyObject*)other, op );
769
770 // defined by type + (shared) MethodInfo + bound self, with special case for fSelf (i.e. pseudo-function)
771 if ( ( Py_TYPE(self) == Py_TYPE(other) && self->fMethodInfo == other->fMethodInfo ) && \
772 ( ( IsPseudoFunc( self ) && IsPseudoFunc( other ) ) || self->fSelf == other->fSelf ) ) {
773 Py_INCREF( Py_True );
774 return Py_True;
775 }
776 Py_INCREF( Py_False );
777 return Py_False;
778 }
779
780
781//= PyROOT method proxy access to internals =================================
782 PyObject* mp_disp( MethodProxy* pymeth, PyObject* sigarg )
783 {
784 // Select and call a specific C++ overload, based on its signature.
785 if ( ! PyROOT_PyUnicode_Check( sigarg ) ) {
786 PyErr_Format( PyExc_TypeError, "disp() argument 1 must be string, not %.50s",
787 sigarg == Py_None ? "None" : Py_TYPE(sigarg)->tp_name );
788 return 0;
789 }
790
792
793 MethodProxy::Methods_t& methods = pymeth->fMethodInfo->fMethods;
794 for ( Int_t i = 0; i < (Int_t)methods.size(); ++i ) {
795
796 PyObject* sig2 = methods[ i ]->GetSignature();
797 if ( PyObject_RichCompareBool( sig1, sig2, Py_EQ ) ) {
798 Py_DECREF( sig2 );
799
800 MethodProxy* newmeth = mp_new( NULL, NULL, NULL );
801 MethodProxy::Methods_t vec; vec.push_back( methods[ i ]->Clone() );
802 newmeth->Set( pymeth->fMethodInfo->fName, vec );
803
804 if ( pymeth->fSelf && ! IsPseudoFunc( pymeth ) ) {
805 Py_INCREF( pymeth->fSelf );
806 newmeth->fSelf = pymeth->fSelf;
807 }
808
809 Py_DECREF( sig1 );
810 return (PyObject*)newmeth;
811 }
812
813 Py_DECREF( sig2 );
814 }
815
816 Py_DECREF( sig1 );
817 PyErr_Format( PyExc_LookupError, "signature \"%s\" not found", PyROOT_PyUnicode_AsString( sigarg ) );
818 return 0;
819 }
820
821//= PyROOT method proxy access to internals =================================
822 PyObject* mp_add_overload( MethodProxy* pymeth, PyObject* new_overload )
823 {
824 TPythonCallback* cb = new TPythonCallback(new_overload);
825 pymeth->AddMethod( cb );
826 Py_INCREF( Py_None );
827 return Py_None;
828 }
829
830 PyMethodDef mp_methods[] = {
831 { (char*)"disp", (PyCFunction)mp_disp, METH_O, (char*)"select overload for dispatch" },
832 { (char*)"__add_overload__", (PyCFunction)mp_add_overload, METH_O, (char*)"add a new overload" },
833 { (char*)NULL, NULL, 0, NULL }
834 };
835
836} // unnamed namespace
837
838////////////////////////////////////////////////////////////////////////////////
839
840#if !defined(_MSC_VER)
841#pragma GCC diagnostic push
842#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
843#endif
844
845//= PyROOT method proxy type =================================================
846PyTypeObject MethodProxy_Type = {
847 PyVarObject_HEAD_INIT( &PyType_Type, 0 )
848 (char*)"ROOT.MethodProxy", // tp_name
849 sizeof(MethodProxy), // tp_basicsize
850 0, // tp_itemsize
851 (destructor)mp_dealloc, // tp_dealloc
852 0, // tp_print (python < 3.8)
853 // tp_vectorcall_offset (python >= 3.8)
854 0, // tp_getattr
855 0, // tp_setattr
856 0, // tp_compare
857 0, // tp_repr
858 0, // tp_as_number
859 0, // tp_as_sequence
860 0, // tp_as_mapping
861 (hashfunc)mp_hash, // tp_hash
862 (ternaryfunc)mp_call, // tp_call
863 0, // tp_str
864 0, // tp_getattro
865 0, // tp_setattro
866 0, // tp_as_buffer
867 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, // tp_flags
868 (char*)"PyROOT method proxy (internal)", // tp_doc
869 (traverseproc)mp_traverse, // tp_traverse
870 (inquiry)mp_clear, // tp_clear
871 (richcmpfunc)mp_richcompare, // tp_richcompare
872 0, // tp_weaklistoffset
873 0, // tp_iter
874 0, // tp_iternext
875 mp_methods, // tp_methods
876 0, // tp_members
877 mp_getset, // tp_getset
878 0, // tp_base
879 0, // tp_dict
880 (descrgetfunc)mp_descrget, // tp_descr_get
881 0, // tp_descr_set
882 0, // tp_dictoffset
883 0, // tp_init
884 0, // tp_alloc
885 (newfunc)mp_new, // tp_new
886 0, // tp_free
887 0, // tp_is_gc
888 0, // tp_bases
889 0, // tp_mro
890 0, // tp_cache
891 0, // tp_subclasses
892 0 // tp_weaklist
893#if PY_VERSION_HEX >= 0x02030000
894 , 0 // tp_del
895#endif
896#if PY_VERSION_HEX >= 0x02060000
897 , 0 // tp_version_tag
898#endif
899#if PY_VERSION_HEX >= 0x03040000
900 , 0 // tp_finalize
901#endif
902#if PY_VERSION_HEX >= 0x03080000
903 , 0 // tp_vectorcall
904#if PY_VERSION_HEX < 0x03090000
905 , 0 // tp_print (python 3.8 only)
906#endif
907#endif
908};
909
910#if !defined(_MSC_VER)
911#pragma GCC diagnostic pop
912#endif
913
914} // namespace PyROOT
915
916
917//- public members -----------------------------------------------------------
918void PyROOT::MethodProxy::Set( const std::string& name, std::vector< PyCallable* >& methods )
919{
920// Fill in the data of a freshly created method proxy.
922 fMethodInfo->fMethods.swap( methods );
923 fMethodInfo->fFlags &= ~TCallContext::kIsSorted;
925
926// special case: all constructors are considered creators by default
927 if ( name == "__init__" )
929
930// special case, in heuristics mode also tag *Clone* methods as creators
932 name.find( "Clone" ) != std::string::npos )
934}
935
936////////////////////////////////////////////////////////////////////////////////
937/// Fill in the data of a freshly created method proxy.
938
940{
941 fMethodInfo->fMethods.push_back( pc );
942 fMethodInfo->fFlags &= ~TCallContext::kIsSorted;
943}
944
945////////////////////////////////////////////////////////////////////////////////
946
948{
949 fMethodInfo->fMethods.insert( fMethodInfo->fMethods.end(),
950 meth->fMethodInfo->fMethods.begin(), meth->fMethodInfo->fMethods.end() );
951 fMethodInfo->fFlags &= ~TCallContext::kIsSorted;
952}
953
954////////////////////////////////////////////////////////////////////////////////
955/// Destructor (this object is reference counted).
956
958{
959 for ( Methods_t::iterator it = fMethods.begin(); it != fMethods.end(); ++it ) {
960 delete *it;
961 }
962 fMethods.clear();
963 delete fRefCount;
964}
#define CO_NOFREE
Definition: MethodProxy.cxx:14
PyObject * fValue
PyObject * fType
PyObject * fTrace
#define Py_TYPE(ob)
Definition: PyROOT.h:166
#define PyROOT_PyUnicode_Append
Definition: PyROOT.h:84
int Py_ssize_t
Definition: PyROOT.h:171
#define PyROOT_PyUnicode_Check
Definition: PyROOT.h:76
#define PyROOT_PyUnicode_AsString
Definition: PyROOT.h:78
#define PyROOT_PyUnicode_AppendAndDel
Definition: PyROOT.h:85
#define PyROOT_PyUnicode_FromString
Definition: PyROOT.h:82
#define PyROOT_PyUnicode_FromFormat
Definition: PyROOT.h:81
#define PyVarObject_HEAD_INIT(type, size)
Definition: PyROOT.h:164
#define e(i)
Definition: RSha256.hxx:103
int Int_t
Definition: RtypesCore.h:41
const Bool_t kFALSE
Definition: RtypesCore.h:88
unsigned long ULong_t
Definition: RtypesCore.h:51
long Long_t
Definition: RtypesCore.h:50
bool Bool_t
Definition: RtypesCore.h:59
const Bool_t kTRUE
Definition: RtypesCore.h:87
char name[80]
Definition: TGX11.cxx:109
_object PyObject
Definition: TPyArg.h:20
void Set(const std::string &name, std::vector< PyCallable * > &methods)
std::vector< PyCallable * > Methods_t
Definition: MethodProxy.h:24
void AddMethod(PyCallable *pc)
Fill in the data of a freshly created method proxy.
MethodInfo_t * fMethodInfo
Definition: MethodProxy.h:52
size_t SizeOf(TCppType_t klass)
Definition: Cppyy.cxx:239
R__EXTERN PyObject * gLifeLine
Definition: PyStrings.h:30
R__EXTERN PyObject * gROOTns
Definition: PyStrings.h:57
PyObject * GetScopeProxy(Cppyy::TCppScope_t)
Retrieve scope proxy from the known ones.
Bool_t IsCreator(UInt_t flags)
Definition: TCallContext.h:65
Bool_t IsConstructor(UInt_t flags)
Definition: TCallContext.h:69
Bool_t ObjectProxy_Check(T *object)
Definition: ObjectProxy.h:91
PyTypeObject MethodProxy_Type
Bool_t IsSorted(UInt_t flags)
Definition: TCallContext.h:61
static constexpr double pc
~MethodInfo_t()
Destructor (this object is reference counted).
MethodProxy::Methods_t fMethods
Definition: MethodProxy.h:32
static ECallFlags sMemoryPolicy
Definition: TCallContext.h:49
auto * m
Definition: textangle.C:8