Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
API.cxx
Go to the documentation of this file.
1// Bindings
2#include "cpyrt.h"
3
4using namespace cppjit;
5#include "cppjit_interop.h"
6#define CPYRT_INTERNAL 1
7#include "cpyrt/API.h"
8#undef CPYRT_INTERNAL
9
10#include "CPPInstance.h"
11#include "CPPOverload.h"
12#include "CPPScope.h"
13#include "ProxyWrappers.h"
14#include "PyStrings.h"
15#include "cpyrt/DispatchPtr.h"
16
17// Standard
18#include <iostream>
19#include <stdio.h>
20#include <string>
21
22//______________________________________________________________________________
23// cpyrt API: Interpreter and Proxy Access
24// ==========================================
25//
26// Access to cppjit Python objects from Cling and C++: allows conversion for
27// instances and type checking for scopes, instances, etc.
28// Adds a few convenience functions to call Python from Cling and expose Python
29// classes to Cling for use in inheritance etc.
30
31//- data ---------------------------------------------------------------------
32static PyObject* gMainDict = nullptr;
33
34namespace cppjit::cpyrt {
35extern PyObject* gThisModule;
36}
37
38//- private helpers ----------------------------------------------------------
39namespace {
40
41static bool Initialize() {
42 // Private initialization method: setup the python interpreter and load the
43 // cppjit module.
44 static bool isInitialized = false;
45 if (isInitialized)
46 return true;
47
48 if (!Py_IsInitialized()) {
49 // this happens if Cling comes in first
50 PyConfig config;
51 PyConfig_InitPythonConfig(&config);
52 PyConfig_SetString(&config, &config.program_name, L"cppjit");
53 Py_InitializeFromConfig(&config);
54
55 // try again to see if the interpreter is initialized
56 if (!Py_IsInitialized()) {
57 // give up ...
58 std::cerr << "Error: python has not been initialized; returning."
59 << std::endl;
60 return false;
61 }
62 }
63
64 // Importing the extension module is what runs the cpyrt initialization
65 // that sets gThisModule.
66 if (!cpyrt::gThisModule) {
67 PyObject* cppjitmod = PyImport_ImportModule("cppjit");
68 if (!cppjitmod)
69 return false;
70 Py_DECREF(cppjitmod);
71 }
72
73 if (!gMainDict) {
74 cpyrt::PythonGILRAII python_gil_raii;
75 // retrieve the main dictionary
76 gMainDict =
77 PyModule_GetDict(PyImport_AddModule(const_cast<char*>("__main__")));
78 // The gMainDict is borrowed, i.e. we are not calling Py_INCREF(gMainDict).
79 // Like this, we avoid unexpectedly affecting how long __main__ is kept
80 // alive. The gMainDict is only used in Exec(), ExecScript(), and Eval(),
81 // which should not be called after __main__ is garbage collected anyway.
82 }
83
84 // declare success ...
85 isInitialized = true;
86 return true;
87}
88
89} // unnamed namespace
90
91//- C++ access to cppjit objects ---------------------------------------------
93 if (!Instance_Check(pyobject)) {
94 PyErr_SetString(
95 PyExc_TypeError,
96 "Instance_GetScopedFinalName : object is not a C++ instance");
97 return "";
98 }
99
100 interop::TCppScope_t pyobjectClass = ((CPPInstance*)pyobject)->ObjectIsA();
101 return interop::GetScopedFinalName(pyobjectClass);
102}
103
104//-----------------------------------------------------------------------------
106 // Extract the object pointer held by the CPPInstance pyobject.
107 if (!Initialize())
108 return nullptr;
109
110 PythonGILRAII python_gil_raii;
111
112 // check validity of cast
113 if (!CPPInstance_Check(pyobject))
114 return nullptr;
115
116 // get held object (may be null)
117 return ((CPPInstance*)pyobject)->GetObject();
118}
119
120//-----------------------------------------------------------------------------
121PyObject* cpyrt::Instance_FromVoidPtr(void* addr, const std::string& classname,
122 bool python_owns) {
123 // Bind the addr to a python object of class defined by classname.
124 if (!Initialize())
125 return nullptr;
126
127 PythonGILRAII python_gil_raii;
128
129 // perform cast (the call will check TClass and addr, and set python errors)
130 PyObject* pyobject =
131 BindCppObjectNoCast(addr, interop::GetScope(classname), false);
132
133 // give ownership, for ref-counting, to the python side, if so requested
134 if (python_owns && CPPInstance_Check(pyobject))
135 ((CPPInstance*)pyobject)->PythonOwns();
136
137 return pyobject;
138}
139
140//-----------------------------------------------------------------------------
142 interop::TCppScope_t klass_scope,
143 bool python_owns) {
144 // Bind the addr to a python object of class defined by classname.
145 if (!Initialize())
146 return nullptr;
147
148 PythonGILRAII python_gil_raii;
149
150 // perform cast (the call will check TClass and addr, and set python errors)
151 PyObject* pyobject = BindCppObjectNoCast(addr, klass_scope, false);
152
153 // give ownership, for ref-counting, to the python side, if so requested
154 if (python_owns && CPPInstance_Check(pyobject))
155 ((CPPInstance*)pyobject)->PythonOwns();
156
157 return pyobject;
158}
159namespace cppjit::cpyrt {
160// version with C type arguments only for use with Numba
161PyObject* Instance_FromVoidPtr(void* addr, const char* classname,
162 int python_owns) {
163 return Instance_FromVoidPtr(addr, std::string(classname), (bool)python_owns);
164}
165} // namespace cppjit::cpyrt
166
167//-----------------------------------------------------------------------------
169 // Test if the given object is of a CPPScope derived type.
170 if (!Initialize())
171 return false;
172
173 PythonGILRAII python_gil_raii;
174 return CPPScope_Check(pyobject);
175}
176
177//-----------------------------------------------------------------------------
179 // Test if the given object is of a CPPScope type.
180 if (!Initialize())
181 return false;
182
183 PythonGILRAII python_gil_raii;
184 return CPPScope_CheckExact(pyobject);
185}
186
187//-----------------------------------------------------------------------------
189 // Test if the given pyobject is of CPPInstance derived type.
190 if (!Initialize())
191 return false;
192
193 PythonGILRAII python_gil_raii;
194 // detailed walk through inheritance hierarchy
195 return CPPInstance_Check(pyobject);
196}
197
198//-----------------------------------------------------------------------------
200 // Test if the given pyobject is of CPPInstance type.
201 if (!Initialize())
202 return false;
203
204 PythonGILRAII python_gil_raii;
205 // direct pointer comparison of type member
206 return CPPInstance_CheckExact(pyobject);
207}
208
209//-----------------------------------------------------------------------------
211 if (!Initialize())
212 return;
213
214 // check validity of cast
215 if (!CPPInstance_Check(pyobject))
216 return;
217
218 ((CPPInstance*)pyobject)->PythonOwns();
219}
220
221//-----------------------------------------------------------------------------
223 if (!Initialize())
224 return;
225
226 // check validity of cast
227 if (!CPPInstance_Check(pyobject))
228 return;
229
230 ((CPPInstance*)pyobject)->CppOwns();
231}
232
233//-----------------------------------------------------------------------------
235 PythonGILRAII python_gil_raii;
236 // Extends on PySequence_Check() to determine whether an object can be
237 // iterated over (technically, all objects can b/c of C++ pointer arithmetic,
238 // hence this check isn't 100% accurate, but neither is PySequence_Check()).
239
240 // Note: simply having the iterator protocol does not constitute a sequence,
241 // bc PySequence_GetItem() would fail.
242
243 // default to PySequence_Check() if called with a non-C++ object
244 if (!CPPInstance_Check(pyobject))
245 return (bool)PySequence_Check(pyobject);
246
247 // all C++ objects should have sq_item defined, but a user-derived class may
248 // have deleted it, in which case this is not a sequence
249 PyTypeObject* t = Py_TYPE(pyobject);
250 if (!t->tp_as_sequence || !t->tp_as_sequence->sq_item)
251 return false;
252
253 // if this is the default getitem, it is only a sequence if it's an array type
254 if (t->tp_as_sequence->sq_item == CPPInstance_Type.tp_as_sequence->sq_item) {
255 if (((CPPInstance*)pyobject)->fFlags & CPPInstance::kIsArray)
256 return true;
257 return false;
258 }
259
260 // TODO: could additionally verify whether __len__ is supported and/or whether
261 // operator()[] takes an int argument type
262
263 return true;
264}
265
266//-----------------------------------------------------------------------------
268 PythonGILRAII python_gil_raii;
269 // Test whether the given instance can safely return to C++
270 if (!CPPInstance_Check(pyobject))
271 return true; // simply don't know
272
273 // the instance fails the lively test if it owns the C++ object while having a
274 // reference count of 1 (meaning: it could delete the C++ instance any moment)
275 if (Py_REFCNT(pyobject) <= 1 &&
276 (((CPPInstance*)pyobject)->fFlags & CPPInstance::kIsOwner))
277 return false;
278
279 return true;
280}
281
282//-----------------------------------------------------------------------------
284 // Test if the given pyobject is of CPPOverload derived type.
285 if (!Initialize())
286 return false;
287
288 PythonGILRAII python_gil_raii;
289 // detailed walk through inheritance hierarchy
290 return CPPOverload_Check(pyobject);
291}
292
293//-----------------------------------------------------------------------------
295 // Test if the given pyobject is of CPPOverload type.
296 if (!Initialize())
297 return false;
298
299 PythonGILRAII python_gil_raii;
300 // direct pointer comparison of type member
301 return CPPOverload_CheckExact(pyobject);
302}
303
304//-----------------------------------------------------------------------------
305void cpyrt::Instance_SetReduceMethod(PyCFunction reduceMethod) {
306 CPPInstance::ReduceMethod() = reduceMethod;
307}
308
309//- access to the python interpreter ----------------------------------------
310bool cpyrt::Import(const std::string& mod_name) {
311 // Import the named python module and create Cling equivalents for its
312 // classes.
313 if (!Initialize())
314 return false;
315
316 PythonGILRAII python_gil_raii;
317
318 PyObject* mod = PyImport_ImportModule(mod_name.c_str());
319 if (!mod) {
320 PyErr_Print();
321 return false;
322 }
323
324 // allow finding to prevent creation of a python proxy for the C++ proxy
325 Py_INCREF(mod);
326 PyModule_AddObject(gThisModule, mod_name.c_str(), mod);
327
328 // force creation of the module as a namespace
329 // TODO: the following is broken (and should live in cppjit_interop.cxx)
330 // TClass::GetClass(mod_name, true);
331
332 PyObject* dct = PyModule_GetDict(mod);
333
334 // create Cling classes for all new python classes
335 PyObject* values = PyDict_Values(dct);
336 for (int i = 0; i < PyList_GET_SIZE(values); ++i) {
337 PyObject* value = PyList_GET_ITEM(values, i);
338 Py_INCREF(value);
339
340 // collect classes
341 if (PyType_Check(value) || PyObject_HasAttr(value, PyStrings::gBases)) {
342 // get full class name (including module)
343 PyObject* pyClName = PyObject_GetAttr(value, PyStrings::gName);
344 if (PyErr_Occurred())
345 PyErr_Clear();
346
347 // build full, qualified name
348 std::string fullname = mod_name;
349 fullname += ".";
350 fullname += PyUnicode_AsUTF8(pyClName);
351
352 Py_XDECREF(pyClName);
353 }
354
355 Py_DECREF(value);
356 }
357
358 Py_DECREF(values);
359
360 // TODO: mod "leaks" here
361 if (PyErr_Occurred())
362 return false;
363 return true;
364}
365
366//-----------------------------------------------------------------------------
367void cpyrt::ExecScript(const std::string& name,
368 const std::vector<std::string>& args) {
369 // Execute a python stand-alone script, with argv CLI arguments.
370 //
371 // example of use:
372 // cpyrt::ExecScript("test.py", {"1", "2", "3"});
373
374 if (!Initialize())
375 return;
376
377 PythonGILRAII python_gil_raii;
378
379 // verify arguments
380 if (name.empty()) {
381 std::cerr << "Error: no file name specified." << std::endl;
382 return;
383 }
384
385 FILE* fp = fopen(name.c_str(), "r");
386 if (!fp) {
387 std::cerr << "Error: could not open file \"" << name << "\"." << std::endl;
388 return;
389 }
390
391 // store a copy of the old cli for restoration
392 PyObject* oldargv = PySys_GetObject("argv"); // borrowed
393 if (oldargv) {
394 PyObject* copy = PyList_GetSlice(oldargv, 0, PyList_Size(oldargv));
395 oldargv = copy; // now owned
396 } else {
397 PyErr_Clear();
398 }
399
400 // build new argv
401 const int argc = (int)args.size() + 1;
402 std::vector<wchar_t*> wargv(argc);
403 wargv[0] = Py_DecodeLocale(name.c_str(), nullptr);
404
405 for (int i = 1; i < argc; ++i) {
406 wargv[i] = Py_DecodeLocale(args[i - 1].c_str(), nullptr);
407 }
408
409 // set sys.argv
410 PyObject* sysmod = PyImport_ImportModule("sys"); // new reference
411 if (sysmod) {
412 PyObject* argv_obj = PyList_New(argc);
413 for (int i = 0; i < argc; ++i) {
414 PyList_SET_ITEM(argv_obj, i, PyUnicode_FromWideChar(wargv[i], -1));
415 }
416 PyObject_SetAttrString(sysmod, "argv", argv_obj);
417 Py_DECREF(argv_obj);
418 Py_DECREF(sysmod);
419 } else {
420 PyErr_Print();
421 }
422
423 // actual script execution
424 PyObject* gbl = PyDict_Copy(gMainDict);
425 PyObject* result = // PyRun_FileEx closes fp (b/c of last argument "1")
426 PyRun_FileEx(fp, const_cast<char*>(name.c_str()), Py_file_input, gbl, gbl,
427 1);
428
429 if (!result)
430 PyErr_Print();
431
432 Py_XDECREF(result);
433 Py_DECREF(gbl);
434
435 // restore original command line
436 if (oldargv) {
437 PySys_SetObject("argv", oldargv);
438 Py_DECREF(oldargv);
439 }
440
441 // free memory from Py_DecodeLocale
442 for (auto ptr : wargv)
443 PyMem_RawFree(ptr);
444}
445
446//-----------------------------------------------------------------------------
447bool cpyrt::Exec(const std::string& cmd) {
448 // Execute a python statement (e.g. "import noddy").
449 if (!Initialize())
450 return false;
451
452 PythonGILRAII python_gil_raii;
453 // execute the command
454 PyObject* result = PyRun_String(const_cast<char*>(cmd.c_str()), Py_file_input,
456
457 // test for error
458 if (result) {
459 Py_DECREF(result);
460 return true;
461 }
462
463 PyErr_Print();
464 return false;
465}
466
467//-----------------------------------------------------------------------------
469 // Enter an interactive python session (exit with ^D). State is preserved
470 // between successive calls.
471 if (!Initialize())
472 return;
473
474 PythonGILRAII python_gil_raii;
475 // enter i/o interactive mode
476 PyRun_InteractiveLoop(stdin, const_cast<char*>("\0"));
477}
static PyObject * gMainDict
Definition API.cxx:32
uint32_t fFlags
_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:142
bool CPPOverload_Check(T *object)
Definition CPPOverload.h:94
CPYRT_EXTERN std::string Instance_GetScopedFinalName(PyObject *pyobject)
Definition API.cxx:92
CPYRT_EXTERN void Instance_SetReduceMethod(PyCFunction reduceMethod)
Definition API.cxx:305
PyObject * gThisModule
Definition CPPMethod.cxx:30
PyTypeObject CPPInstance_Type
bool CPPOverload_CheckExact(T *object)
Definition CPPOverload.h:98
CPYRT_EXTERN bool Scope_CheckExact(PyObject *pyobject)
Definition API.cxx:178
CPYRT_EXTERN bool Sequence_Check(PyObject *pyobject)
Definition API.cxx:234
CPYRT_EXTERN bool Exec(const std::string &cmd)
Definition API.cxx:447
bool CPPInstance_CheckExact(T *object)
CPYRT_EXTERN bool Scope_Check(PyObject *pyobject)
Definition API.cxx:168
CPYRT_EXTERN bool Overload_Check(PyObject *pyobject)
Definition API.cxx:283
CPYRT_EXTERN bool Instance_Check(PyObject *pyobject)
Definition API.cxx:188
bool CPPScope_CheckExact(T *object)
Definition CPPScope.h:93
CPYRT_EXTERN void ExecScript(const std::string &name, const std::vector< std::string > &args)
Definition API.cxx:367
PyObject * BindCppObjectNoCast(interop::TCppObject_t object, interop::TCppScope_t klass, const unsigned flags=0)
CPYRT_EXTERN void Prompt()
Definition API.cxx:468
CPYRT_EXTERN void * Instance_AsVoidPtr(PyObject *pyobject)
Definition API.cxx:105
CPYRT_EXTERN bool Instance_IsLively(PyObject *pyobject)
Definition API.cxx:267
bool CPPScope_Check(T *object)
Definition CPPScope.h:86
bool CPPInstance_Check(T *object)
CPYRT_EXTERN void Instance_SetCppOwns(PyObject *pyobject)
Definition API.cxx:222
CPYRT_EXTERN bool Overload_CheckExact(PyObject *pyobject)
Definition API.cxx:294
PyObject * Instance_FromVoidPtr(void *addr, const char *classname, int python_owns)
Definition API.cxx:161
CPYRT_EXTERN void Instance_SetPythonOwns(PyObject *pyobject)
Definition API.cxx:210
CPYRT_EXTERN bool Instance_CheckExact(PyObject *pyobject)
Definition API.cxx:199
CPYRT_EXTERN bool Import(const std::string &name)
Definition API.cxx:310
TCppScope_t GetScope(const std::string &scope_name, TCppScope_t parent_scope=TCppScope_t{})
std::string GetScopedFinalName(TCppScope_t type)
Cpp::DeclRef TCppScope_t
Definition API.h:41
Definition API.cxx:34