Logo ROOT  
Reference Guide
TCustomPyTypes.cxx
Go to the documentation of this file.
1// Author: Wim Lavrijsen, Dec 2006
2
3// Bindings
4#include "PyROOT.h"
5#include "TCustomPyTypes.h"
6
7#if PY_VERSION_HEX >= 0x03000000
8// TODO: this will break functionality
9#define PyMethod_GET_CLASS( meth ) Py_None
10#endif
11
12
13namespace PyROOT {
14
15#if !defined(_MSC_VER)
16#pragma GCC diagnostic push
17#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
18#endif
19
20//= float type allowed for reference passing =================================
21PyTypeObject TCustomFloat_Type = { // python float is a C/C++ double
22 PyVarObject_HEAD_INIT( &PyType_Type, 0 )
23 (char*)"ROOT.double", // tp_name
24 0, // tp_basicsize
25 0, // tp_itemsize
26 0, // tp_dealloc
27 0, // tp_print (python < 3.8)
28 // tp_vectorcall_offset (python >= 3.8)
29 0, // tp_getattr
30 0, // tp_setattr
31 0, // tp_compare
32 0, // tp_repr
33 0, // tp_as_number
34 0, // tp_as_sequence
35 0, // tp_as_mapping
36 0, // tp_hash
37 0, // tp_call
38 0, // tp_str
39 0, // tp_getattro
40 0, // tp_setattro
41 0, // tp_as_buffer
42 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES |
43 Py_TPFLAGS_BASETYPE, // tp_flags
44 (char*)"PyROOT float object for pass by reference", // tp_doc
45 0, // tp_traverse
46 0, // tp_clear
47 0, // tp_richcompare
48 0, // tp_weaklistoffset
49 0, // tp_iter
50 0, // tp_iternext
51 0, // tp_methods
52 0, // tp_members
53 0, // tp_getset
54 &PyFloat_Type, // tp_base
55 0, // tp_dict
56 0, // tp_descr_get
57 0, // tp_descr_set
58 0, // tp_dictoffset
59 0, // tp_init
60 0, // tp_alloc
61 0, // tp_new
62 0, // tp_free
63 0, // tp_is_gc
64 0, // tp_bases
65 0, // tp_mro
66 0, // tp_cache
67 0, // tp_subclasses
68 0 // tp_weaklist
69#if PY_VERSION_HEX >= 0x02030000
70 , 0 // tp_del
71#endif
72#if PY_VERSION_HEX >= 0x02060000
73 , 0 // tp_version_tag
74#endif
75#if PY_VERSION_HEX >= 0x03040000
76 , 0 // tp_finalize
77#endif
78#if PY_VERSION_HEX >= 0x03080000
79 , 0 // tp_vectorcall
80#if PY_VERSION_HEX < 0x03090000
81 , 0 // tp_print (python 3.8 only)
82#endif
83#endif
84};
85
86//= long type allowed for reference passing ==================================
87PyTypeObject TCustomInt_Type = { // python int is a C/C++ long
88 PyVarObject_HEAD_INIT( &PyType_Type, 0 )
89 (char*)"ROOT.long", // tp_name
90 0, // tp_basicsize
91 0, // tp_itemsize
92 0, // tp_dealloc
93 0, // tp_print (python < 3.8)
94 // tp_vectorcall_offset (python >= 3.8)
95 0, // tp_getattr
96 0, // tp_setattr
97 0, // tp_compare
98 0, // tp_repr
99 0, // tp_as_number
100 0, // tp_as_sequence
101 0, // tp_as_mapping
102 0, // tp_hash
103 0, // tp_call
104 0, // tp_str
105 0, // tp_getattro
106 0, // tp_setattro
107 0, // tp_as_buffer
108 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES |
109 Py_TPFLAGS_BASETYPE, // tp_flags
110 (char*)"PyROOT long object for pass by reference", // tp_doc
111 0, // tp_traverse
112 0, // tp_clear
113 0, // tp_richcompare
114 0, // tp_weaklistoffset
115 0, // tp_iter
116 0, // tp_iternext
117 0, // tp_methods
118 0, // tp_members
119 0, // tp_getset
120 &PyInt_Type, // tp_base
121 0, // tp_dict
122 0, // tp_descr_get
123 0, // tp_descr_set
124 0, // tp_dictoffset
125 0, // tp_init
126 0, // tp_alloc
127 0, // tp_new
128 0, // tp_free
129 0, // tp_is_gc
130 0, // tp_bases
131 0, // tp_mro
132 0, // tp_cache
133 0, // tp_subclasses
134 0 // tp_weaklist
135#if PY_VERSION_HEX >= 0x02030000
136 , 0 // tp_del
137#endif
138#if PY_VERSION_HEX >= 0x02060000
139 , 0 // tp_version_tag
140#endif
141#if PY_VERSION_HEX >= 0x03040000
142 , 0 // tp_finalize
143#endif
144#if PY_VERSION_HEX >= 0x03080000
145 , 0 // tp_vectorcall
146#if PY_VERSION_HEX < 0x03090000
147 , 0 // tp_print (python 3.8 only)
148#endif
149#endif
150};
151
152//= instancemethod object with a more efficient call function ================
153static PyMethodObject* free_list;
154static int numfree = 0;
155#ifndef PyMethod_MAXFREELIST
156#define PyMethod_MAXFREELIST 256
157#endif
158
160#if PY_VERSION_HEX < 0x03000000
161 pyclass
162#endif
163 )
164{
165// from instancemethod, but with custom type (at issue is that instancemethod is not
166// meant to be derived from)
167 PyMethodObject* im;
168 if ( ! PyCallable_Check( func ) ) {
169 PyErr_Format( PyExc_SystemError,
170 "%s:%d: bad argument to internal function",
171 __FILE__, __LINE__ );
172 return NULL;
173 }
174
175 im = free_list;
176 if ( im != NULL ) {
177 free_list = (PyMethodObject*)( im->im_self );
178 (void)PyObject_INIT( im, &TCustomInstanceMethod_Type );
179 }
180 else {
181 im = PyObject_GC_New( PyMethodObject, &TCustomInstanceMethod_Type );
182 if ( im == NULL )
183 return NULL;
184 }
185
186 im->im_weakreflist = NULL;
187 Py_INCREF( func );
188 im->im_func = func;
189 Py_XINCREF( self );
190 im->im_self = self;
191#if PY_VERSION_HEX < 0x03000000
192 Py_XINCREF( pyclass );
193 im->im_class = pyclass;
194#endif
195 PyObject_GC_Track( im );
196 return (PyObject*)im;
197}
198
199////////////////////////////////////////////////////////////////////////////////
200/// from instancemethod, but with custom type (at issue is that instancemethod is not
201/// meant to be derived from)
202
203static void im_dealloc( PyMethodObject* im )
204{
205 PyObject_GC_UnTrack( im );
206
207 if ( im->im_weakreflist != NULL )
208 PyObject_ClearWeakRefs( (PyObject*) im );
209
210 Py_DECREF( im->im_func );
211 Py_XDECREF( im->im_self );
212#if PY_VERSION_HEX < 0x03000000
213 Py_XDECREF( im->im_class );
214#endif
215
217 im->im_self = (PyObject*)free_list;
218 free_list = im;
219 numfree++;
220 } else {
221 PyObject_GC_Del(im);
222 }
223}
224
225////////////////////////////////////////////////////////////////////////////////
226/// The mapping from a method to a function involves reshuffling of self back
227/// into the list of arguments. However, the pythonized methods will then have
228/// to undo that shuffling, which is inefficient. This method is the same as
229/// the one for the instancemethod object, except for the shuffling.
230
231static PyObject* im_call( PyObject* meth, PyObject* args, PyObject* kw )
232{
233 PyObject* self = PyMethod_GET_SELF( meth );
234
235 if ( ! self ) {
236 // unbound methods must be called with an instance of the class (or a
237 // derived class) as first argument
238 Py_ssize_t argc = PyTuple_GET_SIZE( args );
239 PyObject* pyclass = PyMethod_GET_CLASS( meth );
240 if ( 1 <= argc && PyObject_IsInstance( PyTuple_GET_ITEM( args, 0 ), pyclass ) == 1 ) {
241 self = PyTuple_GET_ITEM( args, 0 );
242
243 PyObject* newArgs = PyTuple_New( argc - 1 );
244 for ( int i = 1; i < argc; ++i ) {
245 PyObject* v = PyTuple_GET_ITEM( args, i );
246 Py_INCREF( v );
247 PyTuple_SET_ITEM( newArgs, i-1, v );
248 }
249
250 args = newArgs;
251
252 } else
253 return PyMethod_Type.tp_call( meth, args, kw ); // will set proper error msg
254
255 } else
256 Py_INCREF( args );
257
258 PyCFunctionObject* func = (PyCFunctionObject*)PyMethod_GET_FUNCTION( meth );
259
260// the function is globally shared, so set and reset its "self" (ok, b/c of GIL)
261 Py_INCREF( self );
262 func->m_self = self;
263 PyObject* result = PyCFunction_Call( (PyObject*)func, args, kw );
264 func->m_self = 0;
265 Py_DECREF( self );
266 Py_DECREF( args );
267 return result;
268}
269
270////////////////////////////////////////////////////////////////////////////////
271/// from instancemethod: don't rebind an already bound method, or an unbound method
272/// of a class that's not a base class of pyclass
273
274static PyObject* im_descr_get( PyObject* meth, PyObject* obj, PyObject* pyclass )
275{
276 if ( PyMethod_GET_SELF( meth ) != NULL
277#if PY_VERSION_HEX < 0x03000000
278 || ( PyMethod_GET_CLASS( meth ) != NULL &&
279 ! PyObject_IsSubclass( pyclass, PyMethod_GET_CLASS(meth) ) )
280#endif
281 ) {
282 Py_INCREF( meth );
283 return meth;
284 }
285
286 if ( obj == Py_None )
287 obj = NULL;
288
289 return TCustomInstanceMethod_New( PyMethod_GET_FUNCTION( meth ), obj, pyclass );
290}
291
292//= PyROOT custom instance method type =======================================
294 PyVarObject_HEAD_INIT( &PyType_Type, 0 )
295 (char*)"ROOT.InstanceMethod", // tp_name
296 0, // tp_basicsize
297 0, // tp_itemsize
298 (destructor)im_dealloc, // tp_dealloc
299 0, // tp_print (python < 3.8)
300 // tp_vectorcall_offset (python >= 3.8)
301 0, // tp_getattr
302 0, // tp_setattr
303 0, // tp_compare
304 0, // tp_repr
305 0, // tp_as_number
306 0, // tp_as_sequence
307 0, // tp_as_mapping
308 0, // tp_hash
309 im_call, // tp_call
310 0, // tp_str
311 0, // tp_getattro
312 0, // tp_setattro
313 0, // tp_as_buffer
314 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES |
315 Py_TPFLAGS_BASETYPE, // tp_flags
316 (char*)"PyROOT custom instance method (internal)", // tp_doc
317 0, // tp_traverse
318 0, // tp_clear
319 0, // tp_richcompare
320 0, // tp_weaklistoffset
321 0, // tp_iter
322 0, // tp_iternext
323 0, // tp_methods
324 0, // tp_members
325 0, // tp_getset
326 &PyMethod_Type, // tp_base
327 0, // tp_dict
328 im_descr_get, // tp_descr_get
329 0, // tp_descr_set
330 0, // tp_dictoffset
331 0, // tp_init
332 0, // tp_alloc
333 0, // tp_new
334 0, // tp_free
335 0, // tp_is_gc
336 0, // tp_bases
337 0, // tp_mro
338 0, // tp_cache
339 0, // tp_subclasses
340 0 // tp_weaklist
341#if PY_VERSION_HEX >= 0x02030000
342 , 0 // tp_del
343#endif
344#if PY_VERSION_HEX >= 0x02060000
345 , 0 // tp_version_tag
346#endif
347#if PY_VERSION_HEX >= 0x03040000
348 , 0 // tp_finalize
349#endif
350#if PY_VERSION_HEX >= 0x03080000
351 , 0 // tp_vectorcall
352#if PY_VERSION_HEX < 0x03090000
353 , 0 // tp_print (python 3.8 only)
354#endif
355#endif
356};
357
358#if !defined(_MSC_VER)
359#pragma GCC diagnostic pop
360#endif
361
362} // namespace PyROOT
int Py_ssize_t
Definition: PyROOT.h:171
#define PyVarObject_HEAD_INIT(type, size)
Definition: PyROOT.h:164
#define PyMethod_MAXFREELIST
_object PyObject
Definition: TPyArg.h:20
typedef void((*Func_t)())
static PyObject * im_call(PyObject *meth, PyObject *args, PyObject *kw)
The mapping from a method to a function involves reshuffling of self back into the list of arguments.
static PyMethodObject * free_list
static void im_dealloc(PyMethodObject *im)
from instancemethod, but with custom type (at issue is that instancemethod is not meant to be derived...
static PyObject * im_descr_get(PyObject *meth, PyObject *obj, PyObject *pyclass)
from instancemethod: don't rebind an already bound method, or an unbound method of a class that's not...
PyTypeObject TCustomInstanceMethod_Type
PyTypeObject TCustomInt_Type
static int numfree
PyTypeObject TCustomFloat_Type
Custom builtins, detectable by type, for pass by ref.
PyObject * TCustomInstanceMethod_New(PyObject *func, PyObject *self, PyObject *pyclass)