5 Modify the PyDBObj and PyDBArray classes to return read only objects to prevent
6 accidental changes to the conditions data.
8 This module does not contain any public functions or variables, it just
9 modifies the ROOT interfaces for PyDBObj and PyDBArray to only return read only
15 import cppyy
as _cppyy
23 _ROOT_kIsStatic = 0x00004000
25 _ROOT_kIsConstMethod = 0x10000000
29 """Empty class to check whether an instance is already const wrapped"""
33 def _make_tobject_nonconst(obj):
34 """Make a once read only TObject writeable again"""
35 if isinstance(obj, _TObjectConstWrapper):
36 object.__setattr__(obj,
"__class__", obj.__real_class__)
37 del obj.__real_class__
41 def _make_tobject_const(obj):
43 Make a TObject const: Disallow setting any attributes and calling any
44 methods which are not declared as const. This affects any reference to this
45 object in python and stays permanent. Once called this particular instance
46 will be readonly everywhere and will remain like this.
48 This is done by modifying the __class__ attribute of the object and replace
49 it with a new subclass which hides all non-const, non-static member
50 functions and disable attribute setting. The reason we do it like this is
51 that this is the only way how comparison and copy constructor calls work
52 transparently. E.g with a normal instance of type T 'a' and a const wrapped
53 T 'b' we can do things like a == b, b == a, a < b, b > a, c = T(b).
56 if obj
is None or isinstance(obj, _TObjectConstWrapper):
63 with warnings.catch_warnings():
64 warnings.simplefilter(
"ignore")
66 non_const = [m.GetName()
for m
in obj.Class().GetListOfAllPublicMethods()
67 if not (m.Property() & (_ROOT_kIsConstMethod | _ROOT_kIsStatic))]
68 except AttributeError:
69 raise ValueError(
"Object does not have a valid dictionary: %r" % obj)
71 def __make_const_proxy(obj, name):
72 """return a proxy function which just raises an attribute error on access"""
74 def proxy(self, *args):
75 """raise attribute error when called"""
76 raise AttributeError(
"%s is readonly and method '%s' is not const" % (obj, name))
79 def __setattr(self, name, value):
80 """disallow setting of any attributes"""
81 raise AttributeError(
"%s is readonly, can't set attribute" % obj)
86 scope = {m: property(__make_const_proxy(obj, m))
for m
in non_const}
88 scope[
"__setattr__"] = __setattr
92 normal_type = type(obj)
93 const_type = type(
"const %s" % normal_type.__name__, (normal_type, _TObjectConstWrapper), scope)
95 obj.__real_class__ = normal_type
97 obj.__class__ = const_type
102 def _PyDBArray__iter__(self):
103 """Provide iteration over Belle2::PyDBArray. Needs to be done here since we
104 want to make all returned classes read only"""
105 for i
in range(len(self)):
111 _dbobj_scope = _cppyy._backend.CreateScopeProxy(
"Belle2::PyDBObj")
112 _dbobj_scope.obj =
lambda self: _make_tobject_const(self._obj())
115 _dbobj_scope.__getattr__ =
lambda self, name: getattr(self.obj(), name)
117 _dbarray_scope = _cppyy._backend.CreateScopeProxy(
"Belle2::PyDBArray")
118 _dbarray_scope.__getitem__ =
lambda self, i: _make_tobject_const(self._get(i))
120 _dbarray_scope.__iter__ = _PyDBArray__iter__
123 _dbobj_scope.__iter__ =
lambda self: iter(self.obj())
124 _storeobj_scope = _cppyy._backend.CreateScopeProxy(
"Belle2::PyStoreObj")
125 _storeobj_scope.__iter__ =
lambda self: iter(self.obj())