Repository: jbasko/configmanager

Issue:
read_dict and to_dict names are misleading
These methods actually only work with values or defaults, although the name doesn't suggest that!
Maybe rename these to `export_values()` and `load_values()` instead?

On the other hand, the main task of the library is to provide access to config values...

#108 



Discussion:

Patch:
diff --git a/configmanager/managers.py b/configmanager/managers.py
index fdc3cbb..e395936 100644
--- a/configmanager/managers.py
+++ b/configmanager/managers.py
@@ -242,7 +242,7 @@ class Config(BaseSection):
         for path, _ in self.iter_all(recursive=recursive):
             yield path
 
-    def to_dict(self, with_defaults=True, dict_cls=dict):
+    def dump_values(self, with_defaults=True, dict_cls=dict):
         """
         Export values of all items contained in this section to a dictionary.
         
@@ -251,12 +251,12 @@ class Config(BaseSection):
             of their contents.
         
         See Also:
-            :meth:`.read_dict` does the opposite.
+            :meth:`.load_values` does the opposite.
         """
         values = dict_cls()
         for item_name, item in self._cm__configs.items():
             if is_config_section(item):
-                section_values = item.to_dict(with_defaults=with_defaults, dict_cls=dict_cls)
+                section_values = item.dump_values(with_defaults=with_defaults, dict_cls=dict_cls)
                 if section_values:
                     values[item_name] = section_values
             else:
@@ -265,7 +265,7 @@ class Config(BaseSection):
                         values[item.name] = item.value
         return values
 
-    def read_dict(self, dictionary, as_defaults=False):
+    def load_values(self, dictionary, as_defaults=False):
         """
         Import config values from a dictionary.
         
@@ -281,14 +281,14 @@ class Config(BaseSection):
             as_defaults: if ``True``, the imported values will be set as defaults.
         
         See Also:
-            :meth:`to_dict` does the opposite.
+            :meth:`dump_values` does the opposite.
         """
         for name, value in dictionary.items():
             if name not in self:
                 if as_defaults:
                     if isinstance(value, dict):
                         self[name] = self.create_section()
-                        self[name].read_dict(value, as_defaults=as_defaults)
+                        self[name].load_values(value, as_defaults=as_defaults)
                     else:
                         self[name] = self.create_item(name, default=value)
                 else:
@@ -300,7 +300,7 @@ class Config(BaseSection):
                 else:
                     self[name].value = value
             else:
-                self[name].read_dict(value, as_defaults=as_defaults)
+                self[name].load_values(value, as_defaults=as_defaults)
 
     def reset(self):
         """
diff --git a/configmanager/persistence.py b/configmanager/persistence.py
index 7cdab00..3c315b8 100644
--- a/configmanager/persistence.py
+++ b/configmanager/persistence.py
@@ -72,7 +72,7 @@ class JsonReaderWriter(ConfigReaderWriter):
         # the string we are trying to write is not unicode in Python 2
         # because we open files with encoding=utf-8.
         result = self.json.dumps(
-            config.to_dict(with_defaults=with_defaults, dict_cls=collections.OrderedDict),
+            config.dump_values(with_defaults=with_defaults, dict_cls=collections.OrderedDict),
             ensure_ascii=False,
             indent=2,
             **kwargs
@@ -83,13 +83,13 @@ class JsonReaderWriter(ConfigReaderWriter):
             return result
 
     def load_config_from_file(self, config, file_obj, as_defaults=False, **kwargs):
-        config.read_dict(
+        config.load_values(
             self.json.load(file_obj, object_pairs_hook=collections.OrderedDict, **kwargs),
             as_defaults=as_defaults,
         )
 
     def load_config_from_string(self, config, string, as_defaults=False, **kwargs):
-        config.read_dict(
+        config.load_values(
             self.json.loads(string, object_pairs_hook=collections.OrderedDict, **kwargs),
             as_defaults=as_defaults,
         )
@@ -103,16 +103,16 @@ class YamlReaderWriter(ConfigReaderWriter):
         self.yaml = yaml
 
     def dump_config_to_file(self, config, file_obj, with_defaults=False, **kwargs):
-        self.yaml.dump(config.to_dict(with_defaults=with_defaults), file_obj, **kwargs)
+        self.yaml.dump(config.dump_values(with_defaults=with_defaults), file_obj, **kwargs)
 
     def dump_config_to_string(self, config, with_defaults=False, **kwargs):
-        return self.yaml.dump(config.to_dict(with_defaults=with_defaults), **kwargs)
+        return self.yaml.dump(config.dump_values(with_defaults=with_defaults), **kwargs)
 
     def load_config_from_file(self, config, file_obj, as_defaults=False, **kwargs):
-        config.read_dict(self.yaml.load(file_obj, **kwargs), as_defaults=as_defaults)
+        config.load_values(self.yaml.load(file_obj, **kwargs), as_defaults=as_defaults)
 
     def load_config_from_string(self, config, string, as_defaults=False, **kwargs):
-        config.read_dict(self.yaml.load(string, **kwargs), as_defaults=as_defaults)
+        config.load_values(self.yaml.load(string, **kwargs), as_defaults=as_defaults)
 
 
 class ConfigParserReaderWriter(ConfigReaderWriter):
diff --git a/docs/quickstart.rst.inc b/docs/quickstart.rst.inc
index 5115ce4..d8eb4c1 100644
--- a/docs/quickstart.rst.inc
+++ b/docs/quickstart.rst.inc
@@ -29,7 +29,7 @@ Quick Start
     >>> config.greeting.value
     'Hello, world!'
 
-    >>> config.to_dict()
+    >>> config.dump_values()
     {'greeting': 'Hello, world!'}
 
 5. Change config values. ::
@@ -42,11 +42,11 @@ Quick Start
      '_type': str,
      '_value': 'Hey!'}
 
-    >>> config.read_dict({'greeting': 'Good evening!'})
+    >>> config.load_values({'greeting': 'Good evening!'})
     >>> config.greeting.value
     'Good evening!'
 
-    >>> config.to_dict()
+    >>> config.dump_values()
     {'greeting': 'Good evening!'}
 
 6. Persist the configuration. ::


Test patch:
diff --git a/tests/test_config.py b/tests/test_config.py
index 0a5b7fb..b784044 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -62,7 +62,7 @@ def test_assigning_nameless_item_directly_to_config_should_set_its_name():
     config.dummy['y'] = Item(default=True)
     assert config.dummy.y.name == 'y'
 
-    assert config.to_dict() == {'dummy': {'x': 5, 'y': True}}
+    assert config.dump_values() == {'dummy': {'x': 5, 'y': True}}
 
 
 def test_assigning_item_with_name_directly_to_config_should_preserve_its_name():
@@ -83,7 +83,7 @@ def test_assigning_item_with_name_directly_to_config_should_preserve_its_name():
     assert config.dummy.w.name == 'www'
     assert config.dummy.www.name == 'www'
 
-    assert config.to_dict() == {'dummy': {'a.b': 'AB', 'www': 6}}
+    assert config.dump_values() == {'dummy': {'a.b': 'AB', 'www': 6}}
 
     all_dummies = list(config.dummy.iter_items())
     assert len(all_dummies) == 2
@@ -117,18 +117,18 @@ def test_section_name_must_be_a_string():
 
 def test_to_dict_should_not_include_items_with_no_usable_value():
     config = Config()
-    assert config.to_dict() == {}
+    assert config.dump_values() == {}
 
     config.a = Item()
     config.b = Item()
     config.dummies = Config({'x': Item(), 'y': Item()})
-    assert config.to_dict() == {}
+    assert config.dump_values() == {}
 
     config.dummies.x.value = 'yes'
-    assert config.to_dict() == {'dummies': {'x': 'yes'}}
+    assert config.dump_values() == {'dummies': {'x': 'yes'}}
 
     config.b.value = 'no'
-    assert config.to_dict() == {'dummies': {'x': 'yes'}, 'b': 'no'}
+    assert config.dump_values() == {'dummies': {'x': 'yes'}, 'b': 'no'}
 
 
 def test_read_dict_recursively_loads_values_from_a_dictionary():
@@ -150,13 +150,13 @@ def test_read_dict_recursively_loads_values_from_a_dictionary():
     assert config.a.x.value == 0
     assert config.a.y.value is True
 
-    config.read_dict({
+    config.load_values({
         'a': {'x': '5', 'y': 'no'},
     })
     assert config.a.x.value == 5
     assert config.a.y.value is False
 
-    config.b.c.read_dict({
+    config.b.c.load_values({
         'e': 'haha',  # will be ignored
         'd': {'x': 'XXX'},
     })
@@ -168,7 +168,7 @@ def test_read_dict_as_defaults_loads_default_values_from_a_dictionary():
     config = Config()
 
     # both will be ignored
-    config.read_dict({
+    config.load_values({
         'a': 5,
         'b': True,
     })
@@ -177,7 +177,7 @@ def test_read_dict_as_defaults_loads_default_values_from_a_dictionary():
     assert 'b' not in config
 
     # both will be added
-    config.read_dict({
+    config.load_values({
         'a': 5,
         'b': True,
     }, as_defaults=True)
@@ -269,7 +269,7 @@ def test_allows_iteration_over_all_items(mixed_app_config):
     formatters_items = list(config['logging']['formatters'].iter_items(recursive=True))
     assert len(formatters_items) == 2
 
-    formatters = config['logging']['formatters'].to_dict()
+    formatters = config['logging']['formatters'].dump_values()
     assert formatters['plain'] == {'format': '%(message)s'}
 
 
@@ -356,7 +356,7 @@ def test_forbids_accidental_item_overwrite_via_setattr(mixed_app_config):
 def test_to_dict(mixed_app_config, raw_db_config, raw_logging_config):
     config = mixed_app_config
 
-    config_dict = config.to_dict()
+    config_dict = config.dump_values()
 
     assert isinstance(config_dict, dict)
 
@@ -405,7 +405,7 @@ def test_can_have_a_dict_as_a_config_value_if_wrapped_inside_item():
     # value, not the real thing.
     config.aws.value['secret_key'] = 'NEW_SECRET'
 
-    assert config.to_dict()['aws'] == {'access_key': '123', 'secret_key': 'secret'}
+    assert config.dump_values()['aws'] == {'access_key': '123', 'secret_key': 'secret'}
 
 
 def test_len_of_config_returns_number_of_items_and_sections_in_current_level():
@@ -482,7 +482,7 @@ def test_config_item_value_can_be_unicode_str(tmpdir):
     config2 = Config({'greeting': '', 'name': ''})
     config2.configparser.load(path)
     assert config2.name.value == u'Jānis Bērziņš'
-    assert config1.to_dict(with_defaults=True) == config2.to_dict(with_defaults=True)
+    assert config1.dump_values(with_defaults=True) == config2.dump_values(with_defaults=True)
 
 
 def test_config_of_config_is_a_deep_copy_of_original_config():
@@ -491,13 +491,13 @@ def test_config_of_config_is_a_deep_copy_of_original_config():
 
     config2 = Config(config1)
     assert config1 is not config2
-    assert config1.to_dict() == config2.to_dict()
-    assert config1.to_dict(with_defaults=True) == config2.to_dict(with_defaults=True)
+    assert config1.dump_values() == config2.dump_values()
+    assert config1.dump_values(with_defaults=True) == config2.dump_values(with_defaults=True)
 
     config1.uploads.enabled.value = True
-    config1.uploads.db.read_dict({'user': 'admin'})
+    config1.uploads.db.load_values({'user': 'admin'})
 
-    assert config2.to_dict(with_defaults=True) == {'uploads': {'enabled': False, 'db': {'user': 'root'}}}
+    assert config2.dump_values(with_defaults=True) == {'uploads': {'enabled': False, 'db': {'user': 'root'}}}
 
     config2.uploads.db.user.default = 'default-user'
     assert config1.uploads.db.user.default == 'root'
diff --git a/tests/test_config_declaration_parser.py b/tests/test_config_declaration_parser.py
index 3c76934..4a9ff1e 100644
--- a/tests/test_config_declaration_parser.py
+++ b/tests/test_config_declaration_parser.py
@@ -119,19 +119,19 @@ def test_class_based_config_declaration(app_config_cls_example):
 def test_dict_based_config_declaration(app_config_dict_example, app_config_cls_example):
     dict_tree = Config(app_config_dict_example)
     cls_tree = Config(app_config_cls_example)
-    assert dict_tree.to_dict() == cls_tree.to_dict()
+    assert dict_tree.dump_values() == cls_tree.dump_values()
 
 
 def test_module_based_config_declaration(app_config_module_example, app_config_cls_example):
     module_tree = Config(app_config_module_example)
     cls_tree = Config(app_config_cls_example)
-    assert module_tree.to_dict() == cls_tree.to_dict()
+    assert module_tree.dump_values() == cls_tree.dump_values()
 
 
 def test_mixed_config_declaration(app_config_mixed_example, app_config_cls_example):
     mixed_tree = Config(app_config_mixed_example)
     cls_tree = Config(app_config_cls_example)
-    assert mixed_tree.to_dict() == cls_tree.to_dict()
+    assert mixed_tree.dump_values() == cls_tree.dump_values()
 
 
 def test_default_value_is_deep_copied():
diff --git a/tests/test_configparser.py b/tests/test_configparser.py
index 5e3bd27..70cf393 100644
--- a/tests/test_configparser.py
+++ b/tests/test_configparser.py
@@ -44,7 +44,7 @@ def test_reads_empty_config_from_file_obj(simple_config, empty_config_file):
     with open(empty_config_file) as f:
         simple_config.configparser.load(f)
 
-    assert simple_config.to_dict() == {
+    assert simple_config.dump_values() == {
         'simple': {
             'str': '',
             'int': 0,
@@ -60,7 +60,7 @@ def test_reads_simple_config_from_file_obj(simple_config, simple_config_file):
     with open(simple_config_file) as f:
         simple_config.configparser.load(f)
 
-    assert simple_config.to_dict() == {
+    assert simple_config.dump_values() == {
         'simple': {
             'str': 'hello',
             'int': 5,
@@ -282,4 +282,4 @@ def test_writes_to_and_reads_from_default_section_transparently(tmpdir):
     config2 = Config()
     config2.configparser.load(config_ini, as_defaults=True)
 
-    assert config1.to_dict() == config2.to_dict() == {'greeting': 'Hello', 'name': 'World'}
+    assert config1.dump_values() == config2.dump_values() == {'greeting': 'Hello', 'name': 'World'}
diff --git a/tests/test_json.py b/tests/test_json.py
index 6103beb..39bd306 100644
--- a/tests/test_json.py
+++ b/tests/test_json.py
@@ -44,8 +44,8 @@ def test_json_read_and_write(defaults_json_path, user_json_path):
     with open(defaults_json_path) as f:
         c3.json.load(f, as_defaults=True)
 
-    assert c1.to_dict(with_defaults=False) == {}
-    assert c1.to_dict(with_defaults=True) == {
+    assert c1.dump_values(with_defaults=False) == {}
+    assert c1.dump_values(with_defaults=True) == {
         'uploads': {
             'threads': 1,
             'enabled': False,
@@ -53,20 +53,20 @@ def test_json_read_and_write(defaults_json_path, user_json_path):
         }
     }
 
-    assert c1.to_dict() == c2.to_dict() == c3.to_dict()
+    assert c1.dump_values() == c2.dump_values() == c3.dump_values()
 
     c1.json.load(user_json_path)
     c2.json.load([user_json_path])
     with open(user_json_path) as f:
         c3.json.load(f)
 
-    assert c1.to_dict(with_defaults=False) == {
+    assert c1.dump_values(with_defaults=False) == {
         'uploads': {
             'threads': 5,
             'enabled': True,
         }
     }
-    assert c1.to_dict() == c2.to_dict() == c3.to_dict()
+    assert c1.dump_values() == c2.dump_values() == c3.dump_values()
 
     updates = {
         'uploads': {
@@ -75,23 +75,23 @@ def test_json_read_and_write(defaults_json_path, user_json_path):
         }
     }
 
-    c1.read_dict(updates)
-    c2.read_dict(updates)
-    c3.read_dict(updates)
+    c1.load_values(updates)
+    c2.load_values(updates)
+    c3.load_values(updates)
 
-    assert c1.to_dict() == c2.to_dict() == c3.to_dict()
+    assert c1.dump_values() == c2.dump_values() == c3.dump_values()
 
     c1.json.dump(user_json_path)
     c2.json.load(user_json_path)
-    assert c1.to_dict() == c2.to_dict() == c3.to_dict()
+    assert c1.dump_values() == c2.dump_values() == c3.dump_values()
 
-    assert c1.to_dict() == c2.to_dict() == c3.to_dict()
+    assert c1.dump_values() == c2.dump_values() == c3.dump_values()
 
     with open(user_json_path, 'w') as f:
         c2.json.dump(f)
     c1.json.load(user_json_path)
 
-    assert c1.to_dict() == c2.to_dict() == c3.to_dict()
+    assert c1.dump_values() == c2.dump_values() == c3.dump_values()
 
 
 def test_json_writes_with_defaults_false_by_default(user_json_path):
@@ -121,13 +121,13 @@ def test_json_reads_and_writes_strings():
     assert c.json.dumps(with_defaults=True) == '{\n  "greeting": "Hello"\n}'
 
     c.json.loads('{"something_nonexistent": 1}')
-    assert c.to_dict() == {'greeting': 'Hello'}
+    assert c.dump_values() == {'greeting': 'Hello'}
 
     c.json.loads('{"something_nonexistent": 1}', as_defaults=True)
-    assert c.to_dict() == {'greeting': 'Hello', 'something_nonexistent': 1}
+    assert c.dump_values() == {'greeting': 'Hello', 'something_nonexistent': 1}
 
     c.json.loads('{"greeting": "Hello, world!"}')
-    assert c.to_dict() == {'greeting': 'Hello, world!', 'something_nonexistent': 1}
+    assert c.dump_values() == {'greeting': 'Hello, world!', 'something_nonexistent': 1}
 
 
 def test_json_reads_and_writes_preserve_order(tmpdir):
diff --git a/tests/test_v1.py b/tests/test_v1.py
index 6824e04..70b1209 100644
--- a/tests/test_v1.py
+++ b/tests/test_v1.py
@@ -41,7 +41,7 @@ def test_simple_config():
     assert config.greeting.default == 'Hello, world!'
 
     # Can export all values to a dictionary
-    assert config.to_dict() == {
+    assert config.dump_values() == {
         'greeting': 'Good evening!',
         'threads': 1,
         'throttling_enabled': False,
@@ -120,7 +120,7 @@ def test_nested_config():
     # You can also change values by reading them from a dictionary.
     # Unknown names will be ignored unless you pass as_defaults=True
     # but in that case you will overwrite any previously existing items.
-    config.read_dict({'greeting': 'Good morning!', 'comments': {'enabled': False}})
+    config.load_values({'greeting': 'Good morning!', 'comments': {'enabled': False}})
     assert config.greeting.value == 'Good morning!'
     assert 'comments' not in config
 
@@ -137,11 +137,11 @@ def test_nested_config():
     assert all[('server', 'port')] is config.server.port
 
     # Export all values
-    config_dict = config.to_dict()
+    config_dict = config.dump_values()
     assert config_dict['db'] == {'host': 'localhost', 'user': 'root', 'password': 'secret'}
 
     # Each section is a Config instance too, so you can export those separately too:
-    assert config.server.to_dict() == config_dict['server']
+    assert config.server.dump_values() == config_dict['server']
 
     # You can reset individual items to their default values
     assert config.db.user.value == 'root'
@@ -190,8 +190,7 @@ def test_configparser_integration(tmpdir):
     # configuration without specifying as_defaults=True:
     config.configparser.load(custom_ini_path)
 
-    # other ConfigParser-like methods such as read_dict, loads, read_file are provided too.
-    # when you are done setting config values, you can dump them to file too.
+    # when you are done setting config values, you can write them to a file.
     config.configparser.dump(custom_ini_path)
 
     # Note that default values won't be written unless you explicitly request it
diff --git a/tests/test_yaml.py b/tests/test_yaml.py
index 661ccc2..56b9fbc 100644
--- a/tests/test_yaml.py
+++ b/tests/test_yaml.py
@@ -18,16 +18,16 @@ def test_config_written_to_and_read_from_yaml_file(yaml_path1):
             },
         },
     })
-    original_values = config.to_dict()
+    original_values = config.dump_values()
 
     config.yaml.dump(yaml_path1, with_defaults=True)
 
     config.yaml.load(yaml_path1)
-    assert config.to_dict() == original_values
+    assert config.dump_values() == original_values
 
     config2 = Config()
     config2.yaml.load(yaml_path1, as_defaults=True)
-    assert config2.to_dict() == original_values
+    assert config2.dump_values() == original_values
 
 
 def test_config_written_to_and_read_from_yaml_string():
@@ -42,7 +42,7 @@ def test_config_written_to_and_read_from_yaml_string():
     config = Config()
     config.yaml.loads(config_str, as_defaults=True)
 
-    assert config.to_dict() == {
+    assert config.dump_values() == {
         'uploads': {
             'enabled': True,
             'threads': 5,
@@ -56,4 +56,4 @@ def test_config_written_to_and_read_from_yaml_string():
 
     config2 = Config()
     config2.yaml.loads(config_str2, as_defaults=True)
-    assert config2.to_dict() == config.to_dict()
+    assert config2.dump_values() == config.dump_values()