Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
Pythonize.cxx
Go to the documentation of this file.
1// Bindings
2#include "CPyCppyy.h"
3#include "Pythonize.h"
4#include "Converters.h"
5#include "CPPInstance.h"
6#include "CPPFunction.h"
7#include "CPPOverload.h"
8#include "CustomPyTypes.h"
9#include "LowLevelViews.h"
10#include "ProxyWrappers.h"
11#include "PyCallable.h"
12#include "PyStrings.h"
13#include "TypeManip.h"
14#include "Utility.h"
15
16// Standard
17#include <algorithm>
18#include <complex>
19#include <set>
20#include <stdexcept>
21#include <sstream>
22#include <string>
23#include <utility>
24
25
26//- data and local helpers ---------------------------------------------------
27namespace CPyCppyy {
28 extern PyObject* gThisModule;
29 std::map<std::string, std::vector<PyObject*>> &pythonizations();
30}
31
32namespace {
33
34// for convenience
35using namespace CPyCppyy;
36
37//-----------------------------------------------------------------------------
39// prevents calls to Py_TYPE(pyclass)->tp_getattr, which is unnecessary for our
40// purposes here and could tickle problems w/ spurious lookups into ROOT meta
42 if (dct) {
45 if (attr) {
48 return ret;
49 }
50 }
52 return false;
53}
54
56// get an attribute without causing getattr lookups
58 if (dct) {
61 return attr;
62 }
63 return nullptr;
64}
65
66//-----------------------------------------------------------------------------
67inline bool IsTemplatedSTLClass(const std::string& name, const std::string& klass) {
68// Scan the name of the class and determine whether it is a template instantiation.
69 auto pos = name.find(klass);
70 return (pos == 0 || pos == 5) && name.find("::", name.rfind(">")) == std::string::npos;
71}
72
73// to prevent compiler warnings about const char* -> char*
74inline PyObject* CallPyObjMethod(PyObject* obj, const char* meth)
75{
76// Helper; call method with signature: obj->meth().
77 Py_INCREF(obj);
78 PyObject* result = PyObject_CallMethod(obj, const_cast<char*>(meth), const_cast<char*>(""));
79 Py_DECREF(obj);
80 return result;
81}
82
83//-----------------------------------------------------------------------------
84inline PyObject* CallPyObjMethod(PyObject* obj, const char* meth, PyObject* arg1)
85{
86// Helper; call method with signature: obj->meth(arg1).
87 Py_INCREF(obj);
89 obj, const_cast<char*>(meth), const_cast<char*>("O"), arg1);
90 Py_DECREF(obj);
91 return result;
92}
93
94//-----------------------------------------------------------------------------
96{
97// Helper; converts python index into straight C index.
99 if (idx == (Py_ssize_t)-1 && PyErr_Occurred())
100 return nullptr;
101
103 if (idx >= size || (idx < 0 && idx < -size)) {
104 PyErr_SetString(PyExc_IndexError, "index out of range");
105 return nullptr;
106 }
107
108 PyObject* pyindex = nullptr;
109 if (idx >= 0) {
111 pyindex = index;
112 } else
114
115 return pyindex;
116}
117
118//-----------------------------------------------------------------------------
119inline bool AdjustSlice(const Py_ssize_t nlen, Py_ssize_t& start, Py_ssize_t& stop, Py_ssize_t& step)
120{
121// Helper; modify slice range to match the container.
122 if ((step > 0 && stop <= start) || (step < 0 && start <= stop))
123 return false;
124
125 if (start < 0) start = 0;
126 if (start >= nlen) start = nlen-1;
127 if (step >= nlen) step = nlen;
128
129 stop = step > 0 ? std::min(nlen, stop) : (stop >= 0 ? stop : -1);
130 return true;
131}
132
133//-----------------------------------------------------------------------------
135{
136// Helper; call method with signature: meth(pyindex).
139 if (!pyindex) {
141 return nullptr;
142 }
143
147 return result;
148}
149
150//- "smart pointer" behavior ---------------------------------------------------
152{
154 PyErr_SetString(PyExc_TypeError, "getattr(): attribute name must be string");
155
157 if (!pyptr)
158 return nullptr;
159
160// prevent a potential infinite loop
161 if (Py_TYPE(pyptr) == Py_TYPE(self)) {
164 PyErr_Format(PyExc_AttributeError, "%s object has no attribute \'%s\'",
168
170 return nullptr;
171 }
172
175 return result;
176}
177
179{
180// Follow operator*() if present (available in python as __deref__), so that
181// smart pointers behave as expected.
183 // TODO: these calls come from TemplateProxy and are unlikely to be needed in practice,
184 // whereas as-is, they can accidentally dereference the result of end() on some STL
185 // containers. Obviously, this is a dumb hack that should be resolved more fundamentally.
187 return nullptr;
188 }
189
190
192}
193
194//-----------------------------------------------------------------------------
196{
197// Follow operator->() if present (available in python as __follow__), so that
198// smart pointers behave as expected.
200}
201
202//- pointer checking bool converter -------------------------------------------
204{
205 if (!CPPInstance_Check(self)) {
206 PyErr_SetString(PyExc_TypeError, "C++ object proxy expected");
207 return nullptr;
208 }
209
210 if (!((CPPInstance*)self)->GetObject())
212
214}
215
216//- vector behavior as primitives ----------------------------------------------
217#if PY_VERSION_HEX < 0x03040000
218#define PyObject_LengthHint _PyObject_LengthHint
219#endif
220
221// TODO: can probably use the below getters in the InitializerListConverter
222struct ItemGetter {
223 ItemGetter(PyObject* pyobj) : fPyObject(pyobj) { Py_INCREF(fPyObject); }
224 virtual ~ItemGetter() { Py_DECREF(fPyObject); }
225 virtual Py_ssize_t size() = 0;
226 virtual PyObject* get() = 0;
227 PyObject* fPyObject;
228};
229
230struct CountedItemGetter : public ItemGetter {
231 CountedItemGetter(PyObject* pyobj) : ItemGetter(pyobj), fCur(0) {}
232 Py_ssize_t fCur;
233};
234
235struct TupleItemGetter : public CountedItemGetter {
236 using CountedItemGetter::CountedItemGetter;
237 Py_ssize_t size() override { return PyTuple_GET_SIZE(fPyObject); }
238 PyObject* get() override {
239 if (fCur < PyTuple_GET_SIZE(fPyObject)) {
240 PyObject* item = PyTuple_GET_ITEM(fPyObject, fCur++);
242 return item;
243 }
244 PyErr_SetString(PyExc_StopIteration, "end of tuple");
245 return nullptr;
246 }
247};
248
249struct ListItemGetter : public CountedItemGetter {
250 using CountedItemGetter::CountedItemGetter;
251 Py_ssize_t size() override { return PyList_GET_SIZE(fPyObject); }
252 PyObject* get() override {
253 if (fCur < PyList_GET_SIZE(fPyObject)) {
254 PyObject* item = PyList_GET_ITEM(fPyObject, fCur++);
256 return item;
257 }
258 PyErr_SetString(PyExc_StopIteration, "end of list");
259 return nullptr;
260 }
261};
262
263struct SequenceItemGetter : public CountedItemGetter {
264 using CountedItemGetter::CountedItemGetter;
265 Py_ssize_t size() override {
266 Py_ssize_t sz = PySequence_Size(fPyObject);
267 if (sz < 0) {
268 PyErr_Clear();
269 return PyObject_LengthHint(fPyObject, 8);
270 }
271 return sz;
272 }
273 PyObject* get() override { return PySequence_GetItem(fPyObject, fCur++); }
274};
275
276struct IterItemGetter : public ItemGetter {
277 using ItemGetter::ItemGetter;
278 Py_ssize_t size() override { return PyObject_LengthHint(fPyObject, 8); }
279 PyObject* get() override { return (*(Py_TYPE(fPyObject)->tp_iternext))(fPyObject); }
280};
281
282static ItemGetter* GetGetter(PyObject* args)
283{
284// Create an ItemGetter to loop over the iterable argument, if any.
285 ItemGetter* getter = nullptr;
286
287 if (PyTuple_GET_SIZE(args) == 1) {
288 PyObject* fi = PyTuple_GET_ITEM(args, 0);
290 return nullptr; // do not accept string to fill std::vector<char>
291
292 // TODO: this only tests for new-style buffers, which is too strict, but a
293 // generic check for Py_TYPE(fi)->tp_as_buffer is too loose (note that the
294 // main use case is numpy, which offers the new interface)
296 return nullptr;
297
299 getter = new TupleItemGetter(fi);
300 else if (PyList_CheckExact(fi))
301 getter = new ListItemGetter(fi);
302 else if (PySequence_Check(fi))
303 getter = new SequenceItemGetter(fi);
304 else {
306 if (iter) {
307 getter = new IterItemGetter{iter};
308 Py_DECREF(iter);
309 }
310 else PyErr_Clear();
311 }
312 }
313
314 return getter;
315}
316
317static bool FillVector(PyObject* vecin, PyObject* args, ItemGetter* getter)
318{
319 Py_ssize_t sz = getter->size();
320 if (sz < 0)
321 return false;
322
323// reserve memory as applicable
324 if (0 < sz) {
325 PyObject* res = PyObject_CallMethod(vecin, (char*)"reserve", (char*)"n", sz);
326 Py_DECREF(res);
327 } else // i.e. sz == 0, so empty container: done
328 return true;
329
330 bool fill_ok = true;
331
332// two main options: a list of lists (or tuples), or a list of objects; the former
333// are emplace_back'ed, the latter push_back'ed
335 if (!fi) PyErr_Clear();
337 // use emplace_back to construct the vector entries one by one
338 PyObject* eb_call = PyObject_GetAttrString(vecin, (char*)"emplace_back");
340 bool value_is_vector = false;
342 // if the value_type is a vector, then allow for initialization from sequences
343 if (std::string(CPyCppyy_PyText_AsString(vtype)).rfind("std::vector", 0) != std::string::npos)
344 value_is_vector = true;
345 } else
346 PyErr_Clear();
348
349 if (eb_call) {
351 for (int i = 0; /* until break */; ++i) {
352 PyObject* item = getter->get();
353 if (item) {
355 eb_args = PyTuple_New(1);
357 } else if (PyTuple_CheckExact(item)) {
358 eb_args = item;
359 } else if (PyList_CheckExact(item)) {
362 for (Py_ssize_t j = 0; j < isz; ++j) {
366 }
368 } else {
370 PyErr_Format(PyExc_TypeError, "argument %d is not a tuple or list", i);
371 fill_ok = false;
372 break;
373 }
376 if (!ebres) {
377 fill_ok = false;
378 break;
379 }
381 } else {
382 if (PyErr_Occurred()) {
385 fill_ok = false;
386 else { PyErr_Clear(); }
387 }
388 break;
389 }
390 }
392 }
393 } else {
394 // use push_back to add the vector entries one by one
395 PyObject* pb_call = PyObject_GetAttrString(vecin, (char*)"push_back");
396 if (pb_call) {
397 for (;;) {
398 PyObject* item = getter->get();
399 if (item) {
402 if (!pbres) {
403 fill_ok = false;
404 break;
405 }
407 } else {
408 if (PyErr_Occurred()) {
411 fill_ok = false;
412 else { PyErr_Clear(); }
413 }
414 break;
415 }
416 }
418 }
419 }
420 Py_XDECREF(fi);
421
422 return fill_ok;
423}
424
425PyObject* VectorIAdd(PyObject* self, PyObject* args, PyObject* /* kwds */)
426{
427// Implement fast __iadd__ on std::vector (generic __iadd__ is in Python)
428 ItemGetter* getter = GetGetter(args);
429
430 if (getter) {
431 bool fill_ok = FillVector(self, args, getter);
432 delete getter;
433
434 if (!fill_ok)
435 return nullptr;
436
438 return self;
439 }
440
441// if no getter, it could still be b/c we have a buffer (e.g. numpy); looping over
442// a buffer here is slow, so use insert() instead
443 if (PyTuple_GET_SIZE(args) == 1) {
444 PyObject* fi = PyTuple_GET_ITEM(args, 0);
447 if (vend) {
448 // when __iadd__ is overriden, the operation does not end with
449 // calling the __iadd__ method, but also assigns the result to the
450 // lhs of the iadd. For example, performing vec += arr, Python
451 // first calls our override, and then does vec = vec.iadd(arr).
454
455 if (!it)
456 return nullptr;
457
458 Py_DECREF(it);
459 // Assign the result of the __iadd__ override to the std::vector
461 return self;
462 }
463 }
464 }
465
466 if (!PyErr_Occurred())
467 PyErr_SetString(PyExc_TypeError, "argument is not iterable");
468 return nullptr; // error already set
469}
470
471
472PyObject* VectorInit(PyObject* self, PyObject* args, PyObject* /* kwds */)
473{
474// Specialized vector constructor to allow construction from containers; allowing
475// such construction from initializer_list instead would possible, but can be
476// error-prone. This use case is common enough for std::vector to implement it
477// directly, except for arrays (which can be passed wholesale) and strings (which
478// won't convert properly as they'll be seen as buffers)
479
480 ItemGetter* getter = GetGetter(args);
481
482 if (getter) {
483 // construct an empty vector, then back-fill it
485 if (!result) {
486 delete getter;
487 return nullptr;
488 }
489
490 bool fill_ok = FillVector(self, args, getter);
491 delete getter;
492
493 if (!fill_ok) {
495 return nullptr;
496 }
497
498 return result;
499 }
500
501// The given argument wasn't iterable: simply forward to regular constructor
503 if (realInit) {
504 PyObject* result = PyObject_Call(realInit, args, nullptr);
506 return result;
507 }
508
509 return nullptr;
510}
511
512//---------------------------------------------------------------------------
514{
515 PyObject* pydata = CallPyObjMethod(self, "__real_data");
517 return pydata;
518
520 if (!pylen) {
521 PyErr_Clear();
522 return pydata;
523 }
524
525 long clen = PyInt_AsLong(pylen);
527
529 ((CPPInstance*)pydata)->CastToArray(clen);
530 return pydata;
531 }
532
533 ((LowLevelView*)pydata)->resize((size_t)clen);
534 return pydata;
535}
536
537
538// This function implements __array__, added to std::vector python proxies and causes
539// a bug (see explanation at Utility::AddToClass(pyclass, "__array__"...) in CPyCppyy::Pythonize)
540// The recursive nature of this function, passes each subarray (pydata) to the next call and only
541// the final buffer is cast to a lowlevel view and resized (in VectorData), resulting in only the
542// first 1D array to be returned. See https://github.com/root-project/root/issues/17729
543// It is temporarily removed to prevent errors due to -Wunused-function, since it is no longer added.
544#if 0
545//---------------------------------------------------------------------------
547{
548 PyObject* pydata = VectorData(self, nullptr);
553 return newarr;
554}
555#endif
556
557//-----------------------------------------------------------------------------
558static PyObject* vector_iter(PyObject* v) {
560 if (!vi) return nullptr;
561
562 vi->ii_container = v;
563
564// tell the iterator code to set a life line if this container is a temporary
565 vi->vi_flags = vectoriterobject::kDefault;
566#if PY_VERSION_HEX >= 0x030e0000
568#else
569 if (Py_REFCNT(v) <= 1 || (((CPPInstance*)v)->fFlags & CPPInstance::kIsValue))
570#endif
572
573 Py_INCREF(v);
574
576 if (pyvalue_type) {
578 if (pyvalue_size) {
579 vi->vi_stride = PyLong_AsLong(pyvalue_size);
581 } else {
582 PyErr_Clear();
583 vi->vi_stride = 0;
584 }
585
587 std::string value_type = CPyCppyy_PyText_AsString(pyvalue_type);
588 value_type = Cppyy::ResolveName(value_type);
589 vi->vi_klass = Cppyy::GetScope(value_type);
590 if (!vi->vi_klass) {
591 // look for a special case of pointer to a class type (which is a builtin, but it
592 // is more useful to treat it polymorphically by allowing auto-downcasts)
593 const std::string& clean_type = TypeManip::clean_type(value_type, false, false);
595 if (c && TypeManip::compound(value_type) == "*") {
596 vi->vi_klass = c;
598 }
599 }
600 if (vi->vi_klass) {
601 vi->vi_converter = nullptr;
602 if (!vi->vi_flags) {
603 if (value_type.back() != '*') // meaning, object stored by-value
605 }
606 } else
607 vi->vi_converter = CPyCppyy::CreateConverter(value_type);
608 if (!vi->vi_stride) vi->vi_stride = Cppyy::SizeOf(value_type);
609
610 } else if (CPPScope_Check(pyvalue_type)) {
611 vi->vi_klass = ((CPPClass*)pyvalue_type)->fCppType;
612 vi->vi_converter = nullptr;
613 if (!vi->vi_stride) vi->vi_stride = Cppyy::SizeOf(vi->vi_klass);
614 if (!vi->vi_flags) vi->vi_flags = vectoriterobject::kNeedLifeLine;
615 }
616
617 PyObject* pydata = CallPyObjMethod(v, "__real_data");
618 if (!pydata || Utility::GetBuffer(pydata, '*', 1, vi->vi_data, false) == 0)
619 vi->vi_data = CPPInstance_Check(pydata) ? ((CPPInstance*)pydata)->GetObjectRaw() : nullptr;
621
622 } else {
623 PyErr_Clear();
624 vi->vi_data = nullptr;
625 vi->vi_stride = 0;
626 vi->vi_converter = nullptr;
627 vi->vi_klass = 0;
628 vi->vi_flags = 0;
629 }
630
632
633 vi->ii_pos = 0;
634 vi->ii_len = PySequence_Size(v);
635
637 return (PyObject*)vi;
638}
639
641{
642// Implement python's __getitem__ for std::vector<>s.
643 if (PySlice_Check(index)) {
644 if (!self->GetObject()) {
645 PyErr_SetString(PyExc_TypeError, "unsubscriptable object");
646 return nullptr;
647 }
648
651
652 Py_ssize_t start, stop, step;
654
656 if (!AdjustSlice(nlen, start, stop, step))
657 return nseq;
658
659 const Py_ssize_t sign = step < 0 ? -1 : 1;
660 for (Py_ssize_t i = start; i*sign < stop*sign; i += step) {
663 CallPyObjMethod(nseq, "push_back", item);
666 }
667
668 return nseq;
669 }
670
672}
673
674
676
678{
679// std::vector<bool> is a special-case in C++, and its return type depends on
680// the compiler: treat it special here as well
681 if (!CPPInstance_Check(self) || self->ObjectIsA() != sVectorBoolTypeID) {
683 "require object of type std::vector<bool>, but %s given",
684 Cppyy::GetScopedFinalName(self->ObjectIsA()).c_str());
685 return nullptr;
686 }
687
688 if (!self->GetObject()) {
689 PyErr_SetString(PyExc_TypeError, "unsubscriptable object");
690 return nullptr;
691 }
692
693 if (PySlice_Check(idx)) {
696
697 Py_ssize_t start, stop, step;
700 if (!AdjustSlice(nlen, start, stop, step))
701 return nseq;
702
703 const Py_ssize_t sign = step < 0 ? -1 : 1;
704 for (Py_ssize_t i = start; i*sign < stop*sign; i += step) {
707 CallPyObjMethod(nseq, "push_back", item);
710 }
711
712 return nseq;
713 }
714
716 if (!pyindex)
717 return nullptr;
718
721
722// get hold of the actual std::vector<bool> (no cast, as vector is never a base)
723 std::vector<bool>* vb = (std::vector<bool>*)self->GetObject();
724
725// finally, return the value
726 if (bool((*vb)[index]))
729}
730
732{
733// std::vector<bool> is a special-case in C++, and its return type depends on
734// the compiler: treat it special here as well
735 if (!CPPInstance_Check(self) || self->ObjectIsA() != sVectorBoolTypeID) {
737 "require object of type std::vector<bool>, but %s given",
738 Cppyy::GetScopedFinalName(self->ObjectIsA()).c_str());
739 return nullptr;
740 }
741
742 if (!self->GetObject()) {
743 PyErr_SetString(PyExc_TypeError, "unsubscriptable object");
744 return nullptr;
745 }
746
747 int bval = 0; PyObject* idx = nullptr;
748 if (!PyArg_ParseTuple(args, const_cast<char*>("Oi:__setitem__"), &idx, &bval))
749 return nullptr;
750
752 if (!pyindex)
753 return nullptr;
754
757
758// get hold of the actual std::vector<bool> (no cast, as vector is never a base)
759 std::vector<bool>* vb = (std::vector<bool>*)self->GetObject();
760
761// finally, set the value
762 (*vb)[index] = (bool)bval;
763
765}
766
767
768//- array behavior as primitives ----------------------------------------------
769PyObject* ArrayInit(PyObject* self, PyObject* args, PyObject* /* kwds */)
770{
771// std::array is normally only constructed using aggregate initialization, which
772// is a concept that does not exist in python, so use this custom constructor to
773// to fill the array using setitem
774
775 if (args && PyTuple_GET_SIZE(args) == 1 && PySequence_Check(PyTuple_GET_ITEM(args, 0))) {
776 // construct the empty array, then fill it
778 if (!result)
779 return nullptr;
780
781 PyObject* items = PyTuple_GET_ITEM(args, 0);
783 if (PySequence_Size(self) != fillsz) {
784 PyErr_Format(PyExc_ValueError, "received sequence of size %zd where %zd expected",
787 return nullptr;
788 }
789
791 for (Py_ssize_t i = 0; i < fillsz; ++i) {
797 if (!sires) {
800 return nullptr;
801 } else
803 }
805
806 return result;
807 } else
808 PyErr_Clear();
809
810// The given argument wasn't iterable: simply forward to regular constructor
812 if (realInit) {
813 PyObject* result = PyObject_Call(realInit, args, nullptr);
815 return result;
816 }
817
818 return nullptr;
819}
820
821
822//- map behavior as primitives ------------------------------------------------
824{
825// construct an empty map, then fill it with the key, value pairs
827 if (!result)
828 return nullptr;
829
831 for (Py_ssize_t i = 0; i < PySequence_Size(pairs); ++i) {
833 PyObject* sires = nullptr;
834 if (pair && PySequence_Check(pair) && PySequence_Size(pair) == 2) {
835 PyObject* key = PySequence_GetItem(pair, 0);
839 Py_DECREF(key);
840 }
841 Py_DECREF(pair);
842 if (!sires) {
845 if (!PyErr_Occurred())
846 PyErr_SetString(PyExc_TypeError, "Failed to fill map (argument not a dict or sequence of pairs)");
847 return nullptr;
848 } else
850 }
852
853 return result;
854}
855
856PyObject* MapInit(PyObject* self, PyObject* args, PyObject* /* kwds */)
857{
858// Specialized map constructor to allow construction from mapping containers and
859// from tuples of pairs ("initializer_list style").
860
861// PyMapping_Check is not very discriminatory, as it basically only checks for the
862// existence of __getitem__, hence the most common cases of tuple and list are
863// dropped straight-of-the-bat (the PyMapping_Items call will fail on them).
864 if (PyTuple_GET_SIZE(args) == 1 && PyMapping_Check(PyTuple_GET_ITEM(args, 0)) && \
866 PyObject* assoc = PyTuple_GET_ITEM(args, 0);
867#if PY_VERSION_HEX < 0x03000000
868 // to prevent warning about literal string, expand macro
869 PyObject* items = PyObject_CallMethod(assoc, (char*)"items", nullptr);
870#else
871 // in p3, PyMapping_Items isn't a macro, but a function that short-circuits dict
873#endif
874 if (items && PySequence_Check(items)) {
877 return result;
878 }
879
881 PyErr_Clear();
882
883 // okay to fall through as long as 'self' has not been created (is done in MapFromPairs)
884 }
885
886// tuple of pairs case (some mapping types are sequences)
887 if (PyTuple_GET_SIZE(args) == 1 && PySequence_Check(PyTuple_GET_ITEM(args, 0)))
888 return MapFromPairs(self, PyTuple_GET_ITEM(args, 0));
889
890// The given argument wasn't a mapping or tuple of pairs: forward to regular constructor
892 if (realInit) {
893 PyObject* result = PyObject_Call(realInit, args, nullptr);
895 return result;
896 }
897
898 return nullptr;
899}
900
902{
903// Implement python's __contains__ for std::map/std::set
904 PyObject* result = nullptr;
905
906 PyObject* iter = CallPyObjMethod(self, "find", obj);
907 if (CPPInstance_Check(iter)) {
909 if (CPPInstance_Check(end)) {
910 if (!PyObject_RichCompareBool(iter, end, Py_EQ)) {
912 result = Py_True;
913 }
914 }
915 Py_XDECREF(end);
916 }
917 Py_XDECREF(iter);
918
919 if (!result) {
920 PyErr_Clear(); // e.g. wrong argument type, which should always lead to False
923 }
924
925 return result;
926}
927
928
929//- set behavior as primitives ------------------------------------------------
930PyObject* SetInit(PyObject* self, PyObject* args, PyObject* /* kwds */)
931{
932// Specialized set constructor to allow construction from Python sets.
933 if (PyTuple_GET_SIZE(args) == 1 && PySet_Check(PyTuple_GET_ITEM(args, 0))) {
934 PyObject* pyset = PyTuple_GET_ITEM(args, 0);
935
936 // construct an empty set, then fill it
938 if (!result)
939 return nullptr;
940
942 if (iter) {
943 PyObject* ins_call = PyObject_GetAttrString(self, (char*)"insert");
944
945 IterItemGetter getter{iter};
946 Py_DECREF(iter);
947
948 PyObject* item = getter.get();
949 while (item) {
952 if (!insres) {
955 return nullptr;
956 } else
958 item = getter.get();
959 }
961 }
962
963 return result;
964 }
965
966// The given argument wasn't iterable: simply forward to regular constructor
968 if (realInit) {
969 PyObject* result = PyObject_Call(realInit, args, nullptr);
971 return result;
972 }
973
974 return nullptr;
975}
976
977
978//- STL container iterator support --------------------------------------------
979static const ptrdiff_t PS_END_ADDR = 7; // non-aligned address, so no clash
980static const ptrdiff_t PS_FLAG_ADDR = 11; // id.
981static const ptrdiff_t PS_COLL_ADDR = 13; // id.
982
984{
985// Implement python's __iter__ for low level views used through STL-type begin()/end()
987
988 if (LowLevelView_Check(iter)) {
989 // builtin pointer iteration: can only succeed if a size is available
991 if (sz == -1) {
992 Py_DECREF(iter);
993 return nullptr;
994 }
995 PyObject* lliter = Py_TYPE(iter)->tp_iter(iter);
996 ((indexiterobject*)lliter)->ii_len = sz;
997 Py_DECREF(iter);
998 return lliter;
999 }
1000
1001 if (iter) {
1002 Py_DECREF(iter);
1003 PyErr_SetString(PyExc_TypeError, "unrecognized iterator type for low level views");
1004 }
1005
1006 return nullptr;
1007}
1008
1010{
1011// Implement python's __iter__ for std::iterator<>s
1013 if (iter) {
1015 if (end) {
1016 if (CPPInstance_Check(iter)) {
1017 // use the data member cache to store extra state on the iterator object,
1018 // without it being visible on the Python side
1019 auto& dmc = ((CPPInstance*)iter)->GetDatamemberCache();
1020 dmc.push_back(std::make_pair(PS_END_ADDR, end));
1021
1022 // set a flag, indicating first iteration (reset in __next__)
1024 dmc.push_back(std::make_pair(PS_FLAG_ADDR, Py_False));
1025
1026 // make sure the iterated over collection remains alive for the duration
1027 Py_INCREF(self);
1028 dmc.push_back(std::make_pair(PS_COLL_ADDR, self));
1029 } else {
1030 // could store "end" on the object's dictionary anyway, but if end() returns
1031 // a user-customized object, then its __next__ is probably custom, too
1032 Py_DECREF(end);
1033 }
1034 }
1035 }
1036 return iter;
1037}
1038
1039//- generic iterator support over a sequence with operator[] and size ---------
1040//-----------------------------------------------------------------------------
1041static PyObject* index_iter(PyObject* c) {
1043 if (!ii) return nullptr;
1044
1045 Py_INCREF(c);
1046 ii->ii_container = c;
1047 ii->ii_pos = 0;
1048 ii->ii_len = PySequence_Size(c);
1049
1051 return (PyObject*)ii;
1052}
1053
1054
1055//- safe indexing for STL-like vector w/o iterator dictionaries ---------------
1056/* replaced by indexiterobject iteration, but may still have some future use ...
1057PyObject* CheckedGetItem(PyObject* self, PyObject* obj)
1058{
1059// Implement a generic python __getitem__ for STL-like classes that are missing the
1060// reflection info for their iterators. This is then used for iteration by means of
1061// consecutive indices, it such index is of integer type.
1062 Py_ssize_t size = PySequence_Size(self);
1063 Py_ssize_t idx = PyInt_AsSsize_t(obj);
1064 if ((size == (Py_ssize_t)-1 || idx == (Py_ssize_t)-1) && PyErr_Occurred()) {
1065 // argument conversion problem: let method itself resolve anew and report
1066 PyErr_Clear();
1067 return PyObject_CallMethodOneArg(self, PyStrings::gGetNoCheck, obj);
1068 }
1069
1070 bool inbounds = false;
1071 if (idx < 0) idx += size;
1072 if (0 <= idx && 0 <= size && idx < size)
1073 inbounds = true;
1074
1075 if (inbounds)
1076 return PyObject_CallMethodOneArg(self, PyStrings::gGetNoCheck, obj);
1077 else
1078 PyErr_SetString( PyExc_IndexError, "index out of range" );
1079
1080 return nullptr;
1081}*/
1082
1083
1084//- pair as sequence to allow tuple unpacking --------------------------------
1086{
1087// For std::map<> iteration, unpack std::pair<>s into tuples for the loop.
1088 long idx = PyLong_AsLong(pyindex);
1089 if (idx == -1 && PyErr_Occurred())
1090 return nullptr;
1091
1092 if (!CPPInstance_Check(self) || !((CPPInstance*)self)->GetObject()) {
1093 PyErr_SetString(PyExc_TypeError, "unsubscriptable object");
1094 return nullptr;
1095 }
1096
1097 if ((int)idx == 0)
1099 else if ((int)idx == 1)
1101
1102// still here? Trigger stop iteration
1103 PyErr_SetString(PyExc_IndexError, "out of bounds");
1104 return nullptr;
1105}
1106
1107//- simplistic len() functions -----------------------------------------------
1109 return PyInt_FromLong(2);
1110}
1111
1112
1113//- shared/unique_ptr behavior -----------------------------------------------
1114PyObject* SmartPtrInit(PyObject* self, PyObject* args, PyObject* /* kwds */)
1115{
1116// since the shared/unique pointer will take ownership, we need to relinquish it
1118 if (realInit) {
1119 PyObject* result = PyObject_Call(realInit, args, nullptr);
1121 if (result && PyTuple_GET_SIZE(args) == 1 && CPPInstance_Check(PyTuple_GET_ITEM(args, 0))) {
1123 if (!(cppinst->fFlags & CPPInstance::kIsSmartPtr)) cppinst->CppOwns();
1124 }
1125 return result;
1126 }
1127 return nullptr;
1128}
1129
1130
1131//- string behavior as primitives --------------------------------------------
1132#if PY_VERSION_HEX >= 0x03000000
1133// TODO: this is wrong, b/c it doesn't order
1136}
1137#endif
1138static inline
1139PyObject* CPyCppyy_PyString_FromCppString(std::string_view s, bool native=true) {
1140 if (native)
1141 return PyBytes_FromStringAndSize(s.data(), s.size());
1142 return CPyCppyy_PyText_FromStringAndSize(s.data(), s.size());
1143}
1144
1145static inline
1146PyObject* CPyCppyy_PyString_FromCppString(std::wstring_view s, bool native=true) {
1147 PyObject* pyobj = PyUnicode_FromWideChar(s.data(), s.size());
1148 if (pyobj && native) {
1149 PyObject* pybytes = PyUnicode_AsEncodedString(pyobj, "UTF-8", "strict");
1151 pyobj = pybytes;
1152 }
1153 return pyobj;
1154}
1155
1156#define CPPYY_IMPL_STRING_PYTHONIZATION(type, name) \
1157static inline \
1158PyObject* name##StringGetData(PyObject* self, bool native=true) \
1159{ \
1160 if (CPyCppyy::CPPInstance_Check(self)) { \
1161 type* obj = ((type*)((CPPInstance*)self)->GetObject()); \
1162 if (obj) return CPyCppyy_PyString_FromCppString(*obj, native); \
1163 } \
1164 PyErr_Format(PyExc_TypeError, "object mismatch (%s expected)", #type); \
1165 return nullptr; \
1166} \
1167 \
1168PyObject* name##StringStr(PyObject* self) \
1169{ \
1170 PyObject* pyobj = name##StringGetData(self, false); \
1171 if (!pyobj) { \
1172 /* do a native conversion to make printing possible (debatable) */ \
1173 PyErr_Clear(); \
1174 PyObject* pybytes = name##StringGetData(self, true); \
1175 if (pybytes) { /* should not fail */ \
1176 pyobj = PyObject_Str(pybytes); \
1177 Py_DECREF(pybytes); \
1178 } \
1179 } \
1180 return pyobj; \
1181} \
1182 \
1183PyObject* name##StringBytes(PyObject* self) \
1184{ \
1185 return name##StringGetData(self, true); \
1186} \
1187 \
1188PyObject* name##StringRepr(PyObject* self) \
1189{ \
1190 PyObject* data = name##StringGetData(self, true); \
1191 if (data) { \
1192 PyObject* repr = PyObject_Repr(data); \
1193 Py_DECREF(data); \
1194 return repr; \
1195 } \
1196 return nullptr; \
1197} \
1198 \
1199PyObject* name##StringIsEqual(PyObject* self, PyObject* obj) \
1200{ \
1201 PyObject* data = name##StringGetData(self, PyBytes_Check(obj)); \
1202 if (data) { \
1203 PyObject* result = PyObject_RichCompare(data, obj, Py_EQ); \
1204 Py_DECREF(data); \
1205 return result; \
1206 } \
1207 return nullptr; \
1208} \
1209 \
1210PyObject* name##StringIsNotEqual(PyObject* self, PyObject* obj) \
1211{ \
1212 PyObject* data = name##StringGetData(self, PyBytes_Check(obj)); \
1213 if (data) { \
1214 PyObject* result = PyObject_RichCompare(data, obj, Py_NE); \
1215 Py_DECREF(data); \
1216 return result; \
1217 } \
1218 return nullptr; \
1219}
1220
1221// Only define STLStringCompare:
1222#define CPPYY_IMPL_STRING_PYTHONIZATION_CMP(type, name) \
1223CPPYY_IMPL_STRING_PYTHONIZATION(type, name) \
1224PyObject* name##StringCompare(PyObject* self, PyObject* obj) \
1225{ \
1226 PyObject* data = name##StringGetData(self, PyBytes_Check(obj)); \
1227 int result = 0; \
1228 if (data) { \
1229 result = PyObject_Compare(data, obj); \
1230 Py_DECREF(data); \
1231 } \
1232 if (PyErr_Occurred()) \
1233 return nullptr; \
1234 return PyInt_FromLong(result); \
1235}
1236
1240
1241static inline std::string* GetSTLString(CPPInstance* self) {
1242 if (!CPPInstance_Check(self)) {
1243 PyErr_SetString(PyExc_TypeError, "std::string object expected");
1244 return nullptr;
1245 }
1246
1247 std::string* obj = (std::string*)self->GetObject();
1248 if (!obj)
1249 PyErr_SetString(PyExc_ReferenceError, "attempt to access a null-pointer");
1250
1251 return obj;
1252}
1253
1255{
1256 std::string* obj = GetSTLString(self);
1257 if (!obj)
1258 return nullptr;
1259
1260 char* keywords[] = {(char*)"encoding", (char*)"errors", (char*)nullptr};
1261 const char* encoding = nullptr; const char* errors = nullptr;
1263 const_cast<char*>("s|s"), keywords, &encoding, &errors))
1264 return nullptr;
1265
1266 return PyUnicode_Decode(obj->data(), obj->size(), encoding, errors);
1267}
1268
1270{
1271 std::string* obj = GetSTLString(self);
1272 if (!obj)
1273 return nullptr;
1274
1275 const char* needle = CPyCppyy_PyText_AsString(pyobj);
1276 if (!needle)
1277 return nullptr;
1278
1279 if (obj->find(needle) != std::string::npos) {
1281 }
1282
1284}
1285
1287{
1288 std::string* obj = GetSTLString(self);
1289 if (!obj)
1290 return nullptr;
1291
1292// both str and std::string have a method "replace", but the Python version only
1293// accepts strings and takes no keyword arguments, whereas the C++ version has no
1294// overload that takes a string
1295
1296 if (2 <= PyTuple_GET_SIZE(args) && CPyCppyy_PyText_Check(PyTuple_GET_ITEM(args, 0))) {
1297 PyObject* pystr = CPyCppyy_PyText_FromStringAndSize(obj->data(), obj->size());
1298 PyObject* meth = PyObject_GetAttrString(pystr, (char*)"replace");
1301 Py_DECREF(meth);
1302 return result;
1303 }
1304
1305 PyObject* cppreplace = PyObject_GetAttrString((PyObject*)self, (char*)"__cpp_replace");
1306 if (cppreplace) {
1307 PyObject* result = PyObject_Call(cppreplace, args, nullptr);
1309 return result;
1310 }
1311
1312 PyErr_SetString(PyExc_AttributeError, "\'std::string\' object has no attribute \'replace\'");
1313 return nullptr;
1314}
1315
1316#define CPYCPPYY_STRING_FINDMETHOD(name, cppname, pyname) \
1317PyObject* STLString##name(CPPInstance* self, PyObject* args, PyObject* /*kwds*/) \
1318{ \
1319 std::string* obj = GetSTLString(self); \
1320 if (!obj) \
1321 return nullptr; \
1322 \
1323 PyObject* cppmeth = PyObject_GetAttrString((PyObject*)self, (char*)#cppname);\
1324 if (cppmeth) { \
1325 PyObject* result = PyObject_Call(cppmeth, args, nullptr); \
1326 Py_DECREF(cppmeth); \
1327 if (result) { \
1328 if (PyLongOrInt_AsULong64(result) == (PY_ULONG_LONG)std::string::npos) {\
1329 Py_DECREF(result); \
1330 return PyInt_FromLong(-1); \
1331 } \
1332 return result; \
1333 } \
1334 PyErr_Clear(); \
1335 } \
1336 \
1337 PyObject* pystr = CPyCppyy_PyText_FromStringAndSize(obj->data(), obj->size());\
1338 PyObject* pymeth = PyObject_GetAttrString(pystr, (char*)#pyname); \
1339 Py_DECREF(pystr); \
1340 PyObject* result = PyObject_CallObject(pymeth, args); \
1341 Py_DECREF(pymeth); \
1342 return result; \
1343}
1344
1345// both str and std::string have method "find" and "rfin"; try the C++ version first
1346// and fall back on the Python one in case of failure
1349
1351{
1352 std::string* obj = GetSTLString(self);
1353 if (!obj)
1354 return nullptr;
1355
1356 PyObject* pystr = CPyCppyy_PyText_FromStringAndSize(obj->data(), obj->size());
1359 return attr;
1360}
1361
1362
1363#if 0
1365{
1366// force C++ string types conversion to Python str per Python __repr__ requirements
1368 if (!res || CPyCppyy_PyText_Check(res))
1369 return res;
1371 Py_DECREF(res);
1372 return str_res;
1373}
1374
1376{
1377// force C++ string types conversion to Python str per Python __str__ requirements
1379 if (!res || CPyCppyy_PyText_Check(res))
1380 return res;
1382 Py_DECREF(res);
1383 return str_res;
1384}
1385#endif
1386
1388{
1389// std::string objects hash to the same values as Python strings to allow
1390// matches in dictionaries etc.
1393 Py_DECREF(data);
1394 return h;
1395}
1396
1397
1398//- string_view behavior as primitive ----------------------------------------
1400{
1401// if constructed from a Python unicode object, the constructor will convert it
1402// to a temporary byte string, which is likely to go out of scope too soon; so
1403// buffer it as needed
1405 if (realInit) {
1406 PyObject *strbuf = nullptr, *newArgs = nullptr;
1407 if (PyTuple_GET_SIZE(args) == 1) {
1408 PyObject* arg0 = PyTuple_GET_ITEM(args, 0);
1409 if (PyUnicode_Check(arg0)) {
1410 // convert to the expected bytes array to control the temporary
1411 strbuf = PyUnicode_AsEncodedString(arg0, "UTF-8", "strict");
1412 newArgs = PyTuple_New(1);
1415 } else if (PyBytes_Check(arg0)) {
1416 // tie the life time of the provided string to the string_view
1417 Py_INCREF(arg0);
1418 strbuf = arg0;
1419 }
1420 }
1421
1422 PyObject* result = PyObject_Call(realInit, newArgs ? newArgs : args, nullptr);
1423
1426
1427 // if construction was successful and a string buffer was used, add a
1428 // life line to it from the string_view bound object
1429 if (result && self && strbuf)
1432
1433 return result;
1434 }
1435 return nullptr;
1436}
1437
1438
1439//- STL iterator behavior ----------------------------------------------------
1441{
1442// Python iterator protocol __next__ for STL forward iterators.
1443 bool mustIncrement = true;
1444 PyObject* last = nullptr;
1445 if (CPPInstance_Check(self)) {
1446 auto& dmc = ((CPPInstance*)self)->GetDatamemberCache();
1447 for (auto& p: dmc) {
1448 if (p.first == PS_END_ADDR) {
1449 last = p.second;
1450 Py_INCREF(last);
1451 } else if (p.first == PS_FLAG_ADDR) {
1452 mustIncrement = p.second == Py_True;
1453 if (!mustIncrement) {
1454 Py_DECREF(p.second);
1456 p.second = Py_True;
1457 }
1458 }
1459 }
1460 }
1461
1462 PyObject* next = nullptr;
1463 if (last) {
1464 // handle special case of empty container (i.e. self is end)
1465 if (!PyObject_RichCompareBool(last, self, Py_EQ)) {
1466 bool iter_valid = true;
1467 if (mustIncrement) {
1468 // prefer preinc, but allow post-inc; in both cases, it is "self" that has
1469 // the updated state to dereference
1471 if (!iter) {
1472 PyErr_Clear();
1473 static PyObject* dummy = PyInt_FromLong(1l);
1475 }
1477 Py_XDECREF(iter);
1478 }
1479
1480 if (iter_valid) {
1482 if (!next) PyErr_Clear();
1483 }
1484 }
1485 Py_DECREF(last);
1486 }
1487
1488 if (!next) PyErr_SetString(PyExc_StopIteration, "");
1489 return next;
1490}
1491
1492
1493//- STL complex<T> behavior --------------------------------------------------
1494#define COMPLEX_METH_GETSET(name, cppname) \
1495static PyObject* name##ComplexGet(PyObject* self, void*) { \
1496 return PyObject_CallMethodNoArgs(self, cppname); \
1497} \
1498static int name##ComplexSet(PyObject* self, PyObject* value, void*) { \
1499 PyObject* result = PyObject_CallMethodOneArg(self, cppname, value); \
1500 if (result) { \
1501 Py_DECREF(result); \
1502 return 0; \
1503 } \
1504 return -1; \
1505} \
1506PyGetSetDef name##Complex{(char*)#name, (getter)name##ComplexGet, (setter)name##ComplexSet, nullptr, nullptr};
1507
1510
1513 if (!real) return nullptr;
1514 double r = PyFloat_AsDouble(real);
1515 Py_DECREF(real);
1516 if (r == -1. && PyErr_Occurred())
1517 return nullptr;
1518
1520 if (!imag) return nullptr;
1521 double i = PyFloat_AsDouble(imag);
1522 Py_DECREF(imag);
1523 if (i == -1. && PyErr_Occurred())
1524 return nullptr;
1525
1526 return PyComplex_FromDoubles(r, i);
1527}
1528
1531 if (!real) return nullptr;
1532 double r = PyFloat_AsDouble(real);
1533 Py_DECREF(real);
1534 if (r == -1. && PyErr_Occurred())
1535 return nullptr;
1536
1538 if (!imag) return nullptr;
1539 double i = PyFloat_AsDouble(imag);
1540 Py_DECREF(imag);
1541 if (i == -1. && PyErr_Occurred())
1542 return nullptr;
1543
1544 std::ostringstream s;
1545 s << '(' << r << '+' << i << "j)";
1546 return CPyCppyy_PyText_FromString(s.str().c_str());
1547}
1548
1550{
1551 return PyFloat_FromDouble(((std::complex<double>*)self->GetObject())->real());
1552}
1553
1554static int ComplexDRealSet(CPPInstance* self, PyObject* value, void*)
1555{
1556 double d = PyFloat_AsDouble(value);
1557 if (d == -1.0 && PyErr_Occurred())
1558 return -1;
1559 ((std::complex<double>*)self->GetObject())->real(d);
1560 return 0;
1561}
1562
1563PyGetSetDef ComplexDReal{(char*)"real", (getter)ComplexDRealGet, (setter)ComplexDRealSet, nullptr, nullptr};
1564
1565
1567{
1568 return PyFloat_FromDouble(((std::complex<double>*)self->GetObject())->imag());
1569}
1570
1571static int ComplexDImagSet(CPPInstance* self, PyObject* value, void*)
1572{
1573 double d = PyFloat_AsDouble(value);
1574 if (d == -1.0 && PyErr_Occurred())
1575 return -1;
1576 ((std::complex<double>*)self->GetObject())->imag(d);
1577 return 0;
1578}
1579
1580PyGetSetDef ComplexDImag{(char*)"imag", (getter)ComplexDImagGet, (setter)ComplexDImagSet, nullptr, nullptr};
1581
1583{
1584 double r = ((std::complex<double>*)self->GetObject())->real();
1585 double i = ((std::complex<double>*)self->GetObject())->imag();
1586 return PyComplex_FromDoubles(r, i);
1587}
1588
1589
1590} // unnamed namespace
1591
1592
1593//- public functions ---------------------------------------------------------
1594namespace CPyCppyy {
1595 std::set<std::string> gIteratorTypes;
1596}
1597
1598static inline
1599bool run_pythonizors(PyObject* pyclass, PyObject* pyname, const std::vector<PyObject*>& v)
1600{
1601 PyObject* args = PyTuple_New(2);
1604
1605 bool pstatus = true;
1606 for (auto pythonizor : v) {
1608 if (!result) {
1609 pstatus = false; // TODO: detail the error handling
1610 break;
1611 }
1613 }
1614 Py_DECREF(args);
1615
1616 return pstatus;
1617}
1618
1619bool CPyCppyy::Pythonize(PyObject* pyclass, const std::string& name)
1620{
1621// Add pre-defined pythonizations (for STL and ROOT) to classes based on their
1622// signature and/or class name.
1623 if (!pyclass)
1624 return false;
1625
1627
1628//- method name based pythonization ------------------------------------------
1629
1630// for smart pointer style classes that are otherwise not known as such; would
1631// prefer operator-> as that returns a pointer (which is simpler since it never
1632// has to deal with ref-assignment), but operator* plays better with STL iters
1633// and algorithms
1638
1639// for pre-check of nullptr for boolean types
1641#if PY_VERSION_HEX >= 0x03000000
1642 const char* pybool_name = "__bool__";
1643#else
1644 const char* pybool_name = "__nonzero__";
1645#endif
1647 }
1648
1649// for STL containers, and user classes modeled after them
1650// the attribute must be a CPyCppyy overload, otherwise the check gives false
1651// positives in the case where the class has a non-function attribute that is
1652// called "size".
1653 if (HasAttrDirect(pyclass, PyStrings::gSize, /*mustBeCPyCppyy=*/ true)) {
1654 Utility::AddToClass(pyclass, "__len__", "size");
1655 }
1656
1657 if (!IsTemplatedSTLClass(name, "vector") && // vector is dealt with below
1660 // obtain the name of the return type
1661 const auto& v = Cppyy::GetMethodIndicesFromName(klass->fCppType, "begin");
1662 if (!v.empty()) {
1663 // check return type; if not explicitly an iterator, add it to the "known" return
1664 // types to add the "next" method on use
1666 const std::string& resname = Cppyy::GetMethodResultType(meth);
1667 bool isIterator = gIteratorTypes.find(resname) != gIteratorTypes.end();
1669 if (resname.find("iterator") == std::string::npos)
1670 gIteratorTypes.insert(resname);
1671 isIterator = true;
1672 }
1673
1674 if (isIterator) {
1675 // install iterator protocol a la STL
1678 } else {
1679 // still okay if this is some pointer type of builtin persuasion (general class
1680 // won't work: the return type needs to understand the iterator protocol)
1681 std::string resolved = Cppyy::ResolveName(resname);
1682 if (resolved.back() == '*' && Cppyy::IsBuiltin(resolved.substr(0, resolved.size()-1))) {
1685 }
1686 }
1687 }
1688 }
1689 if (!((PyTypeObject*)pyclass)->tp_iter && // no iterator resolved
1691 // Python will iterate over __getitem__ using integers, but C++ operator[] will never raise
1692 // a StopIteration. A checked getitem (raising IndexError if beyond size()) works in some
1693 // cases but would mess up if operator[] is meant to implement an associative container. So,
1694 // this has to be implemented as an iterator protocol.
1697 }
1698 }
1699
1700// operator==/!= are used in op_richcompare of CPPInstance, which subsequently allows
1701// comparisons to None; if no operator is available, a hook is installed for lazy
1702// lookups in the global and/or class namespace
1703 if (HasAttrDirect(pyclass, PyStrings::gEq, true) && \
1704 Cppyy::GetMethodIndicesFromName(klass->fCppType, "__eq__").empty()) {
1706 if (!klass->fOperators) klass->fOperators = new Utility::PyOperators();
1707 klass->fOperators->fEq = cppol;
1708 // re-insert the forwarding __eq__ from the CPPInstance in case there was a Python-side
1709 // override in the base class
1710 static PyObject* top_eq = nullptr;
1711 if (!top_eq) {
1714 Py_DECREF(top_eq); // make it borrowed
1716 }
1718 }
1719
1720 if (HasAttrDirect(pyclass, PyStrings::gNe, true) && \
1721 Cppyy::GetMethodIndicesFromName(klass->fCppType, "__ne__").empty()) {
1723 if (!klass->fOperators) klass->fOperators = new Utility::PyOperators();
1724 klass->fOperators->fNe = cppol;
1725 // re-insert the forwarding __ne__ (same reason as above for __eq__)
1726 static PyObject* top_ne = nullptr;
1727 if (!top_ne) {
1730 Py_DECREF(top_ne); // make it borrowed
1732 }
1734 }
1735
1736#if 0
1738 // guarantee that the result of __repr__ is a Python string
1739 Utility::AddToClass(pyclass, "__cpp_repr", "__repr__");
1741 }
1742
1744 // guarantee that the result of __str__ is a Python string
1745 Utility::AddToClass(pyclass, "__cpp_str", "__str__");
1747 }
1748#endif
1749
1750 if (Cppyy::IsAggregate(((CPPClass*)pyclass)->fCppType) && name.compare(0, 5, "std::", 5) != 0 &&
1751 name.compare(0, 6, "tuple<", 6) != 0) {
1752 // create a pseudo-constructor to allow initializer-style object creation
1753 Cppyy::TCppType_t kls = ((CPPClass*)pyclass)->fCppType;
1755 if (ndata) {
1756 std::string rname = name;
1758
1759 std::ostringstream initdef;
1760 initdef << "namespace __cppyy_internal {\n"
1761 << "void init_" << rname << "(" << name << "*& self";
1762 bool codegen_ok = true;
1763 std::vector<std::string> arg_types, arg_names, arg_defaults;
1764 arg_types.reserve(ndata); arg_names.reserve(ndata); arg_defaults.reserve(ndata);
1765 for (Cppyy::TCppIndex_t i = 0; i < ndata; ++i) {
1767 continue;
1768
1769 const std::string& txt = Cppyy::GetDatamemberType(kls, i);
1770 const std::string& res = Cppyy::IsEnum(txt) ? txt : Cppyy::ResolveName(txt);
1771 const std::string& cpd = TypeManip::compound(res);
1772 std::string res_clean = TypeManip::clean_type(res, false, true);
1773
1774 if (res_clean == "internal_enum_type_t")
1775 res_clean = txt; // restore (properly scoped name)
1776
1777 if (res.rfind(']') == std::string::npos && res.rfind(')') == std::string::npos) {
1778 if (!cpd.empty()) arg_types.push_back(res_clean+cpd);
1779 else arg_types.push_back("const "+res_clean+"&");
1780 arg_names.push_back(Cppyy::GetDatamemberName(kls, i));
1781 if ((!cpd.empty() && cpd.back() == '*') || Cppyy::IsBuiltin(res_clean))
1782 arg_defaults.push_back("0");
1783 else {
1786 }
1787 } else {
1788 codegen_ok = false; // TODO: how to support arrays, anonymous enums, etc?
1789 break;
1790 }
1791 }
1792
1793 if (codegen_ok && !arg_types.empty()) {
1794 bool defaults_ok = arg_defaults.size() == arg_types.size();
1795 for (std::vector<std::string>::size_type i = 0; i < arg_types.size(); ++i) {
1796 initdef << ", " << arg_types[i] << " " << arg_names[i];
1797 if (defaults_ok) initdef << " = " << arg_defaults[i];
1798 }
1799 initdef << ") {\n self = new " << name << "{";
1800 for (std::vector<std::string>::size_type i = 0; i < arg_names.size(); ++i) {
1801 if (i != 0) initdef << ", ";
1802 initdef << arg_names[i];
1803 }
1804 initdef << "};\n} }";
1805
1806 if (Cppyy::Compile(initdef.str(), true /* silent */)) {
1807 Cppyy::TCppScope_t cis = Cppyy::GetScope("__cppyy_internal");
1808 const auto& mix = Cppyy::GetMethodIndicesFromName(cis, "init_"+rname);
1809 if (mix.size()) {
1810 if (!Utility::AddToClass(pyclass, "__init__",
1811 new CPPFunction(cis, Cppyy::GetMethod(cis, mix[0]))))
1812 PyErr_Clear();
1813 }
1814 }
1815 }
1816 }
1817 }
1818
1819
1820//- class name based pythonization -------------------------------------------
1821
1822 if (IsTemplatedSTLClass(name, "vector")) {
1823
1824 // std::vector<bool> is a special case in C++
1826 if (klass->fCppType == sVectorBoolTypeID) {
1829 } else {
1830 // constructor that takes python collections
1831 Utility::AddToClass(pyclass, "__real_init", "__init__");
1833
1834 // data with size
1835 Utility::AddToClass(pyclass, "__real_data", "data");
1836 PyErr_Clear(); // AddToClass might have failed for data
1838
1839 // The addition of the __array__ utility to std::vector Python proxies causes a
1840 // bug where the resulting array is a single dimension, causing loss of data when
1841 // converting to numpy arrays, for >1dim vectors. Since this C++ pythonization
1842 // was added with the upgrade in 6.32, and is only defined and used recursively,
1843 // the safe option is to disable this function and no longer add it.
1844#if 0
1845 // numpy array conversion
1847#endif
1848
1849 // checked getitem
1851 Utility::AddToClass(pyclass, "_getitem__unchecked", "__getitem__");
1853 }
1854
1855 // vector-optimized iterator protocol
1857
1858 // optimized __iadd__
1860
1861 // helpers for iteration
1862 const std::string& vtype = Cppyy::ResolveName(name+"::value_type");
1863 if (vtype.rfind("value_type") == std::string::npos) { // actually resolved?
1867 }
1868
1869 size_t typesz = Cppyy::SizeOf(name+"::value_type");
1870 if (typesz) {
1874 }
1875 }
1876 }
1877
1878 else if (IsTemplatedSTLClass(name, "array")) {
1879 // constructor that takes python associative collections
1880 Utility::AddToClass(pyclass, "__real_init", "__init__");
1882 }
1883
1884 else if (IsTemplatedSTLClass(name, "map") || IsTemplatedSTLClass(name, "unordered_map")) {
1885 // constructor that takes python associative collections
1886 Utility::AddToClass(pyclass, "__real_init", "__init__");
1888
1890 }
1891
1892 else if (IsTemplatedSTLClass(name, "set")) {
1893 // constructor that takes python associative collections
1894 Utility::AddToClass(pyclass, "__real_init", "__init__");
1896
1898 }
1899
1900 else if (IsTemplatedSTLClass(name, "pair")) {
1903 }
1904
1905 if (IsTemplatedSTLClass(name, "shared_ptr") || IsTemplatedSTLClass(name, "unique_ptr")) {
1906 Utility::AddToClass(pyclass, "__real_init", "__init__");
1908 }
1909
1910 else if (!((PyTypeObject*)pyclass)->tp_iter && \
1911 (name.find("iterator") != std::string::npos || gIteratorTypes.find(name) != gIteratorTypes.end())) {
1912 ((PyTypeObject*)pyclass)->tp_iternext = (iternextfunc)STLIterNext;
1916 }
1917
1918 else if (name == "string" || name == "std::string") { // TODO: ask backend as well
1927 Utility::AddToClass(pyclass, "__cpp_find", "find");
1929 Utility::AddToClass(pyclass, "__cpp_rfind", "rfind");
1931 Utility::AddToClass(pyclass, "__cpp_replace", "replace");
1934
1935 // to allow use of std::string in dictionaries and findable with str
1937 }
1938
1939 else if (name == "basic_string_view<char,char_traits<char> >" || name == "std::basic_string_view<char>") {
1940 Utility::AddToClass(pyclass, "__real_init", "__init__");
1948 }
1949
1950 else if (name == "basic_string<wchar_t,char_traits<wchar_t>,allocator<wchar_t> >" || name == "std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t> >") {
1957 }
1958
1959 else if (name == "complex<double>" || name == "std::complex<double>") {
1960 Utility::AddToClass(pyclass, "__cpp_real", "real");
1962 Utility::AddToClass(pyclass, "__cpp_imag", "imag");
1966 }
1967
1968 else if (IsTemplatedSTLClass(name, "complex")) {
1969 Utility::AddToClass(pyclass, "__cpp_real", "real");
1971 Utility::AddToClass(pyclass, "__cpp_imag", "imag");
1975 }
1976
1977// direct user access; there are two calls here:
1978// - explicit pythonization: won't fall through to the base classes and is preferred if present
1979// - normal pythonization: only called if explicit isn't present, falls through to base classes
1980 bool bUserOk = true; PyObject* res = nullptr;
1984 bUserOk = (bool)res;
1985 } else {
1987 if (func) {
1988 res = PyObject_CallFunctionObjArgs(func, pyclass, pyname, nullptr);
1989 Py_DECREF(func);
1990 bUserOk = (bool)res;
1991 } else
1992 PyErr_Clear();
1993 }
1994 if (!bUserOk) {
1996 return false;
1997 } else {
1998 Py_XDECREF(res);
1999 // pyname handed to args tuple below
2000 }
2001
2002// call registered pythonizors, if any: first run the namespace-specific pythonizors, then
2003// the global ones (the idea is to allow writing a pythonizor that see all classes)
2004 bool pstatus = true;
2006 auto &pyzMap = pythonizations();
2007 if (!outer_scope.empty()) {
2008 auto p = pyzMap.find(outer_scope);
2009 if (p != pyzMap.end()) {
2011 name.substr(outer_scope.size()+2, std::string::npos).c_str());
2014 }
2015 }
2016
2017 if (pstatus) {
2018 auto p = pyzMap.find("");
2019 if (p != pyzMap.end())
2020 pstatus = run_pythonizors(pyclass, pyname, p->second);
2021 }
2022
2024
2025// phew! all done ...
2026 return pstatus;
2027}
#define Py_TYPE(ob)
Definition CPyCppyy.h:196
#define Py_RETURN_TRUE
Definition CPyCppyy.h:272
#define Py_RETURN_FALSE
Definition CPyCppyy.h:276
#define PyInt_FromSsize_t
Definition CPyCppyy.h:217
#define CPyCppyy_PyText_FromStringAndSize
Definition CPyCppyy.h:85
#define PyBytes_Check
Definition CPyCppyy.h:61
#define PyInt_AsSsize_t
Definition CPyCppyy.h:216
#define CPyCppyy_PySliceCast
Definition CPyCppyy.h:189
#define CPyCppyy_PyText_AsString
Definition CPyCppyy.h:76
long Py_hash_t
Definition CPyCppyy.h:114
static PyObject * PyObject_CallMethodOneArg(PyObject *obj, PyObject *name, PyObject *arg)
Definition CPyCppyy.h:385
#define PyBytes_FromStringAndSize
Definition CPyCppyy.h:70
#define Py_RETURN_NONE
Definition CPyCppyy.h:268
#define CPyCppyy_PyText_Type
Definition CPyCppyy.h:94
static PyObject * PyObject_CallMethodNoArgs(PyObject *obj, PyObject *name)
Definition CPyCppyy.h:381
#define CPPYY__next__
Definition CPyCppyy.h:112
#define CPyCppyy_PyText_FromString
Definition CPyCppyy.h:81
#define CPyCppyy_PyText_Check
Definition CPyCppyy.h:74
bool PyUnstable_Object_IsUniqueReferencedTemporary(PyObject *pyobject)
_object PyObject
#define CPPYY_IMPL_STRING_PYTHONIZATION_CMP(type, name)
static bool run_pythonizors(PyObject *pyclass, PyObject *pyname, const std::vector< PyObject * > &v)
#define COMPLEX_METH_GETSET(name, cppname)
#define CPYCPPYY_STRING_FINDMETHOD(name, cppname, pyname)
#define PyObject_LengthHint
std::ios_base::fmtflags fFlags
void FillVector(std::vector< double > &v, int size, T *a)
#define d(i)
Definition RSha256.hxx:102
#define c(i)
Definition RSha256.hxx:101
#define h(i)
Definition RSha256.hxx:106
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
winID h TVirtualViewer3D TVirtualGLPainter p
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void data
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t r
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t index
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t attr
char name[80]
Definition TGX11.cxx:110
const_iterator end() const
PyObject * gCTypesType
Definition PyStrings.cxx:39
PyObject * gRealInit
Definition PyStrings.cxx:42
PyObject * gExPythonize
Definition PyStrings.cxx:72
PyObject * gLifeLine
Definition PyStrings.cxx:29
PyObject * gGetItem
Definition PyStrings.cxx:23
PyObject * gCppBool
Definition PyStrings.cxx:11
PyObject * gCppReal
Definition PyStrings.cxx:64
PyObject * gPythonize
Definition PyStrings.cxx:73
PyObject * gTypeCode
Definition PyStrings.cxx:38
PyObject * gPostInc
Definition PyStrings.cxx:18
PyObject * gCppImag
Definition PyStrings.cxx:65
PyObject * gValueSize
Definition PyStrings.cxx:62
PyObject * gSetItem
Definition PyStrings.cxx:25
PyObject * gGetNoCheck
Definition PyStrings.cxx:24
PyObject * gCppRepr
Definition PyStrings.cxx:35
PyObject * gValueType
Definition PyStrings.cxx:61
void cppscope_to_legalname(std::string &cppscope)
std::string clean_type(const std::string &cppname, bool template_strip=true, bool const_strip=true)
std::string compound(const std::string &name)
std::string extract_namespace(const std::string &name)
Py_ssize_t GetBuffer(PyObject *pyobject, char tc, int size, void *&buf, bool check=true)
Definition Utility.cxx:880
bool AddToClass(PyObject *pyclass, const char *label, PyCFunction cfunc, int flags=METH_VARARGS)
Definition Utility.cxx:186
PyTypeObject VectorIter_Type
static PyObject * GetAttrDirect(PyObject *pyclass, PyObject *pyname)
bool Pythonize(PyObject *pyclass, const std::string &name)
bool CPPOverload_Check(T *object)
Definition CPPOverload.h:94
std::map< std::string, std::vector< PyObject * > > & pythonizations()
bool CPPScope_Check(T *object)
Definition CPPScope.h:81
bool LowLevelView_Check(T *object)
bool CPPInstance_Check(T *object)
PyTypeObject IndexIter_Type
PyObject * gThisModule
Definition CPPMethod.cxx:30
CPYCPPYY_EXTERN Converter * CreateConverter(const std::string &name, cdims_t=0)
std::set< std::string > gIteratorTypes
size_t TCppIndex_t
Definition cpp_cppyy.h:24
RPY_EXPORTED size_t SizeOf(TCppType_t klass)
intptr_t TCppMethod_t
Definition cpp_cppyy.h:22
RPY_EXPORTED bool IsDefaultConstructable(TCppType_t type)
RPY_EXPORTED bool IsEnum(const std::string &type_name)
RPY_EXPORTED std::vector< TCppIndex_t > GetMethodIndicesFromName(TCppScope_t scope, const std::string &name)
RPY_EXPORTED TCppIndex_t GetNumDatamembers(TCppScope_t scope, bool accept_namespace=false)
RPY_EXPORTED bool Compile(const std::string &code, bool silent=false)
RPY_EXPORTED std::string ResolveName(const std::string &cppitem_name)
TCppScope_t TCppType_t
Definition cpp_cppyy.h:19
RPY_EXPORTED bool IsAggregate(TCppType_t type)
RPY_EXPORTED std::string GetScopedFinalName(TCppType_t type)
RPY_EXPORTED bool IsPublicData(TCppScope_t scope, TCppIndex_t idata)
RPY_EXPORTED bool IsBuiltin(const std::string &type_name)
RPY_EXPORTED bool IsStaticData(TCppScope_t scope, TCppIndex_t idata)
RPY_EXPORTED std::string GetDatamemberType(TCppScope_t scope, TCppIndex_t idata)
RPY_EXPORTED TCppMethod_t GetMethod(TCppScope_t scope, TCppIndex_t imeth)
RPY_EXPORTED bool IsSmartPtr(TCppType_t type)
RPY_EXPORTED TCppScope_t GetScope(const std::string &scope_name)
size_t TCppScope_t
Definition cpp_cppyy.h:18
RPY_EXPORTED std::string GetMethodResultType(TCppMethod_t)
RPY_EXPORTED std::string GetDatamemberName(TCppScope_t scope, TCppIndex_t idata)