def popitem(self): c = Config(defaults={'foo': 'bar'}) eq_(c.popitem(), ('foo', 'bar')) eq_(c, {}) c.nested = {'leafkey': 'leafval'} eq_(c.nested.popitem(), ('leafkey', 'leafval')) eq_(c, {'nested': {}})
def clear(self): c = Config(defaults={'foo': 'bar'}) c.clear() eq_(c, {}) c.nested = {'leafkey': 'leafval'} c.nested.clear() eq_(c, {'nested': {}})
def env_vars_override_systemwide(self): os.environ['INVOKE_OUTER_INNER_HOORAY'] = 'env' c = Config( system_prefix=join(CONFIGS_PATH, 'yaml/'), ) c.load_shell_env() assert c.outer.inner.hooray == 'env'
def does_not_deepcopy(self): c = Config(defaults={ # Will merge_dicts happily 'oh': {'dear': {'god': object()}}, # And shallow-copy compound values 'shallow': {'objects': ['copy', 'okay']}, # Will preserve refrences to the innermost dict, sadly. Not # much we can do without incurring deepcopy problems (or # reimplementing it entirely) 'welp': {'cannot': ['have', {'everything': 'we want'}]}, }) c2 = c.clone() # Basic identity assert c is not c2, "Clone had same identity as original!" # Dicts get recreated assert c.oh is not c2.oh, "Top level key had same identity!" assert c.oh.dear is not c2.oh.dear, "Midlevel key had same identity!" # noqa # Basic values get copied err = "Leaf object() had same identity!" assert c.oh.dear.god is not c2.oh.dear.god, err assert c.shallow.objects == c2.shallow.objects err = "Shallow list had same identity!" assert c.shallow.objects is not c2.shallow.objects, err # Deeply nested non-dict objects are stil problematic, oh well err = "Huh, a deeply nested dict-in-a-list had different identity?" assert c.welp.cannot[1] is c2.welp.cannot[1], err err = "Huh, a deeply nested dict-in-a-list value had different identity?" # noqa assert c.welp.cannot[1]['everything'] is c2.welp.cannot[1]['everything'], err # noqa
def env_vars_override_systemwide(self): os.environ['HOORAY'] = 'env' c = Config( system_prefix=join(CONFIGS_PATH, 'yaml', 'invoke'), ) c.load_shell_env() eq_(c.hooray, 'env')
def popitem(self): c = Config(defaults={'foo': 'bar'}) assert c.popitem() == ('foo', 'bar') assert c == {} c.nested = {'leafkey': 'leafval'} assert c.nested.popitem() == ('leafkey', 'leafval') assert c == {'nested': {}}
def clear(self): c = Config(defaults={'foo': 'bar'}) c.clear() assert c == {} c.nested = {'leafkey': 'leafval'} c.nested.clear() assert c == {'nested': {}}
def loads_no_project_specific_file_if_no_project_location_given(self): c = Config() assert c._project_path is None c.load_project() assert list(c._project.keys()) == [] defaults = ['tasks', 'run', 'runners', 'sudo'] assert set(c.keys()) == set(defaults)
def project_specific(self): "Local-to-project conf files" for type_ in TYPES: c = Config(project_location=join(CONFIGS_PATH, type_)) assert 'outer' not in c c.load_project() assert c.outer.inner.hooray == type_
def merging_can_be_deferred(self): c = Config() assert 'foo' not in c._collection assert 'foo' not in c c.load_collection({'foo': 'bar'}, merge=False) assert 'foo' in c._collection assert 'foo' not in c
def performed_explicitly_and_directly(self): # TODO: do we want to update the other levels to allow 'direct' # loading like this, now that they all have explicit methods? c = Config() assert 'foo' not in c c.load_collection({'foo': 'bar'}) assert c.foo == 'bar'
def env_vars_override_user(self): os.environ['OUTER_INNER_HOORAY'] = 'env' c = Config( user_prefix=join(CONFIGS_PATH, 'yaml', 'invoke'), ) c.load_shell_env() eq_(c.outer.inner.hooray, 'env')
def project_overrides_systemwide(self): c = Config( system_prefix=join(CONFIGS_PATH, 'json/'), project_location=join(CONFIGS_PATH, 'yaml'), ) c.load_project() assert c.outer.inner.hooray == 'yaml'
def runtime_overrides_systemwide(self): c = Config( runtime_path=join(CONFIGS_PATH, 'json', 'invoke.json'), system_prefix=join(CONFIGS_PATH, 'yaml/'), ) c.load_runtime() assert c.outer.inner.hooray == 'json'
def preserves_basic_members(self): c1 = Config( defaults={'key': 'default'}, overrides={'key': 'override'}, system_prefix='global', user_prefix='user', project_location='project', runtime_path='runtime.yaml', ) c2 = c1.clone() # NOTE: expecting identical defaults also implicitly tests that # clone() passes in defaults= instead of doing an empty init + # copy. (When that is not the case, we end up with # global_defaults() being rerun and re-added to _defaults...) assert c2._defaults == c1._defaults assert c2._defaults is not c1._defaults assert c2._overrides == c1._overrides assert c2._overrides is not c1._overrides assert c2._system_prefix == c1._system_prefix assert c2._user_prefix == c1._user_prefix assert c2._project_prefix == c1._project_prefix assert c2.prefix == c1.prefix assert c2.file_prefix == c1.file_prefix assert c2.env_prefix == c1.env_prefix assert c2._runtime_path == c1._runtime_path
def env_vars_override_project(self): os.environ['HOORAY'] = 'env' c = Config( project_home=join(CONFIGS_PATH, 'yaml'), ) c.load_shell_env() eq_(c.hooray, 'env')
def env_vars_override_project(self): os.environ['INVOKE_OUTER_INNER_HOORAY'] = 'env' c = Config( project_home=join(CONFIGS_PATH, 'yaml'), ) c.load_shell_env() eq_(c.outer.inner.hooray, 'env')
def setdefault(self): c = Config({'foo': 'bar', 'nested': {'leafkey': 'leafval'}}) eq_(c.setdefault('foo'), 'bar') eq_(c.nested.setdefault('leafkey'), 'leafval') eq_(c.setdefault('notfoo', 'notbar'), 'notbar') eq_(c.notfoo, 'notbar') eq_(c.nested.setdefault('otherleaf', 'otherval'), 'otherval') eq_(c.nested.otherleaf, 'otherval')
def delitem(self): "__delitem__" c = Config(defaults={'foo': 'bar'}) del c['foo'] eq_(c, {}) c.nested = {'leafkey': 'leafval'} del c.nested['leafkey'] eq_(c, {'nested': {}})
def delitem(self): "__delitem__" c = Config(defaults={'foo': 'bar'}) del c['foo'] assert c == {} c.nested = {'leafkey': 'leafval'} del c.nested['leafkey'] assert c == {'nested': {}}
def delattr(self): "__delattr__" c = Config(defaults={'foo': 'bar'}) del c.foo assert c == {} c.nested = {'leafkey': 'leafval'} del c.nested.leafkey assert c == {'nested': {}}
def runtime_overrides_project(self): c = Config( runtime_path=join(CONFIGS_PATH, 'json', 'invoke.json'), project_location=join(CONFIGS_PATH, 'yaml'), ) c.load_runtime() c.load_project() assert c.outer.inner.hooray == 'json'
def delattr(self): "__delattr__" c = Config(defaults={'foo': 'bar'}) del c.foo eq_(c, {}) c.nested = {'leafkey': 'leafval'} del c.nested.leafkey eq_(c, {'nested': {}})
def nonexistent_attrs_can_be_set_to_create_new_top_level_configs(self): # I.e. some_config.foo = 'bar' is like some_config['foo'] = 'bar'. # When this test breaks it usually means some_config.foo = 'bar' # sets a regular attribute - and the configuration itself is never # touched! c = Config() c.some_setting = 'some_value' eq_(c['some_setting'], 'some_value')
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 cli_overrides_override_all(self): "CLI-driven overrides win vs all other layers" # TODO: expand into more explicit tests like the above? meh c = Config( overrides={'outer': {'inner': {'hooray': 'overrides'}}}, runtime_path=join(CONFIGS_PATH, 'json', 'invoke.json') ) c.load_runtime() assert c.outer.inner.hooray == 'overrides'
def arbitrary_types_work_too(self): os.environ['FOO'] = 'whatever' class Meh(object): def __init__(self, thing=None): pass old_obj = Meh() c = Config(defaults={'foo': old_obj}) c.load_shell_env() ok_(isinstance(c.foo, Meh)) ok_(c.foo is not old_obj)
def runtime_can_skip_merging(self): path = join(CONFIGS_PATH, 'yaml', 'invoke.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['FOO'] = 'myunicode' c = Config(defaults={'foo': six.u('myoldvalue')}) c.load_shell_env() eq_(c.foo, 'myunicode') ok_(isinstance(c.foo, str))
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['INVOKE_FOO'] = 'myunicode' c = Config(defaults={'foo': u'myoldvalue'}) c.load_shell_env() assert c.foo == 'myunicode' assert isinstance(c.foo, str)
def system_and_user_files_loaded_automatically(self, merge, load_u, load_s): Config() load_s.assert_called_once_with(merge=False) load_u.assert_called_once_with(merge=False) merge.assert_called_once_with()
def supports_nested_mutation_via_attribute_access(self): c = Config({'foo': {'bar': 'biz'}}) assert c.foo.bar == 'biz' c.foo.bar = 'notbiz' assert c.foo.bar == 'notbiz' assert c['foo']['bar'] == 'notbiz'
def project_location_can_be_set_after_init(self): c = Config() assert 'outer' not in c c.set_project_location(join(CONFIGS_PATH, 'yml')) c.load_project() assert c.outer.inner.hooray == 'yml'
def configure_user_location_prefix(self, load_yaml): Config(user_prefix='whatever/') load_yaml.assert_any_call('whatever/invoke.yaml')
def default_user_prefix_is_homedir_plus_dot(self, load_yaml): Config() load_yaml.assert_any_call(expanduser('~/.invoke.yaml'))
def configure_project_location(self, load_yaml): Config(project_location='someproject').load_project() load_yaml.assert_any_call(join('someproject', 'invoke.yaml'))
def nonexistent_attr_setting_works_nested_too(self): c = Config() c.a_nest = {} assert c['a_nest'] == {} c.a_nest.an_egg = True assert c['a_nest']['an_egg']
def overrides_dict_is_first_posarg(self): c = Config({'new': 'data', 'run': {'hide': True}}) assert c.run.hide is True # default is False assert c.run.warn is False # in global defaults, untouched assert c.new == 'data' # data only present at overrides layer
def non_predeclared_settings_do_not_get_consumed(self): os.environ['INVOKE_HELLO'] = "is it me you're looking for?" c = Config() c.load_shell_env() assert 'HELLO' not in c assert 'hello' not in c
def base_case_defaults_to_INVOKE_prefix(self): os.environ['INVOKE_FOO'] = 'bar' c = Config(defaults={'foo': 'notbar'}) c.load_shell_env() assert c.foo == 'bar'
def defaults_to_None(self): assert Config().env_prefix is None
def is_explicitly_not_hashable(self): with raises(TypeError): hash(Config())
def allows_comparison_with_real_dicts(self): c = Config({'foo': {'bar': 'biz'}}) assert c['foo'] == {'bar': 'biz'}
def overrides_dict_is_also_a_kwarg(self): c = Config(overrides={'run': {'hide': True}}) assert c.run.hide is True
def is_iterable_like_dict(self): c = Config(defaults={'a': 1, 'b': 2}) assert set(c.keys()) == {'a', 'b'} assert set(list(c)) == {'a', 'b'}
def runtime_conf_via_cli_flag(self): c = Config(runtime_path=join(CONFIGS_PATH, 'yaml', 'invoke.yaml')) c.load_runtime() assert c.outer.inner.hooray == 'yaml'
def accepts_defaults_dict_kwarg(self): c = Config(defaults={'super': 'low level'}) assert c.super == 'low level'
def _load(kwarg, type_, **kwargs): path = join(CONFIGS_PATH, type_ + "/") kwargs[kwarg] = path return Config(**kwargs)
def configure_runtime_path(self, load_yaml): Config(runtime_path='some/path.yaml').load_runtime() load_yaml.assert_any_call('some/path.yaml')
def overrides_can_skip_merging(self): c = Config() c.load_overrides({'foo': 'bar'}, merge=False) assert 'foo' not in c c.merge() assert c.foo == 'bar'
def supports_mutation_via_attribute_access(self): c = Config({'foo': 'bar'}) assert c.foo == 'bar' c.foo = 'notbar' assert c.foo == 'notbar' assert c['foo'] == 'notbar'
def allows_dict_and_attr_access(self): # TODO: combine with tests for Context probably c = Config({'foo': 'bar'}) assert c.foo == 'bar' assert c['foo'] == 'bar'
def defaults_to_invoke(self): assert Config().prefix == 'invoke'
def can_be_used_directly_after_init(self): # No load() here... c = Config({'lots of these': 'tests look similar'}) assert c['lots of these'] == 'tests look similar'
def default_system_prefix_is_etc(self, load_yaml): # TODO: make this work on Windows somehow without being a total # tautology? heh. Config() load_yaml.assert_any_call('/etc/invoke.yaml')
def string_display(self): "__str__ and friends" config = Config(defaults={'foo': 'bar'}) assert repr(config) == "<Config: {'foo': 'bar'}>"
def defaults_can_skip_merging(self): c = Config() c.load_defaults({'foo': 'bar'}, merge=False) assert 'foo' not in c c.merge() assert c.foo == 'bar'
def overrides_can_be_given_via_method(self): c = Config(defaults={'foo': 'bar'}) assert c.foo == 'bar' # defaults level c.load_overrides({'foo': 'notbar'}) assert c.foo == 'notbar' # overrides level
def unknown_suffix_in_runtime_path_raises_useful_error(self): c = Config(runtime_path=join(CONFIGS_PATH, 'screw.ini')) with raises(UnknownFileType): c.load_runtime()
def defaults_can_be_given_via_method(self): c = Config() assert 'foo' not in c c.load_defaults({'foo': 'bar'}) assert c.foo == 'bar'