From 2f85a68fb3cd0fc6aa6fac6e038e38bd1d24a617 Mon Sep 17 00:00:00 2001 From: Jonathan Zeltser Date: Wed, 13 Apr 2022 10:08:27 +0300 Subject: [PATCH 1/3] add diff config method to CfgNode --- yacs/config.py | 20 ++++++++++++++++++-- yacs/tests.py | 10 ++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/yacs/config.py b/yacs/config.py index 8068543..7654932 100644 --- a/yacs/config.py +++ b/yacs/config.py @@ -270,6 +270,22 @@ def _immutable(self, is_immutable): if isinstance(v, CfgNode): v._immutable(is_immutable) + def diff_from(self, other): + out = CfgNode() + return self._diff_from(other, out) + + def _diff_from(self, other, out): + for key in other.keys(): + if key not in self.keys(): + out[key] = other[key] + elif self[key] != other[key]: + if isinstance(other[key], CfgNode): + out[key] = CfgNode() + out[key] = self[key]._diff_from(other[key], out[key]) + else: + out[key] = other[key] + return out + def clone(self): """Recursively copy this CfgNode.""" return copy.deepcopy(self) @@ -447,7 +463,7 @@ def _decode_cfg_value(cls, value): def _valid_type(value, allow_cfg_node=False): return (type(value) in _VALID_TYPES) or ( - allow_cfg_node and isinstance(value, CfgNode) + allow_cfg_node and isinstance(value, CfgNode) ) @@ -505,7 +521,7 @@ def _check_and_coerce_cfg_value_type(replacement, original, key, full_key): # If either of them is None, allow type conversion to one of the valid types if (replacement_type == type(None) and original_type in _VALID_TYPES) or ( - original_type == type(None) and replacement_type in _VALID_TYPES + original_type == type(None) and replacement_type in _VALID_TYPES ): return replacement diff --git a/yacs/tests.py b/yacs/tests.py index 1971754..af07e3e 100644 --- a/yacs/tests.py +++ b/yacs/tests.py @@ -253,6 +253,16 @@ def test_load_cfg_from_file(self): with open(f.name, "rt") as f_read: yacs.config.load_cfg(f_read) + def test_diff_cfg(self): + # Test a change in config gives correct diff config + cfg = get_cfg() + cfg2 = cfg.clone() + cfg2.MODEL.TYPE = "a_bar_model" + expected = CN() + expected.MODEL = CN() + expected.MODEL.TYPE = "a_bar_model" + assert cfg.diff_from(cfg2) == expected + def test_load_from_python_file(self): # Case 1: exports CfgNode cfg = get_cfg() From 9f805cc38630d3f378f5e65d6bab728aa194d537 Mon Sep 17 00:00:00 2001 From: Jonathan Zeltser Date: Wed, 13 Apr 2022 10:15:11 +0300 Subject: [PATCH 2/3] fix indents --- yacs/config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yacs/config.py b/yacs/config.py index 7654932..91a84c7 100644 --- a/yacs/config.py +++ b/yacs/config.py @@ -463,7 +463,7 @@ def _decode_cfg_value(cls, value): def _valid_type(value, allow_cfg_node=False): return (type(value) in _VALID_TYPES) or ( - allow_cfg_node and isinstance(value, CfgNode) + allow_cfg_node and isinstance(value, CfgNode) ) @@ -521,7 +521,7 @@ def _check_and_coerce_cfg_value_type(replacement, original, key, full_key): # If either of them is None, allow type conversion to one of the valid types if (replacement_type == type(None) and original_type in _VALID_TYPES) or ( - original_type == type(None) and replacement_type in _VALID_TYPES + original_type == type(None) and replacement_type in _VALID_TYPES ): return replacement From 7a5d39075221639a5793fdec5663a40d9e3f79b9 Mon Sep 17 00:00:00 2001 From: Jonathan Zeltser Date: Wed, 13 Apr 2022 14:51:42 +0300 Subject: [PATCH 3/3] change to a config with three sub nodes, 'add' 'change' and 'minus' --- yacs/config.py | 13 ++++++++++--- yacs/tests.py | 12 ++++++++++-- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/yacs/config.py b/yacs/config.py index 91a84c7..d69d089 100644 --- a/yacs/config.py +++ b/yacs/config.py @@ -272,16 +272,23 @@ def _immutable(self, is_immutable): def diff_from(self, other): out = CfgNode() + out["add"] = CfgNode() + out["minus"] = CfgNode() + out["change"] = CfgNode() + return self._diff_from(other, out) def _diff_from(self, other, out): + for key in self.keys(): + if key not in other.keys(): + out.minus[key] = self[key] for key in other.keys(): if key not in self.keys(): - out[key] = other[key] + out.add[key] = other[key] elif self[key] != other[key]: if isinstance(other[key], CfgNode): - out[key] = CfgNode() - out[key] = self[key]._diff_from(other[key], out[key]) + out.change[key] = CfgNode() + out.change[key] = self[key]._diff_from(other[key], out.change[key]) else: out[key] = other[key] return out diff --git a/yacs/tests.py b/yacs/tests.py index af07e3e..fe4d131 100644 --- a/yacs/tests.py +++ b/yacs/tests.py @@ -257,10 +257,18 @@ def test_diff_cfg(self): # Test a change in config gives correct diff config cfg = get_cfg() cfg2 = cfg.clone() + cfg2.pop("NUM_GPUS") cfg2.MODEL.TYPE = "a_bar_model" + cfg2.TEST = "new_key" expected = CN() - expected.MODEL = CN() - expected.MODEL.TYPE = "a_bar_model" + expected["add"] = CN() + expected.add.TEST = "new_key" + expected["minus"] = CN() + expected.minus.NUM_GPUS = 8 + expected["change"] = CN() + expected.change.MODEL = CN() + expected.change.MODEL.TYPE = "a_bar_model" + print(cfg.diff_from(cfg2)) assert cfg.diff_from(cfg2) == expected def test_load_from_python_file(self):