Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
TPython.cxx
Go to the documentation of this file.
1// Author: Enric Tejedor CERN 08/2019
2// Original PyROOT code by Wim Lavrijsen, LBL
3//
4// /*************************************************************************
5// * Copyright (C) 1995-2019, 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#include "PythonLimitedAPI.h"
13
14// Bindings
15// CPyCppyy.h must be go first, since it includes Python.h, which must be
16// included before any standard header
17#include "CPyCppyy/API.h"
18#include "TPython.h"
19#include "TPyClassGenerator.h"
20
21// ROOT
22#include "TROOT.h"
23#include "TClassRef.h"
24#include "TObject.h"
25
26#include <Riostream.h>
27
28// Standard
29#include <mutex>
30#include <sstream>
31#include <stdio.h>
32#include <string>
33
34/// \class TPython
35/// Accessing the Python interpreter from C++.
36///
37/// The TPython class allows for access to python objects from Cling. The current
38/// functionality is only basic: ROOT objects and builtin types can freely cross
39/// the boundary between the two interpreters, python objects can be instantiated
40/// and their methods can be called. All other cross-coding is based on strings
41/// that are run on the python interpreter.
42///
43/// Examples:
44///
45/// ~~~{.cpp}
46/// $ root -l
47/// // Execute a string of python code.
48/// root [0] TPython::Exec( "print('Hello World!')" );
49/// Hello World!
50///
51/// // Create a TNamed on the python side, and transfer it back and forth.
52/// root [1] std::any res1;
53/// root [2] TPython::Exec("_anyresult = ROOT.std.make_any['TNamed']('hello', '')", &res1);
54/// root [3] TPython::Bind(&std::any_cast<TNamed&>(res1), "n");
55/// root [4] std::any res2;
56/// root [5] TPython::Exec("_anyresult = ROOT.std.make_any['TNamed*', 'TNamed*'](n)", &res2);
57/// root [6] (&std::any_cast<TNamed&>(res1) == std::any_cast<TNamed*>(res2))
58/// (bool) true
59///
60/// // Variables can cross-over by using an `std::any` with a specific name.
61/// root [6] TPython::Exec("_anyresult = ROOT.std.make_any['Int_t'](1 + 1)", &res1);
62/// root [7] std::any_cast<int>(res1)
63/// (int) 2
64/// ~~~
65///
66/// And with a python file `MyPyClass.py` like this:
67/// ~~~{.py}
68/// print 'creating class MyPyClass ... '
69///
70/// class MyPyClass:
71/// def __init__( self ):
72/// print 'in MyPyClass.__init__'
73///
74/// def gime( self, what ):
75/// return what
76/// ~~~
77/// one can load a python module, and use the class. Casts are
78/// necessary as the type information can not be otherwise derived.
79/// ~~~{.cpp}
80/// root [6] TPython::LoadMacro( "MyPyClass.py" );
81/// creating class MyPyClass ...
82/// root [7] MyPyClass m;
83/// in MyPyClass.__init__
84/// root [8] std::string s = (char*)m.gime( "aap" );
85/// root [9] s
86/// (class TString)"aap"
87/// ~~~
88/// It is possible to switch between interpreters by calling `TPython::Prompt()`
89/// on the Cling side, while returning with `^D` (EOF). State is preserved between
90/// successive switches.
91///
92/// The API part provides (direct) C++ access to the bindings functionality of
93/// PyROOT. It allows verifying that you deal with a PyROOT python object in the
94/// first place (CPPInstance_Check for CPPInstance and any derived types, as well
95/// as CPPInstance_CheckExact for CPPInstance's only); and it allows conversions
96/// of `void*` to an CPPInstance and vice versa.
97
98//- data ---------------------------------------------------------------------
99static PyObject *gMainDict = 0;
100
101namespace {
102
104
105// To acquire the GIL as described here:
106// https://docs.python.org/3/c-api/init.html#non-python-created-threads
107class PyGILRAII {
108 PyGILState_STATE m_GILState;
109
110public:
111 PyGILRAII() : m_GILState(PyGILState_Ensure()) {}
112 ~PyGILRAII() { PyGILState_Release(m_GILState); }
113};
114
115struct PyObjDeleter {
116 void operator()(PyObject* obj) const {
117 Py_DecRef(obj);
118 }
119};
120
121using PyObjectRef = std::unique_ptr<PyObject, PyObjDeleter>;
122
123} // namespace
124
125//- static public members ----------------------------------------------------
126/// Initialization method: setup the python interpreter and load the
127/// ROOT module.
129{
130 // Don't initialize Python from two concurrent threads
131 static std::mutex initMutex;
132 const std::lock_guard<std::mutex> lock(initMutex);
133
134 static Bool_t isInitialized = false;
135 if (isInitialized)
136 return true;
137
138 if (!Py_IsInitialized()) {
139 // Trigger the Python initialization indirectly via CPyCppyy
140 CPyCppyy::Scope_Check(nullptr);
141
143 }
144
145 {
146 // For the Python API calls
147 PyGILRAII gilRaii;
148
149 // force loading of the ROOT module
151 if (!rootModule) {
152 PyErr_Print();
153 return false;
154 }
155
156 // to trigger the lazy initialization of the C++ runtime
158 if (!interpreterAttr) {
159 PyErr_Print();
161 return false;
162 }
163
165
166 if (!gMainDict) {
167
168 // retrieve the main dictionary
170 // The gMainDict is borrowed, i.e. we are not calling Py_IncRef(gMainDict).
171 // Like this, we avoid unexpectedly affecting how long __main__ is kept
172 // alive. The gMainDict is only used in Exec(), ExecScript(), and Eval(),
173 // which should not be called after __main__ is garbage collected anyway.
174 }
175
176 // Inject ROOT into __main__
177 if (PyDict_SetItemString(gMainDict, "ROOT", rootModule) != 0) {
178 PyErr_Print();
180 return false;
181 }
182
184 }
185
186 // python side class construction, managed by ROOT
187 gROOT->AddClassGenerator(new TPyClassGenerator);
188
189 // declare success ...
190 isInitialized = true;
191 return true;
192}
193
194////////////////////////////////////////////////////////////////////////////////
195/// Import the named python module and create Cling equivalents for its classes
196/// and methods.
197
199{
200 // setup
201 if (!Initialize())
202 return false;
203
204 PyGILRAII gilRaii;
205
207 return false;
208 }
209
210 // force creation of the module as a namespace
212
216
220
221 // create Cling classes for all new python classes
223 for (int i = 0; i < PyList_Size(values.get()); ++i) {
224 PyObjectRef value{PyList_GetItem(values.get(), i)};
225 Py_IncRef(value.get());
226
227 // collect classes
228 if (PyType_Check(value.get()) || PyObject_HasAttr(value.get(), basesStr.get())) {
229 // get full class name (including module)
231 if (!pyClName) {
232 if (PyErr_Occurred())
233 PyErr_Clear();
235 }
236
237 if (PyErr_Occurred())
238 PyErr_Clear();
239
240 // build full, qualified name
241 std::string fullname = mod_name;
242 fullname += ".";
243 fullname += PyUnicode_AsUTF8AndSize(pyClName.get(), nullptr);
244
245 // force class creation (this will eventually call TPyClassGenerator)
246 TClass::GetClass(fullname.c_str(), true);
247 }
248 }
249
250 return !PyErr_Occurred();
251}
252
253////////////////////////////////////////////////////////////////////////////////
254/// Execute the give python script as if it were a macro (effectively an
255/// execfile in __main__), and create Cling equivalents for any newly available
256/// python classes.
257
258void TPython::LoadMacro(const char *name)
259{
260 // setup
261 if (!Initialize())
262 return;
263
264 PyGILRAII gilRaii;
265
266 // obtain a reference to look for new classes later
268
269// actual execution
270 Exec((std::string("__pyroot_f = open(\"") + name +
271 "\"); "
272 "exec(__pyroot_f.read()); "
273 "__pyroot_f.close(); del __pyroot_f")
274 .c_str());
275
276 // obtain new __main__ contents
278
282
283 // create Cling classes for all new python classes
284 for (int i = 0; i < PyList_Size(current.get()); ++i) {
285 PyObjectRef value{PyList_GetItem(current.get(), i)};
286 Py_IncRef(value.get());
287
288 if (!PySequence_Contains(old.get(), value.get())) {
289 // collect classes
290 if (PyType_Check(value.get()) || PyObject_HasAttr(value.get(), basesStr.get())) {
291 // get full class name (including module)
294
295 if (PyErr_Occurred())
296 PyErr_Clear();
297
298 // need to check for both exact and derived (differences exist between older and newer
299 // versions of python ... bug?)
302 // build full, qualified name
303 std::string fullname = PyUnicode_AsUTF8AndSize(pyModName.get(), nullptr);
304 fullname += '.';
305 fullname += PyUnicode_AsUTF8AndSize(pyClName.get(), nullptr);
306
307 // force class creation (this will eventually call TPyClassGenerator)
308 TClass::GetClass(fullname.c_str(), true);
309 }
310 }
311 }
312 }
313}
314
315////////////////////////////////////////////////////////////////////////////////
316/// Execute a python stand-alone script, with argv CLI arguments.
317///
318/// example of use:
319/// const char* argv[] = { "1", "2", "3" };
320/// TPython::ExecScript( "test.py", sizeof(argv)/sizeof(argv[0]), argv );
321
322void TPython::ExecScript(const char *name, int argc, const char **argv)
323{
324
325 // setup
326 if (!Initialize())
327 return;
328
329 PyGILRAII gilRaii;
330
331 // verify arguments
332 if (!name) {
333 std::cerr << "Error: no file name specified." << std::endl;
334 return;
335 }
336
337 std::vector<std::string> args(argc);
338 for (int i = 0; i < argc; ++i) {
339 args[i] = argv[i];
340 }
342}
343
344////////////////////////////////////////////////////////////////////////////////
345/// Executes a Python command within the current Python environment.
346///
347/// This function initializes the Python environment if it is not already
348/// initialized. It then executes the specified Python command string using the
349/// Python C API.
350///
351/// In the Python command, you can change the value of a special TPyResult
352/// object returned by TPyBuffer(). If the optional result parameter is
353/// non-zero, the result parameter will be swapped with a std::any variable on
354/// the Python side. You need to define this variable yourself, and it needs to
355/// be of type std::any and its name needs to be `"_anyresult"` by default.
356/// Like this, you can pass information from Python back to C++.
357///
358/// \param cmd The Python command to be executed as a string.
359/// \param result Optional pointer to a std::any object that can be used to
360/// transfer results from Python to C++.
361/// \param resultName Name of the Python variable that is swapped over to the std::any result.
362/// The default value is `"_anyresult"`.
363/// \return bool Returns `true` if the command was successfully executed,
364/// otherwise returns `false`.
365
366Bool_t TPython::Exec(const char *cmd, std::any *result, std::string const &resultName)
367{
368 // setup
369 if (!Initialize())
370 return false;
371
372 PyGILRAII gilRaii;
373
374 std::stringstream command;
375 // Add the actual command
376 command << cmd;
377 // Swap the std::any with the one in the C++ world if required
378 if (result) {
379 command << "; ROOT.Internal.SwapWithObjAtAddr['std::any'](" << resultName << ", "
380 << reinterpret_cast<std::intptr_t>(result) << ")";
381 }
382
383 // execute the command
384 return CPyCppyy::Exec(command.str());
385}
386
387////////////////////////////////////////////////////////////////////////////////
388/// Bind a ROOT object with, at the python side, the name "label".
389
390Bool_t TPython::Bind(TObject *object, const char *label)
391{
392 // check given address and setup
393 if (!(object && Initialize()))
394 return false;
395
396 PyGILRAII gilRaii;
397
398 // bind object in the main namespace
399 TClass *klass = object->IsA();
400 if (klass != 0) {
401 PyObjectRef bound{CPyCppyy::Instance_FromVoidPtr((void *)object, klass->GetName())};
402
403 if (bound) {
404 Bool_t bOk = PyDict_SetItemString(gMainDict, label, bound.get()) == 0;
405
406 return bOk;
407 }
408 }
409
410 return false;
411}
412
413////////////////////////////////////////////////////////////////////////////////
414/// Enter an interactive python session (exit with ^D). State is preserved
415/// between successive calls.
416
418{
419 // setup
420 if (!Initialize()) {
421 return;
422 }
423
424 PyGILRAII gilRaii;
425
426 // enter i/o interactive mode
428}
429
430////////////////////////////////////////////////////////////////////////////////
431/// Test whether the type of the given pyobject is of CPPInstance type or any
432/// derived type.
433
435{
436 // setup
437 if (!Initialize())
438 return false;
439
440 PyGILRAII gilRaii;
441
442 // detailed walk through inheritance hierarchy
444}
445
446////////////////////////////////////////////////////////////////////////////////
447/// Test whether the type of the given pyobject is CPPinstance type.
448
450{
451 // setup
452 if (!Initialize())
453 return false;
454
455 PyGILRAII gilRaii;
456
457 // direct pointer comparison of type member
459}
460
461////////////////////////////////////////////////////////////////////////////////
462/// Test whether the type of the given pyobject is of CPPOverload type or any
463/// derived type.
464
466{
467 // setup
468 if (!Initialize())
469 return false;
470
471 PyGILRAII gilRaii;
472
473 // detailed walk through inheritance hierarchy
475}
476
477////////////////////////////////////////////////////////////////////////////////
478/// Test whether the type of the given pyobject is CPPOverload type.
479
481{
482 // setup
483 if (!Initialize())
484 return false;
485
486 PyGILRAII gilRaii;
487
488 // direct pointer comparison of type member
490}
491
492////////////////////////////////////////////////////////////////////////////////
493/// Extract the object pointer held by the CPPInstance pyobject.
494
496{
497 // setup
498 if (!Initialize())
499 return nullptr;
500
501 PyGILRAII gilRaii;
502
503 // get held object (may be null)
505}
506
507////////////////////////////////////////////////////////////////////////////////
508/// Bind the addr to a python object of class defined by classname.
509
511{
512 // setup
513 if (!Initialize())
514 return nullptr;
515
516 PyGILRAII gilRaii;
517
518 // perform cast (the call will check TClass and addr, and set python errors)
519 // give ownership, for ref-counting, to the python side, if so requested
521}
_object PyObject
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
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 value
char name[80]
Definition TGX11.cxx:157
static PyObject * gMainDict
Definition TPython.cxx:99
_object PyObject
Definition TPython.h:23
TRObject operator()(const T1 &t1) const
#define gROOT
Definition TROOT.h:414
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition TClass.h:84
static TClass * GetClass(const char *name, Bool_t load=kTRUE, Bool_t silent=kFALSE)
Static method returning pointer to TClass of the specified class name.
Definition TClass.cxx:2979
Mother of all ROOT objects.
Definition TObject.h:42
static void Prompt()
Enter an interactive python session (exit with ^D).
Definition TPython.cxx:417
static Bool_t CPPOverload_Check(PyObject *pyobject)
Test whether the type of the given pyobject is of CPPOverload type or any derived type.
Definition TPython.cxx:465
static void * CPPInstance_AsVoidPtr(PyObject *pyobject)
Extract the object pointer held by the CPPInstance pyobject.
Definition TPython.cxx:495
static void ExecScript(const char *name, int argc=0, const char **argv=nullptr)
Execute a python stand-alone script, with argv CLI arguments.
Definition TPython.cxx:322
static Bool_t Import(const char *name)
Import the named python module and create Cling equivalents for its classes and methods.
Definition TPython.cxx:198
static Bool_t CPPInstance_CheckExact(PyObject *pyobject)
Test whether the type of the given pyobject is CPPinstance type.
Definition TPython.cxx:449
static Bool_t Bind(TObject *object, const char *label)
Bind a ROOT object with, at the python side, the name "label".
Definition TPython.cxx:390
static void LoadMacro(const char *name)
Execute the give python script as if it were a macro (effectively an execfile in main),...
Definition TPython.cxx:258
static Bool_t Exec(const char *cmd, std::any *result=nullptr, std::string const &resultName="_anyresult")
Executes a Python command within the current Python environment.
Definition TPython.cxx:366
static Bool_t CPPOverload_CheckExact(PyObject *pyobject)
Test whether the type of the given pyobject is CPPOverload type.
Definition TPython.cxx:480
static Bool_t Initialize()
Initialization method: setup the python interpreter and load the ROOT module.
Definition TPython.cxx:128
static Bool_t CPPInstance_Check(PyObject *pyobject)
Test whether the type of the given pyobject is of CPPInstance type or any derived type.
Definition TPython.cxx:434
static PyObject * CPPInstance_FromVoidPtr(void *addr, const char *classname, Bool_t python_owns=kFALSE)
Bind the addr to a python object of class defined by classname.
Definition TPython.cxx:510
CPYCPPYY_EXTERN bool Instance_CheckExact(PyObject *pyobject)
Definition API.cxx:189
CPYCPPYY_EXTERN void Prompt()
Definition API.cxx:489
CPYCPPYY_EXTERN bool Overload_Check(PyObject *pyobject)
Definition API.cxx:248
CPYCPPYY_EXTERN bool Overload_CheckExact(PyObject *pyobject)
Definition API.cxx:259
CPYCPPYY_EXTERN bool Import(const std::string &name)
Definition API.cxx:276
CPYCPPYY_EXTERN void ExecScript(const std::string &name, const std::vector< std::string > &args)
Definition API.cxx:335
CPYCPPYY_EXTERN bool Instance_Check(PyObject *pyobject)
Definition API.cxx:178
CPYCPPYY_EXTERN PyObject * Instance_FromVoidPtr(void *addr, const std::string &classname, bool python_owns=false)
Definition API.cxx:133
CPYCPPYY_EXTERN void * Instance_AsVoidPtr(PyObject *pyobject)
Definition API.cxx:118
CPYCPPYY_EXTERN bool Scope_Check(PyObject *pyobject)
Definition API.cxx:158
CPYCPPYY_EXTERN bool Exec(const std::string &cmd)
Definition API.cxx:413