12Modify the PyDBObj and PyDBArray classes to return read only objects to prevent
13accidental changes to the conditions data.
14Also modify the functions fillArray and readArray of the PyStoreArray class to
15ensure a safe and simple usage of those function.
17This module does not contain any public functions or variables, it just
18modifies the ROOT interfaces for PyDBObj and PyDBArray to only return read only
31_ROOT_kIsPublic = 0x00000200
33_ROOT_kIsStatic = 0x00004000
35_ROOT_kIsConstMethod = 0x10000000
39def pythonization(lazy=True, namespace=""):
41 Pythonizor decorator to be used in pythonization modules for pythonizations.
42 These pythonizations functions are invoked upon usage of the class.
46 If lazy is true, the class is pythonized upon first usage, otherwise
47 upon import of the ROOT module.
49 def pythonization_impl(fn):
51 The real decorator. This structure is adopted to deal with parameters
53 Function that implements some pythonization.
54 The function must accept two parameters: the class
55 to be pythonized and the name of that class.
58 cppyy.py.add_pythonization(fn, namespace)
61 return pythonization_impl
64def _make_tobject_const(obj):
66 Make a TObject const: Disallow setting any attributes and calling any
67 methods which are not declared as const. This affects any reference to this
68 object in python and stays permanent. Once called this particular instance
69 will be readonly everywhere and will remain like this.
71 This is done by replacing all setter functions with function objects that
72 just raise an attribute error when called. The class will be still the same
80 non_const = [m.GetName()
for m
in obj.Class().GetListOfMethods()
if (m.Property() & _ROOT_kIsPublic)
81 and not (m.Property() & (_ROOT_kIsConstMethod | _ROOT_kIsStatic))]
82 except AttributeError:
83 raise ValueError(f
"Object does not have a valid dictionary: {obj!r}")
86 for name
in non_const:
87 def __proxy(*args, **argk):
88 """raise attribute error when called"""
89 raise AttributeError(f
"{obj} is readonly and method '{name}' is not const")
91 setattr(obj, name, __proxy)
97def _PyDBArray__iter__(self):
98 """Provide iteration over Belle2::PyDBArray. Needs to be done here since we
99 want to make all returned classes read only"""
100 for i
in range(len(self)):
104numpy_to_cpp = {
"int16":
"short *",
105 "uint16":
"unsigned short *",
107 "uint32":
"unsigned int *",
109 "uint64":
"unsigned long *",
110 "float32":
"float *",
111 "float64":
"double *",
112 "float96":
"long double *"}
114cpp_to_numpy = {
"short":
"short",
115 "unsigned short":
"ushort",
117 "unsigned int":
"uintc",
119 "unsigned long":
"ulong",
122 "long double":
"longdouble",
123 "Belle2::VxdID":
"ushort"}
128 Class that throws an exception when a specific constructor is not found.
129 The error message will contain the signature of the wanted constructor,
130 as well as the signature of the available constructors.
132 def __init__(self, members, list_constructors, obj_name):
137 Contains the name of the parameters of the wanted constructor and their types
139 list_constructors: list of dictionaries
140 Contains all the available constructors, with the names of their parameters
144 Name of the class of which the constructor is wanted
147 self.
message =
"No corresponding constructor found. Looking for signature: \n"
156 ", ".join([
" ".join(i)
for i
in list(zip(self.
members.values(), self.
members.keys()))]) +
157 ")\n Available constructors:\n")
160 self.
name +
"(" +
", ".join([
" ".join(i)
for i
in list(zip(lis.values(), lis.keys()))]) +
")\n")
164def _wrap_fill_array(func):
166 def fill_array(pyStoreArray, **kwargs):
169 list_constructors = []
171 obj_class = pyStoreArray.getClass()
172 obj_classname = obj_class.GetName()
174 for meth
in obj_class.GetListOfMethods():
175 if meth.GetName() == obj_classname.split(
":")[-1]:
178 for ar
in meth.GetListOfMethodArgs():
179 d[ar.GetName()] = ar.GetTypeName()
180 list_constructors.append(d)
183 if d.keys() == kwargs.keys():
187 m_d = {list(kwargs.keys())[i]: numpy_to_cpp[type(list(kwargs.values())[i]
188 [0]).__name__].split(
"*")[0]
for i
in range(len(kwargs.keys()))}
192 for k
in kwargs.keys():
193 if kwargs[k][0].dtype != np.dtype(cpp_to_numpy[d[k]]):
195 kwargs[k] = kwargs[k].astype(cpp_to_numpy[d[k]])
197 raise ValueError((f
"Impossible to convert type of input arrays ({type(kwargs[k][0]).__name__})" +
198 f
" into the type of the corresponding class member '{k}' ({np.dtype(cpp_to_numpy[d[k]])})"))
199 arr_types.append(numpy_to_cpp[type(kwargs[k][0]).__name__])
201 l_arr = list(kwargs.values())
202 if not all(len(l_arr[0]) == len(arr)
for arr
in l_arr[1:]):
203 raise ValueError(
"The lengths of the passed arrays are not the same")
205 length = len(l_arr[0])
207 func[(obj_classname, *arr_types)](pyStoreArray, length, *[kwargs[k]
for k
in d.keys()])
212def _wrap_read_array(func):
214 def read_array(pyStoreArray):
218 obj_class = pyStoreArray.getClass()
219 obj_classname = obj_class.GetName()
221 fillValuesMethod = obj_class.GetMethodAny(
"fillValues")
222 if not fillValuesMethod:
223 raise ReferenceError(f
"The method fillValues is not implemented for the class {obj_classname}")
225 for ar
in fillValuesMethod.GetListOfMethodArgs():
226 kwargs.setdefault(ar.GetName(), np.zeros(len(pyStoreArray), dtype=cpp_to_numpy[ar.GetFullTypeName().split(
"*")[0]]))
228 l_types = [numpy_to_cpp[kwargs[v].dtype.name]
for v
in kwargs.keys()]
230 func[(obj_classname, *l_types)](pyStoreArray, *kwargs.values())
237@pythonization(namespace="Belle2")
238def _pythonize(klass, name):
239 """Adjust the python interface of some Py* classes"""
240 if not name.startswith(
"Py"):
243 if name ==
"PyDBObj":
246 klass.obj =
lambda self: _make_tobject_const(self._obj())
249 klass.__getattr__ =
lambda self, name: getattr(self.obj(), name)
252 klass.__iter__ =
lambda self: iter(self.obj())
253 elif name ==
"PyDBArray":
255 klass.__getitem__ =
lambda self, i: _make_tobject_const(self._get(i))
257 klass.__iter__ = _PyDBArray__iter__
258 elif name ==
"PyStoreObj":
261 klass.__iter__ =
lambda self: iter(self.obj())
262 elif name ==
"PyStoreArray":
263 klass.fillArray = _wrap_fill_array(klass.fillArray)
264 klass.readArray = _wrap_read_array(klass.readArray)
__init__(self, members, list_constructors, obj_name)
list_constructors
List of constructors.
str message
Member contatining the final message of the exception.