Re: [PATCH net-next v1 02/12] tools/net/ynl: Support sub-messages in nested attribute spaces
From: Alessandro Marcolini <hidden>
Date: 2024-01-27 18:51:33
Also in:
linux-doc
On 1/27/24 18:18, Donald Hunter wrote:
Jakub Kicinski [off-list ref] writes:quoted
Is it possible to check at which "level" of the chainmap the key was found? If so we can also construct a 'chainmap of attr sets' and make sure that the key level == attr set level. I.e. that we got a hit at the first level which declares a key of that name. More crude option - we could construct a list of dicts (the levels within the chainmap) and keys they can't contain. Once we got a hit for a sub-message key at level A, all dicts currently on top of A are not allowed to add that key. Once we're done with the message we scan thru the list and make sure the keys haven't appeared? Another random thought, should we mark the keys which can "descend" somehow? IDK, put a ~ in front? selector: ~kind or some other char?Okay, so I think the behaviour we need is to either search current scope or search the outermost scope. My suggestion would be to replace the ChainMap approach with just choosing between current and outermost scope. The unusual case is needing to search the outermost scope so using a prefix e.g. '/' for that would work. We can have 'selector: kind' continue to refer to current scope and then have 'selector: /kind' refer to the outermost scope. If we run into a case that requires something other than current or outermost then we could add e.g. '../kind' so that the scope to search is always explicitly identified.
Wouldn't add different chars in front of the selctor value be confusing?
IMHO the solution of using a ChainMap with levels could be an easier solution. We could just modify the __getitem__() method to output both the value and the level, and the get() method to add the chance to specify a level (in our case the level found in the spec) and error out if the specified level doesn't match with the found one. Something like this:
from collections import ChainMap
class LevelChainMap(ChainMap):
def __getitem__(self, key):
for mapping in self.maps:
try:
return mapping[key], self.maps[::-1].index(mapping)
except KeyError:
pass
return self.__missing__(key)
def get(self, key, default=None, level=None):
val, lvl = self[key] if key in self else (default, None)
if level:
if lvl != level:
raise Exception("Level mismatch")
return val, lvl
# example usage
c = LevelChainMap({'a':1}, {'inner':{'a':1}}, {'outer': {'inner':{'a':1}}})
print(c.get('a', level=2))
print(c.get('a', level=1)) #raise err
This will leave the spec as it is and will require small changes.
What do you think?