From 8c9115b574702df41c72e04b4392e41bdc321f5c Mon Sep 17 00:00:00 2001 From: mik2k2 <44849223+mik2k2@users.noreply.github.com> Date: Fri, 27 Mar 2020 20:51:04 +0100 Subject: [PATCH 1/3] add optional sections --- src/configobj/__init__.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/configobj/__init__.py b/src/configobj/__init__.py index 928c208..b342d58 100644 --- a/src/configobj/__init__.py +++ b/src/configobj/__init__.py @@ -1931,6 +1931,13 @@ def _set_configspec(self, section, copy): for entry in configspec.sections: if entry == '__many__': continue + if entry.startswith('__optional__'): + configspec_entry = configspec[entry] + entry = entry[len('__optional__'):] + if entry not in section: + continue + else: + configspec_entry = configspec[entry] if entry not in section: section[entry] = {} section[entry]._created = True @@ -1941,7 +1948,7 @@ def _set_configspec(self, section, copy): # Could be a scalar when we expect a section if isinstance(section[entry], Section): - section[entry].configspec = configspec[entry] + section[entry].configspec = configspec_entry def _write_line(self, indent_string, entry, this_entry, comment): From e803fa026e1625e4ddd51de0ea7c2eb9baf0d049 Mon Sep 17 00:00:00 2001 From: mik2k2 <44849223+mik2k2@users.noreply.github.com> Date: Fri, 27 Mar 2020 20:51:57 +0100 Subject: [PATCH 2/3] add test for optional sections --- src/tests/test_configobj.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/tests/test_configobj.py b/src/tests/test_configobj.py index c72e364..3a35fea 100644 --- a/src/tests/test_configobj.py +++ b/src/tests/test_configobj.py @@ -1412,3 +1412,16 @@ def test_writing_out_dict_value_with_unrepr(self): c = ConfigObj(cfg, unrepr=True) assert repr(c) == "ConfigObj({'thing': {'a': 1}})" assert c.write() == ["thing = {'a': 1}"] + + +def test_optional_sections(): + c = ConfigObj({'sec': {'name': 'value'}}, configspec={'__optional__sec': {'name': 'string'}}) + assert c.validate(Validator()) == True + c = ConfigObj({'unrelated sec': {}}, + configspec={'unrelated sec': {}, + '__optional__sec': {'some name': 'string', + 'subsec': {}, + 'something else': 'integer'}}) + assert c.validate(Validator()) == True + c = ConfigObj({'sec': {'name': 'a string'}}, configspec={'__optional__sec': {'name': 'integer'}}) + assert c.validate(Validator()) != True From af87d35d8a11961868e3441a6539d5ae8f9e5b0c Mon Sep 17 00:00:00 2001 From: mik2k2 <44849223+mik2k2@users.noreply.github.com> Date: Fri, 27 Mar 2020 20:52:31 +0100 Subject: [PATCH 3/3] add documentation for optional sections --- docs/configobj.rst | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/docs/configobj.rst b/docs/configobj.rst index e183b98..549a634 100644 --- a/docs/configobj.rst +++ b/docs/configobj.rst @@ -683,6 +683,17 @@ have an explicit configspec. See `Repeated Sections`_ for examples. +Mentioning Optional Sections +############################ + +It is possible to mark sections as optional. This is done by prepending +``__optional__`` to the section name. If the optional section is present, +it can be accessed normally (without the ``__optional__`` prefix). +If it is not present, it won't show up in the validated ``ConfigObj``. + +See `Optional Sections`_ for examples. + + Mentioning SimpleVal #################### @@ -1815,6 +1826,30 @@ If you want to use repeated values alongside repeated sections you can call one other ``___many___`` (with three underscores). +Optional Sections +----------------- + +Optional sections are sections that have to either be fully present, +or not be in the file at all. This is useful for components that +require configuration but aren't required. As opposed to single +values, these can't just have a default of ``None``. + +An optional section is created by prepending ``__optional__`` to the +section name in the configspec. When an optional section is present, +it is validated normally. If it it not present, validation ignores the +part of the configspec describing the section. + +For example, we might be configuring a planet. It has some attributes and +*might* have a human settlement. If it has one, we want it to be validated +according to a spec. To do this, we make the ``settlement`` section optional :: + + [planet] + mass = integer + radius = integer + [[__optional__settlement]] + inhabitants = integer + name = string + Copy Mode ---------