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