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
51#endif
52#if PY_VERSION_HEX < 0x03080000
54#else
55 PyConfig config;
57 PyConfig_SetString(&config, &config.program_name, L"cppyy");
59#endif
60#if PY_VERSION_HEX >= 0x03020000
61#if PY_VERSION_HEX < 0x03090000
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
90 PyImport_AddModule(const_cast<char*>("__main__")));
91 // The gMainDict is borrowed, i.e. we are not calling Py_INCREF(gMainDict).
92 // Like this, we avoid unexpectedly affecting how long __main__ is kept
93 // alive. The gMainDict is only used in Exec(), ExecScript(), and Eval(),
94 // which should not be called after __main__ is garbage collected anyway.
95 }
96
97// declare success ...
98 isInitialized = true;
99 return true;
100}
101
102} // unnamed namespace
103
104
105//- C++ access to cppyy objects ---------------------------------------------
107{
108 if (!Instance_Check(pyobject)) {
109 PyErr_SetString(PyExc_TypeError, "Instance_GetScopedFinalName : object is not a C++ instance");
110 return "";
111 }
112
115}
116
117//-----------------------------------------------------------------------------
119{
120// Extract the object pointer held by the CPPInstance pyobject.
121 if (!Initialize())
122 return nullptr;
123
124// check validity of cast
126 return nullptr;
127
128// get held object (may be null)
129 return ((CPPInstance*)pyobject)->GetObject();
130}
131
132//-----------------------------------------------------------------------------
134 void* addr, const std::string& classname, bool python_owns)
135{
136// Bind the addr to a python object of class defined by classname.
137 if (!Initialize())
138 return nullptr;
139
140// perform cast (the call will check TClass and addr, and set python errors)
142
143// give ownership, for ref-counting, to the python side, if so requested
145 ((CPPInstance*)pyobject)->PythonOwns();
146
147 return pyobject;
148}
149
150namespace CPyCppyy {
151// version with C type arguments only for use with Numba
152PyObject* Instance_FromVoidPtr(void* addr, const char* classname, int python_owns) {
153 return Instance_FromVoidPtr(addr, std::string(classname), (bool)python_owns);
154}
155} // namespace CPyCppyy
156
157//-----------------------------------------------------------------------------
159{
160// Test if the given object is of a CPPScope derived type.
161 if (!Initialize())
162 return false;
163
164 return CPPScope_Check(pyobject);
165}
166
167//-----------------------------------------------------------------------------
169{
170// Test if the given object is of a CPPScope type.
171 if (!Initialize())
172 return false;
173
175}
176
177//-----------------------------------------------------------------------------
179{
180// Test if the given pyobject is of CPPInstance derived type.
181 if (!Initialize())
182 return false;
183
184// detailed walk through inheritance hierarchy
186}
187
188//-----------------------------------------------------------------------------
190{
191// Test if the given pyobject is of CPPInstance type.
192 if (!Initialize())
193 return false;
194
195// direct pointer comparison of type member
197}
198
199//-----------------------------------------------------------------------------
201{
202// Extends on PySequence_Check() to determine whether an object can be iterated
203// over (technically, all objects can b/c of C++ pointer arithmetic, hence this
204// check isn't 100% accurate, but neither is PySequence_Check()).
205
206// Note: simply having the iterator protocol does not constitute a sequence, bc
207// PySequence_GetItem() would fail.
208
209// default to PySequence_Check() if called with a non-C++ object
211 return (bool)PySequence_Check(pyobject);
212
213// all C++ objects should have sq_item defined, but a user-derived class may
214// have deleted it, in which case this is not a sequence
216 if (!t->tp_as_sequence || !t->tp_as_sequence->sq_item)
217 return false;
218
219// if this is the default getitem, it is only a sequence if it's an array type
220 if (t->tp_as_sequence->sq_item == CPPInstance_Type.tp_as_sequence->sq_item) {
221 if (((CPPInstance*)pyobject)->fFlags & CPPInstance::kIsArray)
222 return true;
223 return false;
224 }
225
226// TODO: could additionally verify whether __len__ is supported and/or whether
227// operator()[] takes an int argument type
228
229 return true;
230}
231
232//-----------------------------------------------------------------------------
234{
235// Test whether the given instance can safely return to C++
237 return true; // simply don't know
238
239// the instance fails the lively test if it owns the C++ object while having a
240// reference count of 1 (meaning: it could delete the C++ instance any moment)
241 if (Py_REFCNT(pyobject) <= 1 && (((CPPInstance*)pyobject)->fFlags & CPPInstance::kIsOwner))
242 return false;
243
244 return true;
245}
246
247//-----------------------------------------------------------------------------
249{
250// Test if the given pyobject is of CPPOverload derived type.
251 if (!Initialize())
252 return false;
253
254// detailed walk through inheritance hierarchy
256}
257
258//-----------------------------------------------------------------------------
260{
261// Test if the given pyobject is of CPPOverload type.
262 if (!Initialize())
263 return false;
264
265// direct pointer comparison of type member
267}
268
269//-----------------------------------------------------------------------------
271{
272 CPPInstance::ReduceMethod() = reduceMethod;
273}
274
275//- access to the python interpreter ----------------------------------------
276bool CPyCppyy::Import(const std::string& mod_name)
277{
278// Import the named python module and create Cling equivalents for its classes.
279 if (!Initialize())
280 return false;
281
283 if (!mod) {
284 PyErr_Print();
285 return false;
286 }
287
288// allow finding to prevent creation of a python proxy for the C++ proxy
289 Py_INCREF(mod);
291
292// force creation of the module as a namespace
293// TODO: the following is broken (and should live in Cppyy.cxx)
294// TClass::GetClass(mod_name, true);
295
297
298// create Cling classes for all new python classes
299 PyObject* values = PyDict_Values(dct);
300 for (int i = 0; i < PyList_GET_SIZE(values); ++i) {
301 PyObject* value = PyList_GET_ITEM(values, i);
303
304 // collect classes
305 if (PyClass_Check(value) || PyObject_HasAttr(value, PyStrings::gBases)) {
306 // get full class name (including module)
307 PyObject* pyClName = PyObject_GetAttr(value, PyStrings::gName);
308 if (PyErr_Occurred())
309 PyErr_Clear();
310
311 // build full, qualified name
312 std::string fullname = mod_name;
313 fullname += ".";
315
316 // force class creation (this will eventually call TPyClassGenerator)
317 // TODO: the following is broken (and should live in Cppyy.cxx) to
318 // TClass::GetClass(fullname.c_str(), true);
319
321 }
322
324 }
325
326 Py_DECREF(values);
327
328// TODO: mod "leaks" here
329 if (PyErr_Occurred())
330 return false;
331 return true;
332}
333
334//-----------------------------------------------------------------------------
335void CPyCppyy::ExecScript(const std::string& name, const std::vector<std::string>& args)
336{
337// Execute a python stand-alone script, with argv CLI arguments.
338//
339// example of use:
340// CPyCppyy::ExecScript("test.py", {"1", "2", "3"});
341
342 if (!Initialize())
343 return;
344
345// verify arguments
346 if (name.empty()) {
347 std::cerr << "Error: no file name specified." << std::endl;
348 return;
349 }
350
351 FILE* fp = fopen(name.c_str(), "r");
352 if (!fp) {
353 std::cerr << "Error: could not open file \"" << name << "\"." << std::endl;
354 return;
355 }
356
357// store a copy of the old cli for restoration
358 PyObject* oldargv = PySys_GetObject("argv"); // borrowed
359 if (oldargv) {
361 oldargv = copy; // now owned
362 } else {
363 PyErr_Clear();
364 }
365
366// build new argv
367 const int argc = (int)args.size() + 1;
368 std::vector<wchar_t*> wargv(argc);
369
370 wargv[0] = Py_DecodeLocale(name.c_str(), nullptr);
371
372 for (int i = 1; i < argc; ++i) {
373 wargv[i] = Py_DecodeLocale(args[i - 1].c_str(), nullptr);
374 }
375
376// set sys.argv
377 PyObject* sysmod = PyImport_ImportModule("sys"); // new reference
378 if (sysmod) {
380 for (int i = 0; i < argc; ++i) {
382 }
386 } else {
387 PyErr_Print();
388 }
389
390// actual script execution
392 PyObject* result = // PyRun_FileEx closes fp (b/c of last argument "1")
393 PyRun_FileEx(fp, const_cast<char*>(name.c_str()), Py_file_input, gbl, gbl, 1);
394
395 if (!result)
396 PyErr_Print();
397
399 Py_DECREF(gbl);
400
401// restore original command line
402 if (oldargv) {
403 PySys_SetObject("argv", oldargv);
405 }
406
407// free memory from Py_DecodeLocale
408 for (auto ptr : wargv)
409 PyMem_RawFree(ptr);
410}
411
412//-----------------------------------------------------------------------------
413bool CPyCppyy::Exec(const std::string& cmd)
414{
415// Execute a python statement (e.g. "import noddy").
416 if (!Initialize())
417 return false;
418
419// execute the command
421 PyRun_String(const_cast<char*>(cmd.c_str()), Py_file_input, gMainDict, gMainDict);
422
423// test for error
424 if (result) {
426 return true;
427 }
428
429 PyErr_Print();
430 return false;
431}
432
433//-----------------------------------------------------------------------------
434const CPyCppyy::PyResult CPyCppyy::Eval(const std::string& expr)
435{
436// Evaluate a python expression.
437//
438// Caution: do not hold on to the return value: either store it in a builtin
439// type (implicit casting will work), or in a pointer to a cppyy object (explicit
440// casting to a void* is required).
441 if (!Initialize())
442 return PyResult();
443
444// evaluate the expression
446 PyRun_String(const_cast<char*>(expr.c_str()), Py_eval_input, gMainDict, gMainDict);
447
448// report errors as appropriate; return void
449 if (!result) {
450 PyErr_Print();
451 return PyResult();
452 }
453
454// results that require no conversion
458 return PyResult(result);
459
460// explicit conversion for python type required
462
463// retrieve class name and the module in which it resides
464 PyObject* name = PyObject_GetAttr(pyclass, PyStrings::gName);
465 PyObject* module = PyObject_GetAttr(pyclass, PyStrings::gModule);
466
467 // concat name
468 std::string qname =
469 std::string(CPyCppyy_PyText_AsString(module)) + \
473
474// locate cppyy style class with this name
475 // TODO: use Cppyy.cxx ...
476 //TClass* klass = TClass::GetClass(qname.c_str());
477 void* klass = nullptr;
478
479// construct general cppyy python object that pretends to be of class 'klass'
480 if (klass)
481 return PyResult(result);
482
483// no conversion, return null pointer object
485 return PyResult();
486}
487
488//-----------------------------------------------------------------------------
490// Enter an interactive python session (exit with ^D). State is preserved
491// between successive calls.
492 if (!Initialize())
493 return;
494
495// enter i/o interactive mode
496 PyRun_InteractiveLoop(stdin, const_cast<char*>("\0"));
497}
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
uint32_t fFlags
_object PyObject
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
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:157
PyTypeObject CPPInstance_Type
CPYCPPYY_EXTERN bool Instance_CheckExact(PyObject *pyobject)
Definition API.cxx:189
CPYCPPYY_EXTERN void Prompt()
Definition API.cxx:489
CPYCPPYY_EXTERN bool Overload_Check(PyObject *pyobject)
Definition API.cxx:248
CPYCPPYY_EXTERN bool Overload_CheckExact(PyObject *pyobject)
Definition API.cxx:259
CPYCPPYY_EXTERN bool Import(const std::string &name)
Definition API.cxx:276
CPYCPPYY_EXTERN void ExecScript(const std::string &name, const std::vector< std::string > &args)
Definition API.cxx:335
CPYCPPYY_EXTERN bool Instance_IsLively(PyObject *pyobject)
Definition API.cxx:233
PyObject * BindCppObjectNoCast(Cppyy::TCppObject_t object, Cppyy::TCppType_t klass, const unsigned flags=0)
bool CPPOverload_Check(T *object)
Definition CPPOverload.h:94
CPYCPPYY_EXTERN bool Sequence_Check(PyObject *pyobject)
Definition API.cxx:200
bool CPPScope_Check(T *object)
Definition CPPScope.h:81
CPYCPPYY_EXTERN bool Instance_Check(PyObject *pyobject)
Definition API.cxx:178
CPYCPPYY_EXTERN PyObject * Instance_FromVoidPtr(void *addr, const std::string &classname, bool python_owns=false)
Definition API.cxx:133
CPYCPPYY_EXTERN bool Scope_CheckExact(PyObject *pyobject)
Definition API.cxx:168
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_SetReduceMethod(PyCFunction reduceMethod)
Definition API.cxx:270
CPYCPPYY_EXTERN std::string Instance_GetScopedFinalName(PyObject *pyobject)
Definition API.cxx:106
CPYCPPYY_EXTERN void * Instance_AsVoidPtr(PyObject *pyobject)
Definition API.cxx:118
CPYCPPYY_EXTERN bool Scope_Check(PyObject *pyobject)
Definition API.cxx:158
CPYCPPYY_EXTERN bool Exec(const std::string &cmd)
Definition API.cxx:413
bool CPPOverload_CheckExact(T *object)
CPYCPPYY_EXTERN const PyResult Eval(const std::string &expr)
Definition API.cxx:434
TCppScope_t TCppType_t
Definition cpp_cppyy.h:35
RPY_EXPORTED std::string GetScopedFinalName(TCppType_t type)
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