diff options
| -rw-r--r-- | python/vyos/xml_ref/__init__.py | 3 | ||||
| -rw-r--r-- | python/vyos/xml_ref/definition.py | 49 | 
2 files changed, 43 insertions, 9 deletions
| diff --git a/python/vyos/xml_ref/__init__.py b/python/vyos/xml_ref/__init__.py index 62d3680a1..ad2130dca 100644 --- a/python/vyos/xml_ref/__init__.py +++ b/python/vyos/xml_ref/__init__.py @@ -48,6 +48,9 @@ def is_leaf(path: list) -> bool:  def cli_defined(path: list, node: str, non_local=False) -> bool:      return load_reference().cli_defined(path, node, non_local=non_local) +def from_source(d: dict, path: list) -> bool: +    return load_reference().from_source(d, path) +  def component_version() -> dict:      return load_reference().component_version() diff --git a/python/vyos/xml_ref/definition.py b/python/vyos/xml_ref/definition.py index 33a49ca69..2ff53ee46 100644 --- a/python/vyos/xml_ref/definition.py +++ b/python/vyos/xml_ref/definition.py @@ -13,7 +13,12 @@  # You should have received a copy of the GNU Lesser General Public License  # along with this library.  If not, see <http://www.gnu.org/licenses/>. -from typing import Optional, Union, Any +from typing import Optional, Union, Any, TYPE_CHECKING + +# https://peps.python.org/pep-0484/#forward-references +# for type 'ConfigDict' +if TYPE_CHECKING: +    from vyos.config import ConfigDict  class Xml:      def __init__(self): @@ -210,19 +215,42 @@ class Xml:              return False          return True +    def _set_source_recursive(self, o: Union[dict, str, list], b: bool): +        d = {} +        if not isinstance(o, dict): +            d = {'_source': b} +        else: +            for k, v in o.items(): +                d[k] = self._set_source_recursive(v, b) +            d |= {'_source': b} +        return d +      # use local copy of function in module configdict, to avoid circular      # import +    # +    # extend dict_merge to keep track of keys only in source      def _dict_merge(self, source, destination):          from copy import deepcopy -        tmp = deepcopy(destination) +        dest = deepcopy(destination) +        from_source = {}          for key, value in source.items(): -            if key not in tmp: -                tmp[key] = value +            if key not in dest: +                dest[key] = value +                from_source[key] = self._set_source_recursive(value, True)              elif isinstance(source[key], dict): -                tmp[key] = self._dict_merge(source[key], tmp[key]) +                dest[key], f = self._dict_merge(source[key], dest[key]) +                f |= {'_source': False} +                from_source[key] = f + +        return dest, from_source -        return tmp +    def from_source(self, d: dict, path: list) -> bool: +        for key in path: +            d  = d[key] if key in d else {} +            if not d or not isinstance(d, dict): +                return False +        return d.get('_source', False)      def _relative_defaults(self, rpath: list, conf: dict, recursive=False) -> dict:          res: dict = {} @@ -264,13 +292,16 @@ class Xml:          return res -    def merge_defaults(self, path: list, conf: dict, get_first_key=False, -                       recursive=False) -> dict: +    def merge_defaults(self, path: list, conf: Union[dict, 'ConfigDict'], +                       get_first_key=False, recursive=False) -> dict:          """Return config dict with defaults non-destructively merged          This merges non-recursive defaults relative to the config dict.          """          d = self.relative_defaults(path, conf, get_first_key=get_first_key,                                     recursive=recursive) -        d = self._dict_merge(d, conf) +        d, f = self._dict_merge(d, conf) +        d = type(conf)(d) +        if hasattr(d, '_from_defaults'): +            setattr(d, '_from_defaults', f)          return d | 
