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