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