diff --git a/README.md b/README.md index 1d92e81..a3ff415 100644 --- a/README.md +++ b/README.md @@ -219,7 +219,7 @@ Note that all of these functions, in addition from being type hinted, also stron To set a default configuration value programmatically: ```python -g.set_defualt_string('some.default.k', 'default value') +g.set_default_string('some.default.k', 'default value') ``` Defined by the function signature @@ -352,4 +352,4 @@ Currently only dynamic secret support is generation of credentials for database. to maintain ease of library. To get a dynamic secret from the database, use the function `generate_database_dynamic_secret` which will raise Runtime exceptions if the proper setup is not done -otherwise update gestalt config map with the username and password \ No newline at end of file +otherwise update gestalt config map with the username and password diff --git a/gestalt/__init__.py b/gestalt/__init__.py index 1e5b10d..df253aa 100644 --- a/gestalt/__init__.py +++ b/gestalt/__init__.py @@ -395,6 +395,7 @@ def __get( ) split_keys = key.split(self.__delim_char) consider_keys = list() + config_val = None for split_key in split_keys: consider_keys.append(split_key) joined_key = self.__delim_char.join(consider_keys) @@ -402,8 +403,10 @@ def __get( key_to_search=joined_key, default=default, object_type=t) - if config_val is not None: + if config_val is not None and config_val != default: return config_val + if default is not None: + return default raise ValueError( f'Given key {key} is not in any configuration and no default is provided' diff --git a/setup.py b/setup.py index bce283c..99cb925 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ def readme(): reqs_list = list(map(lambda x: x.rstrip(), reqs)) setup(name='gestalt-cfg', - version='3.3.3', + version='3.3.4', description='A sensible configuration library for Python', long_description=readme(), long_description_content_type="text/markdown", diff --git a/tests/test_gestalt.py b/tests/test_gestalt.py index 61bdb0a..bbf82cc 100644 --- a/tests/test_gestalt.py +++ b/tests/test_gestalt.py @@ -54,7 +54,7 @@ def test_combine_into_empty_dict(): # Testing JSON Loading def test_loading_json(): g = gestalt.Gestalt() - g.add_config_path('./tests/testdata') + g.add_config_path("./tests/testdata") g.build_config() x = g.dump() assert len(x) @@ -62,7 +62,7 @@ def test_loading_json(): def test_loading_json_file(): g = gestalt.Gestalt() - g.add_config_file('./tests/testdata/testjson.json') + g.add_config_file("./tests/testdata/testjson.json") g.build_config() x = g.dump() assert len(x) @@ -71,57 +71,57 @@ def test_loading_json_file(): def test_loading_file_dir(): g = gestalt.Gestalt() with pytest.raises(ValueError) as terr: - g.add_config_file('./tests/testdata') - assert 'is not a file' in terr + g.add_config_file("./tests/testdata") + assert "is not a file" in terr def test_loading_file_nonexist(): g = gestalt.Gestalt() with pytest.raises(ValueError) as terr: - g.add_config_file('./tests/testdata/nothere.yaml') + g.add_config_file("./tests/testdata/nothere.yaml") g.build_config() - assert 'is not a file' in terr + assert "is not a file" in terr def test_loading_file_bad_yaml(): g = gestalt.Gestalt() with pytest.raises(ValueError) as terr: - g.add_config_file('./tests/testdatabad/testyaml.yaml') + g.add_config_file("./tests/testdatabad/testyaml.yaml") g.build_config() assert terr.type is ValueError - assert 'but cannot be read as such' in terr.value.args[0] + assert "but cannot be read as such" in terr.value.args[0] def test_loading_file_bad_json(): g = gestalt.Gestalt() with pytest.raises(ValueError) as terr: - g.add_config_file('./tests/testdatabad/testjson.json') + g.add_config_file("./tests/testdatabad/testjson.json") g.build_config() assert terr.type is ValueError - assert 'but cannot be read as such' in terr.value.args[0] + assert "but cannot be read as such" in terr.value.args[0] def test_loading_dir_bad_files(): g = gestalt.Gestalt() with pytest.raises(ValueError) as terr: - g.add_config_path('./tests/testdatabad') + g.add_config_path("./tests/testdatabad") g.build_config() assert terr.type is ValueError - assert 'but cannot be read as such' in terr.value.args[0] + assert "but cannot be read as such" in terr.value.args[0] def test_loading_dir_bad_files_yaml_only(): g = gestalt.Gestalt() with pytest.raises(ValueError) as terr: - g.add_config_path('./tests/testdatabadyaml') + g.add_config_path("./tests/testdatabadyaml") g.build_config() assert terr.type is ValueError - assert 'but cannot be read as such' in terr.value.args[0] + assert "but cannot be read as such" in terr.value.args[0] def test_loading_yaml_file(): g = gestalt.Gestalt() - g.add_config_file('./tests/testdata/testyaml.yaml') + g.add_config_file("./tests/testdata/testyaml.yaml") g.build_config() x = g.dump() assert len(x) @@ -130,329 +130,345 @@ def test_loading_yaml_file(): def test_loading_json_nonexist_dir(): g = gestalt.Gestalt() with pytest.raises(ValueError) as terr: - g.add_config_path('./nonexistpath') - assert 'does not exist' in terr + g.add_config_path("./nonexistpath") + assert "does not exist" in terr def test_loading_json_file_not_dir(): g = gestalt.Gestalt() with pytest.raises(ValueError) as terr: - g.add_config_path('./setup.py') - assert 'is not a directory' in terr + g.add_config_path("./setup.py") + assert "is not a directory" in terr def test_get_wrong_type(): g = gestalt.Gestalt() - g.add_config_path('./tests/testdata') + g.add_config_path("./tests/testdata") g.build_config() with pytest.raises(TypeError) as terr: - g.get_string('numbers') - assert 'is not of type' in terr + g.get_string("numbers") + assert "is not of type" in terr def test_get_non_exist_key(): g = gestalt.Gestalt() - g.add_config_path('./tests/testdata') + g.add_config_path("./tests/testdata") g.build_config() with pytest.raises(ValueError) as terr: - g.get_string('non-exist') - assert 'is not in any configuration and no default is provided' in terr + g.get_string("non-exist") + assert "is not in any configuration and no default is provided" in terr def test_get_key_wrong_type(): g = gestalt.Gestalt() - g.add_config_path('./tests/testdata') + g.add_config_path("./tests/testdata") g.build_config() with pytest.raises(TypeError) as terr: g.get_string(1234) - assert 'is not of string type' in terr + assert "is not of string type" in terr def test_get_key_wrong_default_type(): g = gestalt.Gestalt() - g.add_config_path('./tests/testdata') + g.add_config_path("./tests/testdata") g.build_config() with pytest.raises(TypeError) as terr: - g.get_string('nonexist', 1234) - assert 'Provided default is of incorrect type' in terr + g.get_string("nonexist", 1234) + assert "Provided default is of incorrect type" in terr def test_get_json_string(): g = gestalt.Gestalt() - g.add_config_path('./tests/testdata') + g.add_config_path("./tests/testdata") g.build_config() - testval = g.get_string('yarn') - assert testval == 'blue skies' + testval = g.get_string("yarn") + assert testval == "blue skies" def test_get_json_default_string(): g = gestalt.Gestalt() - g.add_config_path('./tests/testdata') + g.add_config_path("./tests/testdata") g.build_config() - testval = g.get_string('nonexist', 'mydefval') - assert testval == 'mydefval' + testval = g.get_string("nonexist", "mydefval") + assert testval == "mydefval" def test_get_json_set_default_string(): g = gestalt.Gestalt() - g.add_config_path('./tests/testdata') + g.add_config_path("./tests/testdata") g.build_config() - g.set_default_string('nonexisttest', 'otherdefval') - testval = g.get_string('nonexisttest') - assert testval == 'otherdefval' + g.set_default_string("nonexisttest", "otherdefval") + testval = g.get_string("nonexisttest") + assert testval == "otherdefval" def test_get_json_int(): g = gestalt.Gestalt() - g.add_config_path('./tests/testdata') + g.add_config_path("./tests/testdata") g.build_config() - testval = g.get_int('numbers') + testval = g.get_int("numbers") assert testval == 12345678 def test_get_json_float(): g = gestalt.Gestalt() - g.add_config_path('./tests/testdata') + g.add_config_path("./tests/testdata") g.build_config() - testval = g.get_float('strangenumbers') + testval = g.get_float("strangenumbers") assert testval == 123.456 def test_get_json_bool(): g = gestalt.Gestalt() - g.add_config_path('./tests/testdata') + g.add_config_path("./tests/testdata") g.build_config() - testval = g.get_bool('truthy') + testval = g.get_bool("truthy") assert testval is True def test_get_json_list(): g = gestalt.Gestalt() - g.add_config_path('./tests/testdata') + g.add_config_path("./tests/testdata") g.build_config() - testval = g.get_list('listing') + testval = g.get_list("listing") assert testval - assert 'dog' in testval - assert 'cat' in testval + assert "dog" in testval + assert "cat" in testval def test_get_json_nested(): g = gestalt.Gestalt() - g.add_config_path('./tests/testdata') + g.add_config_path("./tests/testdata") g.build_config() - testval = g.get_string('deep.nested1') - assert testval == 'hello' + testval = g.get_string("deep.nested1") + assert testval == "hello" def test_get_yaml_nested(): g = gestalt.Gestalt() - g.add_config_path('./tests/testdata') + g.add_config_path("./tests/testdata") g.build_config() - testval = g.get_string('deep_yaml.nest1.nest2.foo') - assert testval == 'hello' + testval = g.get_string("deep_yaml.nest1.nest2.foo") + assert testval == "hello" + + +def test_get_yaml_nested_default(): + g = gestalt.Gestalt() + g.add_config_path("./tests/testdata") + g.build_config() + testval = g.get_string("deep_yaml.nest1.nest2.foo", 'default') + assert testval == "hello" + + +def test_get_yaml_missing_nested_default(): + g = gestalt.Gestalt() + g.add_config_path("./tests/testdata") + g.build_config() + testval = g.get_string("deep_yaml.nest1.nest2.fob", 'default') + assert testval == "default" # Test Set Overriding def test_set_string(): g = gestalt.Gestalt() - g.set_string('mykey', 'myval') - testval = g.get_string('mykey') - assert testval == 'myval' + g.set_string("mykey", "myval") + testval = g.get_string("mykey") + assert testval == "myval" def test_set_int(): g = gestalt.Gestalt() - g.set_int('mykey', 1234) - testval = g.get_int('mykey') + g.set_int("mykey", 1234) + testval = g.get_int("mykey") assert testval == 1234 def test_set_float(): g = gestalt.Gestalt() - g.set_float('mykey', 45.23) - testval = g.get_float('mykey') + g.set_float("mykey", 45.23) + testval = g.get_float("mykey") assert testval == 45.23 def test_set_bool(): g = gestalt.Gestalt() - g.set_bool('mykey', False) - testval = g.get_bool('mykey') + g.set_bool("mykey", False) + testval = g.get_bool("mykey") assert testval is False def test_set_list(): g = gestalt.Gestalt() - g.set_list('mykey', ['hi', 'bye']) - testval = g.get_list('mykey') + g.set_list("mykey", ["hi", "bye"]) + testval = g.get_list("mykey") assert testval - assert 'hi' in testval - assert 'bye' in testval + assert "hi" in testval + assert "bye" in testval def test_set_int_get_bad(): g = gestalt.Gestalt() - g.set_int('mykey', 1234) + g.set_int("mykey", 1234) with pytest.raises(TypeError) as terr: - g.get_string('mykey') - assert 'Given set key is not of type' in terr + g.get_string("mykey") + assert "Given set key is not of type" in terr def test_set_bad_key_type(): g = gestalt.Gestalt() with pytest.raises(TypeError) as terr: - g.set_string(1234, 'myval') - assert 'is not of string type' in terr + g.set_string(1234, "myval") + assert "is not of string type" in terr def test_set_bad_type(): g = gestalt.Gestalt() with pytest.raises(TypeError) as terr: - g.set_string('mykey', 123) - assert 'is not of type' in terr + g.set_string("mykey", 123) + assert "is not of type" in terr def test_re_set_bad_type(): g = gestalt.Gestalt() - g.set_string('mykey', '123') + g.set_string("mykey", "123") with pytest.raises(TypeError) as terr: - g.set_int('mykey', 123) - assert 'Overriding key' in terr + g.set_int("mykey", 123) + assert "Overriding key" in terr def test_set_override(): g = gestalt.Gestalt() - g.add_config_path('./tests/testdata') + g.add_config_path("./tests/testdata") g.build_config() - testval = g.get_int('numbers') + testval = g.get_int("numbers") assert testval == 12345678 - g.set_int('numbers', 6543) - testval = g.get_int('numbers') + g.set_int("numbers", 6543) + testval = g.get_int("numbers") assert testval == 6543 def test_set_bad_type_file_config(): g = gestalt.Gestalt() - g.add_config_path('./tests/testdata') + g.add_config_path("./tests/testdata") g.build_config() with pytest.raises(TypeError) as terr: - g.set_string('numbers', 'notgood') - assert 'File config has' in terr + g.set_string("numbers", "notgood") + assert "File config has" in terr def test_set_bad_type_default_config(): g = gestalt.Gestalt() - g.set_default_string('mykey', 'mystring') + g.set_default_string("mykey", "mystring") with pytest.raises(TypeError) as terr: - g.set_int('mykey', 123) - assert 'Default config has' in terr + g.set_int("mykey", 123) + assert "Default config has" in terr # Test Env Variables def test_get_env_string(): g = gestalt.Gestalt() g.auto_env() - os.environ['MYKEY'] = 'myval' - testval = g.get_string('mykey') - assert testval == 'myval' + os.environ["MYKEY"] = "myval" + testval = g.get_string("mykey") + assert testval == "myval" def test_get_env_int(): g = gestalt.Gestalt() g.auto_env() - os.environ['MYKEY'] = '999' - testval = g.get_int('mykey') + os.environ["MYKEY"] = "999" + testval = g.get_int("mykey") assert testval == 999 def test_get_nested_env_string(): g = gestalt.Gestalt() g.auto_env() - os.environ['MY_KEY'] = 'myval' - testval = g.get_string('my.key') - assert testval == 'myval' + os.environ["MY_KEY"] = "myval" + testval = g.get_string("my.key") + assert testval == "myval" def test_get_env_bad_type(): g = gestalt.Gestalt() g.auto_env() - os.environ['MY_KEY'] = 'myval' + os.environ["MY_KEY"] = "myval" with pytest.raises(TypeError) as terr: - g.get_int('my.key') + g.get_int("my.key") assert "could not be converted to type" in terr # Test Default Values def test_set_default_string(): g = gestalt.Gestalt() - g.set_default_string('mykey', 'myval') - testval = g.get_string('mykey') - assert testval == 'myval' + g.set_default_string("mykey", "myval") + testval = g.get_string("mykey") + assert testval == "myval" def test_set_default_int(): g = gestalt.Gestalt() - g.set_default_int('mykey', 1234) - testval = g.get_int('mykey') + g.set_default_int("mykey", 1234) + testval = g.get_int("mykey") assert testval == 1234 def test_set_default_float(): g = gestalt.Gestalt() - g.set_default_float('mykey', 1234.05) - testval = g.get_float('mykey') + g.set_default_float("mykey", 1234.05) + testval = g.get_float("mykey") assert testval == 1234.05 def test_set_default_bool(): g = gestalt.Gestalt() - g.set_default_bool('mykey', False) - testval = g.get_bool('mykey') + g.set_default_bool("mykey", False) + testval = g.get_bool("mykey") assert testval is False def test_set_default_list(): g = gestalt.Gestalt() - g.set_default_list('mykey', ['bear', 'bull']) - testval = g.get_list('mykey') + g.set_default_list("mykey", ["bear", "bull"]) + testval = g.get_list("mykey") assert testval - assert 'bear' in testval - assert 'bull' in testval + assert "bear" in testval + assert "bull" in testval def test_set_default_int_get_bad(): g = gestalt.Gestalt() - g.set_default_int('mykey', 1234) + g.set_default_int("mykey", 1234) with pytest.raises(TypeError) as terr: - g.get_string('mykey') - assert 'Given default set key is not of type' in terr + g.get_string("mykey") + assert "Given default set key is not of type" in terr def test_set_default_string_bad_key(): g = gestalt.Gestalt() with pytest.raises(TypeError) as terr: - g.set_default_string(1234, 'myval') - assert 'Given key is not of string type' in terr + g.set_default_string(1234, "myval") + assert "Given key is not of string type" in terr def test_set_default_string_bad_val(): g = gestalt.Gestalt() with pytest.raises(TypeError) as terr: - g.set_default_string('mykey', 123) - assert 'Input value when setting default' in terr + g.set_default_string("mykey", 123) + assert "Input value when setting default" in terr def test_set_default_string_bad_val_override(): g = gestalt.Gestalt() with pytest.raises(TypeError) as terr: - g.set_default_string('mykey', 'myval') - g.set_default_int('mykey', 1234) - assert 'Overriding default key' in terr + g.set_default_string("mykey", "myval") + g.set_default_int("mykey", 1234) + assert "Overriding default key" in terr def test_override_nested_config(): g = gestalt.Gestalt() - g.add_config_path('./tests/testoverride/') + g.add_config_path("./tests/testoverride/") g.build_config() assert g.get_int("local") == 123456 assert g.get_string("nested1.nested2") == "final" @@ -463,19 +479,19 @@ def test_override_nested_config(): def test_set_default_bad_type_file_config(): g = gestalt.Gestalt() - g.add_config_path('./tests/testdata') + g.add_config_path("./tests/testdata") g.build_config() with pytest.raises(TypeError) as terr: - g.set_default_string('numbers', 'notgood') - assert 'File config has' in terr + g.set_default_string("numbers", "notgood") + assert "File config has" in terr def test_set_default_bad_type_set_config(): g = gestalt.Gestalt() - g.set_string('mykey', 'mystring') + g.set_string("mykey", "mystring") with pytest.raises(TypeError) as terr: - g.set_default_int('mykey', 123) - assert 'Set config has' in terr + g.set_default_int("mykey", 123) + assert "Set config has" in terr def test_vault_setup(): @@ -553,7 +569,6 @@ def except_once(self, **kwargs): with patch("gestalt.vault.sleep", side_effect=except_once, autospec=True) as mock_sleep: - with patch("gestalt.vault.hvac.Client") as mock_client: v = Vault(role="test-role", jwt="test-jwt") @@ -610,7 +625,7 @@ def test_vault_start_dynamic_lease(mock_vault_workers): "lease_duration": 5, "data": { "data": "mock_data" - } + }, } mock_vault_client_patch = patch("gestalt.vault.hvac.Client.read", @@ -620,9 +635,8 @@ def test_vault_start_dynamic_lease(mock_vault_workers): mock_kube_token_queue = Mock() with patch( "gestalt.vault.Queue", - side_effect=[mock_dynamic_token_queue, - mock_kube_token_queue]) as mock_queues: - + side_effect=[mock_dynamic_token_queue, mock_kube_token_queue], + ) as mock_queues: v = Vault(role=None, jwt=None) g = gestalt.Gestalt() g.add_config_file("./tests/testvault/testmount.json")