summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Estabrook <jestabro@vyos.io>2023-07-04 19:09:04 -0500
committerJohn Estabrook <jestabro@vyos.io>2023-07-07 08:33:51 -0500
commit4c4a2ddd4cc0ac2dc6e3908c1480c017e9dab3e5 (patch)
treede0229cbb74f6d321f11f022fcfbaa546feddf79
parent38a8bcfc407fefea357dfbb5f337562593a265b5 (diff)
downloadvyos-1x-4c4a2ddd4cc0ac2dc6e3908c1480c017e9dab3e5.tar.gz
vyos-1x-4c4a2ddd4cc0ac2dc6e3908c1480c017e9dab3e5.zip
config: T5330: retain information of internal _dict_merge
-rw-r--r--python/vyos/xml_ref/__init__.py3
-rw-r--r--python/vyos/xml_ref/definition.py49
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