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 ---------------------------------------------------------------------
29 static PyObject* gMainDict = nullptr;
30 
31 namespace CPyCppyy {
33 }
34 
35 
36 //- private helpers ----------------------------------------------------------
37 namespace {
38 
39 static 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 ----------------------------------------
206 bool 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 //-----------------------------------------------------------------------------
265 void 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 //-----------------------------------------------------------------------------
331 bool 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 //-----------------------------------------------------------------------------
352 const 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 }
bool CPPInstance_Check(T *object)
Definition: CPPInstance.h:118
CPYCPPYY_EXTERN void * Instance_AsVoidPtr(PyObject *pyobject)
Definition: API.cxx:92
#define Py_TYPE(ob)
Definition: CPyCppyy.h:209
CPYCPPYY_EXTERN bool Scope_Check(PyObject *pyobject)
Definition: API.cxx:126
CPYCPPYY_EXTERN bool Import(const std::string &name)
Definition: API.cxx:206
PyObject * BindCppObjectNoCast(Cppyy::TCppObject_t object, Cppyy::TCppType_t klass, const unsigned flags=0)
CPYCPPYY_EXTERN bool Overload_CheckExact(PyObject *pyobject)
Definition: API.cxx:194
CPYCPPYY_EXTERN bool Instance_IsLively(PyObject *pyobject)
Definition: API.cxx:168
PyObject * gThisModule
Definition: API.cxx:32
bool CPPOverload_Check(T *object)
Definition: CPPOverload.h:79
CPYCPPYY_EXTERN void Prompt()
Definition: API.cxx:407
static PyObject * gMainDict
Definition: API.cxx:29
void Initialize(Bool_t useTMVAStyle=kTRUE)
Definition: tmvaglob.cxx:176
_object PyObject
Definition: PyMethodBase.h:41
bool CPPScope_CheckExact(T *object)
Definition: CPPScope.h:82
bool CPPOverload_CheckExact(T *object)
Definition: CPPOverload.h:85
#define PyBytes_Check
Definition: CPyCppyy.h:83
CPYCPPYY_EXTERN bool Instance_Check(PyObject *pyobject)
Definition: API.cxx:146
bool CPPInstance_CheckExact(T *object)
Definition: CPPInstance.h:128
static constexpr double L
CPYCPPYY_EXTERN PyObject * Instance_FromVoidPtr(void *addr, const std::string &classname, bool python_owns=false)
Definition: API.cxx:107
CPYCPPYY_EXTERN bool Overload_Check(PyObject *pyobject)
Definition: API.cxx:183
bool CPPScope_Check(T *object)
Definition: CPPScope.h:76
typedef void((*Func_t)())
CPYCPPYY_EXTERN bool Exec(const std::string &cmd)
Definition: API.cxx:331
CPYCPPYY_EXTERN const PyResult Eval(const std::string &expr)
Definition: API.cxx:352
auto * l
Definition: textangle.C:4
CPYCPPYY_EXTERN bool Scope_CheckExact(PyObject *pyobject)
Definition: API.cxx:136
RPY_EXPORTED TCppScope_t GetScope(const std::string &scope_name)
CPYCPPYY_EXTERN void ExecScript(const std::string &name, const std::vector< std::string > &args)
Definition: API.cxx:265
PyObject * gModule
Definition: PyStrings.cxx:24
PyObject * gBases
Definition: PyStrings.cxx:8
PyObject * gName
Definition: PyStrings.cxx:26
CPYCPPYY_EXTERN bool Instance_CheckExact(PyObject *pyobject)
Definition: API.cxx:157
char name[80]
Definition: TGX11.cxx:109
#define CPyCppyy_PyText_AsString
Definition: CPyCppyy.h:97