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