Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
PyROOTModule.cxx
Go to the documentation of this file.
1// Author: Enric Tejedor CERN 06/2018
2// Original PyROOT code by Wim Lavrijsen, LBL
3
4/*************************************************************************
5 * Copyright (C) 1995-2018, Rene Brun and Fons Rademakers. *
6 * All rights reserved. *
7 * *
8 * For the licensing terms see $ROOTSYS/LICENSE. *
9 * For the list of contributors see $ROOTSYS/README/CREDITS. *
10 *************************************************************************/
11
12// Bindings
13#include "PyROOTPythonize.h"
14
15// Cppyy
16#include "cpyrt/API.h"
17
18// ROOT
19#include "TError.h"
20#include "TInterpreter.h"
21#include "TROOT.h"
22#include "TSystem.h"
23#include "TVirtualPad.h"
24#include "RConfigure.h"
25
26// Standard
27#include <any>
28#include <string>
29#include <sstream>
30#include <utility>
31#include <vector>
32
33#include "IOHandler.cxx"
34
35namespace PyROOT {
36
38
39/// \brief A PyObject wrapper to track reference counting of external objects
40///
41/// This wrapper can be useful in shared ownership scenarios when a C++ object
42/// is created on the Python side and there is no easy way to track its ownership
43/// on the C++ side. If multiple instances of this class are given the same
44/// Python proxy, they will increase/decrease its reference counting following
45/// Python rules and ensure proper destruction of the underlying C++ object when
46/// no other Python objects are referencing it.
47class PyObjRefCounter final {
48 PyObject *fObject{nullptr};
49
50 void Reset(PyObject *object)
51 {
52 if (fObject) {
53 Py_DecRef(fObject);
54 fObject = nullptr;
55 }
56 if (object) {
57 Py_INCREF(object);
59 }
60 }
61
62public:
63 PyObjRefCounter(PyObject *object) { Reset(object); }
64
65 ~PyObjRefCounter() { Reset(nullptr); }
66
67 PyObjRefCounter(const PyObjRefCounter &other) { Reset(other.fObject); }
68
70 {
71 Reset(other.fObject);
72 return *this;
73 }
74
76 {
77 fObject = other.fObject;
78 other.fObject = nullptr;
79 }
80
82 {
83 fObject = other.fObject;
84 other.fObject = nullptr;
85 return *this;
86 }
87};
88
90{
91 PyObject *object = nullptr;
92
93 PyArg_ParseTuple(args, "O:PyObjRefCounterAsStdAny", &object);
94
95 // The std::any is managed by Python
96 return cppjit::cpyrt::Instance_FromVoidPtr(new std::any{std::in_place_type<PyObjRefCounter>, object}, "std::any",
97 /*python_owns=*/true);
98}
99
100} // namespace PyROOT
101
102namespace {
103
104/// Turns a ROOT warning into a Python warning, see ROOT::Internal::SetWarningHandler().
105bool WarningHandler(const char *location, const char *msg)
106{
107 PyGILState_STATE state = PyGILState_Ensure();
108 // Python code can't run while an exception is pending, e.g. a previous
109 // warning turned into an error: decline, and ROOT prints the warning.
110 bool handled = !PyErr_Occurred();
111 if (handled) {
112 // Prints or, per the warnings filters, raises: the exception stays
113 // pending until the call into C++ returns to Python.
114 PyErr_WarnExplicit(nullptr, msg, location, 0, "ROOT", nullptr);
115 }
116 PyGILState_Release(state);
117 return handled;
118}
119
120int (*gPreviousInputHook)() = nullptr;
121
122/// Processes GUI events while the Python prompt waits for input. Runs no
123/// Python code, so a Ctrl-C arriving meanwhile stays pending for the prompt.
124int GUIEventInputHook()
125{
126 PyGILState_STATE state = PyGILState_Ensure();
127 if (gPad && gPad->IsWeb())
128 gPad->UpdateAsync();
130 PyGILState_Release(state);
131
132 return gPreviousInputHook ? gPreviousInputHook() : 0;
133}
134
135/// Turn ROOT warnings into Python warnings.
136PyObject *InstallWarningHandler(PyObject * /*self*/, PyObject * /*args*/)
137{
138 ROOT::Internal::SetWarningHandler(&WarningHandler);
140}
141
142/// Install the PyOS_InputHook that processes GUI events, chaining to the
143/// previous hook.
144PyObject *InstallGUIEventInputHook(PyObject * /*self*/, PyObject * /*args*/)
145{
146 if (PyOS_InputHook != &GUIEventInputHook) {
147 gPreviousInputHook = PyOS_InputHook;
148 PyOS_InputHook = &GUIEventInputHook;
149 }
151}
152
153} // namespace
154
155// Methods offered by the interface
156static PyMethodDef gPyROOTMethods[] = {
157 {"AddCPPInstancePickling", (PyCFunction)PyROOT::AddCPPInstancePickling, METH_NOARGS,
158 "Add a custom pickling mechanism for Cppyy Python proxy objects"},
159 {"AddPrettyPrintingPyz", (PyCFunction)PyROOT::AddPrettyPrintingPyz, METH_VARARGS,
160 "Add pretty printing pythonization"},
161 {"InstallWarningHandler", (PyCFunction)InstallWarningHandler, METH_NOARGS,
162 "Turn ROOT warnings into Python warnings"},
163 {"InstallGUIEventInputHook", (PyCFunction)InstallGUIEventInputHook, METH_NOARGS,
164 "Install an input hook to process GUI events"},
165 {"_CPPInstance__expand__", (PyCFunction)PyROOT::CPPInstanceExpand, METH_VARARGS,
166 "Deserialize a pickled object"},
167 {"JupyROOTExecutor", (PyCFunction)JupyROOTExecutor, METH_VARARGS, "Create JupyROOTExecutor"},
168 {"JupyROOTDeclarer", (PyCFunction)JupyROOTDeclarer, METH_VARARGS, "Create JupyROOTDeclarer"},
169 {"JupyROOTExecutorHandler_Clear", (PyCFunction)JupyROOTExecutorHandler_Clear, METH_NOARGS,
170 "Clear JupyROOTExecutorHandler"},
171 {"JupyROOTExecutorHandler_Ctor", (PyCFunction)JupyROOTExecutorHandler_Ctor, METH_NOARGS,
172 "Create JupyROOTExecutorHandler"},
173 {"JupyROOTExecutorHandler_Poll", (PyCFunction)JupyROOTExecutorHandler_Poll, METH_NOARGS,
174 "Poll JupyROOTExecutorHandler"},
175 {"JupyROOTExecutorHandler_EndCapture", (PyCFunction)JupyROOTExecutorHandler_EndCapture, METH_NOARGS,
176 "End capture JupyROOTExecutorHandler"},
177 {"JupyROOTExecutorHandler_InitCapture", (PyCFunction)JupyROOTExecutorHandler_InitCapture, METH_NOARGS,
178 "Init capture JupyROOTExecutorHandler"},
179 {"JupyROOTExecutorHandler_GetStdout", (PyCFunction)JupyROOTExecutorHandler_GetStdout, METH_NOARGS,
180 "Get stdout JupyROOTExecutorHandler"},
181 {"JupyROOTExecutorHandler_GetStderr", (PyCFunction)JupyROOTExecutorHandler_GetStderr, METH_NOARGS,
182 "Get stderr JupyROOTExecutorHandler"},
183 {"JupyROOTExecutorHandler_Dtor", (PyCFunction)JupyROOTExecutorHandler_Dtor, METH_NOARGS,
184 "Destruct JupyROOTExecutorHandler"},
185 {"PyObjRefCounterAsStdAny", (PyCFunction)PyROOT::PyObjRefCounterAsStdAny, METH_VARARGS,
186 "Wrap a reference count to any Python object in a std::any for resource management in C++"},
187 {NULL, NULL, 0, NULL}};
188
189struct module_state {
191};
192
193#define GETSTATE(m) ((struct module_state *)PyModule_GetState(m))
194
195static int rootmodule_traverse(PyObject *m, visitproc visit, void *arg)
196{
197 Py_VISIT(GETSTATE(m)->error);
198 return 0;
199}
200
202{
203 Py_CLEAR(GETSTATE(m)->error);
204 return 0;
205}
206
207static struct PyModuleDef moduledef = {PyModuleDef_HEAD_INIT, "libROOTPythonizations", NULL,
208 sizeof(struct module_state), gPyROOTMethods, NULL,
210
211/// Initialization of extension module libROOTPythonizations
212
214{
215 using namespace PyROOT;
216
217 // setup PyROOT
218 gRootModule = PyModule_Create(&moduledef);
219 if (!gRootModule)
220 return nullptr;
221
222 // keep gRootModule, but do not increase its reference count even as it is borrowed,
223 // or a self-referencing cycle would be created
224
225 return gRootModule;
226}
PyObject * JupyROOTExecutorHandler_Ctor(PyObject *, PyObject *)
PyObject * JupyROOTDeclarer(PyObject *, PyObject *args)
PyObject * JupyROOTExecutorHandler_GetStdout(PyObject *, PyObject *)
PyObject * JupyROOTExecutor(PyObject *, PyObject *args)
PyObject * JupyROOTExecutorHandler_EndCapture(PyObject *, PyObject *)
PyObject * JupyROOTExecutorHandler_Poll(PyObject *, PyObject *)
PyObject * JupyROOTExecutorHandler_Clear(PyObject *, PyObject *)
PyObject * JupyROOTExecutorHandler_GetStderr(PyObject *, PyObject *)
PyObject * JupyROOTExecutorHandler_InitCapture(PyObject *, PyObject *)
PyObject * JupyROOTExecutorHandler_Dtor(PyObject *, PyObject *)
_object PyObject
static struct PyModuleDef moduledef
#define GETSTATE(m)
PyObject * PyInit_libROOTPythonizations()
Initialization of extension module libROOTPythonizations.
static int rootmodule_clear(PyObject *m)
static int rootmodule_traverse(PyObject *m, visitproc visit, void *arg)
static PyMethodDef gPyROOTMethods[]
R__EXTERN TSystem * gSystem
Definition TSystem.h:582
#define gPad
A PyObject wrapper to track reference counting of external objects.
PyObjRefCounter(PyObject *object)
PyObjRefCounter & operator=(PyObjRefCounter &&other)
void Reset(PyObject *object)
PyObjRefCounter(const PyObjRefCounter &other)
PyObjRefCounter(PyObjRefCounter &&other)
PyObjRefCounter & operator=(const PyObjRefCounter &other)
virtual Bool_t ProcessEvents()
Process pending events (GUI, timers, sockets).
Definition TSystem.cxx:419
#define Py_RETURN_NONE
Definition cpyrt.h:69
PyObject * PyObjRefCounterAsStdAny(PyObject *, PyObject *args)
PyObject * AddCPPInstancePickling(PyObject *self, PyObject *args)
Set reduce attribute for CPPInstance objects.
PyObject * CPPInstanceExpand(PyObject *self, PyObject *args)
Deserialize pickled objects.
PyObject * gRootModule
PyObject * AddPrettyPrintingPyz(PyObject *self, PyObject *args)
Add pretty printing pythonization.
void SetWarningHandler(WarningHandlerFunc_t handler)
For the Python bindings: install an error handler that passes warnings to handler,...
PyObject * Instance_FromVoidPtr(void *addr, const char *classname, int python_owns)
Definition API.cxx:161
PyObject * error
TMarker m
Definition textangle.C:8