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('') lines.append('')
return '\n'.join(lines) 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): def _ask_value(self, prompt=None, **kwargs):
""" Ask to user to enter value of this option and return it """ """ Ask to user to enter value of this option and return it """
if self.comment: if self.comment:
@ -236,7 +244,7 @@ class BaseOption: # pylint: disable=too-many-instance-attributes
) )
else: else:
prompt += f'[{self.to_config(default_value)}] ' prompt += f'[{self.to_config(default_value)}] '
value = input(prompt) value = self._get_user_input(prompt)
return default_value if value == '' else value return default_value if value == '' else value
def ask_value(self, set_it=True): def ask_value(self, set_it=True):
@ -758,6 +766,7 @@ class Config: # pylint: disable=too-many-instance-attributes
log.exception( log.exception(
'Failed to write generated configuration file %s', filepath) 'Failed to write generated configuration file %s', filepath)
return False return False
self.load_file(filepath)
return True return True
@property @property
@ -765,7 +774,7 @@ class Config: # pylint: disable=too-many-instance-attributes
""" Get ordered list of section names """ """ Get ordered list of section names """
return sorted(self.sections.keys(), key=lambda section: self.sections[section].order) 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 """ """ Get arguments parser """
if self.options_parser: if self.options_parser:
return 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', 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( self.options_parser.add_argument(
'-d', '-d',
'--debug', '--debug',
@ -903,6 +920,11 @@ class Config: # pylint: disable=too-many-instance-attributes
if execute_callback: if execute_callback:
self._loaded() 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 return options
def load_options(self, options, execute_callback=True): def load_options(self, options, execute_callback=True):

View file

@ -3,8 +3,13 @@
""" Tests on config lib """ """ Tests on config lib """
import logging import logging
import os
import configparser
import pytest
from mylib.config import Config, ConfigSection from mylib.config import Config, ConfigSection
from mylib.config import BooleanOption
from mylib.config import StringOption from mylib.config import StringOption
runned = {} runned = {}
@ -225,3 +230,117 @@ def test_logging_splited_stdout_stderr(capsys):
assert info_msg not in captured.err assert info_msg not in captured.err
assert err_msg in captured.err assert err_msg in captured.err
assert err_msg not in captured.out 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} =
"""