def setdefault(self): c = Config({"foo": "bar", "nested": {"leafkey": "leafval"}}) assert c.setdefault("foo") == "bar" assert c.nested.setdefault("leafkey") == "leafval" assert c.setdefault("notfoo", "notbar") == "notbar" assert c.notfoo == "notbar" nested = c.nested.setdefault("otherleaf", "otherval") assert nested == "otherval" assert c.nested.otherleaf == "otherval"
def runtime_can_skip_merging(self): path = join(CONFIGS_PATH, "yaml", "raft.yaml") config = Config(runtime_path=path, lazy=True) assert "outer" not in config._runtime assert "outer" not in config config.load_runtime(merge=False) # Test that we loaded into the per-level dict, but not the # central/merged config. assert "outer" in config._runtime assert "outer" not in config
def unicode_replaced_with_env_value(self): # Python 3 doesn't allow you to put 'bytes' objects into # os.environ, so the test makes no sense there. if six.PY3: return os.environ["RAFT_FOO"] = "myunicode" c = Config(defaults={"foo": u"myoldvalue"}) c.load_shell_env() assert c.foo == "myunicode" assert isinstance(c.foo, str)
def preserves_merged_config(self): c = Config( defaults={"key": "default"}, overrides={"key": "override"} ) assert c.key == "override" assert c._defaults["key"] == "default" c2 = c.clone() assert c2.key == "override" assert c2._defaults["key"] == "default" assert c2._overrides["key"] == "override"
def pop(self): # Root c = Config(defaults={"foo": "bar"}) assert c.pop("foo") == "bar" assert c == {} # With the default arg assert c.pop("wut", "fine then") == "fine then" # Leaf (different key to avoid AmbiguousMergeError) c.nested = {"leafkey": "leafval"} assert c.nested.pop("leafkey") == "leafval" assert c == {"nested": {}}
def nested_dict_values_also_allow_dual_access(self): # TODO: ditto c = Config({"foo": "bar", "biz": {"baz": "boz"}}) # Sanity check - nested doesn't somehow kill simple top level assert c.foo == "bar" assert c["foo"] == "bar" # Actual check assert c.biz.baz == "boz" assert c["biz"]["baz"] == "boz" assert c.biz["baz"] == "boz" assert c["biz"].baz == "boz"
def project_can_skip_merging(self): config = Config( project_location=join(CONFIGS_PATH, "yml"), lazy=True ) assert "outer" not in config._project assert "outer" not in config config.load_project(merge=False) # Test that we loaded into the per-level dict, but not the # central/merged config. assert "outer" in config._project assert "outer" not in config
def arbitrary_types_work_too(self): os.environ["RAFT_FOO"] = "whatever" class Meh(object): def __init__(self, thing=None): pass old_obj = Meh() c = Config(defaults={"foo": old_obj}) c.load_shell_env() assert isinstance(c.foo, Meh) assert c.foo is not old_obj
def booleans(self): for input_, result in ( ("0", False), ("1", True), ("", False), ("meh", True), ("false", True), ): os.environ["RAFT_FOO"] = input_ c = Config(defaults={"foo": bool()}) c.load_shell_env() assert c.foo == result
def reinstatement_of_deleted_values_works_ok(self): # Sounds like a stupid thing to test, but when we have to track # deletions and mutations manually...it's an easy thing to overlook c = Config(defaults={"foo": "bar"}) assert c.foo == "bar" del c["foo"] # Sanity checks assert "foo" not in c assert len(c) == 0 # Put it back again...as a different value, for funsies c.foo = "formerly bar" # And make sure it stuck assert c.foo == "formerly bar"
def merging_does_not_wipe_user_modifications_or_deletions(self): c = Config({"foo": {"bar": "biz"}, "error": True}) c.foo.bar = "notbiz" del c["error"] assert c["foo"]["bar"] == "notbiz" assert "error" not in c c.merge() # Will be back to 'biz' if user changes don't get saved on their # own (previously they are just mutations on the cached central # config) assert c["foo"]["bar"] == "notbiz" # And this would still be here, too assert "error" not in c
def attr_access_has_useful_error_msg(self): c = Config() try: c.nope except AttributeError as e: expected = """ No attribute or config key found for 'nope' Valid keys: ['run', 'runners', 'sudo', 'tasks', 'timeouts'] Valid real attributes: ['clear', 'clone', 'env_prefix', 'file_prefix', 'from_data', 'global_defaults', 'load_base_conf_files', 'load_collection', 'load_defaults', 'load_overrides', 'load_project', 'load_runtime', 'load_shell_env', 'load_system', 'load_user', 'merge', 'pop', 'popitem', 'prefix', 'set_project_location', 'set_runtime_path', 'setdefault', 'update'] """.strip() # noqa assert str(e) == expected else: assert False, "Didn't get an AttributeError on bad key!"
def numeric_types_become_casted(self): tests = [ (int, "5", 5), (float, "5.5", 5.5), # TODO: more? ] # Can't use '5L' in Python 3, even having it in a branch makes # it upset. if not six.PY3: tests.append((long, "5", long(5))) # noqa for old, new_, result in tests: os.environ["RAFT_FOO"] = new_ c = Config(defaults={"foo": old()}) c.load_shell_env() assert c.foo == result
def does_not_reload_file_data(self, load_yaml): path = join(CONFIGS_PATH, "yaml/") c = Config(system_prefix=path) c2 = c.clone() assert c2.outer.inner.hooray == "yaml" # Crummy way to say "only got called with this specific invocation # one time" (since assert_calls_with gets mad about other # invocations w/ different args) calls = load_yaml.call_args_list my_call = call("{}raft.yaml".format(path)) try: calls.remove(my_call) assert my_call not in calls except ValueError: err = "{} not found in {} even once!" assert False, err.format(my_call, calls)
def non_conflicting_values_are_merged(self): # NOTE: this is really just basic clone behavior. class MyConfig(Config): @staticmethod def global_defaults(): orig = Config.global_defaults() orig["new"] = {"data": "ohai"} return orig c = Config(defaults={"other": {"data": "hello"}}) c["runtime"] = {"modification": "sup"} c2 = c.clone(into=MyConfig) # New default data from MyConfig present assert c2.new.data == "ohai" # As well as old default data from the cloned instance assert c2.other.data == "hello" # And runtime user mods from the cloned instance assert c2.runtime.modification == "sup"
def update(self): c = Config( defaults={"foo": "bar", "nested": {"leafkey": "leafval"}} ) # Regular update(dict) c.update({"foo": "notbar"}) assert c.foo == "notbar" c.nested.update({"leafkey": "otherval"}) assert c.nested.leafkey == "otherval" # Apparently allowed but wholly useless c.update() expected = {"foo": "notbar", "nested": {"leafkey": "otherval"}} assert c == expected # Kwarg edition c.update(foo="otherbar") assert c.foo == "otherbar" # Iterator of 2-tuples edition c.nested.update( [("leafkey", "yetanotherval"), ("newleaf", "turnt")] ) assert c.nested.leafkey == "yetanotherval" assert c.nested.newleaf == "turnt"
def user_overrides_systemwide(self): c = Config( system_prefix=join(CONFIGS_PATH, "yaml/"), user_prefix=join(CONFIGS_PATH, "json/"), ) assert c.outer.inner.hooray == "json"
def json_prevents_python(self): c = Config(system_prefix=join(CONFIGS_PATH, "json-and-python/")) assert "python_only" not in c assert "json-only" in c assert c.shared == "json-value"
def yml_prevents_json_or_python(self): c = Config(system_prefix=join(CONFIGS_PATH, "three-of-em/")) assert "json-only" not in c assert "python_only" not in c assert "yml-only" in c assert c.shared == "yml-value"
def runtime_overrides_collection(self): c = Config(runtime_path=join(CONFIGS_PATH, "json", "raft.json")) c.load_collection({"outer": {"inner": {"hooray": "defaults"}}}) c.load_runtime() assert c.outer.inner.hooray == "json"
def runtime_overrides_env_vars(self): os.environ["RAFT_OUTER_INNER_HOORAY"] = "env" c = Config(runtime_path=join(CONFIGS_PATH, "json", "raft.json")) c.load_runtime() c.load_shell_env() assert c.outer.inner.hooray == "json"
def env_vars_override_collection(self): os.environ["RAFT_OUTER_INNER_HOORAY"] = "env" c = Config() c.load_collection({"outer": {"inner": {"hooray": "defaults"}}}) c.load_shell_env() assert c.outer.inner.hooray == "env"
def env_vars_override_systemwide(self): os.environ["RAFT_OUTER_INNER_HOORAY"] = "env" c = Config(system_prefix=join(CONFIGS_PATH, "yaml/")) c.load_shell_env() assert c.outer.inner.hooray == "env"
def env_vars_override_project(self): os.environ["RAFT_OUTER_INNER_HOORAY"] = "env" c = Config(project_location=join(CONFIGS_PATH, "yaml")) c.load_project() c.load_shell_env() assert c.outer.inner.hooray == "env"
def project_overrides_collection(self): c = Config(project_location=join(CONFIGS_PATH, "yaml")) c.load_project() c.load_collection({"outer": {"inner": {"hooray": "defaults"}}}) assert c.outer.inner.hooray == "yaml"
def user_overrides_collection(self): c = Config(user_prefix=join(CONFIGS_PATH, "json/")) c.load_collection({"outer": {"inner": {"hooray": "defaults"}}}) assert c.outer.inner.hooray == "json"
def systemwide_overrides_collection(self): c = Config(system_prefix=join(CONFIGS_PATH, "yaml/")) c.load_collection({"outer": {"inner": {"hooray": "defaults"}}}) assert c.outer.inner.hooray == "yaml"
def collection_overrides_defaults(self): c = Config(defaults={"nested": {"setting": "default"}}) c.load_collection({"nested": {"setting": "collection"}}) assert c.nested.setting == "collection"
def _uncastable_type(self, default): os.environ["RAFT_FOO"] = "stuff" c = Config(defaults={"foo": default}) c.load_shell_env()
def boolean_type_inputs_with_non_boolean_defaults(self): for input_ in ("0", "1", "", "meh", "false"): os.environ["RAFT_FOO"] = input_ c = Config(defaults={"foo": "bar"}) c.load_shell_env() assert c.foo == input_