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 Py_INCREF(gMainDict);
92 }
93
94// declare success ...
95 isInitialized = true;
96 return true;
97}
98
99} // unnamed namespace
100
101
102//- C++ access to cppyy objects ---------------------------------------------
104{
105// Extract the object pointer held by the CPPInstance pyobject.
106 if (!Initialize())
107 return nullptr;
108
109// check validity of cast
110 if (!CPPInstance_Check(pyobject))
111 return nullptr;
112
113// get held object (may be null)
114 return ((CPPInstance*)pyobject)->GetObject();
115}
116
117//-----------------------------------------------------------------------------
119 void* addr, const std::string& classname, bool python_owns)
120{
121// Bind the addr to a python object of class defined by classname.
122 if (!Initialize())
123 return nullptr;
124
125// perform cast (the call will check TClass and addr, and set python errors)
126 PyObject* pyobject = BindCppObjectNoCast(addr, Cppyy::GetScope(classname), false);
127
128// give ownership, for ref-counting, to the python side, if so requested
129 if (python_owns && CPPInstance_Check(pyobject))
130 ((CPPInstance*)pyobject)->PythonOwns();
131
132 return pyobject;
133}
134
135namespace CPyCppyy {
136// version with C type arguments only for use with Numba
137PyObject* Instance_FromVoidPtr(void* addr, const char* classname, int python_owns) {
138 return Instance_FromVoidPtr(addr, std::string(classname), (bool)python_owns);
139}
140} // namespace CPyCppyy
141
142//-----------------------------------------------------------------------------
144{
145// Test if the given object is of a CPPScope derived type.
146 if (!Initialize())
147 return false;
148
149 return CPPScope_Check(pyobject);
150}
151
152//-----------------------------------------------------------------------------
154{
155// Test if the given object is of a CPPScope type.
156 if (!Initialize())
157 return false;
158
159 return CPPScope_CheckExact(pyobject);
160}
161
162//-----------------------------------------------------------------------------
164{
165// Test if the given pyobject is of CPPInstance derived type.
166 if (!Initialize())
167 return false;
168
169// detailed walk through inheritance hierarchy
170 return CPPInstance_Check(pyobject);
171}
172
173//-----------------------------------------------------------------------------
175{
176// Test if the given pyobject is of CPPInstance type.
177 if (!Initialize())
178 return false;
179
180// direct pointer comparison of type member
181 return CPPInstance_CheckExact(pyobject);
182}
183
184//-----------------------------------------------------------------------------
186{
187// Test whether the given instance can safely return to C++
188 if (!CPPInstance_Check(pyobject))
189 return true; // simply don't know
190
191// the instance fails the lively test if it owns the C++ object while having a
192// reference count of 1 (meaning: it could delete the C++ instance any moment)
193 if (pyobject->ob_refcnt <= 1 && (((CPPInstance*)pyobject)->fFlags & CPPInstance::kIsOwner))
194 return false;
195
196 return true;
197}
198
199//-----------------------------------------------------------------------------
201{
202// Test if the given pyobject is of CPPOverload derived type.
203 if (!Initialize())
204 return false;
205
206// detailed walk through inheritance hierarchy
207 return CPPOverload_Check(pyobject);
208}
209
210//-----------------------------------------------------------------------------
212{
213// Test if the given pyobject is of CPPOverload type.
214 if (!Initialize())
215 return false;
216
217// direct pointer comparison of type member
218 return CPPOverload_CheckExact(pyobject);
219}
220
221
222//- access to the python interpreter ----------------------------------------
223bool CPyCppyy::Import(const std::string& mod_name)
224{
225// Import the named python module and create Cling equivalents for its classes.
226 if (!Initialize())
227 return false;
228
229 PyObject* mod = PyImport_ImportModule(mod_name.c_str());
230 if (!mod) {
231 PyErr_Print();
232 return false;
233 }
234
235// allow finding to prevent creation of a python proxy for the C++ proxy
236 Py_INCREF(mod);
237 PyModule_AddObject(gThisModule, mod_name.c_str(), mod);
238
239// force creation of the module as a namespace
240// TODO: the following is broken (and should live in Cppyy.cxx)
241// TClass::GetClass(mod_name, true);
242
243 PyObject* dct = PyModule_GetDict(mod);
244
245// create Cling classes for all new python classes
246 PyObject* values = PyDict_Values(dct);
247 for (int i = 0; i < PyList_GET_SIZE(values); ++i) {
248 PyObject* value = PyList_GET_ITEM(values, i);
249 Py_INCREF(value);
250
251 // collect classes
252 if (PyClass_Check(value) || PyObject_HasAttr(value, PyStrings::gBases)) {
253 // get full class name (including module)
254 PyObject* pyClName = PyObject_GetAttr(value, PyStrings::gName);
255 if (PyErr_Occurred())
256 PyErr_Clear();
257
258 // build full, qualified name
259 std::string fullname = mod_name;
260 fullname += ".";
261 fullname += CPyCppyy_PyText_AsString(pyClName);
262
263 // force class creation (this will eventually call TPyClassGenerator)
264 // TODO: the following is broken (and should live in Cppyy.cxx) to
265 // TClass::GetClass(fullname.c_str(), true);
266
267 Py_XDECREF(pyClName);
268 }
269
270 Py_DECREF(value);
271 }
272
273 Py_DECREF(values);
274
275// TODO: mod "leaks" here
276 if (PyErr_Occurred())
277 return false;
278 return true;
279}
280
281//-----------------------------------------------------------------------------
282void CPyCppyy::ExecScript(const std::string& name, const std::vector<std::string>& args)
283{
284// Execute a python stand-alone script, with argv CLI arguments.
285//
286// example of use:
287// CPyCppyy::ExecScript("test.py", {"1", "2", "3"});
288
289 if (!Initialize())
290 return;
291
292// verify arguments
293 if (name.empty()) {
294 std::cerr << "Error: no file name specified." << std::endl;
295 return;
296 }
297
298 FILE* fp = fopen(name.c_str(), "r");
299 if (!fp) {
300 std::cerr << "Error: could not open file \"" << name << "\"." << std::endl;
301 return;
302 }
303
304// store a copy of the old cli for restoration
305 PyObject* oldargv = PySys_GetObject(const_cast<char*>("argv")); // borrowed
306 if (!oldargv) // e.g. apache
307 PyErr_Clear();
308 else {
309 PyObject* l = PyList_New(PyList_GET_SIZE(oldargv));
310 for (int i = 0; i < PyList_GET_SIZE(oldargv); ++i) {
311 PyObject* item = PyList_GET_ITEM(oldargv, i);
312 Py_INCREF(item);
313 PyList_SET_ITEM(l, i, item); // steals ref
314 }
315 oldargv = l;
316 }
317
318// create and set (add program name) the new command line
319#if PY_VERSION_HEX < 0x03000000
320 int argc = args.size() + 1;
321 const char** argv = new const char*[argc];
322 for (int i = 1; i < argc; ++i) argv[i] = args[i-1].c_str();
323 argv[0] = Py_GetProgramName();
324 PySys_SetArgv(argc, const_cast<char**>(argv));
325 delete [] argv;
326#else
327// TODO: fix this to work like above ...
328 (void)args;
329#endif
330
331// actual script execution
332 PyObject* gbl = PyDict_Copy(gMainDict);
333 PyObject* result = // PyRun_FileEx closes fp (b/c of last argument "1")
334 PyRun_FileEx(fp, const_cast<char*>(name.c_str()), Py_file_input, gbl, gbl, 1);
335 if (!result)
336 PyErr_Print();
337 Py_XDECREF(result);
338 Py_DECREF(gbl);
339
340// restore original command line
341 if (oldargv) {
342 PySys_SetObject(const_cast<char*>("argv"), oldargv);
343 Py_DECREF(oldargv);
344 }
345}
346
347//-----------------------------------------------------------------------------
348bool CPyCppyy::Exec(const std::string& cmd)
349{
350// Execute a python statement (e.g. "import noddy").
351 if (!Initialize())
352 return false;
353
354// execute the command
356 PyRun_String(const_cast<char*>(cmd.c_str()), Py_file_input, gMainDict, gMainDict);
357
358// test for error
359 if (result) {
360 Py_DECREF(result);
361 return true;
362 }
363
364 PyErr_Print();
365 return false;
366}
367
368//-----------------------------------------------------------------------------
369const CPyCppyy::PyResult CPyCppyy::Eval(const std::string& expr)
370{
371// Evaluate a python expression.
372//
373// Caution: do not hold on to the return value: either store it in a builtin
374// type (implicit casting will work), or in a pointer to a cppyy object (explicit
375// casting to a void* is required).
376 if (!Initialize())
377 return PyResult();
378
379// evaluate the expression
381 PyRun_String(const_cast<char*>(expr.c_str()), Py_eval_input, gMainDict, gMainDict);
382
383// report errors as appropriate; return void
384 if (!result) {
385 PyErr_Print();
386 return PyResult();
387 }
388
389// results that require no conversion
390 if (result == Py_None || CPPInstance_Check(result) ||
392 PyFloat_Check(result) || PyLong_Check(result) || PyInt_Check(result))
393 return PyResult(result);
394
395// explicit conversion for python type required
396 PyObject* pyclass = (PyObject*)Py_TYPE(result);
397
398// retrieve class name and the module in which it resides
399 PyObject* name = PyObject_GetAttr(pyclass, PyStrings::gName);
400 PyObject* module = PyObject_GetAttr(pyclass, PyStrings::gModule);
401
402 // concat name
403 std::string qname =
404 std::string(CPyCppyy_PyText_AsString(module)) + \
406 Py_DECREF(module);
407 Py_DECREF(name);
408
409// locate cppyy style class with this name
410 // TODO: use Cppyy.cxx ...
411 //TClass* klass = TClass::GetClass(qname.c_str());
412 void* klass = nullptr;
413
414// construct general cppyy python object that pretends to be of class 'klass'
415 if (klass)
416 return PyResult(result);
417
418// no conversion, return null pointer object
419 Py_DECREF(result);
420 return PyResult();
421}
422
423//-----------------------------------------------------------------------------
425// Enter an interactive python session (exit with ^D). State is preserved
426// between successive calls.
427 if (!Initialize())
428 return;
429
430// enter i/o interactive mode
431 PyRun_InteractiveLoop(stdin, const_cast<char*>("\0"));
432}
static PyObject * gMainDict
Definition API.cxx:29
#define Py_TYPE(ob)
Definition CPyCppyy.h:196
#define PyBytes_Check
Definition CPyCppyy.h:61
#define CPyCppyy_PyText_AsString
Definition CPyCppyy.h:76
_object PyObject
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
CPYCPPYY_EXTERN bool Instance_CheckExact(PyObject *pyobject)
Definition API.cxx:174
CPYCPPYY_EXTERN void Prompt()
Definition API.cxx:424
CPYCPPYY_EXTERN bool Overload_Check(PyObject *pyobject)
Definition API.cxx:200
CPYCPPYY_EXTERN bool Overload_CheckExact(PyObject *pyobject)
Definition API.cxx:211
CPYCPPYY_EXTERN bool Import(const std::string &name)
Definition API.cxx:223
CPYCPPYY_EXTERN void ExecScript(const std::string &name, const std::vector< std::string > &args)
Definition API.cxx:282
CPYCPPYY_EXTERN bool Instance_IsLively(PyObject *pyobject)
Definition API.cxx:185
PyObject * BindCppObjectNoCast(Cppyy::TCppObject_t object, Cppyy::TCppType_t klass, const unsigned flags=0)
bool CPPOverload_Check(T *object)
Definition CPPOverload.h:90
bool CPPScope_Check(T *object)
Definition CPPScope.h:81
CPYCPPYY_EXTERN bool Instance_Check(PyObject *pyobject)
Definition API.cxx:163
CPYCPPYY_EXTERN PyObject * Instance_FromVoidPtr(void *addr, const std::string &classname, bool python_owns=false)
Definition API.cxx:118
CPYCPPYY_EXTERN bool Scope_CheckExact(PyObject *pyobject)
Definition API.cxx:153
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:103
CPYCPPYY_EXTERN bool Scope_Check(PyObject *pyobject)
Definition API.cxx:143
CPYCPPYY_EXTERN bool Exec(const std::string &cmd)
Definition API.cxx:348
bool CPPOverload_CheckExact(T *object)
Definition CPPOverload.h:96
CPYCPPYY_EXTERN const PyResult Eval(const std::string &expr)
Definition API.cxx:369
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