ATLAS Offline Software
Loading...
Searching...
No Matches
Node.py
Go to the documentation of this file.
40
41import warnings
42
43# locked state, allows transitions, locked forever,
44# means it can not later be unlocked
45# dynamic flag loading is possibly even if locked forever
46# since existing nodes do not need to be unlocked
47
48_UNLOCKED = 0
49_LOCKED = 1
50_LOCKED_FOREVER = 2
51
52class Node:
53 """
54 Basic node class, so we can get all the egregious flags from a
55 flags object, and use, and pass round, the individual nodes, as
56 actual nodes without all the nonsense limitations of using the
57 flags directly
58 """
59
60 def __init__(self, name="root", flags=None, root=None ):
61 object.__setattr__(self, "_lockstate", _UNLOCKED)
62 object.__setattr__(self, "_name", name)
63 object.__setattr__(self, "_children", {})
64 object.__setattr__(self, "_flags", flags)
65 object.__setattr__(self, "_root", root)
66
67 def __setattr__(self, name, value):
68 self._fail_if_locked()
69 if name in self._children : raise AttributeError(f"node {name} already exists")
70 current = self.__dict__.get(name)
71 if type(current).__name__ == "CfgFlag":
72 current.set(value)
73 else:
74 object.__setattr__(self, name, value)
75
76 def __getattribute__(self, name):
77 value = object.__getattribute__(self, name)
78 if type(value).__name__ == "CfgFlag":
79 flags = object.__getattribute__(self, "_flags")
80 return value.get(flags)
81 return value
82
83 def __getattr__(self, name):
84 """ will attempt a dynamic load from flags if need be """
85 if name in self._children : return self._children[name]
86 if self._root :
87 if self._root._loadflags( f"{self.name()}.{name}" ) :
88 if name in self._children : return self._children[name]
89 if name in self._root._children : return self._root._children[name]
90 if self._root._loadflags( name ) :
91 if name in self._root._children : return self._root._children[name]
92 else:
93 if self._loadflags( name ) :
94 if name in self._children : return self._children[name]
95
96 raise AttributeError(f"no attribute {name}")
97
98 def __contains__(self, name):
99 return self.has(name)
100
101 def __copy__(self):
102 return self.clone()
103
104 def __delattr__(self, name):
105 self.delete(name)
106
107 def __delitem__(self, name):
108 self.delete(name)
109
110 def lock(self, lockstate=_LOCKED):
111 for node in self.nodes() : node.lock(lockstate)
112 object.__setattr__(self, "_lockstate", lockstate)
113
114 def unlock(self):
116 if self._lockstate == _LOCKED:
117 object.__setattr__(self, "_lockstate", _UNLOCKED)
118 for node in self.nodes(): node.unlock()
119
120 def lock_forever(self):
121 self.lock(_LOCKED_FOREVER)
122
123 def islocked(self):
124 return self._lockstate != _UNLOCKED
125
126 def locked(self):
127 return self.islocked()
128
130 if self._lockstate == _LOCKED_FOREVER : raise RuntimeError(f"Node {self._name} is permanently locked; ")
131
133 if self._lockstate == _LOCKED : raise RuntimeError(f"Node {self._name} is locked; ")
134 if self._lockstate == _LOCKED_FOREVER : raise RuntimeError(f"Node {self._name} is permanently locked; ")
135
136 def _loadflags(self, name ) :
137 """ load dynamic flags - can only load flags on the root node """
138
139 if self._root : return self._root._loadflags( name )
140
141 flags = self._flags
142 if flags is None : raise AttributeError(f"no attribute {name}")
143 lockstate = self._lockstate
144 object.__setattr__(self, "_lockstate", _UNLOCKED)
145 try:
146 if name in flags._loaded : return self._stragglers(flags)
147 if name in flags._dynaflags : return self._addDynaFlags(name)
148 for key in flags._dynaflags :
149 if name in key : return self._addDynaFlags(key)
150 return False
151 finally:
152 # ensure the lock state is restored if something goes wrong ...
153 if lockstate != _UNLOCKED : self.lock(lockstate)
154
155 def _addDynaFlags(self, name) :
156 flags = self._flags
157 self._flags._loadDynaFlags(name)
158 return self._stragglers(flags)
159
160 def _stragglers(self, flags, dbg="") :
161 for key, val in self._flags._flagdict.items() :
162 if self._hasFlagPath(key) : continue
163 self._addFlagUnlock( key, val, flags )
164 return True
165
166 def _rootnode(self):
167 if self._root : return self._root
168 return self
169
170 def _setrootnode(self, rootnode ):
171 if self._root is rootnode : return
172 object.__setattr__(self, "_root", rootnode )
173 for node in self.nodes() : node._setrootnode(rootnode)
174
175 def name(self) :
176 return self._name
177
178 def add(self, attr_name, value):
179 """ add an attribute to this node """
180 setattr(self, attr_name, value)
181
182 def _addFlagImpl(self, tag, value, flags=None, unlock=False):
183 """ addFlag method like that in the egregious flags """
184 node = self
185 if flags is not None and self._flags is None : node._flags = flags
186 names = tag.split(".")
187 for name in names[:-1]:
188 node = node._addNewNode(name, flags )
189 if unlock : object.__setattr__( node, "_lockstate", _UNLOCKED )
190 if flags is not None : node._flags = flags
191 if names[-1] in node.__dict__ : return False
192 # just use a value at the moment, can use CfgFlag(value, type) if we really have to(
193 setattr(node, names[-1], value)
194 return True
195
196 def addFlag(self, tag, value, flags=None):
197 if not self._addFlagImpl( tag, value, flags, False ):
198 raise KeyError(f"flag {tag} already exists")
199
200 def _addFlagUnlock(self, tag, value, flags=None):
201 self._addFlagImpl( tag, value, flags, True )
202
203 def addNode( self, item, flags=None):
204 if isinstance(item, Node) : return self._addNode(item)
205 if isinstance(item, str) : return self._addNewNode(item, flags)
206 raise TypeError(f"Cannot construct node from type {type(item).__name__}, value {item}")
207
208 def _addNewNode(self, name : str, flags=None):
209 """ add a new child node """
210 self._fail_if_locked()
211 if name in self.__dict__: raise AttributeError(f"attribute {name} already exists")
212 rootnode = self._rootnode()
213 if name not in self._children :
214 self._children[name] = ( node := Node(name, root=rootnode) )
215 if flags is not None : node._flags = flags
216 return self._children[name]
217
218 def _addNode(self, node ):
219 """ an en existing node as a child node """
220 self._fail_if_locked()
221 name = node.name()
222 if name in self.__dict__: raise AttributeError(f"attribute {name} already exists")
223 rootnode = self._rootnode()
224 if name not in self._children :
225 if node._root and rootnode is not node._root :
226 raise RuntimeError(f"cannot add node {name} from a different tree")
227 self._children[node.name()] = node
228 if not node._root : node._setrootnode( rootnode )
229 return self._children[node.name()]
230
231 def has(self, name ):
232 return hasattr( self, name )
233
234 def hasFlag(self, name ):
235 return name in self.__dict__
236
237 def hasNode(self, name):
238 return name in self._children
239
240 def _hasFlagPath(self, name):
241 parts = name.split('.')
242 node = self._rootnode()
243 for part in parts[:-1]:
244 if not node.hasNode(part) : return False
245 node = node.node(part)
246 return node.hasFlag(parts[-1])
247
248 def nodes(self):
249 return self._children.values()
250
251 def node_flags(self):
252 for key, value in self.__dict__.items():
253 if not key.startswith("_"):
254 yield key, getattr(self, key)
255
256 def node(self, name):
257 if self.hasNode(name) : return self._children[name]
258 raise RuntimeError(f"So such child: {name}")
259
260 def delete(self, name):
261 self._fail_if_locked()
262 if name in self._children : del self._children[name]
263 elif name in self.__dict__ : object.__delattr__(self, name) # delattr( self, name )
264 else : raise AttributeError(f"Node {self._name} has no member {name}")
265
266 def rename( self, name, newname ):
267 if newname in self._children or newname in self.__dict__ :
268 raise RuntimeError(f"name {newname} already in node")
269
270 if name in self.__dict__: setattr(self, newname, self.__dict__.pop(name) )
271 elif name in self._children :
272 self._children[newname] = ( node := self._children.pop(name) )
273 node._name = newname
274
275 def clone(self, name=None):
276 """Return an independent copy" of this node"""
277
278 if name is not None : newnode = Node(name)
279 else: newnode = Node(self._name)
280
281 for key, value in self.__dict__.items():
282 if key in {"_name", "_children", "_lockstate", "_root" } : continue
283 setattr(newnode, key, value)
284
285 for node in self.nodes():
286 child = node.clone()
287 child._root = None
288 newnode._addNode(child)
289
290 return newnode
291
292
293 def print(self, spacer=""):
294 print( f"{spacer}{self._name} (Node)" )
295 if spacer=="" : spacer = "|__ "
296 else: spacer=f" {spacer}"
297 for key, value in self.__dict__.items():
298 if key in {"_name","_children","_lockstate", "_flags", "_root"} : continue
299 print( f"{spacer}{key}\t: {value}" )
300 for node in self.nodes() : node.print( spacer )
301
302
303 def dump(self): self.print()
304
305
306 def cloneAndReplace(self, targetname, flagname ) :
307 # we really, really, should not have this ...
308 warnings.warn( "cloneAndReplace() is unnecessary for Node flags; "
309 "use the appropriate Node directly",
310 RuntimeWarning, stacklevel=2 )
311 # eventually, instead of delegation, should raise an exception ...
312 # raise RuntimeError( "cloneAndReplace() should not be used for node flags" )
313 return self._rootnode()._flags.cloneAndReplace( targetname, flagname )
314
315
316def notDefined(flags, name):
317 """ check whether the flags have an actual item with this name
318 NB using hasFlag(name) is really dengerous, as that seems to
319 actually evaluate any actual functions ? shouldn't
320 something like that be called testFlag() ?
321 """
322 return name not in flags._flagdict
323
324
325
326def decode_flags( flags, domain=None ) :
327 """ take an egregious flags list and convert to a lovely tree
328 or, if it is a lovely tree already, just hand it back
329 """
330
331 if isinstance( flags, Node ) : return flags
332
333 root = Node("root")
334
335 for tag, value in flags._flagdict.items():
336 root.addFlag( tag, value, flags )
337
338 return root
339
340
341
342
343def update_flags( flags, node, domain="" ) :
344 """ take a lovely tree and convert to an egregious flags list,
345 only adding items that are not in the original flags list """
346
347 for key, value in node.__dict__.items():
348 if key in {"_name","_children","_lockstate", "_flags", "_root" } : continue
349 name = f"{domain}.{key}"
350 if notDefined(flags,name) : flags.addFlag( name, value )
351
352 for n in node.nodes() :
353 if domain=="" : update_flags( flags, n, f"{n.name()}" )
354 else: update_flags( flags, n, f"{domain}.{n.name()}" )
355
356
357
358def topnode(flags) :
359 """ Escape hatch in case the user needs to access a node
360 from the top node that has the Same Name, as a node
361 on the local node"""
362 return flags._rootnode()
virtual void lock()=0
Interface to allow an object to lock itself when made const in SG.
node(node *n=0, const std::string &d="", TObject *t=0)
Definition node.h:32
void name(const std::string &n)
Definition node.h:40
nodes(self)
Definition Node.py:248
print(self, spacer="")
Definition Node.py:293
lock(self, lockstate=_LOCKED)
Definition Node.py:110
addFlag(self, tag, value, flags=None)
Definition Node.py:196
_setrootnode(self, rootnode)
Definition Node.py:170
unlock(self)
Definition Node.py:114
__getattr__(self, name)
Definition Node.py:83
addNode(self, item, flags=None)
Definition Node.py:203
_hasFlagPath(self, name)
Definition Node.py:240
__contains__(self, name)
Definition Node.py:98
_addNewNode(self, str name, flags=None)
Definition Node.py:208
__delitem__(self, name)
Definition Node.py:107
__init__(self, name="root", flags=None, root=None)
Definition Node.py:60
__delattr__(self, name)
Definition Node.py:104
__copy__(self)
Definition Node.py:101
locked(self)
Definition Node.py:126
hasNode(self, name)
Definition Node.py:237
has(self, name)
Definition Node.py:231
_addNode(self, node)
Definition Node.py:218
_fail_if_locked(self)
Definition Node.py:132
cloneAndReplace(self, targetname, flagname)
Definition Node.py:306
__setattr__(self, name, value)
Definition Node.py:67
_addDynaFlags(self, name)
Definition Node.py:155
hasFlag(self, name)
Definition Node.py:234
clone(self, name=None)
Definition Node.py:275
_addFlagImpl(self, tag, value, flags=None, unlock=False)
Definition Node.py:182
_stragglers(self, flags, dbg="")
Definition Node.py:160
_loadflags(self, name)
Definition Node.py:136
_addFlagUnlock(self, tag, value, flags=None)
Definition Node.py:200
lock_forever(self)
Definition Node.py:120
node_flags(self)
Definition Node.py:251
node(self, name)
Definition Node.py:256
add(self, attr_name, value)
Definition Node.py:178
__getattribute__(self, name)
Definition Node.py:76
return self._root._loadflags(name) _root
Definition Node.py:86
islocked(self)
Definition Node.py:123
_fail_if_locked_forever(self)
Definition Node.py:129
raise AttributeError(f) _children
Definition Node.py:69
raise AttributeError(f) __dict__
Definition Node.py:211
_rootnode(self)
Definition Node.py:166
rename(self, name, newname)
Definition Node.py:266
delete(self, name)
Definition Node.py:260
T * get(TKey *tobj)
get a TObject* from a TKey* (why can't a TObject be a TKey?)
Definition hcg.cxx:132
-event-from-file
topnode(flags)
Definition Node.py:358
notDefined(flags, name)
Definition Node.py:316
decode_flags(flags, domain=None)
Definition Node.py:326
update_flags(flags, node, domain="")
Definition Node.py:343