Skip to content

Commit 7ca0c4b

Browse files
committed
[Python] Use separate GetBuffer helper in Pythonization library
The `Utiliy::GetBuffer` function from CPyCppyy is an overkill for the simple usecase in the ROOT Pythonization library. We can have our own helper there, avoiding yet another usage of internal CPyCppyy functions.
1 parent 2265970 commit 7ca0c4b

4 files changed

Lines changed: 52 additions & 6 deletions

File tree

bindings/pyroot/pythonizations/src/PyROOTModule.cxx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,39 @@ PyObject *PyObjRefCounterAsStdAny(PyObject * /*self*/, PyObject *args)
144144
/*python_owns=*/true);
145145
}
146146

147+
// Helper function to get the pointer to a buffer (heavily simplified copy of
148+
// CPyCppyy::Utility::GetBuffer).
149+
void GetBuffer(PyObject *pyobject, void *&buf)
150+
{
151+
buf = nullptr;
152+
153+
// Exclude text-like objects (policy decision)
154+
if (PyBytes_Check(pyobject) || PyUnicode_Check(pyobject))
155+
return;
156+
157+
// Fast path: bytearray
158+
if (PyByteArray_CheckExact(pyobject)) {
159+
buf = PyByteArray_AsString(pyobject);
160+
return;
161+
}
162+
163+
// Buffer protocol
164+
if (PyObject_CheckBuffer(pyobject)) {
165+
// Avoid potential issues with empty sequences
166+
if (PySequence_Check(pyobject) && PySequence_Size(pyobject) == 0)
167+
return;
168+
169+
Py_buffer view;
170+
if (PyObject_GetBuffer(pyobject, &view, PyBUF_SIMPLE) == 0) {
171+
if (view.buf && view.len > 0)
172+
buf = view.buf;
173+
PyBuffer_Release(&view);
174+
} else {
175+
PyErr_Clear();
176+
}
177+
}
178+
}
179+
147180
} // namespace PyROOT
148181

149182
// Methods offered by the interface

bindings/pyroot/pythonizations/src/PythonLimitedAPI.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
#ifndef ROOT_PythonLimitedAPI_h
22
#define ROOT_PythonLimitedAPI_h
33

4-
// Use what is in the limited API since Python 3.10
4+
// Use what is in the limited API since Python 3.11 if we're building with at
5+
// least Python 3.11. The reason why we can't go back to 3.10 is that that at
6+
// that point, the new buffer interface was not part of the limited API yet.
7+
#if PY_VERSION_HEX >= 0x030B0000
8+
59
// On Windows we can't use the stable ABI yet: it requires linking against a
610
// different libpython, so as long as we don't build all translation units in
711
// the ROOT Pythonization library with the stable ABI we should not use it.
812
#ifndef _WIN32
9-
#define Py_LIMITED_API 0x030A0000
13+
#define Py_LIMITED_API 0x030B0000
14+
#endif
15+
1016
#endif
1117

1218
#include <Python.h>

bindings/pyroot/pythonizations/src/TClassPyz.cxx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222

2323
using namespace CPyCppyy;
2424

25+
namespace PyROOT{
26+
void GetBuffer(PyObject *pyobject, void *&buf);
27+
}
28+
2529
// Cast the void* returned by TClass::DynamicCast to the right type
2630
PyObject *TClassDynamicCastPyz(PyObject *self, PyObject *args)
2731
{
@@ -50,7 +54,7 @@ PyObject *TClassDynamicCastPyz(PyObject *self, PyObject *args)
5054
} else if (PyInt_Check(pyobject) || PyLong_Check(pyobject)) {
5155
address = (void *)PyLong_AsLongLong(pyobject);
5256
} else {
53-
Utility::GetBuffer(pyobject, '*', 1, address, false);
57+
PyROOT::GetBuffer(pyobject, address);
5458
}
5559

5660
// Now use binding to return a usable class. Upcast: result is a base.

bindings/pyroot/pythonizations/src/TTreePyz.cxx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
#include "../../cppyy/CPyCppyy/src/CPyCppyy.h"
1414
#include "../../cppyy/CPyCppyy/src/CPPInstance.h"
1515
#include "../../cppyy/CPyCppyy/src/ProxyWrappers.h"
16-
#include "../../cppyy/CPyCppyy/src/Utility.h"
1716
#include "../../cppyy/CPyCppyy/src/Dimensions.h"
1817

1918
#include "CPyCppyy/API.h"
@@ -47,6 +46,10 @@ TClass *GetTClass(PyObject *pyobj)
4746

4847
using namespace CPyCppyy;
4948

49+
namespace PyROOT{
50+
void GetBuffer(PyObject *pyobject, void *&buf);
51+
}
52+
5053
static TBranch *SearchForBranch(TTree *tree, const char *name)
5154
{
5255
TBranch *branch = tree->GetBranch(name);
@@ -251,7 +254,7 @@ PyObject *TryBranchLeafListOverload(int argc, PyObject *args)
251254
if (CPyCppyy::Instance_Check(address))
252255
buf = CPyCppyy::Instance_AsVoidPtr(address);
253256
else
254-
Utility::GetBuffer(address, '*', 1, buf, false);
257+
PyROOT::GetBuffer(address, buf);
255258

256259
if (buf) {
257260
TBranch *branch = nullptr;
@@ -331,7 +334,7 @@ PyObject *TryBranchPtrToPtrOverloads(int argc, PyObject *args)
331334
argc += 1;
332335
}
333336
} else {
334-
Utility::GetBuffer(address, '*', 1, buf, false);
337+
PyROOT::GetBuffer(address, buf);
335338
}
336339

337340
if (buf && !klName.empty()) {

0 commit comments

Comments
 (0)