It was recently agreed that on configuration, only interface changes should be applied (vs reapplying the whole interface configuration). This change should be quite straight forward (there may be a number of option dependencies when one value change requires other to be re-applied) but one of the must-have requirement is that code must now track the keys which have been set in the configuration.
Looking at how it could be implemented, I noticed that the interface configuration code has been changed to use jmespath.search
The api of the call is indeed more pleasant that using raw python dict:
tmp = jmespath.search('ipv6.address.no_default_link_local', config)
vs
tmp = config.get('ipv6', {}).get('address', {}).get('no_default_link_local', None)
I think this "." notation for the key is nice and could be adopted. This also goes toward showing that some helper code is required to improve configuration handling and make the code of the conf mode extension clearer and easier.
Reading the jmespath code, it is not really optimised for speed but for flexibility. Also, very few features are used, only the search function. The same result could be achieved with a dedicated helper function performing this dict walk instead
Something like (code not tested, here for demonstration)
def search (config, path): parts = path.split('.') inside = parts[:-1] if not inside: return config[path] c = config for p in parts[:-1]: c = c.get(p, {}) return c.get(parts[-1], None)
As we have a need to track which keys are changed, this function could be converted to be a method of ConfModeDict (a subclass of dict) and renamed get.
This would not break/change any of the current code using the dict without the nested search feature.
This would also have the side effect to allow to better typo detection. Should a typo then occur on a key, this could be better caught at runtime and reported with an exception.
Currently, as the dict already contains the default value, should a typo occur, the default value is returned instead.
Subclassing dict would also allow intercepting __setitem__ (the under-the-hood function assigning a value to a dict when [] = is used). __delitem__ would also need to be redefined (as it would mean that any set value was unset). It would then be possible to know if a value was set or not and get a list of only the values which were set.
The class could also contain the "defaults" inside, allowing to compare any set value with its default and not returning it, if the value explicitly set is still the default.
The API I would suggest for this class would be something like this (suggestion only until real implementation helps with details):
class ConfModeDict(dict): def __init__(self, defaults): self._configured = dict([(k,False) for k in defaults]) self._defaults = deepcopy(defaults) def get(self, key, default=False): def configured(self, include_defaults=False): # return a list keys which were configured, if True include values even if what was configured is the default # to allow the interception when data is set in the dict def __setitems__(self, k, v): def __delitems__(self, k):
if implementated for the interface code, this change new class could easily be used in place of the current dict. Only the setup of the dict would have to change, using it more widely should not break any of the conf modules.
The documentation would "only" be updated to let users know that ConfModeDict should be used when writing conf code, but this transition can be progressive.