Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
API.cxx
Go to the documentation of this file.
1// Bindings
2#include "CPyCppyy.h"
3#define CPYCPPYY_INTERNAL 1
4#include "CPyCppyy/API.h"
5#undef CPYCPPYY_INTERNAL
6
7#include "CPPInstance.h"
8#include "CPPOverload.h"
9#include "CPPScope.h"
10#include "ProxyWrappers.h"
11#include "PyStrings.h"
12
13// Standard
14#include <stdio.h>
15#include <iostream>
16#include <string>
17
18//______________________________________________________________________________
19// CPyCppyy API: Interpreter and Proxy Access
20// ==========================================
21//
22// Access to cppyy Python objects from Cling and C++: allows conversion for
23// instances and type checking for scopes, instances, etc.
24// Adds a few convenience functions to call Python from Cling and expose Python
25// classes to Cling for use in inheritance etc.
26
27
28//- data ---------------------------------------------------------------------
29static PyObject* gMainDict = nullptr;
30
31namespace CPyCppyy {
32 extern PyObject* gThisModule;
33}
34
35
36//- private helpers ----------------------------------------------------------
37namespace {
38
39static bool Initialize()
40{
41// Private initialization method: setup the python interpreter and load the
42// cppyy module.
43 static bool isInitialized = false;
44 if (isInitialized)
45 return true;
46
47 if (!Py_IsInitialized()) {
48 // this happens if Cling comes in first
49#if PY_VERSION_HEX < 0x03020000
50 PyEval_InitThreads();
51#endif
52#if PY_VERSION_HEX < 0x03080000
53 Py_Initialize();
54#else
55 PyConfig config;
56 PyConfig_InitPythonConfig(&config);
57 PyConfig_SetString(&config, &config.program_name, L"cppyy");
58 Py_InitializeFromConfig(&config);
59#endif
60#if PY_VERSION_HEX >= 0x03020000
61#if PY_VERSION_HEX < 0x03090000
62 PyEval_InitThreads();
63#endif
64#endif
65
66 // try again to see if the interpreter is initialized
67 if (!Py_IsInitialized()) {
68 // give up ...
69 std::cerr << "Error: python has not been initialized; returning." << std::endl;
70 return false;
71 }
72
73 // set the command line arguments on python's sys.argv
74#if PY_VERSION_HEX < 0x03000000
75 char* argv[] = {const_cast<char*>("cppyy")};
76#elif PY_VERSION_HEX < 0x03080000
77 wchar_t* argv[] = {const_cast<wchar_t*>(L"cppyy")};
78#endif
79#if PY_VERSION_HEX < 0x03080000
80 PySys_SetArgv(sizeof(argv)/sizeof(argv[0]), argv);
81#endif
82
83 // force loading of the cppyy module
84 PyRun_SimpleString(const_cast<char*>("import cppyy"));
85 }
86
87 if (!gMainDict) {
88 // retrieve the main dictionary
89 gMainDict = PyModule_GetDict(
90 PyImport_AddModule(const_cast<char*>("__main__")));
91 // The gMainDict is borrowed, i.e. we are not calling Py_INCREF(gMainDict).
92 // Like this, we avoid unexpectedly affecting how long __main__ is kept
93 // alive. The gMainDict is only used in Exec(), ExecScript(), and Eval(),
94 // which should not be called after __main__ is garbage collected anyway.
95 }
96
97// declare success ...
98 isInitialized = true;
99 return true;
100}
101
102} // unnamed namespace
103
104
105//- C++ access to cppyy objects ---------------------------------------------
107{
108// Extract the object pointer held by the CPPInstance pyobject.
109 if (!Initialize())
110 return nullptr;
111
112// check validity of cast
113 if (!CPPInstance_Check(pyobject))
114 return nullptr;
115
116// get held object (may be null)
117 return ((CPPInstance*)pyobject)->GetObject();
118}
119
120//-----------------------------------------------------------------------------
122 void* addr, const std::string& classname, bool python_owns)
123{
124// Bind the addr to a python object of class defined by classname.
125 if (!Initialize())
126 return nullptr;
127
128// perform cast (the call will check TClass and addr, and set python errors)
129 PyObject* pyobject = BindCppObjectNoCast(addr, Cppyy::GetScope(classname), false);
130
131// give ownership, for ref-counting, to the python side, if so requested
132 if (python_owns && CPPInstance_Check(pyobject))
133 ((CPPInstance*)pyobject)->PythonOwns();
134
135 return pyobject;
136}
137
138namespace CPyCppyy {
139// version with C type arguments only for use with Numba
140PyObject* Instance_FromVoidPtr(void* addr, const char* classname, int python_owns) {
141 return Instance_FromVoidPtr(addr, std::string(classname), (bool)python_owns);
142}
143} // namespace CPyCppyy
144
145//-----------------------------------------------------------------------------
147{
148// Test if the given object is of a CPPScope derived type.
149 if (!Initialize())
150 return false;
151
152 return CPPScope_Check(pyobject);
153}
154
155//-----------------------------------------------------------------------------
157{
158// Test if the given object is of a CPPScope type.
159 if (!Initialize())
160 return false;
161
162 return CPPScope_CheckExact(pyobject);
163}
164
165//-----------------------------------------------------------------------------
167{
168// Test if the given pyobject is of CPPInstance derived type.
169 if (!Initialize())
170 return false;
171
172// detailed walk through inheritance hierarchy
173 return CPPInstance_Check(pyobject);
174}
175
176//-----------------------------------------------------------------------------
178{
179// Test if the given pyobject is of CPPInstance type.
180 if (!Initialize())
181 return false;
182
183// direct pointer comparison of type member
184 return CPPInstance_CheckExact(pyobject);
185}
186
187//-----------------------------------------------------------------------------
189{
190// Extends on PySequence_Check() to determine whether an object can be iterated
191// over (technically, all objects can b/c of C++ pointer arithmetic, hence this
192// check isn't 100% accurate, but neither is PySequence_Check()).
193
194// Note: simply having the iterator protocol does not constitute a sequence, bc
195// PySequence_GetItem() would fail.
196
197// default to PySequence_Check() if called with a non-C++ object
198 if (!CPPInstance_Check(pyobject))
199 return (bool)PySequence_Check(pyobject);
200
201// all C++ objects should have sq_item defined, but a user-derived class may
202// have deleted it, in which case this is not a sequence
203 PyTypeObject* t = Py_TYPE(pyobject);
204 if (!t->tp_as_sequence || !t->tp_as_sequence->sq_item)
205 return false;
206
207// if this is the default getitem, it is only a sequence if it's an array type
208 if (t->tp_as_sequence->sq_item == CPPInstance_Type.tp_as_sequence->sq_item) {
209 if (((CPPInstance*)pyobject)->fFlags & CPPInstance::kIsArray)
210 return true;
211 return false;
212 }
213
214// TODO: could additionally verify whether __len__ is supported and/or whether
215// operator()[] takes an int argument type
216
217 return true;
218}
219
220//-----------------------------------------------------------------------------
222{
223// Test whether the given instance can safely return to C++
224 if (!CPPInstance_Check(pyobject))
225 return true; // simply don't know
226
227// the instance fails the lively test if it owns the C++ object while having a
228// reference count of 1 (meaning: it could delete the C++ instance any moment)
229 if (pyobject->ob_refcnt <= 1 && (((CPPInstance*)pyobject)->fFlags & CPPInstance::kIsOwner))
230 return false;
231
232 return true;
233}
234
235//-----------------------------------------------------------------------------
237{
238// Test if the given pyobject is of CPPOverload derived type.
239 if (!Initialize())
240 return false;
241
242// detailed walk through inheritance hierarchy
243 return CPPOverload_Check(pyobject);
244}
245
246//-----------------------------------------------------------------------------
248{
249// Test if the given pyobject is of CPPOverload type.
250 if (!Initialize())
251 return false;
252
253// direct pointer comparison of type member
254 return CPPOverload_CheckExact(pyobject);
255}
256
257
258//- access to the python interpreter ----------------------------------------
259bool CPyCppyy::Import(const std::string& mod_name)
260{
261// Import the named python module and create Cling equivalents for its classes.
262 if (!Initialize())
263 return false;
264
265 PyObject* mod = PyImport_ImportModule(mod_name.c_str());
266 if (!mod) {
267 PyErr_Print();
268 return false;
269 }
270
271// allow finding to prevent creation of a python proxy for the C++ proxy
272 Py_INCREF(mod);
273 PyModule_AddObject(gThisModule, mod_name.c_str(), mod);
274
275// force creation of the module as a namespace
276// TODO: the following is broken (and should live in Cppyy.cxx)
277// TClass::GetClass(mod_name, true);
278
279 PyObject* dct = PyModule_GetDict(mod);
280
281// create Cling classes for all new python classes
282 PyObject* values = PyDict_Values(dct);
283 for (int i = 0; i < PyList_GET_SIZE(values); ++i) {
284 PyObject* value = PyList_GET_ITEM(values, i);
285 Py_INCREF(value);
286
287 // collect classes
288 if (PyClass_Check(value) || PyObject_HasAttr(value, PyStrings::gBases)) {
289 // get full class name (including module)
290 PyObject* pyClName = PyObject_GetAttr(value, PyStrings::gName);
291 if (PyErr_Occurred())
292 PyErr_Clear();
293
294 // build full, qualified name
295 std::string fullname = mod_name;
296 fullname += ".";
297 fullname += CPyCppyy_PyText_AsString(pyClName);
298
299 // force class creation (this will eventually call TPyClassGenerator)
300 // TODO: the following is broken (and should live in Cppyy.cxx) to
301 // TClass::GetClass(fullname.c_str(), true);
302
303 Py_XDECREF(pyClName);
304 }
305
306 Py_DECREF(value);
307 }
308
309 Py_DECREF(values);
310
311// TODO: mod "leaks" here
312 if (PyErr_Occurred())
313 return false;
314 return true;
315}
316
317//-----------------------------------------------------------------------------
318void CPyCppyy::ExecScript(const std::string& name, const std::vector<std::string>& args)
319{
320// Execute a python stand-alone script, with argv CLI arguments.
321//
322// example of use:
323// CPyCppyy::ExecScript("test.py", {"1", "2", "3"});
324
325 if (!Initialize())
326 return;
327
328// verify arguments
329 if (name.empty()) {
330 std::cerr << "Error: no file name specified." << std::endl;
331 return;
332 }
333
334 FILE* fp = fopen(name.c_str(), "r");
335 if (!fp) {
336 std::cerr << "Error: could not open file \"" << name << "\"." << std::endl;
337 return;
338 }
339
340// store a copy of the old cli for restoration
341 PyObject* oldargv = PySys_GetObject(const_cast<char*>("argv")); // borrowed
342 if (!oldargv) // e.g. apache
343 PyErr_Clear();
344 else {
345 PyObject* l = PyList_New(PyList_GET_SIZE(oldargv));
346 for (int i = 0; i < PyList_GET_SIZE(oldargv); ++i) {
347 PyObject* item = PyList_GET_ITEM(oldargv, i);
348 Py_INCREF(item);
349 PyList_SET_ITEM(l, i, item); // steals ref
350 }
351 oldargv = l;
352 }
353
354// create and set (add program name) the new command line
355 int argc = args.size() + 1;
356#if PY_VERSION_HEX < 0x03000000
357// This is a legacy implementation for Python 2
358 const char** argv = new const char*[argc];
359 for (int i = 1; i < argc; ++i) argv[i] = args[i-1].c_str();
360 argv[0] = Py_GetProgramName();
361 PySys_SetArgv(argc, const_cast<char**>(argv));
362 delete [] argv;
363#else
364// This is a common code block for Python 3. We prefer using objects to
365// automatize memory management and not introduce even more preprocessor
366// branching for deletion at the end of the method.
367//
368// FUTURE IMPROVEMENT ONCE OLD PYTHON VERSIONS ARE NOT SUPPORTED BY CPPYY:
369// Right now we use C++ objects to automatize memory management. One could use
370// RAAI and the Python memory allocation API (PEP 445) once some old Python
371// version is deprecated in CPPYY. That new feature is available since version
372// 3.4 and the preprocessor branching to also support that would be so
373// complicated to make the code unreadable.
374 std::vector<std::wstring> argv2;
375 argv2.reserve(argc);
376 argv2.emplace_back(name.c_str(), &name[name.size()]);
377
378 for (int i = 1; i < argc; ++i) {
379 auto iarg = args[i - 1].c_str();
380 argv2.emplace_back(iarg, &iarg[strlen(iarg)]);
381 }
382
383#if PY_VERSION_HEX < 0x03080000
384// Before version 3.8, the code is one simple line
385 wchar_t *argv2_arr[argc];
386 for (int i = 0; i < argc; ++i) {
387 argv2_arr[i] = const_cast<wchar_t *>(argv2[i].c_str());
388 }
389 PySys_SetArgv(argc, argv2_arr);
390
391#else
392// Here we comply to "PEP 587 – Python Initialization Configuration" to avoid
393// deprecation warnings at compile time.
394 class PyConfigHelperRAAI {
395 public:
396 PyConfigHelperRAAI(const std::vector<std::wstring> &argv2)
397 {
398 PyConfig_InitPythonConfig(&fConfig);
399 fConfig.parse_argv = 1;
400 UpdateArgv(argv2);
401 InitFromConfig();
402 }
403 ~PyConfigHelperRAAI() { PyConfig_Clear(&fConfig); }
404
405 private:
406 void InitFromConfig() { Py_InitializeFromConfig(&fConfig); };
407 void UpdateArgv(const std::vector<std::wstring> &argv2)
408 {
409 auto WideStringListAppendHelper = [](PyWideStringList *wslist, const wchar_t *wcstr) {
410 PyStatus append_status = PyWideStringList_Append(wslist, wcstr);
411 if (PyStatus_IsError(append_status)) {
412 std::wcerr << "Error: could not append element " << wcstr << " to arglist - " << append_status.err_msg
413 << std::endl;
414 }
415 };
416
417#if PY_VERSION_HEX < 0x30d00f0
418 WideStringListAppendHelper(&fConfig.argv, Py_GetProgramName());
419#else
420 PyObject* progname = PySys_GetObject("executable"); // borrowed
421 wchar_t buf[4096];
422 Py_ssize_t sz = CPyCppyy_PyUnicode_AsWideChar(progname, buf, 4095);
423 if (0 < sz)
424 WideStringListAppendHelper(&fConfig.argv, buf);
425#endif
426 for (const auto &iarg : argv2) {
427 WideStringListAppendHelper(&fConfig.argv, iarg.c_str());
428 }
429 }
430 PyConfig fConfig;
431 };
432
433 PyConfigHelperRAAI pych(argv2);
434
435#endif // of the else branch of PY_VERSION_HEX < 0x03080000
436#endif // of the else branch of PY_VERSION_HEX < 0x03000000
437
438// actual script execution
439 PyObject* gbl = PyDict_Copy(gMainDict);
440 PyObject* result = // PyRun_FileEx closes fp (b/c of last argument "1")
441 PyRun_FileEx(fp, const_cast<char*>(name.c_str()), Py_file_input, gbl, gbl, 1);
442 if (!result)
443 PyErr_Print();
444 Py_XDECREF(result);
445 Py_DECREF(gbl);
446
447// restore original command line
448 if (oldargv) {
449 PySys_SetObject(const_cast<char*>("argv"), oldargv);
450 Py_DECREF(oldargv);
451 }
452}
453
454//-----------------------------------------------------------------------------
455bool CPyCppyy::Exec(const std::string& cmd)
456{
457// Execute a python statement (e.g. "import noddy").
458 if (!Initialize())
459 return false;
460
461// execute the command
463 PyRun_String(const_cast<char*>(cmd.c_str()), Py_file_input, gMainDict, gMainDict);
464
465// test for error
466 if (result) {
467 Py_DECREF(result);
468 return true;
469 }
470
471 PyErr_Print();
472 return false;
473}
474
475//-----------------------------------------------------------------------------
476const CPyCppyy::PyResult CPyCppyy::Eval(const std::string& expr)
477{
478// Evaluate a python expression.
479//
480// Caution: do not hold on to the return value: either store it in a builtin
481// type (implicit casting will work), or in a pointer to a cppyy object (explicit
482// casting to a void* is required).
483 if (!Initialize())
484 return PyResult();
485
486// evaluate the expression
488 PyRun_String(const_cast<char*>(expr.c_str()), Py_eval_input, gMainDict, gMainDict);
489
490// report errors as appropriate; return void
491 if (!result) {
492 PyErr_Print();
493 return PyResult();
494 }
495
496// results that require no conversion
497 if (result == Py_None || CPPInstance_Check(result) ||
499 PyFloat_Check(result) || PyLong_Check(result) || PyInt_Check(result))
500 return PyResult(result);
501
502// explicit conversion for python type required
503 PyObject* pyclass = (PyObject*)Py_TYPE(result);
504
505// retrieve class name and the module in which it resides
506 PyObject* name = PyObject_GetAttr(pyclass, PyStrings::gName);
507 PyObject* module = PyObject_GetAttr(pyclass, PyStrings::gModule);
508
509 // concat name
510 std::string qname =
511 std::string(CPyCppyy_PyText_AsString(module)) + \
513 Py_DECREF(module);
514 Py_DECREF(name);
515
516// locate cppyy style class with this name
517 // TODO: use Cppyy.cxx ...
518 //TClass* klass = TClass::GetClass(qname.c_str());
519 void* klass = nullptr;
520
521// construct general cppyy python object that pretends to be of class 'klass'
522 if (klass)
523 return PyResult(result);
524
525// no conversion, return null pointer object
526 Py_DECREF(result);
527 return PyResult();
528}
529
530//-----------------------------------------------------------------------------
532// Enter an interactive python session (exit with ^D). State is preserved
533// between successive calls.
534 if (!Initialize())
535 return;
536
537// enter i/o interactive mode
538 PyRun_InteractiveLoop(stdin, const_cast<char*>("\0"));
539}
static PyObject * gMainDict
Definition API.cxx:29
#define Py_TYPE(ob)
Definition CPyCppyy.h:196
static Py_ssize_t CPyCppyy_PyUnicode_AsWideChar(PyObject *pyobj, wchar_t *w, Py_ssize_t size)
Definition CPyCppyy.h:201
int Py_ssize_t
Definition CPyCppyy.h:215
#define PyBytes_Check
Definition CPyCppyy.h:61
#define CPyCppyy_PyText_AsString
Definition CPyCppyy.h:76
_object PyObject
std::ios_base::fmtflags fFlags
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
PyTypeObject CPPInstance_Type
CPYCPPYY_EXTERN bool Instance_CheckExact(PyObject *pyobject)
Definition API.cxx:177
CPYCPPYY_EXTERN void Prompt()
Definition API.cxx:531
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_IsLively(PyObject *pyobject)
Definition API.cxx:221
PyObject * BindCppObjectNoCast(Cppyy::TCppObject_t object, Cppyy::TCppType_t klass, const unsigned flags=0)
bool CPPOverload_Check(T *object)
Definition CPPOverload.h:90
CPYCPPYY_EXTERN bool Sequence_Check(PyObject *pyobject)
Definition API.cxx:188
bool CPPScope_Check(T *object)
Definition CPPScope.h:81
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 bool Scope_CheckExact(PyObject *pyobject)
Definition API.cxx:156
bool CPPInstance_Check(T *object)
bool CPPInstance_CheckExact(T *object)
PyObject * gThisModule
Definition CPPMethod.cxx:30
bool CPPScope_CheckExact(T *object)
Definition CPPScope.h:91
CPYCPPYY_EXTERN void * Instance_AsVoidPtr(PyObject *pyobject)
Definition API.cxx:106
CPYCPPYY_EXTERN bool Scope_Check(PyObject *pyobject)
Definition API.cxx:146
CPYCPPYY_EXTERN bool Exec(const std::string &cmd)
Definition API.cxx:455
bool CPPOverload_CheckExact(T *object)
Definition CPPOverload.h:96
CPYCPPYY_EXTERN const PyResult Eval(const std::string &expr)
Definition API.cxx:476
RPY_EXPORTED TCppScope_t GetScope(const std::string &scope_name)
RooArgList L(Args_t &&... args)
Definition RooArgList.h:156
void Initialize(Bool_t useTMVAStyle=kTRUE)
Definition tmvaglob.cxx:176
TLine l
Definition textangle.C:4