diff --git a/confuse/core.py b/confuse/core.py index e8d5e8b..c088980 100644 --- a/confuse/core.py +++ b/confuse/core.py @@ -676,6 +676,24 @@ def dump(self, full: bool = True, redact: bool = False) -> str: temp_root.redactions = self.redactions out_dict = temp_root.flatten(redact=redact) + # `keys()` enumerates the sources in priority order, so keys that + # only appear in a lower-priority source (such as the defaults) are + # listed after every key of the highest-priority source. Restore the + # documented ordering by putting the keys of the default source + # first, in the order they appear there. + default_keys = [ + key for source in self.sources if source.default for key in source.keys() + ] + if default_keys: + ordered = OrderedDict() + for key in default_keys: + if key in out_dict: + ordered[key] = out_dict[key] + for key, value in out_dict.items(): + if key not in ordered: + ordered[key] = value + out_dict = ordered + yaml_out = yaml.dump( out_dict, Dumper=yaml_util.Dumper, diff --git a/test/test_dump.py b/test/test_dump.py index 4f6e265..ce1db62 100644 --- a/test/test_dump.py +++ b/test/test_dump.py @@ -63,6 +63,17 @@ def test_dump_sans_defaults(self): yaml = config.dump(full=False).strip() assert yaml == "baz: qux" + def test_dump_follows_default_key_order(self): + config = confuse.Configuration("myapp", read=False) + config.add({"foo": "bar", "baz": "qux"}) + config.sources[0].default = True + # A higher-priority source that lists the keys in a different order + # and only overrides the second one. + config.set({"baz": "override"}) + + yaml = config.dump().strip() + assert yaml == "foo: bar\nbaz: override" + class RedactTest(unittest.TestCase): def test_no_redaction(self):