def merging_dict_into_nondict_raises_error(self): # TODO: or...should it?! If a user really wants to take a pre-existing # config path and make it 'deeper' by overwriting e.g. a string with a # dict of strings (or whatever)...should they be allowed to? d1 = {'foo': 'bar'} d2 = {'foo': {'uh': 'oh'}} merge_dicts(d1, d2)
def merging_dict_into_nondict_raises_error(self): # TODO: or...should it?! If a user really wants to take a pre-existing # config path and make it 'deeper' by overwriting e.g. a string with a # dict of strings (or whatever)...should they be allowed to? d1 = {"foo": "bar"} d2 = {"foo": {"uh": "oh"}} with raises(AmbiguousMergeError): merge_dicts(d1, d2)
def global_defaults(): defaults = FabConfig.global_defaults() ours = { 'platform': 'Win64', 'configuration': 'DevelopmentEditor', 'type': 'Editor', } merge_dicts(defaults, ours) return defaults
def mixed_branch_levels_merges_ok(self): d1 = {"foo": {"bar": {"biz": "baz"}}, "meh": 17, "myown": "ok"} d2 = {"foo": {"bar": {"biz": "notbaz"}}, "meh": 25} merge_dicts(d1, d2) expected = { "foo": {"bar": {"biz": "notbaz"}}, "meh": 25, "myown": "ok", } assert d1 == expected
def mixed_branch_levels_merges_ok(self): d1 = {'foo': {'bar': {'biz': 'baz'}}, 'meh': 17, 'myown': 'ok'} d2 = {'foo': {'bar': {'biz': 'notbaz'}}, 'meh': 25} merge_dicts(d1, d2) expected = { 'foo': {'bar': {'biz': 'notbaz'}}, 'meh': 25, 'myown': 'ok', } assert d1 == expected
def global_defaults(): defaults = FabConfig.global_defaults() ours = { 'tasks': { 'search_root': 'fub', 'collection_name': 'tasks', }, } merge_dicts(defaults, ours) return defaults
def global_defaults(): """ Default configuration values and behavior toggles. Fabric only extends this method in order to make minor adjustments and additions to Invoke's `~invoke.config.Config.global_defaults`; see its documentation for the base values, such as the config subtrees controlling behavior of ``run`` or how ``tasks`` behave. For Fabric-specific modifications and additions to the Invoke-level defaults, see our own config docs at :ref:`default-values`. .. versionadded:: 2.0 """ # TODO: hrm should the run-related things actually be derived from the # runner_class? E.g. Local defines local stuff, Remote defines remote # stuff? Doesn't help with the final config tree tho... # TODO: as to that, this is a core problem, Fabric wants split # local/remote stuff, eg replace_env wants to be False for local and # True remotely; shell wants to differ depending on target (and either # way, does not want to use local interrogation for remote) # TODO: is it worth moving all of our 'new' settings to a discrete # namespace for cleanliness' sake? e.g. ssh.port, ssh.user etc. # It wouldn't actually simplify this code any, but it would make it # easier for users to determine what came from which library/repo. defaults = InvokeConfig.global_defaults() ours = { # New settings "connect_kwargs": {}, "forward_agent": False, "gateway": None, # TODO 3.0: change to True and update all docs accordingly. "inline_ssh_env": False, "load_ssh_configs": True, "port": 22, "run": { "replace_env": True }, "runners": { "remote": Remote }, "ssh_config_path": None, "tasks": { "collection_name": "fabfile" }, # TODO: this becomes an override/extend once Invoke grows execution # timeouts (which should be timeouts.execute) "timeouts": { "connect": None }, "user": get_local_user(), } merge_dicts(defaults, ours) return defaults
def global_defaults(): """ Default configuration values and behavior toggles. Fabric only extends this method in order to make minor adjustments and additions to Invoke's `~invoke.config.Config.global_defaults`; see its documentation for the base values, such as the config subtrees controlling behavior of ``run`` or how ``tasks`` behave. For Fabric-specific modifications and additions to the Invoke-level defaults, see our own config docs at :ref:`default-values`. .. versionadded:: 2.0 """ # TODO: hrm should the run-related things actually be derived from the # runner_class? E.g. Local defines local stuff, Remote defines remote # stuff? Doesn't help with the final config tree tho... # TODO: as to that, this is a core problem, Fabric wants split # local/remote stuff, eg replace_env wants to be False for local and # True remotely; shell wants to differ depending on target (and either # way, does not want to use local interrogation for remote) # TODO: is it worth moving all of our 'new' settings to a discrete # namespace for cleanliness' sake? e.g. ssh.port, ssh.user etc. # It wouldn't actually simplify this code any, but it would make it # easier for users to determine what came from which library/repo. defaults = InvokeConfig.global_defaults() ours = { # New settings 'connect_kwargs': {}, 'forward_agent': False, 'gateway': None, 'load_ssh_configs': True, 'port': 22, 'run': { 'replace_env': True, }, 'runners': { 'remote': Remote, }, 'ssh_config_path': None, 'tasks': { 'collection_name': 'fabfile', }, # TODO: this becomes an override/extend once Invoke grows execution # timeouts (which should be timeouts.execute) 'timeouts': { 'connect': None, }, 'user': get_local_user(), } merge_dicts(defaults, ours) return defaults
def mixed_branch_levels_merges_ok(self): d1 = {'foo': {'bar': {'biz': 'baz'}}, 'meh': 17, 'myown': 'ok'} d2 = {'foo': {'bar': {'biz': 'notbaz'}}, 'meh': 25} merge_dicts(d1, d2) expected = { 'foo': { 'bar': { 'biz': 'notbaz' } }, 'meh': 25, 'myown': 'ok', } assert d1 == expected
def global_defaults(): their_defaults = Config.global_defaults() my_defaults = { "run": { # "config": "cmake_build.yaml", "echo": True, }, } return merge_dicts(their_defaults, my_defaults)
def global_defaults(): base_defaults = Config.global_defaults() overrides = { "tasks": {"collection_name": PROG_NAME}, "run": { "shell": "C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", "echo": True, "debug": True, }, } return merge_dicts(base=base_defaults, updates=overrides)
def dict_value_merges_are_not_references(self): core = {} coll = {'foo': {'bar': {'biz': 'coll value'}}} proj = {'foo': {'bar': {'biz': 'proj value'}}} # Initial merge - when bug present, this sets core['foo'] to the entire # 'foo' dict in 'proj' as a reference - meaning it 'links' back to the # 'proj' dict whenever other things are merged into it merge_dicts(core, proj) eq_(core, {'foo': {'bar': {'biz': 'proj value'}}}) eq_(proj['foo']['bar']['biz'], 'proj value') # Identity tests can also prove the bug early ok_(core['foo'] is not proj['foo'], "Core foo is literally proj foo!") # Subsequent merge - just overwrites leaf values this time (thus no # real change, but this is what real config merge code does, so why # not) merge_dicts(core, proj) eq_(core, {'foo': {'bar': {'biz': 'proj value'}}}) eq_(proj['foo']['bar']['biz'], 'proj value') # The problem merge - when bug present, core['foo'] references 'foo' # inside 'proj', so this ends up tweaking "core" but it actually # affects "proj" as well! merge_dicts(core, coll) # Expect that the core dict got the update from 'coll'... eq_(core, {'foo': {'bar': {'biz': 'coll value'}}}) # BUT that 'proj' remains UNTOUCHED eq_(proj['foo']['bar']['biz'], 'proj value')
def dict_value_merges_are_not_references(self): core = {} coll = {"foo": {"bar": {"biz": "coll value"}}} proj = {"foo": {"bar": {"biz": "proj value"}}} # Initial merge - when bug present, this sets core['foo'] to the entire # 'foo' dict in 'proj' as a reference - meaning it 'links' back to the # 'proj' dict whenever other things are merged into it merge_dicts(core, proj) assert core == {"foo": {"bar": {"biz": "proj value"}}} assert proj["foo"]["bar"]["biz"] == "proj value" # Identity tests can also prove the bug early assert ( core["foo"] is not proj["foo"] ), "Core foo is literally proj foo!" # noqa # Subsequent merge - just overwrites leaf values this time (thus no # real change, but this is what real config merge code does, so why # not) merge_dicts(core, proj) assert core == {"foo": {"bar": {"biz": "proj value"}}} assert proj["foo"]["bar"]["biz"] == "proj value" # The problem merge - when bug present, core['foo'] references 'foo' # inside 'proj', so this ends up tweaking "core" but it actually # affects "proj" as well! merge_dicts(core, coll) # Expect that the core dict got the update from 'coll'... assert core == {"foo": {"bar": {"biz": "coll value"}}} # BUT that 'proj' remains UNTOUCHED assert proj["foo"]["bar"]["biz"] == "proj value"
def global_defaults() -> dict: """Set the global default configuration, before loading any other config.""" defaults = Config.global_defaults() # Load default configuration with open_text('roberto', 'default_config.yaml') as f: defaults = merge_dicts(defaults, yaml.safe_load(f)) # Git version and branch information try: git_describe = subprocess.run(['git', 'describe', '--tags'], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, check=True).stdout.decode('utf-8') except subprocess.CalledProcessError: # May fail, e.g. when there are no tags. git_describe = '0.0.0-0-notag' defaults['git'].update(parse_git_describe(git_describe)) # First try to get a decent branch name branch = subprocess.run(["git", "rev-parse", "--abbrev-ref", "HEAD"], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, check=True).stdout.decode('utf-8').strip() # If that failed, try to get the tag if branch == 'HEAD': try: branch = subprocess.run( ["git", "describe", "--tags", "--exact-match"], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, check=True).stdout.decode('utf-8').strip() except subprocess.CalledProcessError: # Final attempt, just the sha. try: branch = subprocess.run( ["git", "rev-parse", "HEAD"], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, check=True).stdout.decode('utf-8').strip() except subprocess.CalledProcessError: branch = '__nogit__' defaults['git']['branch'] = branch return defaults
def global_defaults(): hkn_defaults = { 'deploy': { 'name': 'default', 'host': 'apphost.ocf.berkeley.edu', 'path': { 'root': '/home/h/hk/hkn/hknweb', 'repo': 'repo', 'releases': 'releases', 'current': 'current', 'shared': 'shared', }, 'repo_url': 'https://github.com/compserv/hknweb.git', 'branch': 'master', 'linked_files': [], 'linked_dirs': [], 'keep_releases': 10, }, } return merge_dicts(Config.global_defaults(), hkn_defaults)
def global_defaults(): hkn_defaults = { "deploy": { "name": "default", "user": "******", "host": "apphost.ocf.berkeley.edu", "path": { "root": "/home/h/hk/hkn/hknweb", "repo": "repo", "releases": "releases", "current": "current", "shared": "shared", }, "repo_url": "https://github.com/compserv/hknweb.git", "branch": "master", "linked_files": [], "linked_dirs": [], "keep_releases": 10, }, } return merge_dicts(Config.global_defaults(), hkn_defaults)
def updating_with_None_acts_like_merging_empty_dict(self): # When bug present, AttributeError is raised on a None.items() d1 = {"my": "data"} d2 = None merge_dicts(d1, d2) assert d1 == {"my": "data"}
def merge_file_types_by_reference(self): d1 = {} d2 = {"foo": open(__file__)} merge_dicts(d1, d2) assert d1["foo"].closed is False
def nested_leaf_values_merge_ok(self): d1 = {'foo': {'bar': {'biz': 'baz'}}} d2 = {'foo': {'bar': {'biz': 'notbaz'}}} merge_dicts(d1, d2) eq_(d1, {'foo': {'bar': {'biz': 'notbaz'}}})
def orthogonal_data_merges(self): d1 = {'foo': 'bar'} d2 = {'biz': 'baz'} merge_dicts(d1, d2) eq_(d1, {'foo': 'bar', 'biz': 'baz'})
def non_dict_type_mismatch_overwrites_ok(self): d1 = {"foo": "bar"} d2 = {"foo": [1, 2, 3]} merge_dicts(d1, d2) assert d1 == {"foo": [1, 2, 3]}
def merging_nondict_into_dict_raises_error(self): d1 = {"foo": {"uh": "oh"}} d2 = {"foo": "bar"} with raises(AmbiguousMergeError): merge_dicts(d1, d2)
def merging_nondict_into_dict_raises_error(self): d1 = {'foo': {'uh': 'oh'}} d2 = {'foo': 'bar'} merge_dicts(d1, d2)
def updates_arg_values_win(self): d1 = {'foo': 'bar'} d2 = {'foo': 'notbar'} merge_dicts(d1, d2) eq_(d1, {'foo': 'notbar'})
def non_dict_type_mismatch_overwrites_ok(self): d1 = {'foo': 'bar'} d2 = {'foo': [1, 2, 3]} merge_dicts(d1, d2) eq_(d1, {'foo': [1, 2, 3]})
def merging_nondict_into_dict_raises_error(self): d1 = {'foo': {'uh': 'oh'}} d2 = {'foo': 'bar'} with raises(AmbiguousMergeError): merge_dicts(d1, d2)
def merging_data_onto_empty_dict(self): d1 = {} d2 = {'foo': 'bar'} merge_dicts(d1, d2) eq_(d1, d2)
def merge_file_types_by_reference(self): d1 = {} d2 = {'foo': open(__file__)} merge_dicts(d1, d2) eq_(d1['foo'].closed, False)
def orthogonal_data_merges(self): d1 = {"foo": "bar"} d2 = {"biz": "baz"} merge_dicts(d1, d2) assert d1 == {"foo": "bar", "biz": "baz"}
def mixed_branch_levels_merges_ok(self): d1 = {'foo': {'bar': {'biz': 'baz'}}, 'meh': 17, 'myown': 'ok'} d2 = {'foo': {'bar': {'biz': 'notbaz'}}, 'meh': 25} merge_dicts(d1, d2) eq_(d1, {'foo': {'bar': {'biz': 'notbaz'}}, 'meh': 25, 'myown': 'ok'})
def updates_arg_values_win(self): d1 = {"foo": "bar"} d2 = {"foo": "notbar"} merge_dicts(d1, d2) assert d1 == {"foo": "notbar"}
def nested_leaf_values_merge_ok(self): d1 = {"foo": {"bar": {"biz": "baz"}}} d2 = {"foo": {"bar": {"biz": "notbaz"}}} merge_dicts(d1, d2) assert d1 == {"foo": {"bar": {"biz": "notbaz"}}}
def merging_data_onto_empty_dict(self): d1 = {} d2 = {"foo": "bar"} merge_dicts(d1, d2) assert d1 == d2