Compare commits
4 commits
135a742b6e
...
e9477b1566
Author | SHA1 | Date | |
---|---|---|---|
|
e9477b1566 | ||
|
508a28e5c8 | ||
|
6a7368deb5 | ||
|
014a0802f8 |
2 changed files with 143 additions and 2 deletions
|
@ -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):
|
||||
|
|
|
@ -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} =
|
||||
"""
|
||||
|
|
Loading…
Reference in a new issue