提交 56e42ff2 编写于 作者: B Beraldo Leal 提交者: Cleber Rosa

r.i.p old settings.py

This is removing settings.py old module in favor of future.settings.
Signed-off-by: NBeraldo Leal <bleal@redhat.com>
上级 f4ed866a
......@@ -124,7 +124,6 @@ class Parser:
# Load settings from file, if user provides one
if self.args.config is not None:
settings.settings.process_config_path(self.args.config)
future_settings.process_config_path(self.args.config)
# Use parent parsing to avoid breaking the output of --help option
......
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; specifically version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# See LICENSE for more details.
#
# This code was inspired in the autotest project,
# client/shared/settings.py
# Author: Travis Miller <raphtee@google.com>
"""
Reads the avocado settings from a .ini file (with Python's configparser).
"""
import ast
import configparser
import glob
import os
from pkg_resources import (get_distribution, resource_filename, resource_isdir,
resource_listdir)
from .settings_dispatcher import SettingsDispatcher
# pylint: disable-msg=too-many-locals
# pylint: disable-msg=too-many-arguments
class SettingsError(Exception):
"""
Base settings error.
"""
class SettingsValueError(SettingsError):
"""
Error thrown when we could not convert successfully a key to a value.
"""
class ConfigFileNotFound(SettingsError):
"""
Error thrown when the main settings file could not be found.
"""
def __init__(self, path_list):
super(ConfigFileNotFound, self).__init__()
self.path_list = path_list
def __str__(self):
return ("Could not find the avocado config file after looking in: %s" %
self.path_list)
class Settings:
"""
Simple wrapper around configparser, with a key type conversion available.
"""
no_default = object()
def __init__(self, config_path=None):
"""
Constructor. Tries to find the main settings file and load it.
:param config_path: Path to a config file. Useful for unittesting.
"""
self.config = configparser.ConfigParser()
self.config_paths = []
self.all_config_paths = []
_source_tree_root = os.path.dirname(os.path.dirname(os.path.dirname(
__file__)))
# In case "examples" file exists in root, we are running from tree
self.intree = bool(os.path.exists(os.path.join(_source_tree_root,
'examples')))
if config_path is None:
if 'VIRTUAL_ENV' in os.environ:
cfg_dir = os.path.join(os.environ['VIRTUAL_ENV'], 'etc')
user_dir = os.environ['VIRTUAL_ENV']
else:
cfg_dir = '/etc'
user_dir = os.path.expanduser("~")
_config_dir_system = os.path.join(cfg_dir, 'avocado')
_config_dir_system_extra = os.path.join(cfg_dir, 'avocado', 'conf.d')
_config_dir_local = os.path.join(user_dir, '.config', 'avocado')
config_filename = 'avocado.conf'
config_path_system = os.path.join(_config_dir_system, config_filename)
config_path_local = os.path.join(_config_dir_local, config_filename)
config_pkg_base = os.path.join('etc', 'avocado', config_filename)
config_path_pkg = resource_filename('avocado', config_pkg_base)
_config_pkg_extra = os.path.join('etc', 'avocado', 'conf.d')
if resource_isdir('avocado', _config_pkg_extra):
config_pkg_extra = resource_listdir('avocado',
_config_pkg_extra)
_config_pkg_extra = resource_filename('avocado', _config_pkg_extra)
else:
config_pkg_extra = []
# First try pkg/in-tree config
self.all_config_paths.append(config_path_pkg)
for extra_file in (os.path.join(_config_pkg_extra, _)
for _ in config_pkg_extra
if _.endswith('.conf')):
self.all_config_paths.append(extra_file)
# Override with system config
self.all_config_paths.append(config_path_system)
for extra_file in glob.glob(os.path.join(_config_dir_system_extra,
'*.conf')):
self.all_config_paths.append(extra_file)
# Allow plugins to modify/extend the list of configs
dispatcher = SettingsDispatcher()
if dispatcher.extensions:
dispatcher.map_method('adjust_settings_paths',
self.all_config_paths)
# Register user config as last to always take precedence
if os.path.exists(config_path_local):
self.all_config_paths.append(config_path_local)
else:
# Only used by unittests (the --config parses the file later)
self.all_config_paths.append(config_path)
self.config_paths = self.config.read(self.all_config_paths)
if not self.config_paths:
raise ConfigFileNotFound(self.all_config_paths)
def process_config_path(self, path_):
"""
Update list of config paths and process the given path
"""
self.all_config_paths.append(path_)
self.config_paths.extend(self.config.read(path_))
def _handle_no_value(self, section, key, default):
"""
What to do if key in section has no value.
:param section: Config file section.
:param key: Config file key, relative to section.
:param default: Default value for key, in case it does not exist.
:returns: Default value, if a default value was provided.
:raises: SettingsError, in case no default was provided.
"""
if default is self.no_default:
msg = ("Value '%s' not found in section '%s'" %
(key, section))
raise SettingsError(msg)
return default
def _handle_no_section(self, section, default):
"""
What to do if section doesn't exist.
:param section: Config file section.
:param default: Default value for key, in case it does not exist.
:returns: Default value, if a default value was provided.
:raises: SettingsError, in case no default was provided.
"""
if default is self.no_default:
msg = "Section '%s' doesn't exist in configuration" % section
raise SettingsError(msg)
return default
def get_value(self, section, key, key_type=str, default=no_default,
allow_blank=False):
"""
Get value from key in a given config file section.
:param section: Config file section.
:type section: str
:param key: Config file key, relative to section.
:type key: str
:param key_type: Type of key.
:type key_type: either string based names representing types,
including `str`, `int`, `float`, `bool`,
`list` and `path`, or the types themselves
limited to :class:`str`, :class:`int`,
:class:`float`, :class:`bool` and
:class:`list`.
:param default: Default value for the key, if none found.
:param allow_blank: Whether an empty value for the key is allowed.
:returns: value, if one available in the config.
default value, if one provided.
:raises: SettingsError, in case key is not set and no default
was provided.
"""
def _get_method_or_type(value_type):
returns = {'str': str,
'path': os.path.expanduser,
'bool': bool,
'int': int,
'float': float,
'list': ast.literal_eval}
# This is just to cover some old tests, makes no sense here
if isinstance(value_type, type):
value_type = value_type.__name__
try:
return returns[value_type]
except KeyError:
return str
def _string_to_bool(value):
if value.lower() == 'false':
return False
return True
def _prepend_base_path(value):
if not value.startswith(('/', '~')):
dist = get_distribution('avocado-framework')
return os.path.join(dist.location, 'avocado', value)
return value
def _get_empty_value(value_type):
returns = {'str': "",
'path': "",
'bool': False,
'int': 0,
'float': 0.0,
'list': []}
if isinstance(value_type, type):
value_type = value_type.__name__
try:
return returns[value_type]
except KeyError:
return None
def _get_value_as_type(value, value_type):
# strip off leading and trailing white space
value_stripped = value.strip()
if not value_stripped:
return _get_empty_value(value_type)
method_or_type = _get_method_or_type(value_type)
# Handle special cases
if method_or_type == bool:
return _string_to_bool(value_stripped)
if method_or_type is os.path.expanduser:
value_stripped = _prepend_base_path(value_stripped)
# Handle other cases
return method_or_type(value_stripped)
try:
val = self.config.get(section, key)
except configparser.NoSectionError:
return self._handle_no_section(section, default)
except configparser.Error:
return self._handle_no_value(section, key, default)
if not val.strip() and not allow_blank:
return self._handle_no_value(section, key, default)
try:
return _get_value_as_type(val, key_type)
except Exception as details:
raise SettingsValueError("Could not convert value %r to type %s "
"(settings key %s, section %s): %s" %
(val, key_type, key, section, details))
settings = Settings() # pylint: disable-msg=invalid-name
import argparse
import os
import tempfile
import unittest
from pkg_resources import get_distribution
from avocado.core import settings
from avocado.core.future.settings import DuplicatedNamespace
from avocado.core.future.settings import settings as future_settings
example_1 = """[foo]
str_key = frobnicate
int_key = 1
float_key = 1.25
bool_key = True
list_key = ['I', 'love', 'settings']
empty_key =
path = ~/path/at/home
relative_path = path/at/home
home_path = ~
"""
class SettingsTest(unittest.TestCase):
def setUp(self):
self.config_file = tempfile.NamedTemporaryFile('w', delete=False)
self.config_file.write(example_1)
self.config_file.close()
self.settings = settings.Settings(self.config_file.name)
def test_string_conversion(self):
self.assertEqual(self.settings.get_value('foo', 'str_key', str),
'frobnicate')
def test_int_conversion(self):
self.assertEqual(self.settings.get_value('foo', 'int_key', int), 1)
def test_float_conversion(self):
self.assertEqual(self.settings.get_value('foo', 'float_key', float),
1.25)
def test_bool_conversion(self):
self.assertTrue(self.settings.get_value('foo', 'bool_key', bool))
def test_path_homedir(self):
raw_from_settings = '~/path/at/home'
path_from_settings = self.settings.get_value('foo', 'path', 'path')
home_str_from_settings = self.settings.get_value('foo', 'home_path', str)
self.assertEqual(path_from_settings[-13:],
raw_from_settings[-13:])
self.assertGreaterEqual(len(path_from_settings),
len(raw_from_settings))
self.assertEqual(os.path.expanduser(home_str_from_settings),
self.settings.get_value('foo', 'home_path', 'path'))
def test_relative_path(self):
dist = get_distribution('avocado-framework')
path_from_settings = self.settings.get_value('foo',
'relative_path',
'path')
self.assertTrue(path_from_settings.startswith(dist.location))
def test_path_on_str_key(self):
self.assertEqual(self.settings.get_value('foo', 'path', str),
'~/path/at/home')
def test_list_conversion(self):
self.assertEqual(self.settings.get_value('foo', 'list_key', list),
['I', 'love', 'settings'])
def test_default(self):
self.assertEqual(self.settings.get_value('foo', 'non_existing',
str, "ohnoes"), "ohnoes")
def test_non_existing_key(self):
with self.assertRaises(settings.SettingsError):
self.settings.get_value('foo', 'non_existing', str)
def test_allow_blank_true_str(self):
self.assertEqual(self.settings.get_value('foo', 'empty_key', str,
allow_blank=True), "")
def test_allow_blank_true_int(self):
self.assertEqual(self.settings.get_value('foo', 'empty_key', int,
allow_blank=True), 0)
def test_allow_blank_true_float(self):
self.assertEqual(self.settings.get_value('foo', 'empty_key', float,
allow_blank=True), 0.0)
def test_allow_blank_true_list(self):
self.assertEqual(self.settings.get_value('foo', 'empty_key', list,
allow_blank=True), [])
def test_allow_blank_true_bool(self):
self.assertEqual(self.settings.get_value('foo', 'empty_key', bool,
allow_blank=True), False)
def test_allow_blank_true_other(self):
self.assertEqual(self.settings.get_value('foo', 'empty_key', 'baz',
allow_blank=True), None)
def test_allow_blank_false(self):
with self.assertRaises(settings.SettingsError):
self.settings.get_value('foo', 'empty_key', str)
def test_register_value(self):
future_settings.register_option(section='foo',
key='bar',
default=1,
key_type=int,
help_msg='foo bar')
result = future_settings.as_dict()
self.assertEqual(result['foo.bar'], 1)
self.assertIsInstance(result['foo.bar'], int)
def test_registered_already(self):
with self.assertRaises(DuplicatedNamespace):
future_settings.register_option(section='foo',
key='bar',
default=1,
help_msg='foo bar')
future_settings.register_option(section='foo',
key='bar',
default=1,
help_msg='foo bar')
def test_update_argparse(self):
future_settings.register_option(section='bar',
key='foo',
default=1,
help_msg='bar foo')
parser = argparse.ArgumentParser(description='Basic parser.')
future_settings.add_argparser_to_option('bar.foo', parser, '--bar-foo')
stored_parser = future_settings._namespaces.get('bar.foo').parser
self.assertIsInstance(stored_parser, argparse.ArgumentParser)
def tearDown(self):
os.unlink(self.config_file.name)
if __name__ == '__main__':
unittest.main()
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册