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('')
|
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):
|
||||||
|
|
|
@ -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} =
|
||||||
|
"""
|
||||||
|
|
Loading…
Reference in a new issue