From d07cf9949bbdcfea823b636e2ae98c32027ad0bd Mon Sep 17 00:00:00 2001 From: Timmy Welch Date: Sun, 19 Feb 2023 18:07:14 -0800 Subject: [PATCH] Allow adding settings to existing groups Calling add_group or add_persistent_group twice will add any new settings defined. Raise a ValueError if add_group or add_persistent_group is called during a call to add_group or add_persistent_group. --- settngs.py | 20 +++++++++++++++++--- tests/settngs_test.py | 41 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/settngs.py b/settngs.py index 0d2c3b1..d43d9cf 100644 --- a/settngs.py +++ b/settngs.py @@ -150,6 +150,11 @@ class Setting: def __repr__(self) -> str: # pragma: no cover return self.__str__() + def __eq__(self, other: object) -> bool: + if not isinstance(other, Setting): + return NotImplemented + return self.__dict__ == other.__dict__ + def get_dest(self, prefix: str, names: Sequence[str], dest: str | None) -> tuple[str, str, bool]: dest_name = None flag = False @@ -496,13 +501,15 @@ class Manager: def add_group(self, name: str, group: Callable[[Manager], None], exclusive_group: bool = False) -> None: """ - The primary way to add define options on this class + The primary way to add define options on this class. Args: name: The name of the group to define group: A function that registers individual options using :meth:`add_setting` exclusive_group: If this group is an argparse exclusive group """ + if self.current_group_name != '': + raise ValueError('Sub groups are not allowed') self.current_group_name = name self.exclusive_group = exclusive_group group(self) @@ -511,16 +518,23 @@ class Manager: def add_persistent_group(self, name: str, group: Callable[[Manager], None], exclusive_group: bool = False) -> None: """ - The primary way to add define options on this class + The primary way to add define options on this class. + This group allows existing values to persist even if there is no corresponding setting defined for it. Args: name: The name of the group to define group: A function that registers individual options using :meth:`add_setting` exclusive_group: If this group is an argparse exclusive group """ + if self.current_group_name != '': + raise ValueError('Sub groups are not allowed') self.current_group_name = name self.exclusive_group = exclusive_group - self.definitions[self.current_group_name] = Group(True, {}) + if self.current_group_name in self.definitions: + if not self.definitions[self.current_group_name].persistent: + raise ValueError('Group already existis and is not persistent') + else: + self.definitions[self.current_group_name] = Group(True, {}) group(self) self.current_group_name = '' self.exclusive_group = False diff --git a/tests/settngs_test.py b/tests/settngs_test.py index bc696b3..81b7ae2 100644 --- a/tests/settngs_test.py +++ b/tests/settngs_test.py @@ -2,6 +2,7 @@ from __future__ import annotations import argparse import json +from collections import defaultdict import pytest @@ -141,7 +142,7 @@ def test_clean_config(settngs_manager): assert cleaned['persistent']['hello'] == 'success' -def test_parse_cmdline(settngs_manager, tmp_path): +def test_parse_cmdline(settngs_manager): settngs_manager.add_group('tst', lambda parser: parser.add_setting('--test', default='hello', cmdline=True)) normalized, _ = settngs_manager.parse_cmdline(['--test', 'success']) @@ -150,7 +151,7 @@ def test_parse_cmdline(settngs_manager, tmp_path): assert normalized['tst']['test'] == 'success' -def test_parse_cmdline_with_namespace(settngs_manager, tmp_path): +def test_parse_cmdline_with_namespace(settngs_manager): settngs_manager.add_group('tst', lambda parser: parser.add_setting('--test', default='hello', cmdline=True)) normalized, _ = settngs_manager.parse_cmdline( @@ -286,6 +287,42 @@ def test_cli_explicit_default(settngs_manager, tmp_path): assert normalized['tst']['test'] == 'success' +def test_adding_to_existing_group(settngs_manager, tmp_path): + def default_to_regular(d): + if isinstance(d, defaultdict): + d = {k: default_to_regular(v) for k, v in d.items()} + return d + settngs_manager.add_group('tst', lambda parser: parser.add_setting('--test', default='success')) + settngs_manager.add_group('tst', lambda parser: parser.add_setting('--test2', default='success')) + + def tst(parser): + parser.add_setting('--test', default='success') + parser.add_setting('--test2', default='success') + + settngs_manager2 = settngs.Manager() + settngs_manager2.add_group('tst', tst) + + assert default_to_regular(settngs_manager.definitions) == default_to_regular(settngs_manager2.definitions) + + +def test_adding_to_existing_persistent_group(settngs_manager, tmp_path): + def default_to_regular(d): + if isinstance(d, defaultdict): + d = {k: default_to_regular(v) for k, v in d.items()} + return d + settngs_manager.add_persistent_group('tst', lambda parser: parser.add_setting('--test', default='success')) + settngs_manager.add_persistent_group('tst', lambda parser: parser.add_setting('--test2', default='success')) + + def tst(parser): + parser.add_setting('--test', default='success') + parser.add_setting('--test2', default='success') + + settngs_manager2 = settngs.Manager() + settngs_manager2.add_persistent_group('tst', tst) + + assert default_to_regular(settngs_manager.definitions) == default_to_regular(settngs_manager2.definitions) + + def test_example(capsys, tmp_path, monkeypatch): monkeypatch.chdir(tmp_path) settings_file = tmp_path / 'settings.json'