Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
GenericPyz.cxx
Go to the documentation of this file.
1// Author: Stefan Wunsch, Enric Tejedor CERN 06/2018
2// Original PyROOT code by Wim Lavrijsen, LBL
3
4/*************************************************************************
5 * Copyright (C) 1995-2018, Rene Brun and Fons Rademakers. *
6 * All rights reserved. *
7 * *
8 * For the licensing terms see $ROOTSYS/LICENSE. *
9 * For the list of contributors see $ROOTSYS/README/CREDITS. *
10 *************************************************************************/
11
12#include "Python.h"
13
14#include "CPyCppyy/API.h"
15
16#include "../../cppyy/CPyCppyy/src/CPyCppyy.h"
17#include "../../cppyy/CPyCppyy/src/CPPInstance.h"
18#include "../../cppyy/CPyCppyy/src/Utility.h"
19
20#include "PyROOTPythonize.h"
21
22#include "TClass.h"
23#include "TInterpreter.h"
24#include "TInterpreterValue.h"
25
26#include <map>
27
28namespace {
29
30std::string GetScopedFinalNameFromPyObject(const PyObject *pyobj)
31{
32 return Cppyy::GetScopedFinalName(((CPyCppyy::CPPInstance *)pyobj)->ObjectIsA());
33}
34
35} // namespace
36
37using namespace CPyCppyy;
38
39// We take as unique identifier the declId of the class to
40// treat the case where a class is loaded, an instance printed,
41// the class unloaded and reloaded with changes.
42static ULong64_t GetClassID(const char *clName)
43{
44 if (auto cl = TClass::GetClass(clName)) {
45 if (auto clInfo = cl->GetClassInfo()) {
46 return reinterpret_cast<ULong64_t>(gInterpreter->GetDeclId(clInfo));
47 }
48 }
49 return 0;
50}
51
53{
54 // Map holding the classID of the classes and the pointer
55 // to the printer function.
56 static std::map<ULong64_t, void *> declIDPrinterMap;
57
58 auto cppObj = CPyCppyy::Instance_AsVoidPtr(self);
59 if (!cppObj)
60 // Proxied cpp object is null, use cppyy's generic __repr__
61 return PyObject_Repr(self);
62
63 // We jit the helper only once, at the first invocation of any
64 // printer. The integer parameter is there to make sure we have
65 // different instances of the printing function in presence of
66 // unload-reload events.
67 if (0 == declIDPrinterMap.size()) {
68 std::string printerCode = "namespace ROOT::Internal::Pythonizations::ValuePrinters"
69 "{"
70 " template<class T, ULong64_t> std::string ValuePrinter(void *obj)"
71 " {"
72 " return cling::printValue((T *)obj);"
73 " }"
74 "}";
75 gInterpreter->Declare(printerCode.c_str());
76 }
77
78 const std::string className = GetScopedFinalNameFromPyObject(self);
79
80 std::string printResult;
81
82 if (const auto classID = GetClassID(className.c_str())) {
83 // If we never encountered this class, we jit the function which
84 // is necessary to print it and store it in the map instantiated
85 // above. Otherwise, we just use the pointer to the previously
86 // jitted function. This allows to jit the printer only once per
87 // type, at the modest price of a typename and pointer stored in
88 // memory.
89 auto &printerFuncrPtr = declIDPrinterMap[classID];
90
91 if (!printerFuncrPtr) {
92 std::string printFuncName = "ROOT::Internal::Pythonizations::ValuePrinters::ValuePrinter<" + className + ", " +
93 std::to_string(classID) + ">";
94 printerFuncrPtr = (void *)gInterpreter->Calc(printFuncName.c_str());
95 }
96 printResult = ((std::string(*)(void *))printerFuncrPtr)(cppObj);
97 } else {
98 // If something went wrong, we use the slow method
99 printResult = gInterpreter->ToString(className.c_str(), cppObj);
100 }
101
102 if (printResult.find("@0x") == 0) {
103 // Fall back to __repr__ if we just get an address from cling
104 return PyObject_Repr(self);
105 } else {
106 return PyUnicode_FromString(printResult.c_str());
107 }
108}
109
110////////////////////////////////////////////////////////////////////////////
111/// \brief Add pretty printing pythonization
112/// \param[in] self Always null, since this is a module function.
113/// \param[in] args Pointer to a Python tuple object containing the arguments
114/// received from Python.
115///
116/// This function adds the following pythonizations to print the object more
117/// user-friendly than cppyy by using the output of cling::printValue as the
118/// return value of the special method __str__.
120{
121 PyObject *pyclass = PyTuple_GetItem(args, 0);
122 Utility::AddToClass(pyclass, "__str__", (PyCFunction)ClingPrintValue);
124}
#define Py_RETURN_NONE
Definition CPyCppyy.h:268
static ULong64_t GetClassID(const char *clName)
PyObject * ClingPrintValue(PyObject *self, PyObject *)
_object PyObject
unsigned long long ULong64_t
Definition RtypesCore.h:70
#define gInterpreter
static TClass * GetClass(const char *name, Bool_t load=kTRUE, Bool_t silent=kFALSE)
Static method returning pointer to TClass of the specified class name.
Definition TClass.cxx:3037
bool AddToClass(PyObject *pyclass, const char *label, PyCFunction cfunc, int flags=METH_VARARGS)
Definition Utility.cxx:182
CPYCPPYY_EXTERN void * Instance_AsVoidPtr(PyObject *pyobject)
Definition API.cxx:106
RPY_EXPORTED std::string GetScopedFinalName(TCppType_t type)
PyObject * AddPrettyPrintingPyz(PyObject *self, PyObject *args)
Add pretty printing pythonization.