Hello community, here is the log from the commit of package python-knack for openSUSE:Factory checked in at 2019-05-22 11:16:33 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-knack (Old) and /work/SRC/openSUSE:Factory/.python-knack.new.5148 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-knack" Wed May 22 11:16:33 2019 rev:7 rq:704423 version:0.6.1 Changes: -------- --- /work/SRC/openSUSE:Factory/python-knack/python-knack.changes 2019-05-06 13:25:09.841020349 +0200 +++ /work/SRC/openSUSE:Factory/.python-knack.new.5148/python-knack.changes 2019-05-22 11:16:43.162502081 +0200 @@ -1,0 +2,8 @@ +Tue May 21 09:51:57 UTC 2019 - John Paul Adrian Glaubitz <[email protected]> + +- Update to 0.6.1: + * Always read from local for configured_default +- from version 0.6.0 + * Support local context chained config file + +------------------------------------------------------------------- Old: ---- v0.5.4.tar.gz New: ---- v0.6.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-knack.spec ++++++ --- /var/tmp/diff_new_pack.Rn3gtL/_old 2019-05-22 11:16:45.266500609 +0200 +++ /var/tmp/diff_new_pack.Rn3gtL/_new 2019-05-22 11:16:45.270500606 +0200 @@ -18,7 +18,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-knack -Version: 0.5.4 +Version: 0.6.1 Release: 0 Summary: A Command-Line Interface framework License: MIT ++++++ v0.5.4.tar.gz -> v0.6.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/knack-0.5.4/knack/commands.py new/knack-0.6.1/knack/commands.py --- old/knack-0.5.4/knack/commands.py 2019-03-29 20:11:28.000000000 +0100 +++ new/knack-0.6.1/knack/commands.py 2019-04-26 02:11:43.000000000 +0200 @@ -78,7 +78,10 @@ return defaults_section = self.cli_ctx.config.defaults_section_name + use_local_config_original = self.cli_ctx.config.use_local_config + self.cli_ctx.config.set_to_use_local_config(True) config_value = self.cli_ctx.config.get(defaults_section, default_key, None) + self.cli_ctx.config.set_to_use_local_config(use_local_config_original) if config_value: logger.info("Configured default '%s' for arg %s", config_value, arg.name) overrides.settings['default'] = DefaultStr(config_value) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/knack-0.5.4/knack/config.py new/knack-0.6.1/knack/config.py --- old/knack-0.5.4/knack/config.py 2019-03-29 20:11:28.000000000 +0100 +++ new/knack-0.6.1/knack/config.py 2019-04-26 02:11:43.000000000 +0200 @@ -25,7 +25,7 @@ _DEFAULT_CONFIG_FILE_NAME = 'config' _CONFIG_DEFAULTS_SECTION = 'defaults' - def __init__(self, config_dir=None, config_env_var_prefix=None, config_file_name=None): + def __init__(self, config_dir=None, config_env_var_prefix=None, config_file_name=None, use_local_config=None): """ Manages configuration options available in the CLI :param config_dir: The directory to store config files @@ -38,7 +38,6 @@ config_dir = config_dir or CLIConfig._DEFAULT_CONFIG_DIR ensure_dir(config_dir) config_env_var_prefix = config_env_var_prefix or CLIConfig._DEFAULT_CONFIG_ENV_VAR_PREFIX - self.config_parser = get_config_parser() env_var_prefix = '{}_'.format(config_env_var_prefix.upper()) default_config_dir = os.path.expanduser(config_dir) self.config_dir = os.environ.get('{}CONFIG_DIR'.format(env_var_prefix), default_config_dir) @@ -46,7 +45,24 @@ self.config_path = os.path.join(self.config_dir, configuration_file_name) self._env_var_format = '{}{}'.format(env_var_prefix, '{section}_{option}') self.defaults_section_name = CLIConfig._CONFIG_DEFAULTS_SECTION - self.config_parser.read(self.config_path) + self.use_local_config = use_local_config + self._config_file_chain = [] + current_dir = os.getcwd() + config_dir_name = os.path.basename(self.config_dir) + while current_dir: + current_config_dir = os.path.join(current_dir, config_dir_name) + # Stop if already in the default .azure + if (os.path.normcase(os.path.normpath(current_config_dir)) == + os.path.normcase(os.path.normpath(self.config_dir))): + break + if os.path.isdir(current_config_dir): + self._config_file_chain.append(_ConfigFile(current_config_dir, + os.path.join(current_config_dir, configuration_file_name))) + # Stop if already in root drive + if current_dir == os.path.dirname(current_dir): + break + current_dir = os.path.dirname(current_dir) + self._config_file_chain.append(_ConfigFile(self.config_dir, self.config_path)) def env_var_name(self, section, option): return self._env_var_format.format(section=section.upper(), @@ -55,16 +71,38 @@ def has_option(self, section, option): if self.env_var_name(section, option) in os.environ: return True - return self.config_parser.has_option(section, option) + config_files = self._config_file_chain if self.use_local_config else self._config_file_chain[-1:] + return bool(next((f for f in config_files if f.has_option(section, option)), False)) def get(self, section, option, fallback=_UNSET): - try: - env = self.env_var_name(section, option) - return os.environ[env] if env in os.environ else self.config_parser.get(section, option) - except (configparser.NoSectionError, configparser.NoOptionError): - if fallback is _UNSET: - raise - return fallback + env = self.env_var_name(section, option) + if env in os.environ: + return os.environ[env] + last_ex = None + for config in self._config_file_chain if self.use_local_config else self._config_file_chain[-1:]: + try: + return config.get(section, option) + except (configparser.NoSectionError, configparser.NoOptionError) as ex: + last_ex = ex + + if fallback is _UNSET: + raise last_ex # pylint:disable=raising-bad-type + return fallback + + def items(self, section): + import re + pattern = self.env_var_name(section, '.+') + candidates = [(k.split('_')[-1], os.environ[k], k) for k in os.environ.keys() if re.match(pattern, k)] + result = {c[0]: c for c in candidates} + for config in self._config_file_chain if self.use_local_config else self._config_file_chain[-1:]: + try: + entries = config.items(section) + for name, value in entries: + if name not in result: + result[name] = (name, value, config.config_path) + except (configparser.NoSectionError, configparser.NoOptionError): + pass + return [{'name': name, 'value': value, 'source': source} for name, value, source in result.values()] def getint(self, section, option, fallback=_UNSET): return int(self.get(section, option, fallback)) @@ -78,6 +116,57 @@ raise ValueError('Not a boolean: {}'.format(val)) return CLIConfig._BOOLEAN_STATES[val.lower()] + def set_value(self, section, option, value): + if self.use_local_config: + current_config_dir = os.path.join(os.getcwd(), os.path.basename(self.config_dir)) + config_file_path = os.path.join(current_config_dir, os.path.basename(self.config_path)) + if config_file_path == self._config_file_chain[0].config_path: + self._config_file_chain[0].set_value(section, option, value) + else: + config = _ConfigFile(current_config_dir, config_file_path) + config.set_value(section, option, value) + self._config_file_chain.insert(0, config) + else: + self._config_file_chain[-1].set_value(section, option, value) + + def set_to_use_local_config(self, use_local_config): + self.use_local_config = use_local_config + + +class _ConfigFile(object): + _BOOLEAN_STATES = {'1': True, 'yes': True, 'true': True, 'on': True, + '0': False, 'no': False, 'false': False, 'off': False} + + def __init__(self, config_dir, config_path): + self.config_dir = config_dir + self.config_path = config_path + self.config_parser = get_config_parser() + if os.path.exists(config_path): + self.config_parser.read(config_path) + + def items(self, section): + return self.config_parser.items(section) if self.config_parser else [] + + def has_option(self, section, option): + return self.config_parser.has_option(section, option) if self.config_parser else False + + def get(self, section, option): + if self.config_parser: + return self.config_parser.get(section, option) + raise configparser.NoOptionError(section, option) + + def getint(self, section, option): + return int(self.get(section, option)) + + def getfloat(self, section, option): + return float(self.get(section, option)) + + def getboolean(self, section, option): + val = str(self.get(section, option)) + if val.lower() not in _ConfigFile._BOOLEAN_STATES: + raise ValueError('Not a boolean: {}'.format(val)) + return _ConfigFile._BOOLEAN_STATES[val.lower()] + def set(self, config): ensure_dir(self.config_dir) with open(self.config_path, 'w') as configfile: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/knack-0.5.4/knack/log.py new/knack-0.6.1/knack/log.py --- old/knack-0.5.4/knack/log.py 2019-03-29 20:11:28.000000000 +0100 +++ new/knack-0.6.1/knack/log.py 2019-04-26 02:11:43.000000000 +0200 @@ -45,8 +45,8 @@ return wrap_msg_with_color cls.COLOR_MAP = { - logging.CRITICAL: _color_wrapper(colorama.Fore.RED), - logging.ERROR: _color_wrapper(colorama.Fore.RED), + logging.CRITICAL: _color_wrapper(colorama.Fore.LIGHTRED_EX), + logging.ERROR: _color_wrapper(colorama.Fore.LIGHTRED_EX), logging.WARNING: _color_wrapper(colorama.Fore.YELLOW), logging.INFO: _color_wrapper(colorama.Fore.GREEN), logging.DEBUG: _color_wrapper(colorama.Fore.CYAN) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/knack-0.5.4/setup.py new/knack-0.6.1/setup.py --- old/knack-0.5.4/setup.py 2019-03-29 20:11:28.000000000 +0100 +++ new/knack-0.6.1/setup.py 2019-04-26 02:11:43.000000000 +0200 @@ -9,7 +9,7 @@ from codecs import open from setuptools import setup, find_packages -VERSION = '0.5.4' +VERSION = '0.6.1' DEPENDENCIES = [ 'argcomplete', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/knack-0.5.4/tests/test_config.py new/knack-0.6.1/tests/test_config.py --- old/knack-0.5.4/tests/test_config.py 2019-03-29 20:11:28.000000000 +0100 +++ new/knack-0.6.1/tests/test_config.py 2019-04-26 02:11:43.000000000 +0200 @@ -25,8 +25,9 @@ section = 'MySection' option = 'myoption' value = 'myvalue' - self.cli_config.config_parser.add_section(section) - self.cli_config.config_parser.set(section, option, value) + self.cli_config.set_value(section, option, value) + self.assertTrue(self.cli_config.has_option(section, option)) + self.cli_config.set_to_use_local_config(True) self.assertTrue(self.cli_config.has_option(section, option)) def test_has_option_env(self): @@ -34,18 +35,23 @@ section = 'MySection' option = 'myoption' self.assertTrue(self.cli_config.has_option(section, option)) + self.cli_config.set_to_use_local_config(True) + self.assertTrue(self.cli_config.has_option(section, option)) def test_has_option_env_no(self): section = 'MySection' option = 'myoption' self.assertFalse(self.cli_config.has_option(section, option)) + self.cli_config.set_to_use_local_config(True) + self.assertFalse(self.cli_config.has_option(section, option)) def test_get(self): section = 'MySection' option = 'myoption' value = 'myvalue' - self.cli_config.config_parser.add_section(section) - self.cli_config.config_parser.set(section, option, value) + self.cli_config.set_value(section, option, value) + self.assertEqual(self.cli_config.get(section, option), value) + self.cli_config.set_to_use_local_config(True) self.assertEqual(self.cli_config.get(section, option), value) def test_get_env(self): @@ -54,17 +60,28 @@ option = 'myoption' value = 'myvalue' self.assertEqual(self.cli_config.get(section, option), value) + self.cli_config.set_to_use_local_config(True) + self.assertEqual(self.cli_config.get(section, option), value) def test_get_not_found_section(self): section = 'MySection' option = 'myoption' with self.assertRaises(configparser.NoSectionError): self.cli_config.get(section, option) + self.cli_config.set_to_use_local_config(True) + with self.assertRaises(configparser.NoSectionError): + self.cli_config.get(section, option) def test_get_not_found_option(self): section = 'MySection' option = 'myoption' - self.cli_config.config_parser.add_section(section) + option_other = 'option' + value = 'myvalue' + self.cli_config.set_value(section, option_other, value) + with self.assertRaises(configparser.NoOptionError): + self.cli_config.get(section, option) + self.cli_config.set_to_use_local_config(True) + self.cli_config.set_value(section, option_other, value) with self.assertRaises(configparser.NoOptionError): self.cli_config.get(section, option) @@ -72,21 +89,29 @@ section = 'MySection' option = 'myoption' self.assertEqual(self.cli_config.get(section, option, fallback='fallback'), 'fallback') + self.cli_config.set_to_use_local_config(True) + self.assertEqual(self.cli_config.get(section, option, fallback='fallback'), 'fallback') def test_getint(self): section = 'MySection' option = 'myoption' value = '123' - self.cli_config.config_parser.add_section(section) - self.cli_config.config_parser.set(section, option, value) + self.cli_config.set_value(section, option, value) + self.assertEqual(self.cli_config.getint(section, option), int(value)) + self.cli_config.set_to_use_local_config(True) self.assertEqual(self.cli_config.getint(section, option), int(value)) def test_getint_error(self): section = 'MySection' option = 'myoption' value = 'not_an_int' - self.cli_config.config_parser.add_section(section) - self.cli_config.config_parser.set(section, option, value) + self.cli_config.set_value(section, option, value) + with self.assertRaises(ValueError): + self.cli_config.getint(section, option) + self.cli_config.set_to_use_local_config(True) + with self.assertRaises(ValueError): + self.cli_config.getint(section, option) + self.cli_config.set_value(section, option, value) with self.assertRaises(ValueError): self.cli_config.getint(section, option) @@ -94,16 +119,20 @@ section = 'MySection' option = 'myoption' value = '123.456' - self.cli_config.config_parser.add_section(section) - self.cli_config.config_parser.set(section, option, value) + self.cli_config.set_value(section, option, value) + self.assertEqual(self.cli_config.getfloat(section, option), float(value)) + self.cli_config.set_to_use_local_config(True) self.assertEqual(self.cli_config.getfloat(section, option), float(value)) def test_getfloat_error(self): section = 'MySection' option = 'myoption' value = 'not_a_float' - self.cli_config.config_parser.add_section(section) - self.cli_config.config_parser.set(section, option, value) + self.cli_config.set_value(section, option, value) + with self.assertRaises(ValueError): + self.cli_config.getfloat(section, option) + self.cli_config.set_to_use_local_config(True) + self.cli_config.set_value(section, option, value) with self.assertRaises(ValueError): self.cli_config.getfloat(section, option) @@ -111,16 +140,20 @@ section = 'MySection' option = 'myoption' value = 'true' - self.cli_config.config_parser.add_section(section) - self.cli_config.config_parser.set(section, option, value) + self.cli_config.set_value(section, option, value) + self.assertTrue(self.cli_config.getboolean(section, option)) + self.cli_config.set_to_use_local_config(True) self.assertTrue(self.cli_config.getboolean(section, option)) def test_getboolean_error(self): section = 'MySection' option = 'myoption' value = 'not_a_boolean' - self.cli_config.config_parser.add_section(section) - self.cli_config.config_parser.set(section, option, value) + self.cli_config.set_value(section, option, value) + with self.assertRaises(ValueError): + self.cli_config.getboolean(section, option) + self.cli_config.set_to_use_local_config(True) + self.cli_config.set_value(section, option, value) with self.assertRaises(ValueError): self.cli_config.getboolean(section, option) @@ -133,14 +166,20 @@ def test_set_config_value_duplicate_section_ok(self): self.cli_config.set_value('test_section', 'test_option', 'a_value') self.cli_config.set_value('test_section', 'test_option_another', 'another_value') - config = get_config_parser() - config.read(self.cli_config.config_path) - self.assertEqual(config.get('test_section', 'test_option'), 'a_value') - self.assertEqual(config.get('test_section', 'test_option_another'), 'another_value') + self.assertEqual(self.cli_config.get('test_section', 'test_option'), 'a_value') + self.assertEqual(self.cli_config.get('test_section', 'test_option_another'), 'another_value') + self.cli_config.set_to_use_local_config(True) + self.cli_config.set_value('test_section', 'test_option', 'a_value') + self.cli_config.set_value('test_section', 'test_option_another', 'another_value') + self.assertEqual(self.cli_config.get('test_section', 'test_option'), 'a_value') + self.assertEqual(self.cli_config.get('test_section', 'test_option_another'), 'another_value') def test_set_config_value_not_string(self): with self.assertRaises(TypeError): self.cli_config.set_value('test_section', 'test_option', False) + self.cli_config.set_to_use_local_config(True) + with self.assertRaises(TypeError): + self.cli_config.set_value('test_section', 'test_option', False) def test_set_config_value_file_permissions(self): self.cli_config.set_value('test_section', 'test_option', 'a_value') @@ -155,6 +194,90 @@ self.assertFalse(bool(file_mode & stat.S_IWOTH)) self.assertFalse(bool(file_mode & stat.S_IXOTH)) + def test_has_option_local(self): + section = 'MySection' + option = 'myoption' + value = 'myvalue' + # check local config + self.cli_config.set_to_use_local_config(True) + self.cli_config.set_value(section, option, value) + self.assertTrue(self.cli_config.has_option(section, option)) + # check default config + self.cli_config.set_to_use_local_config(False) + self.assertFalse(self.cli_config.has_option(section, option)) + self.cli_config.set_value(section, option, value) + self.assertTrue(self.cli_config.has_option(section, option)) + + def test_get_local(self): + section = 'MySection' + option = 'myoption' + value = 'myvalue' + local_value = 'localvalue' + # check local config + self.cli_config.set_to_use_local_config(True) + self.cli_config.set_value(section, option, local_value) + self.assertEqual(self.cli_config.get(section, option), local_value) + # check default config + self.cli_config.set_to_use_local_config(False) + self.assertFalse(self.cli_config.has_option(section, option)) + self.cli_config.set_value(section, option, value) + self.assertEqual(self.cli_config.get(section, option), value) + + def test_getint_local(self): + section = 'MySection' + option = 'myoption' + value = '123' + local_value = '1234' + # check local config + self.cli_config.set_to_use_local_config(True) + self.cli_config.set_value(section, option, local_value) + self.assertEqual(self.cli_config.getint(section, option), int(local_value)) + # check default config + self.cli_config.set_to_use_local_config(False) + self.assertFalse(self.cli_config.has_option(section, option)) + self.cli_config.set_value(section, option, value) + self.assertEqual(self.cli_config.getint(section, option), int(value)) + + def test_getfloat_local(self): + section = 'MySection' + option = 'myoption' + value = '123.456' + local_value = '1234.56' + # check local config + self.cli_config.set_to_use_local_config(True) + self.cli_config.set_value(section, option, local_value) + self.assertEqual(self.cli_config.getfloat(section, option), float(local_value)) + # check default config + self.cli_config.set_to_use_local_config(False) + self.assertFalse(self.cli_config.has_option(section, option)) + self.cli_config.set_value(section, option, value) + self.assertEqual(self.cli_config.getfloat(section, option), float(value)) + + def test_getboolean_local(self): + section = 'MySection' + option = 'myoption' + local_value = 'true' + value = 'false' + # check local config + self.cli_config.set_to_use_local_config(True) + self.cli_config.set_value(section, option, local_value) + self.assertTrue(self.cli_config.getboolean(section, option)) + # check default config + self.cli_config.set_to_use_local_config(False) + self.assertFalse(self.cli_config.has_option(section, option)) + self.cli_config.set_value(section, option, value) + self.assertFalse(self.cli_config.getboolean(section, option)) + + def test_set_config_value_duplicate_section_ok_local(self): + self.cli_config.set_to_use_local_config(True) + self.cli_config.set_value('test_section', 'test_option', 'a_value') + self.cli_config.set_value('test_section', 'test_option_another', 'another_value') + self.assertEqual(self.cli_config.get('test_section', 'test_option'), 'a_value') + self.assertEqual(self.cli_config.get('test_section', 'test_option_another'), 'another_value') + self.cli_config.set_to_use_local_config(False) + self.assertFalse(self.cli_config.has_option('test_section', 'test_option')) + self.assertFalse(self.cli_config.has_option('test_section', 'test_option_another')) + if __name__ == '__main__': unittest.main() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/knack-0.5.4/tests/test_log.py new/knack-0.6.1/tests/test_log.py --- old/knack-0.5.4/tests/test_log.py 2019-03-29 20:11:28.000000000 +0100 +++ new/knack-0.6.1/tests/test_log.py 2019-04-26 02:11:43.000000000 +0200 @@ -85,7 +85,7 @@ class TestCustomStreamHandler(unittest.TestCase): - expectation = {logging.CRITICAL: colorama.Fore.RED, logging.ERROR: colorama.Fore.RED, + expectation = {logging.CRITICAL: colorama.Fore.LIGHTRED_EX, logging.ERROR: colorama.Fore.LIGHTRED_EX, logging.WARNING: colorama.Fore.YELLOW, logging.INFO: colorama.Fore.GREEN, logging.DEBUG: colorama.Fore.CYAN}
