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
84// Make sure the cppyy extension module is imported, as this is what runs the
85// CPyCppyy module initialization that sets up internals such as gThisModule.
88 if (!cppyymod)
89 return false;
91 }
92
93 if (!gMainDict) {
94 // retrieve the main dictionary
96 PyImport_AddModule(const_cast<char*>("__main__")));
97 // The gMainDict is borrowed, i.e. we are not calling Py_INCREF(gMainDict).
98 // Like this, we avoid unexpectedly affecting how long __main__ is kept
99 // alive. The gMainDict is only used in Exec(), ExecScript(), and Eval(),
100 // which should not be called after __main__ is garbage collected anyway.
101 }
102
103// declare success ...
104 isInitialized = true;
105 return true;
106}
107
108} // unnamed namespace
109
110
111//- C++ access to cppyy objects ---------------------------------------------
113{
114 if (!Instance_Check(pyobject)) {
115 PyErr_SetString(PyExc_TypeError, "Instance_GetScopedFinalName : object is not a C++ instance");
116 return "";
117 }
118
121}
122
123//-----------------------------------------------------------------------------
125{
126// Extract the object pointer held by the CPPInstance pyobject.
127 if (!Initialize())
128 return nullptr;
129
130// check validity of cast
132 return nullptr;
133
134// get held object (may be null)
135 return ((CPPInstance*)pyobject)->GetObject();
136}
137
138//-----------------------------------------------------------------------------
140 void* addr, const std::string& classname, bool python_owns)
141{
142// Bind the addr to a python object of class defined by classname.
143 if (!Initialize())
144 return nullptr;
145
146// perform cast (the call will check TClass and addr, and set python errors)
148
149// give ownership, for ref-counting, to the python side, if so requested
151 ((CPPInstance*)pyobject)->PythonOwns();
152
153 return pyobject;
154}
155
156namespace CPyCppyy {
157// version with C type arguments only for use with Numba
158PyObject* Instance_FromVoidPtr(void* addr, const char* classname, int python_owns) {
159 return Instance_FromVoidPtr(addr, std::string(classname), (bool)python_owns);
160}
161} // namespace CPyCppyy
162
163//-----------------------------------------------------------------------------
165{
166// Test if the given object is of a CPPScope derived type.
167 if (!Initialize())
168 return false;
169
170 return CPPScope_Check(pyobject);
171}
172
173//-----------------------------------------------------------------------------
175{
176// Test if the given object is of a CPPScope type.
177 if (!Initialize())
178 return false;
179
181}
182
183//-----------------------------------------------------------------------------
185{
186// Test if the given pyobject is of CPPInstance derived type.
187 if (!Initialize())
188 return false;
189
190// detailed walk through inheritance hierarchy
192}
193
194//-----------------------------------------------------------------------------
196{
197// Test if the given pyobject is of CPPInstance type.
198 if (!Initialize())
199 return false;
200
201// direct pointer comparison of type member
203}
204
205//-----------------------------------------------------------------------------
207{
208 if (!Initialize())
209 return;
210
211// check validity of cast
213 return;
214
215 ((CPPInstance *)pyobject)->PythonOwns();
216}
217
218//-----------------------------------------------------------------------------
220{
221 if (!Initialize())
222 return;
223
224// check validity of cast
226 return;
227
228 ((CPPInstance *)pyobject)->CppOwns();
229}
230
231//-----------------------------------------------------------------------------
233{
234// Extends on PySequence_Check() to determine whether an object can be iterated
235// over (technically, all objects can b/c of C++ pointer arithmetic, hence this
236// check isn't 100% accurate, but neither is PySequence_Check()).
237
238// Note: simply having the iterator protocol does not constitute a sequence, bc
239// PySequence_GetItem() would fail.
240
241// default to PySequence_Check() if called with a non-C++ object
243 return (bool)PySequence_Check(pyobject);
244
245// all C++ objects should have sq_item defined, but a user-derived class may
246// have deleted it, in which case this is not a sequence
248 if (!t->tp_as_sequence || !t->tp_as_sequence->sq_item)
249 return false;
250
251// if this is the default getitem, it is only a sequence if it's an array type
252 if (t->tp_as_sequence->sq_item == CPPInstance_Type.tp_as_sequence->sq_item) {
253 if (((CPPInstance*)pyobject)->fFlags & CPPInstance::kIsArray)
254 return true;
255 return false;
256 }
257
258// TODO: could additionally verify whether __len__ is supported and/or whether
259// operator()[] takes an int argument type
260
261 return true;
262}
263
264//-----------------------------------------------------------------------------
266{
267// Test whether the given instance can safely return to C++
269 return true; // simply don't know
270
271// the instance fails the lively test if it owns the C++ object while having a
272// reference count of 1 (meaning: it could delete the C++ instance any moment)
273 if (Py_REFCNT(pyobject) <= 1 && (((CPPInstance*)pyobject)->fFlags & CPPInstance::kIsOwner))
274 return false;
275
276 return true;
277}
278
279//-----------------------------------------------------------------------------
281{
282// Test if the given pyobject is of CPPOverload derived type.
283 if (!Initialize())
284 return false;
285
286// detailed walk through inheritance hierarchy
288}
289
290//-----------------------------------------------------------------------------
292{
293// Test if the given pyobject is of CPPOverload type.
294 if (!Initialize())
295 return false;
296
297// direct pointer comparison of type member
299}
300
301//-----------------------------------------------------------------------------
303{
304 CPPInstance::ReduceMethod() = reduceMethod;
305}
306
307//- access to the python interpreter ----------------------------------------
308bool CPyCppyy::Import(const std::string& mod_name)
309{
310// Import the named python module and create Cling equivalents for its classes.
311 if (!Initialize())
312 return false;
313
315 if (!mod) {
316 PyErr_Print();
317 return false;
318 }
319
320// allow finding to prevent creation of a python proxy for the C++ proxy
321 Py_INCREF(mod);
323
324// force creation of the module as a namespace
325// TODO: the following is broken (and should live in Cppyy.cxx)
326// TClass::GetClass(mod_name, true);
327
329
330// create Cling classes for all new python classes
331 PyObject* values = PyDict_Values(dct);
332 for (int i = 0; i < PyList_GET_SIZE(values); ++i) {
333 PyObject* value = PyList_GET_ITEM(values, i);
335
336 // collect classes
337 if (PyClass_Check(value) || PyObject_HasAttr(value, PyStrings::gBases)) {
338 // get full class name (including module)
339 PyObject* pyClName = PyObject_GetAttr(value, PyStrings::gName);
340 if (PyErr_Occurred())
341 PyErr_Clear();
342
343 // build full, qualified name
344 std::string fullname = mod_name;
345 fullname += ".";
347
349 }
350
352 }
353
354 Py_DECREF(values);
355
356// TODO: mod "leaks" here
357 if (PyErr_Occurred())
358 return false;
359 return true;
360}
361
362//-----------------------------------------------------------------------------
363void CPyCppyy::ExecScript(const std::string& name, const std::vector<std::string>& args)
364{
365// Execute a python stand-alone script, with argv CLI arguments.
366//
367// example of use:
368// CPyCppyy::ExecScript("test.py", {"1", "2", "3"});
369
370 if (!Initialize())
371 return;
372
373// verify arguments
374 if (name.empty()) {
375 std::cerr << "Error: no file name specified." << std::endl;
376 return;
377 }
378
379 FILE* fp = fopen(name.c_str(), "r");
380 if (!fp) {
381 std::cerr << "Error: could not open file \"" << name << "\"." << std::endl;
382 return;
383 }
384
385// store a copy of the old cli for restoration
386 PyObject* oldargv = PySys_GetObject("argv"); // borrowed
387 if (oldargv) {
389 oldargv = copy; // now owned
390 } else {
391 PyErr_Clear();
392 }
393
394// build new argv
395 const int argc = (int)args.size() + 1;
396 std::vector<wchar_t*> wargv(argc);
397
398 wargv[0] = Py_DecodeLocale(name.c_str(), nullptr);
399
400 for (int i = 1; i < argc; ++i) {
401 wargv[i] = Py_DecodeLocale(args[i - 1].c_str(), nullptr);
402 }
403
404// set sys.argv
405 PyObject* sysmod = PyImport_ImportModule("sys"); // new reference
406 if (sysmod) {
408 for (int i = 0; i < argc; ++i) {
410 }
414 } else {
415 PyErr_Print();
416 }
417
418// actual script execution
420 PyObject* result = // PyRun_FileEx closes fp (b/c of last argument "1")
421 PyRun_FileEx(fp, const_cast<char*>(name.c_str()), Py_file_input, gbl, gbl, 1);
422
423 if (!result)
424 PyErr_Print();
425
427 Py_DECREF(gbl);
428
429// restore original command line
430 if (oldargv) {
431 PySys_SetObject("argv", oldargv);
433 }
434
435// free memory from Py_DecodeLocale
436 for (auto ptr : wargv)
437 PyMem_RawFree(ptr);
438}
439
440//-----------------------------------------------------------------------------
441bool CPyCppyy::Exec(const std::string& cmd)
442{
443// Execute a python statement (e.g. "import noddy").
444 if (!Initialize())
445 return false;
446
447// execute the command
449 PyRun_String(const_cast<char*>(cmd.c_str()), Py_file_input, gMainDict, gMainDict);
450
451// test for error
452 if (result) {
454 return true;
455 }
456
457 PyErr_Print();
458 return false;
459}
460
461//-----------------------------------------------------------------------------
463// Enter an interactive python session (exit with ^D). State is preserved
464// between successive calls.
465 if (!Initialize())
466 return;
467
468// enter i/o interactive mode
469 PyRun_InteractiveLoop(stdin, const_cast<char*>("\0"));
470}
static PyObject * gMainDict
Definition API.cxx:29
#define Py_TYPE(ob)
Definition CPyCppyy.h:196
#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:145
PyTypeObject CPPInstance_Type
CPYCPPYY_EXTERN bool Instance_CheckExact(PyObject *pyobject)
Definition API.cxx:195
CPYCPPYY_EXTERN void Prompt()
Definition API.cxx:462
CPYCPPYY_EXTERN bool Overload_Check(PyObject *pyobject)
Definition API.cxx:280
CPYCPPYY_EXTERN bool Overload_CheckExact(PyObject *pyobject)
Definition API.cxx:291
CPYCPPYY_EXTERN bool Import(const std::string &name)
Definition API.cxx:308
CPYCPPYY_EXTERN void ExecScript(const std::string &name, const std::vector< std::string > &args)
Definition API.cxx:363
CPYCPPYY_EXTERN bool Instance_IsLively(PyObject *pyobject)
Definition API.cxx:265
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:232
bool CPPScope_Check(T *object)
Definition CPPScope.h:81
CPYCPPYY_EXTERN bool Instance_Check(PyObject *pyobject)
Definition API.cxx:184
CPYCPPYY_EXTERN PyObject * Instance_FromVoidPtr(void *addr, const std::string &classname, bool python_owns=false)
Definition API.cxx:139
CPYCPPYY_EXTERN bool Scope_CheckExact(PyObject *pyobject)
Definition API.cxx:174
bool CPPInstance_Check(T *object)
bool CPPInstance_CheckExact(T *object)
CPYCPPYY_EXTERN void Instance_SetCppOwns(PyObject *pyobject)
Definition API.cxx:219
CPYCPPYY_EXTERN void Instance_SetPythonOwns(PyObject *pyobject)
Definition API.cxx:206
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:302
CPYCPPYY_EXTERN std::string Instance_GetScopedFinalName(PyObject *pyobject)
Definition API.cxx:112
CPYCPPYY_EXTERN void * Instance_AsVoidPtr(PyObject *pyobject)
Definition API.cxx:124
CPYCPPYY_EXTERN bool Scope_Check(PyObject *pyobject)
Definition API.cxx:164
CPYCPPYY_EXTERN bool Exec(const std::string &cmd)
Definition API.cxx:441
bool CPPOverload_CheckExact(T *object)
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