Compare commits

...

4 commits

Author SHA1 Message Date
Benjamin Renard
e9477b1566 config: add optional --reconfigure parameter
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-09 13:34:07 +01:00
Benjamin Renard
508a28e5c8 tests: add some tests on BooleanOption 2023-01-09 13:33:14 +01:00
Benjamin Renard
6a7368deb5 ConfigOption: add _get_user_input method to allow to mock it in tests 2023-01-09 13:32:25 +01:00
Benjamin Renard
014a0802f8 config: make sure to reload file after saving it
To ensure the configparser object is uptodate.
2023-01-09 13:27:52 +01:00
2 changed files with 143 additions and 2 deletions

View file

@ -222,6 +222,14 @@ class BaseOption: # pylint: disable=too-many-instance-attributes
lines.append('')
return '\n'.join(lines)
@staticmethod
def _get_user_input(prompt):
"""
Get user console input
Note: do not use directly input() to allow to mock it in tests
"""
return input(prompt)
def _ask_value(self, prompt=None, **kwargs):
""" Ask to user to enter value of this option and return it """
if self.comment:
@ -236,7 +244,7 @@ class BaseOption: # pylint: disable=too-many-instance-attributes
)
else:
prompt += f'[{self.to_config(default_value)}] '
value = input(prompt)
value = self._get_user_input(prompt)
return default_value if value == '' else value
def ask_value(self, set_it=True):
@ -758,6 +766,7 @@ class Config: # pylint: disable=too-many-instance-attributes
log.exception(
'Failed to write generated configuration file %s', filepath)
return False
self.load_file(filepath)
return True
@property
@ -765,7 +774,7 @@ class Config: # pylint: disable=too-many-instance-attributes
""" Get ordered list of section names """
return sorted(self.sections.keys(), key=lambda section: self.sections[section].order)
def get_arguments_parser(self, **kwargs):
def get_arguments_parser(self, reconfigure=False, **kwargs):
""" Get arguments parser """
if self.options_parser:
return self.options_parser
@ -798,6 +807,14 @@ class Config: # pylint: disable=too-many-instance-attributes
help='Save current configuration to file',
)
if reconfigure:
self.options_parser.add_argument(
'--reconfigure',
action='store_true',
dest='mylib_config_reconfigure',
help='Reconfigure and update configuration file',
)
self.options_parser.add_argument(
'-d',
'--debug',
@ -903,6 +920,11 @@ class Config: # pylint: disable=too-many-instance-attributes
if execute_callback:
self._loaded()
if self.get_option('mylib_config_reconfigure', default=False):
if self.ask_values(set_it=True) and self.save():
sys.exit(0)
sys.exit(1)
return options
def load_options(self, options, execute_callback=True):

View file

@ -3,8 +3,13 @@
""" Tests on config lib """
import logging
import os
import configparser
import pytest
from mylib.config import Config, ConfigSection
from mylib.config import BooleanOption
from mylib.config import StringOption
runned = {}
@ -225,3 +230,117 @@ def test_logging_splited_stdout_stderr(capsys):
assert info_msg not in captured.err
assert err_msg in captured.err
assert err_msg not in captured.out
#
# Test option types
#
@pytest.fixture()
def config_with_file(tmpdir):
config = Config('Test app')
config_dir = tmpdir.mkdir('config')
config_file = config_dir.join('config.ini')
config.save(os.path.join(config_file.dirname, config_file.basename))
return config
def generate_mock_input(expected_prompt, input_value):
def mock_input(self, prompt): # pylint: disable=unused-argument
assert prompt == expected_prompt
return input_value
return mock_input
# Boolean option
def test_boolean_option_from_config(config_with_file):
section = config_with_file.add_section('test')
default = True
option = section.add_option(
BooleanOption, 'test_bool', default=default)
config_with_file.save()
option.set(not default)
assert option._from_config is not default
option.set(default)
assert not option._isset_in_config_file
with pytest.raises(configparser.NoOptionError):
assert option._from_config is default
def test_boolean_option_ask_value(mocker):
config = Config('Test app')
section = config.add_section('test')
name = 'test_bool'
option = section.add_option(
BooleanOption, name, default=True)
mocker.patch(
'mylib.config.BooleanOption._get_user_input',
generate_mock_input(f'{name}: [Y/n] ', 'y')
)
assert option.ask_value(set_it=False) is True
mocker.patch(
'mylib.config.BooleanOption._get_user_input',
generate_mock_input(f'{name}: [Y/n] ', 'Y')
)
assert option.ask_value(set_it=False) is True
mocker.patch(
'mylib.config.BooleanOption._get_user_input',
generate_mock_input(f'{name}: [Y/n] ', '')
)
assert option.ask_value(set_it=False) is True
mocker.patch(
'mylib.config.BooleanOption._get_user_input',
generate_mock_input(f'{name}: [Y/n] ', 'n')
)
assert option.ask_value(set_it=False) is False
mocker.patch(
'mylib.config.BooleanOption._get_user_input',
generate_mock_input(f'{name}: [Y/n] ', 'N')
)
assert option.ask_value(set_it=False) is False
def test_boolean_option_to_config():
config = Config('Test app')
section = config.add_section('test')
default = True
option = section.add_option(BooleanOption, 'test_bool', default=default)
assert option.to_config(True) == 'true'
assert option.to_config(False) == 'false'
def test_boolean_option_export_to_config(config_with_file):
section = config_with_file.add_section('test')
name = 'test_bool'
comment = 'Test boolean'
default = True
option = section.add_option(
BooleanOption, name, default=default, comment=comment)
assert option.export_to_config() == f"""# {comment}
# Default: {str(default).lower()}
# {name} =
"""
option.set(not default)
assert option.export_to_config() == f"""# {comment}
# Default: {str(default).lower()}
{name} = {str(not default).lower()}
"""
option.set(default)
assert option.export_to_config() == f"""# {comment}
# Default: {str(default).lower()}
# {name} =
"""