Make get_namespace consistent with normalize_config
This commit is contained in:
parent
14357c14de
commit
e57ee25a60
@ -294,6 +294,9 @@ def normalize_config(
|
||||
persistent: Include unknown keys in persistent groups
|
||||
"""
|
||||
|
||||
if not file and not cmdline:
|
||||
raise ValueError('Invalid parameters: you must set either file or cmdline to True')
|
||||
|
||||
normalized: Values = {}
|
||||
options, definitions = config
|
||||
for group_name, group in definitions.items():
|
||||
@ -366,7 +369,9 @@ def defaults(definitions: Definitions) -> Config[Values]:
|
||||
return normalize_config(Config(Namespace(), definitions), file=True, cmdline=True)
|
||||
|
||||
|
||||
def get_namespace(config: Config[T], defaults: bool = True, persistent: bool = True) -> Config[Namespace]:
|
||||
def get_namespace(
|
||||
config: Config[T], file: bool = False, cmdline: bool = False, defaults: bool = True, persistent: bool = True,
|
||||
) -> Config[Namespace]:
|
||||
"""
|
||||
Returns an Namespace object with options in the form "{group_name}_{setting_name}"
|
||||
`options` should already be normalized.
|
||||
@ -378,33 +383,36 @@ def get_namespace(config: Config[T], defaults: bool = True, persistent: bool = T
|
||||
persistent: Include unknown keys in persistent groups
|
||||
"""
|
||||
|
||||
if not file and not cmdline:
|
||||
raise ValueError('Invalid parameters: you must set either file or cmdline to True')
|
||||
|
||||
if isinstance(config.values, Namespace):
|
||||
options, definitions = normalize_config(config, True, True, defaults=defaults, persistent=persistent)
|
||||
options, definitions = normalize_config(config, file, cmdline, defaults=defaults, persistent=persistent)
|
||||
else:
|
||||
options, definitions = config
|
||||
namespace = Namespace()
|
||||
for group_name, group in definitions.items():
|
||||
|
||||
group_options = get_options(config, group_name)
|
||||
if group.persistent and persistent:
|
||||
group_options = get_options(config, group_name)
|
||||
for name, value in group_options.items():
|
||||
if name in group.v:
|
||||
internal_name, default = group.v[name].internal_name, group.v[name].default == value
|
||||
setting_file, setting_cmdline = group.v[name].file, group.v[name].cmdline
|
||||
value, default = get_option(options, group.v[name])
|
||||
internal_name = group.v[name].internal_name
|
||||
else:
|
||||
setting_file = setting_cmdline = True
|
||||
internal_name, default = f'{group_name}_' + sanitize_name(name), None
|
||||
|
||||
if hasattr(namespace, internal_name):
|
||||
raise Exception(f'Duplicate internal name: {internal_name}')
|
||||
|
||||
if not default or default and defaults:
|
||||
if ((setting_cmdline and cmdline) or (setting_file and file)) and (not default or (default and defaults)):
|
||||
setattr(namespace, internal_name, value)
|
||||
|
||||
else:
|
||||
for setting_name, setting in group.v.items():
|
||||
if hasattr(namespace, setting.internal_name):
|
||||
raise Exception(f'Duplicate internal name: {setting.internal_name}')
|
||||
for setting_name, setting in group.v.items():
|
||||
if (setting.cmdline and cmdline) or (setting.file and file):
|
||||
value, default = get_option(options, setting)
|
||||
|
||||
if not default or default and defaults:
|
||||
if not default or (default and defaults):
|
||||
# User has set a custom value or has requested the default value
|
||||
setattr(namespace, setting.internal_name, value)
|
||||
return Config(namespace, definitions)
|
||||
|
||||
@ -479,7 +487,7 @@ def parse_cmdline(
|
||||
if isinstance(config.values, Namespace):
|
||||
namespace = config.values
|
||||
else:
|
||||
namespace = get_namespace(config, defaults=False)[0]
|
||||
namespace = get_namespace(config, file=True, cmdline=True, defaults=False)[0]
|
||||
else:
|
||||
namespace = config
|
||||
argparser = create_argparser(definitions, description, epilog)
|
||||
@ -497,7 +505,7 @@ def parse_config(
|
||||
) -> tuple[Config[Values], bool]:
|
||||
file_options, success = parse_file(definitions, config_path)
|
||||
cmdline_options = parse_cmdline(
|
||||
definitions, description, epilog, args, get_namespace(file_options, defaults=False),
|
||||
definitions, description, epilog, args, get_namespace(file_options, file=True, cmdline=True, defaults=False),
|
||||
)
|
||||
|
||||
final_options = normalize_config(cmdline_options, file=True, cmdline=True)
|
||||
@ -605,19 +613,41 @@ class Manager:
|
||||
)
|
||||
|
||||
@overload
|
||||
def get_namespace(self, options: Values, defaults: bool = True) -> Namespace:
|
||||
def get_namespace(
|
||||
self,
|
||||
options: Values,
|
||||
file: bool = False,
|
||||
cmdline: bool = False,
|
||||
defaults: bool = True,
|
||||
persistent: bool = True,
|
||||
) -> Namespace:
|
||||
...
|
||||
|
||||
@overload
|
||||
def get_namespace(self, options: Config[Values], defaults: bool = True) -> Config[Namespace]:
|
||||
def get_namespace(
|
||||
self,
|
||||
options: Config[Values],
|
||||
file: bool = False,
|
||||
cmdline: bool = False,
|
||||
defaults: bool = True,
|
||||
persistent: bool = True,
|
||||
) -> Config[Namespace]:
|
||||
...
|
||||
|
||||
def get_namespace(self, options: Values | Config[Values], defaults: bool = True) -> Config[Namespace] | Namespace:
|
||||
def get_namespace(
|
||||
self,
|
||||
options: Values | Config[Values],
|
||||
file: bool = False,
|
||||
cmdline: bool = False,
|
||||
defaults: bool = True,
|
||||
persistent: bool = True,
|
||||
) -> Config[Namespace] | Namespace:
|
||||
if isinstance(options, Config):
|
||||
self.definitions = options[1]
|
||||
return get_namespace(options, defaults=defaults)
|
||||
return get_namespace(options, file=file, cmdline=cmdline, defaults=defaults, persistent=persistent)
|
||||
else:
|
||||
return get_namespace(Config(options, self.definitions), defaults=defaults)
|
||||
config = Config(options, self.definitions)
|
||||
return get_namespace(config, file=file, cmdline=cmdline, defaults=defaults, persistent=persistent)
|
||||
|
||||
def parse_file(self, filename: pathlib.Path) -> tuple[Config[Values], bool]:
|
||||
return parse_file(filename=filename, definitions=self.definitions)
|
||||
@ -668,10 +698,10 @@ def _main(args: list[str] | None = None) -> None:
|
||||
manager.add_persistent_group('persistent', persistent)
|
||||
|
||||
file_config, success = manager.parse_file(settings_path)
|
||||
file_namespace = manager.get_namespace(file_config)
|
||||
file_namespace = manager.get_namespace(file_config, file=True, cmdline=True)
|
||||
|
||||
merged_config = manager.parse_cmdline(args=args, namespace=file_namespace)
|
||||
merged_namespace = manager.get_namespace(merged_config)
|
||||
merged_namespace = manager.get_namespace(merged_config, file=True, cmdline=True)
|
||||
|
||||
print(f'Hello {merged_config.values["example"]["hello"]}') # noqa: T201
|
||||
if merged_namespace.values.example_save:
|
||||
|
@ -78,6 +78,8 @@ max_line_length = 120
|
||||
[flake8]
|
||||
extend-ignore = E501, A003
|
||||
max_line_length = 120
|
||||
per-file-ignores =
|
||||
*_test.py: LN001
|
||||
|
||||
[coverage:run]
|
||||
plugins = covdefaults
|
||||
|
@ -2,7 +2,9 @@ from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import pathlib
|
||||
from collections import defaultdict
|
||||
from typing import Generator
|
||||
|
||||
import pytest
|
||||
|
||||
@ -14,7 +16,7 @@ from testing.settngs import success
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def settngs_manager():
|
||||
def settngs_manager() -> Generator[settngs.Manager, None, None]:
|
||||
manager = settngs.Manager()
|
||||
yield manager
|
||||
|
||||
@ -53,94 +55,170 @@ def test_add_setting(settngs_manager):
|
||||
assert settngs_manager.add_setting('--test') is None
|
||||
|
||||
|
||||
def test_get_defaults(settngs_manager):
|
||||
settngs_manager.add_setting('--test', default='hello')
|
||||
defaults, _ = settngs_manager.defaults()
|
||||
assert defaults['']['test'] == 'hello'
|
||||
class TestValues:
|
||||
|
||||
def test_get_defaults(self, settngs_manager):
|
||||
settngs_manager.add_setting('--test', default='hello')
|
||||
defaults, _ = settngs_manager.defaults()
|
||||
assert defaults['']['test'] == 'hello'
|
||||
|
||||
def test_get_defaults_group(self, settngs_manager):
|
||||
settngs_manager.add_group('tst', lambda parser: parser.add_setting('--test', default='hello'))
|
||||
defaults, _ = settngs_manager.defaults()
|
||||
assert defaults['tst']['test'] == 'hello'
|
||||
|
||||
def test_cmdline_only(self, settngs_manager):
|
||||
settngs_manager.add_group('tst', lambda parser: parser.add_setting('--test', default='hello', file=False))
|
||||
settngs_manager.add_group('tst2', lambda parser: parser.add_setting('--test2', default='hello', cmdline=False))
|
||||
|
||||
file_normalized, _ = settngs_manager.normalize_config(settngs_manager.defaults(), file=True)
|
||||
cmdline_normalized, _ = settngs_manager.normalize_config(settngs_manager.defaults(), cmdline=True)
|
||||
|
||||
assert 'test' not in file_normalized['tst'] # cmdline option not in normalized config
|
||||
assert 'test2' in file_normalized['tst2'] # file option in normalized config
|
||||
|
||||
assert 'test' in cmdline_normalized['tst'] # cmdline option in normalized config
|
||||
assert 'test2' not in cmdline_normalized['tst2'] # file option not in normalized config
|
||||
|
||||
def test_cmdline_only_persistent_group(self, settngs_manager):
|
||||
settngs_manager.add_persistent_group('tst', lambda parser: parser.add_setting('--test', default='hello', file=False))
|
||||
settngs_manager.add_group('tst2', lambda parser: parser.add_setting('--test2', default='hello', cmdline=False))
|
||||
|
||||
file_normalized, _ = settngs_manager.normalize_config(settngs_manager.defaults(), file=True)
|
||||
cmdline_normalized, _ = settngs_manager.normalize_config(settngs_manager.defaults(), cmdline=True)
|
||||
|
||||
assert 'test' not in file_normalized['tst']
|
||||
assert 'test2' in file_normalized['tst2']
|
||||
|
||||
assert 'test' in cmdline_normalized['tst']
|
||||
assert 'test2' not in cmdline_normalized['tst2']
|
||||
|
||||
def test_normalize_defaults(self, settngs_manager):
|
||||
settngs_manager.add_group('tst', lambda parser: parser.add_setting('--test', default='hello'))
|
||||
settngs_manager.add_group('tst', lambda parser: parser.add_setting('--test2', default='hello'))
|
||||
settngs_manager.add_persistent_group('tst_persistent', lambda parser: parser.add_setting('--test', default='hello'))
|
||||
|
||||
defaults = settngs_manager.defaults()
|
||||
defaults_normalized = settngs_manager.normalize_config(defaults, file=True, defaults=False)
|
||||
assert defaults_normalized.values['tst'] == {}
|
||||
assert defaults_normalized.values['tst_persistent'] == {}
|
||||
|
||||
non_defaults = settngs_manager.defaults()
|
||||
non_defaults.values['tst']['test'] = 'world'
|
||||
non_defaults.values['tst_persistent']['test'] = 'world'
|
||||
non_defaults_normalized = settngs_manager.normalize_config(non_defaults, file=True, defaults=False)
|
||||
|
||||
assert non_defaults_normalized.values['tst'] == {'test': 'world'}
|
||||
assert non_defaults_normalized.values['tst_persistent'] == {'test': 'world'}
|
||||
|
||||
def test_normalize(self, settngs_manager):
|
||||
settngs_manager.add_group('tst', lambda parser: parser.add_setting('--test', default='hello'))
|
||||
settngs_manager.add_persistent_group('persistent', lambda parser: parser.add_setting('--world', default='world'))
|
||||
|
||||
defaults = settngs_manager.defaults()
|
||||
defaults.values['test'] = 'fail' # Not defined in settngs_manager, should be removed
|
||||
defaults.values['persistent']['hello'] = 'success' # Not defined in settngs_manager, should stay
|
||||
|
||||
normalized, _ = settngs_manager.normalize_config(defaults, file=True)
|
||||
|
||||
assert 'test' not in normalized
|
||||
assert 'tst' in normalized
|
||||
assert 'test' in normalized['tst']
|
||||
assert normalized['tst']['test'] == 'hello'
|
||||
assert normalized['persistent']['hello'] == 'success'
|
||||
assert normalized['persistent']['world'] == 'world'
|
||||
|
||||
|
||||
class TestNamespace:
|
||||
|
||||
def test_get_defaults(self, settngs_manager):
|
||||
settngs_manager.add_setting('--test', default='hello')
|
||||
defaults, _ = settngs_manager.get_namespace(settngs_manager.defaults(), file=True, cmdline=True)
|
||||
assert defaults.test == 'hello'
|
||||
|
||||
def test_get_defaults_group(self, settngs_manager):
|
||||
settngs_manager.add_group('tst', lambda parser: parser.add_setting('--test', default='hello'))
|
||||
defaults, _ = settngs_manager.get_namespace(settngs_manager.defaults(), file=True, cmdline=True)
|
||||
assert defaults.tst_test == 'hello'
|
||||
|
||||
def test_cmdline_only(self, settngs_manager):
|
||||
settngs_manager.add_group('tst', lambda parser: parser.add_setting('--test', default='hello', file=False))
|
||||
settngs_manager.add_group('tst2', lambda parser: parser.add_setting('--test2', default='hello', cmdline=False))
|
||||
|
||||
file_normalized, _ = settngs_manager.get_namespace(settngs_manager.normalize_config(settngs_manager.defaults(), file=True), file=True)
|
||||
cmdline_normalized, _ = settngs_manager.get_namespace(settngs_manager.normalize_config(settngs_manager.defaults(), cmdline=True), cmdline=True)
|
||||
|
||||
assert 'tst_test' not in file_normalized.__dict__
|
||||
assert 'tst2_test2' in file_normalized.__dict__
|
||||
|
||||
assert 'tst_test' in cmdline_normalized.__dict__
|
||||
assert 'tst2_test2' not in cmdline_normalized.__dict__
|
||||
|
||||
def test_cmdline_only_persistent_group(self, settngs_manager):
|
||||
settngs_manager.add_persistent_group('tst', lambda parser: parser.add_setting('--test', default='hello', file=False))
|
||||
settngs_manager.add_group('tst2', lambda parser: parser.add_setting('--test2', default='hello', cmdline=False))
|
||||
|
||||
file_normalized, _ = settngs_manager.get_namespace(settngs_manager.normalize_config(settngs_manager.defaults(), file=True), file=True)
|
||||
cmdline_normalized, _ = settngs_manager.get_namespace(settngs_manager.normalize_config(settngs_manager.defaults(), cmdline=True), cmdline=True)
|
||||
|
||||
assert 'tst_test' not in file_normalized.__dict__
|
||||
assert 'tst2_test2' in file_normalized.__dict__
|
||||
|
||||
assert 'tst_test' in cmdline_normalized.__dict__
|
||||
assert 'tst2_test2' not in cmdline_normalized.__dict__
|
||||
|
||||
def test_normalize_defaults(self, settngs_manager):
|
||||
settngs_manager.add_group('tst', lambda parser: parser.add_setting('--test', default='hello'))
|
||||
settngs_manager.add_group('tst', lambda parser: parser.add_setting('--test2', default='hello'))
|
||||
settngs_manager.add_persistent_group('tst_persistent', lambda parser: parser.add_setting('--test', default='hello'))
|
||||
|
||||
defaults = settngs_manager.defaults()
|
||||
defaults_normalized = settngs_manager.get_namespace(settngs_manager.normalize_config(defaults, file=True, defaults=False), file=True, defaults=False)
|
||||
assert defaults_normalized.values.__dict__ == {}
|
||||
|
||||
non_defaults = settngs_manager.get_namespace(settngs_manager.defaults(), file=True, cmdline=True)
|
||||
non_defaults.values.tst_test = 'world'
|
||||
non_defaults.values.tst_persistent_test = 'world'
|
||||
non_defaults_normalized = settngs_manager.get_namespace(settngs_manager.normalize_config(non_defaults, file=True, defaults=False), file=True, defaults=False)
|
||||
|
||||
assert non_defaults_normalized.values.tst_test == 'world'
|
||||
assert non_defaults_normalized.values.tst_persistent_test == 'world'
|
||||
|
||||
def test_normalize(self, settngs_manager):
|
||||
settngs_manager.add_group('tst', lambda parser: parser.add_setting('--test', default='hello'))
|
||||
settngs_manager.add_persistent_group('persistent', lambda parser: parser.add_setting('--world', default='world'))
|
||||
|
||||
defaults = settngs_manager.get_namespace(settngs_manager.defaults(), file=True, cmdline=True)
|
||||
defaults.values.test = 'fail' # Not defined in settngs_manager, should be removed
|
||||
defaults.values.persistent_hello = 'success' # Not defined in settngs_manager, should stay
|
||||
|
||||
normalized, _ = settngs_manager.get_namespace(settngs_manager.normalize_config(defaults, file=True), file=True)
|
||||
|
||||
assert not hasattr(normalized, 'test')
|
||||
assert hasattr(normalized, 'tst_test')
|
||||
assert normalized.tst_test == 'hello'
|
||||
assert normalized.persistent_hello == 'success'
|
||||
assert normalized.persistent_world == 'world'
|
||||
|
||||
|
||||
def test_get_defaults_namespace(settngs_manager):
|
||||
settngs_manager.add_setting('--test', default='hello')
|
||||
defaults, _ = settngs_manager.get_namespace(settngs_manager.defaults())
|
||||
defaults, _ = settngs_manager.get_namespace(settngs_manager.defaults(), file=True, cmdline=True)
|
||||
assert defaults.test == 'hello'
|
||||
|
||||
|
||||
def test_get_namespace_with_namespace(settngs_manager):
|
||||
settngs_manager.add_setting('--test', default='hello')
|
||||
defaults, _ = settngs_manager.get_namespace(argparse.Namespace(test='success'))
|
||||
defaults, _ = settngs_manager.get_namespace(argparse.Namespace(test='success'), file=True)
|
||||
assert defaults.test == 'success'
|
||||
|
||||
|
||||
def test_get_defaults_group(settngs_manager):
|
||||
settngs_manager.add_group('tst', lambda parser: parser.add_setting('--test', default='hello'))
|
||||
defaults, _ = settngs_manager.defaults()
|
||||
assert defaults['tst']['test'] == 'hello'
|
||||
|
||||
|
||||
def test_get_namespace_group(settngs_manager):
|
||||
settngs_manager.add_group('tst', lambda parser: parser.add_setting('--test', default='hello'))
|
||||
defaults, _ = settngs_manager.get_namespace(settngs_manager.defaults())
|
||||
defaults, _ = settngs_manager.get_namespace(settngs_manager.defaults(), file=True)
|
||||
assert defaults.tst_test == 'hello'
|
||||
|
||||
|
||||
def test_cmdline_only(settngs_manager):
|
||||
settngs_manager.add_group('tst', lambda parser: parser.add_setting('--test', default='hello', file=False))
|
||||
settngs_manager.add_group('tst2', lambda parser: parser.add_setting('--test2', default='hello', cmdline=False))
|
||||
|
||||
file_normalized, _ = settngs_manager.normalize_config(settngs_manager.defaults(), file=True)
|
||||
cmdline_normalized, _ = settngs_manager.normalize_config(settngs_manager.defaults(), cmdline=True)
|
||||
|
||||
assert 'test' in cmdline_normalized['tst']
|
||||
assert 'test2' not in cmdline_normalized['tst2']
|
||||
|
||||
assert 'test' not in file_normalized['tst']
|
||||
assert 'test2' in file_normalized['tst2']
|
||||
|
||||
|
||||
def test_cmdline_only_persistent_group(settngs_manager):
|
||||
settngs_manager.add_persistent_group('tst', lambda parser: parser.add_setting('--test', default='hello', file=False))
|
||||
settngs_manager.add_group('tst2', lambda parser: parser.add_setting('--test2', default='hello', cmdline=False))
|
||||
|
||||
file_normalized, _ = settngs_manager.normalize_config(settngs_manager.defaults(), file=True)
|
||||
cmdline_normalized, _ = settngs_manager.normalize_config(settngs_manager.defaults(), cmdline=True)
|
||||
|
||||
assert 'test' in cmdline_normalized['tst']
|
||||
assert 'test2' not in cmdline_normalized['tst2']
|
||||
|
||||
assert 'test' not in file_normalized['tst']
|
||||
assert 'test2' in file_normalized['tst2']
|
||||
|
||||
|
||||
def test_normalize(settngs_manager):
|
||||
settngs_manager.add_group('tst', lambda parser: parser.add_setting('--test', default='hello'))
|
||||
settngs_manager.add_persistent_group('persistent', lambda parser: parser.add_setting('--world', default='world'))
|
||||
|
||||
defaults = settngs_manager.defaults()
|
||||
defaults.values['test'] = 'fail' # Not defined in settngs_manager, should be removed
|
||||
defaults.values['persistent']['hello'] = 'success' # Not defined in settngs_manager, should stay
|
||||
|
||||
defaults_namespace = settngs_manager.get_namespace(settngs_manager.defaults())
|
||||
defaults_namespace.values.test = 'fail' # Not defined in settngs_manager, should be removed
|
||||
defaults_namespace.values.persistent_hello = 'success' # Not defined in settngs_manager, should stay
|
||||
|
||||
normalized, _ = settngs_manager.normalize_config(defaults, file=True)
|
||||
normalized_from_namespace = settngs_manager.normalize_config(defaults_namespace, file=True)
|
||||
normalized_namespace, _ = settngs_manager.get_namespace(normalized_from_namespace)
|
||||
|
||||
assert 'test' not in normalized
|
||||
assert 'tst' in normalized
|
||||
assert 'test' in normalized['tst']
|
||||
assert normalized['tst']['test'] == 'hello'
|
||||
assert normalized['persistent']['hello'] == 'success'
|
||||
assert normalized['persistent']['world'] == 'world'
|
||||
|
||||
assert not hasattr(normalized_namespace, 'test')
|
||||
assert hasattr(normalized_namespace, 'tst_test')
|
||||
assert normalized_namespace.tst_test == 'hello'
|
||||
assert normalized_namespace.persistent_hello == 'success'
|
||||
assert normalized_namespace.persistent_world == 'world'
|
||||
|
||||
|
||||
def test_clean_config(settngs_manager):
|
||||
settngs_manager.add_group('tst', lambda parser: parser.add_setting('--test', default='hello', cmdline=False))
|
||||
settngs_manager.add_group('tst2', lambda parser: parser.add_setting('--test2', default='hello', file=False))
|
||||
@ -331,7 +409,7 @@ def test_adding_to_existing_group(settngs_manager, tmp_path):
|
||||
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 test_adding_to_existing_persistent_group(settngs_manager: settngs.Manager, tmp_path: pathlib.Path) -> None:
|
||||
def default_to_regular(d):
|
||||
if isinstance(d, defaultdict):
|
||||
d = {k: default_to_regular(v) for k, v in d.items()}
|
||||
|
Loading…
Reference in New Issue
Block a user