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 ---------------------------------------------------------------------
98static PyObject *gMainDict = 0;
99
100namespace {
101
102class CachedPyString {
103
104public:
105 CachedPyString(const char *name) : fObj{PyUnicode_FromString(name)} {}
106
107 CachedPyString(CachedPyString const &) = delete;
108 CachedPyString(CachedPyString &&) = delete;
109 CachedPyString &operator=(CachedPyString const &) = delete;
110 CachedPyString &operator=(CachedPyString &&) = delete;
111
112 ~CachedPyString() { Py_DECREF(fObj); }
113
114 PyObject *obj() { return fObj; }
115
116private:
117 PyObject *fObj = nullptr;
118};
119
120PyThreadState *mainThreadState;
121
122// To acquire the GIL as described here:
123// https://docs.python.org/3/c-api/init.html#non-python-created-threads
124class PyGILRAII {
125 PyGILState_STATE m_GILState;
126
127public:
128 PyGILRAII() : m_GILState(PyGILState_Ensure()) {}
129 ~PyGILRAII() { PyGILState_Release(m_GILState); }
130};
131
132} // namespace
133
134//- static public members ----------------------------------------------------
135/// Initialization method: setup the python interpreter and load the
136/// ROOT module.
138{
139 // Don't initialize Python from two concurrent threads
140 static std::mutex initMutex;
141 const std::lock_guard<std::mutex> lock(initMutex);
142
143 static Bool_t isInitialized = kFALSE;
144 if (isInitialized)
145 return kTRUE;
146
147 if (!Py_IsInitialized()) {
148// this happens if Cling comes in first
149#if PY_VERSION_HEX < 0x03020000
150 PyEval_InitThreads();
151#endif
152
153// set the command line arguments on python's sys.argv
154#if PY_VERSION_HEX < 0x03000000
155 char *argv[] = {const_cast<char *>("root")};
156#else
157 wchar_t *argv[] = {const_cast<wchar_t *>(L"root")};
158#endif
159 int argc = sizeof(argv) / sizeof(argv[0]);
160#if PY_VERSION_HEX < 0x030b0000
161 Py_Initialize();
162#else
163 PyStatus status;
164 PyConfig config;
165
166 PyConfig_InitPythonConfig(&config);
167
168 status = PyConfig_SetArgv(&config, argc, argv);
169 if (PyStatus_Exception(status)) {
170 PyConfig_Clear(&config);
171 std::cerr << "Error when setting command line arguments." << std::endl;
172 return kFALSE;
173 }
174
175 status = Py_InitializeFromConfig(&config);
176 if (PyStatus_Exception(status)) {
177 PyConfig_Clear(&config);
178 std::cerr << "Error when initializing Python." << std::endl;
179 return kFALSE;
180 }
181 PyConfig_Clear(&config);
182#endif
183#if PY_VERSION_HEX >= 0x03020000
184#if PY_VERSION_HEX < 0x03090000
185 PyEval_InitThreads();
186#endif
187#endif
188
189 // try again to see if the interpreter is initialized
190 if (!Py_IsInitialized()) {
191 // give up ...
192 std::cerr << "Error: python has not been intialized; returning." << std::endl;
193 return kFALSE;
194 }
195
196#if PY_VERSION_HEX < 0x030b0000
197 PySys_SetArgv(argc, argv);
198#endif
199
200 mainThreadState = PyEval_SaveThread();
201 }
202
203 {
204 // For the Python API calls
205 PyGILRAII gilRaii;
206
207 // force loading of the ROOT module
208 const int ret = PyRun_SimpleString(const_cast<char *>("import ROOT"));
209 if (ret != 0) {
210 std::cerr << "Error: import ROOT failed, check your PYTHONPATH environmental variable." << std::endl;
211 return kFALSE;
212 }
213
214 if (!gMainDict) {
215
216 // retrieve the main dictionary
217 gMainDict = PyModule_GetDict(PyImport_AddModule(const_cast<char *>("__main__")));
218 // The gMainDict is borrowed, i.e. we are not calling Py_INCREF(gMainDict).
219 // Like this, we avoid unexpectedly affecting how long __main__ is kept
220 // alive. The gMainDict is only used in Exec(), ExecScript(), and Eval(),
221 // which should not be called after __main__ is garbage collected anyway.
222 }
223 }
224
225 // python side class construction, managed by ROOT
226 gROOT->AddClassGenerator(new TPyClassGenerator);
227
228 // declare success ...
229 isInitialized = kTRUE;
230 return kTRUE;
231}
232
233////////////////////////////////////////////////////////////////////////////////
234/// Import the named python module and create Cling equivalents for its classes
235/// and methods.
236
237Bool_t TPython::Import(const char *mod_name)
238{
239 // setup
240 if (!Initialize())
241 return false;
242
243 PyGILRAII gilRaii;
244
245 if (!CPyCppyy::Import(mod_name)) {
246 return false;
247 }
248
249 // force creation of the module as a namespace
250 TClass::GetClass(mod_name, kTRUE);
251
252 PyObject *modNameObj = PyUnicode_FromString(mod_name);
253 PyObject *mod = PyImport_GetModule(modNameObj);
254 PyObject *dct = PyModule_GetDict(mod);
255
256 CachedPyString basesStr{"__bases__"};
257 CachedPyString cppNameStr{"__cpp_name__"};
258 CachedPyString nameStr{"__name__"};
259
260 // create Cling classes for all new python classes
261 PyObject *values = PyDict_Values(dct);
262 for (int i = 0; i < PyList_GET_SIZE(values); ++i) {
263 PyObject *value = PyList_GET_ITEM(values, i);
264 Py_INCREF(value);
265
266 // collect classes
267 if (PyType_Check(value) || PyObject_HasAttr(value, basesStr.obj())) {
268 // get full class name (including module)
269 PyObject *pyClName = PyObject_GetAttr(value, cppNameStr.obj());
270 if (!pyClName) {
271 pyClName = PyObject_GetAttr(value, nameStr.obj());
272 }
273
274 if (PyErr_Occurred())
275 PyErr_Clear();
276
277 // build full, qualified name
278 std::string fullname = mod_name;
279 fullname += ".";
280 fullname += PyUnicode_AsUTF8(pyClName);
281
282 // force class creation (this will eventually call TPyClassGenerator)
283 TClass::GetClass(fullname.c_str(), kTRUE);
284
285 Py_XDECREF(pyClName);
286 }
287
288 Py_DECREF(value);
289 }
290
291 Py_DECREF(values);
292 Py_DECREF(mod);
293 Py_DECREF(modNameObj);
294
295 if (PyErr_Occurred())
296 return kFALSE;
297 return kTRUE;
298}
299
300////////////////////////////////////////////////////////////////////////////////
301/// Execute the give python script as if it were a macro (effectively an
302/// execfile in __main__), and create Cling equivalents for any newly available
303/// python classes.
304
305void TPython::LoadMacro(const char *name)
306{
307 // setup
308 if (!Initialize())
309 return;
310
311 PyGILRAII gilRaii;
312
313 // obtain a reference to look for new classes later
314 PyObject *old = PyDict_Values(gMainDict);
315
316// actual execution
317#if PY_VERSION_HEX < 0x03000000
318 Exec((std::string("execfile(\"") + name + "\")").c_str());
319#else
320 Exec((std::string("__pyroot_f = open(\"") + name +
321 "\"); "
322 "exec(__pyroot_f.read()); "
323 "__pyroot_f.close(); del __pyroot_f")
324 .c_str());
325#endif
326
327 // obtain new __main__ contents
328 PyObject *current = PyDict_Values(gMainDict);
329
330 CachedPyString basesStr{"__bases__"};
331 CachedPyString moduleStr{"__module__"};
332 CachedPyString nameStr{"__name__"};
333
334 // create Cling classes for all new python classes
335 for (int i = 0; i < PyList_GET_SIZE(current); ++i) {
336 PyObject *value = PyList_GET_ITEM(current, i);
337 Py_INCREF(value);
338
339 if (!PySequence_Contains(old, value)) {
340 // collect classes
341 if (PyType_Check(value) || PyObject_HasAttr(value, basesStr.obj())) {
342 // get full class name (including module)
343 PyObject *pyModName = PyObject_GetAttr(value, moduleStr.obj());
344 PyObject *pyClName = PyObject_GetAttr(value, nameStr.obj());
345
346 if (PyErr_Occurred())
347 PyErr_Clear();
348
349 // need to check for both exact and derived (differences exist between older and newer
350 // versions of python ... bug?)
351 if ((pyModName && pyClName) && ((PyUnicode_CheckExact(pyModName) && PyUnicode_CheckExact(pyClName)) ||
352 (PyUnicode_Check(pyModName) && PyUnicode_Check(pyClName)))) {
353 // build full, qualified name
354 std::string fullname = PyUnicode_AsUTF8(pyModName);
355 fullname += '.';
356 fullname += PyUnicode_AsUTF8(pyClName);
357
358 // force class creation (this will eventually call TPyClassGenerator)
359 TClass::GetClass(fullname.c_str(), kTRUE);
360 }
361
362 Py_XDECREF(pyClName);
363 Py_XDECREF(pyModName);
364 }
365 }
366
367 Py_DECREF(value);
368 }
369
370 Py_DECREF(current);
371 Py_DECREF(old);
372}
373
374////////////////////////////////////////////////////////////////////////////////
375/// Execute a python stand-alone script, with argv CLI arguments.
376///
377/// example of use:
378/// const char* argv[] = { "1", "2", "3" };
379/// TPython::ExecScript( "test.py", sizeof(argv)/sizeof(argv[0]), argv );
380
381void TPython::ExecScript(const char *name, int argc, const char **argv)
382{
383
384 // setup
385 if (!Initialize())
386 return;
387
388 PyGILRAII gilRaii;
389
390 // verify arguments
391 if (!name) {
392 std::cerr << "Error: no file name specified." << std::endl;
393 return;
394 }
395
396 std::vector<std::string> args(argc);
397 for (int i = 0; i < argc; ++i) {
398 args[i] = argv[i];
399 }
401}
402
403////////////////////////////////////////////////////////////////////////////////
404/// Executes a Python command within the current Python environment.
405///
406/// This function initializes the Python environment if it is not already
407/// initialized. It then executes the specified Python command string using the
408/// Python C API.
409///
410/// In the Python command, you can change the value of a special TPyResult
411/// object returned by TPyBuffer(). If the optional result parameter is
412/// non-zero, the result parameter will be swapped with a std::any variable on
413/// the Python side. You need to define this variable yourself, and it needs to
414/// be of type std::any and its name needs to be `"_anyresult"` by default.
415/// Like this, you can pass information from Python back to C++.
416///
417/// \param cmd The Python command to be executed as a string.
418/// \param result Optional pointer to a std::any object that can be used to
419/// transfer results from Python to C++.
420/// \param resultName Name of the Python variable that is swapped over to the std::any result.
421/// The default value is `"_anyresult"`.
422/// \return bool Returns `true` if the command was successfully executed,
423/// otherwise returns `false`.
424
425Bool_t TPython::Exec(const char *cmd, std::any *result, std::string const &resultName)
426{
427 // setup
428 if (!Initialize())
429 return kFALSE;
430
431 PyGILRAII gilRaii;
432
433 std::stringstream command;
434 // Add the actual command
435 command << cmd;
436 // Swap the std::any with the one in the C++ world if required
437 if (result) {
438 command << "; ROOT.Internal.SwapWithObjAtAddr['std::any'](" << resultName << ", "
439 << reinterpret_cast<std::intptr_t>(result) << ")";
440 }
441
442 // execute the command
443 PyObject *pyObjectResult =
444 PyRun_String(const_cast<char *>(command.str().c_str()), Py_file_input, gMainDict, gMainDict);
445
446 // test for error
447 if (pyObjectResult) {
448 Py_DECREF(pyObjectResult);
449 return kTRUE;
450 }
451
452 PyErr_Print();
453 return kFALSE;
454}
455
456////////////////////////////////////////////////////////////////////////////////
457/// Bind a ROOT object with, at the python side, the name "label".
458
459Bool_t TPython::Bind(TObject *object, const char *label)
460{
461 // check given address and setup
462 if (!(object && Initialize()))
463 return kFALSE;
464
465 PyGILRAII gilRaii;
466
467 // bind object in the main namespace
468 TClass *klass = object->IsA();
469 if (klass != 0) {
470 PyObject *bound = CPyCppyy::Instance_FromVoidPtr((void *)object, klass->GetName());
471
472 if (bound) {
473 Bool_t bOk = PyDict_SetItemString(gMainDict, const_cast<char *>(label), bound) == 0;
474 Py_DECREF(bound);
475
476 return bOk;
477 }
478 }
479
480 return kFALSE;
481}
482
483////////////////////////////////////////////////////////////////////////////////
484/// Enter an interactive python session (exit with ^D). State is preserved
485/// between successive calls.
486
488{
489 // setup
490 if (!Initialize()) {
491 return;
492 }
493
494 PyGILRAII gilRaii;
495
496 // enter i/o interactive mode
497 PyRun_InteractiveLoop(stdin, const_cast<char *>("\0"));
498}
499
500////////////////////////////////////////////////////////////////////////////////
501/// Test whether the type of the given pyobject is of CPPInstance type or any
502/// derived type.
503
505{
506 // setup
507 if (!Initialize())
508 return kFALSE;
509
510 PyGILRAII gilRaii;
511
512 // detailed walk through inheritance hierarchy
513 return CPyCppyy::Instance_Check(pyobject);
514}
515
516////////////////////////////////////////////////////////////////////////////////
517/// Test whether the type of the given pyobject is CPPinstance type.
518
520{
521 // setup
522 if (!Initialize())
523 return kFALSE;
524
525 PyGILRAII gilRaii;
526
527 // direct pointer comparison of type member
528 return CPyCppyy::Instance_CheckExact(pyobject);
529}
530
531////////////////////////////////////////////////////////////////////////////////
532/// Test whether the type of the given pyobject is of CPPOverload type or any
533/// derived type.
534
536{
537 // setup
538 if (!Initialize())
539 return kFALSE;
540
541 PyGILRAII gilRaii;
542
543 // detailed walk through inheritance hierarchy
544 return CPyCppyy::Overload_Check(pyobject);
545}
546
547////////////////////////////////////////////////////////////////////////////////
548/// Test whether the type of the given pyobject is CPPOverload type.
549
551{
552 // setup
553 if (!Initialize())
554 return kFALSE;
555
556 PyGILRAII gilRaii;
557
558 // direct pointer comparison of type member
559 return CPyCppyy::Overload_CheckExact(pyobject);
560}
561
562////////////////////////////////////////////////////////////////////////////////
563/// Extract the object pointer held by the CPPInstance pyobject.
564
566{
567 // setup
568 if (!Initialize())
569 return 0;
570
571 PyGILRAII gilRaii;
572
573 // get held object (may be null)
574 return CPyCppyy::Instance_AsVoidPtr(pyobject);
575}
576
577////////////////////////////////////////////////////////////////////////////////
578/// Bind the addr to a python object of class defined by classname.
579
580PyObject *TPython::CPPInstance_FromVoidPtr(void *addr, const char *classname, Bool_t python_owns)
581{
582 // setup
583 if (!Initialize())
584 return 0;
585
586 PyGILRAII gilRaii;
587
588 // perform cast (the call will check TClass and addr, and set python errors)
589 // give ownership, for ref-counting, to the python side, if so requested
590 return CPyCppyy::Instance_FromVoidPtr(addr, classname, python_owns);
591}
_object PyObject
constexpr Bool_t kFALSE
Definition RtypesCore.h:94
constexpr Bool_t kTRUE
Definition RtypesCore.h:93
#define ClassImp(name)
Definition Rtypes.h:382
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:98
_object PyObject
Definition TPython.h:31
Binding & operator=(OUT(*fun)(void))
#define gROOT
Definition TROOT.h:406
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition TClass.h:81
TClass * IsA() const override
Definition TClass.h:619
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:3037
const char * GetName() const override
Returns name of object.
Definition TNamed.h:47
Mother of all ROOT objects.
Definition TObject.h:41
Accessing the Python interpreter from C++.
Definition TPython.h:43
static void Prompt()
Enter an interactive python session (exit with ^D).
Definition TPython.cxx:487
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:535
static void * CPPInstance_AsVoidPtr(PyObject *pyobject)
Extract the object pointer held by the CPPInstance pyobject.
Definition TPython.cxx:565
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:381
static Bool_t Import(const char *name)
Import the named python module and create Cling equivalents for its classes and methods.
Definition TPython.cxx:237
static Bool_t CPPInstance_CheckExact(PyObject *pyobject)
Test whether the type of the given pyobject is CPPinstance type.
Definition TPython.cxx:519
static Bool_t Bind(TObject *object, const char *label)
Bind a ROOT object with, at the python side, the name "label".
Definition TPython.cxx:459
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:305
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:425
static Bool_t CPPOverload_CheckExact(PyObject *pyobject)
Test whether the type of the given pyobject is CPPOverload type.
Definition TPython.cxx:550
static Bool_t Initialize()
Initialization method: setup the python interpreter and load the ROOT module.
Definition TPython.cxx:137
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:504
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:580
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