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