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
+
+- 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}