mirror of
1
0
Fork 0

Add black as tox environment, and run it

This commit is contained in:
Kurt McKee 2022-04-30 20:42:36 -05:00
parent b5499c7dc5
commit 566ba0b853
11 changed files with 486 additions and 337 deletions

View File

@ -7,7 +7,13 @@ from .context import Context
class Dispatcher(object): class Dispatcher(object):
def __init__( def __init__(
self, base_directory, only=None, skip=None, exit_on_failure=False, options=Namespace(), plugins=None, self,
base_directory,
only=None,
skip=None,
exit_on_failure=False,
options=Namespace(),
plugins=None,
): ):
self._log = Messenger() self._log = Messenger()
self._setup_context(base_directory, options) self._setup_context(base_directory, options)

View File

@ -44,7 +44,9 @@ class Clean(Plugin):
self._log.debug("Ignoring nonexistent directory %s" % target) self._log.debug("Ignoring nonexistent directory %s" % target)
return True return True
for item in os.listdir(os.path.expandvars(os.path.expanduser(target))): for item in os.listdir(os.path.expandvars(os.path.expanduser(target))):
path = os.path.abspath(os.path.join(os.path.expandvars(os.path.expanduser(target)), item)) path = os.path.abspath(
os.path.join(os.path.expandvars(os.path.expanduser(target)), item)
)
if recursive and os.path.isdir(path): if recursive and os.path.isdir(path):
# isdir implies not islink -- we don't want to descend into # isdir implies not islink -- we don't want to descend into
# symlinked directories. okay to do a recursive call here # symlinked directories. okay to do a recursive call here

View File

@ -45,7 +45,7 @@ def wrap_function(function, function_path, arg_index, kwarg_key, root):
msg = "The '{0}' argument to {1}() must be rooted in {2}" msg = "The '{0}' argument to {1}() must be rooted in {2}"
msg = msg.format(kwarg_key, function_path, root) msg = msg.format(kwarg_key, function_path, root)
assert value[:len(str(root))] == str(root), msg assert value[: len(str(root))] == str(root), msg
return function(*args, **kwargs) return function(*args, **kwargs)
@ -78,7 +78,7 @@ def wrap_open(root):
msg = "The 'file' argument to open() must be rooted in {0}" msg = "The 'file' argument to open() must be rooted in {0}"
msg = msg.format(root) msg = msg.format(root)
if value != os.devnull and "w" in mode: if value != os.devnull and "w" in mode:
assert value[:len(str(root))] == str(root), msg assert value[: len(str(root))] == str(root), msg
return wrapped(*args, **kwargs) return wrapped(*args, **kwargs)
@ -161,9 +161,7 @@ def root():
# to ensure the variable closures work correctly. # to ensure the variable closures work correctly.
function_path = "{0}.{1}".format(module.__name__, function_name) function_path = "{0}.{1}".format(module.__name__, function_name)
function = getattr(module, function_name) function = getattr(module, function_name)
wrapped = wrap_function( wrapped = wrap_function(function, function_path, arg_index, kwarg_key, current_root)
function, function_path, arg_index, kwarg_key, current_root
)
patches.append(mock.patch(function_path, wrapped)) patches.append(mock.patch(function_path, wrapped))
# open() must be separately wrapped. # open() must be separately wrapped.
@ -251,7 +249,7 @@ class Dotfiles(object):
msg = "The config file path must be rooted in {0}" msg = "The config file path must be rooted in {0}"
msg = msg.format(root) msg = msg.format(root)
assert path[:len(str(root))] == str(root), msg assert path[: len(str(root))] == str(root), msg
self.config_filename = path self.config_filename = path
else: else:

View File

@ -16,7 +16,9 @@ class Directory(dotbot.Plugin):
self._log.debug("Attempting to get options from Context") self._log.debug("Attempting to get options from Context")
options = self._context.options() options = self._context.options()
if len(options.plugin_dirs) != 1: if len(options.plugin_dirs) != 1:
self._log.debug("Context.options.plugins length is %i, expected 1" % len(options.plugins)) self._log.debug(
"Context.options.plugins length is %i, expected 1" % len(options.plugins)
)
return False return False
with open(os.path.abspath(os.path.expanduser("~/flag")), "w") as file: with open(os.path.abspath(os.path.expanduser("~/flag")), "w") as file:

View File

@ -16,10 +16,14 @@ class File(dotbot.Plugin):
self._log.debug("Attempting to get options from Context") self._log.debug("Attempting to get options from Context")
options = self._context.options() options = self._context.options()
if len(options.plugins) != 1: if len(options.plugins) != 1:
self._log.debug("Context.options.plugins length is %i, expected 1" % len(options.plugins)) self._log.debug(
"Context.options.plugins length is %i, expected 1" % len(options.plugins)
)
return False return False
if not options.plugins[0].endswith("file.py"): if not options.plugins[0].endswith("file.py"):
self._log.debug("Context.options.plugins[0] is %s, expected end with file.py" % options.plugins[0]) self._log.debug(
"Context.options.plugins[0] is %s, expected end with file.py" % options.plugins[0]
)
return False return False
with open(os.path.abspath(os.path.expanduser("~/flag")), "w") as file: with open(os.path.abspath(os.path.expanduser("~/flag")), "w") as file:

View File

@ -15,10 +15,7 @@ import pytest
# For these reasons, if the tests are running on Windows with Python < 3.8 # For these reasons, if the tests are running on Windows with Python < 3.8
# or with PyPy, the entire link test suite must be skipped. # or with PyPy, the entire link test suite must be skipped.
# #
if ( if sys.platform[:5] == "win32" and (sys.version_info < (3, 8) or "pypy" in sys.version.lower()):
sys.platform[:5] == "win32"
and (sys.version_info < (3, 8) or "pypy" in sys.version.lower())
):
reason = "It is impossible to perform link tests on this platform" reason = "It is impossible to perform link tests on this platform"
pytestmark = pytest.mark.skip(reason=reason) pytestmark = pytest.mark.skip(reason=reason)
@ -27,12 +24,16 @@ def test_clean_default(root, home, dotfiles, run_dotbot):
"""Verify clean uses default unless overridden.""" """Verify clean uses default unless overridden."""
os.symlink(os.path.join(root, "nowhere"), os.path.join(home, ".g")) os.symlink(os.path.join(root, "nowhere"), os.path.join(home, ".g"))
dotfiles.write_config([{ dotfiles.write_config(
"clean": { [
"~/nonexistent": {"force": True}, {
"~/": None, "clean": {
}, "~/nonexistent": {"force": True},
}]) "~/": None,
},
}
]
)
run_dotbot() run_dotbot()
assert not os.path.isdir(os.path.join(home, "nonexistent")) assert not os.path.isdir(os.path.join(home, "nonexistent"))
@ -140,10 +141,12 @@ def test_clean_defaults_2(root, home, dotfiles, run_dotbot):
"""Verify that explicit clean defaults override the implicit default.""" """Verify that explicit clean defaults override the implicit default."""
os.symlink(os.path.join(root, "nowhere"), os.path.join(home, ".g")) os.symlink(os.path.join(root, "nowhere"), os.path.join(home, ".g"))
dotfiles.write_config([ dotfiles.write_config(
{"defaults": {"clean": {"force": True}}}, [
{"clean": ["~"]}, {"defaults": {"clean": {"force": True}}},
]) {"clean": ["~"]},
]
)
run_dotbot() run_dotbot()
assert not os.path.islink(os.path.join(home, ".g")) assert not os.path.islink(os.path.join(home, ".g"))

View File

@ -7,12 +7,16 @@ import pytest
def test_except_create(capfd, home, dotfiles, run_dotbot): def test_except_create(capfd, home, dotfiles, run_dotbot):
"""Verify that `--except` works as intended.""" """Verify that `--except` works as intended."""
dotfiles.write_config([ dotfiles.write_config(
{"create": ["~/a"]}, [
{"shell": [ {"create": ["~/a"]},
{"command": "echo success", "stdout": True}, {
]}, "shell": [
]) {"command": "echo success", "stdout": True},
]
},
]
)
run_dotbot("--except", "create") run_dotbot("--except", "create")
assert not os.path.exists(os.path.join(home, "a")) assert not os.path.exists(os.path.join(home, "a"))
@ -23,12 +27,16 @@ def test_except_create(capfd, home, dotfiles, run_dotbot):
def test_except_shell(capfd, home, dotfiles, run_dotbot): def test_except_shell(capfd, home, dotfiles, run_dotbot):
"""Verify that `--except` works as intended.""" """Verify that `--except` works as intended."""
dotfiles.write_config([ dotfiles.write_config(
{"create": ["~/a"]}, [
{"shell": [ {"create": ["~/a"]},
{"command": "echo failure", "stdout": True}, {
]}, "shell": [
]) {"command": "echo failure", "stdout": True},
]
},
]
)
run_dotbot("--except", "shell") run_dotbot("--except", "shell")
assert os.path.exists(os.path.join(home, "a")) assert os.path.exists(os.path.join(home, "a"))
@ -39,12 +47,16 @@ def test_except_shell(capfd, home, dotfiles, run_dotbot):
def test_except_multiples(capfd, home, dotfiles, run_dotbot): def test_except_multiples(capfd, home, dotfiles, run_dotbot):
"""Verify that `--except` works with multiple exceptions.""" """Verify that `--except` works with multiple exceptions."""
dotfiles.write_config([ dotfiles.write_config(
{"create": ["~/a"]}, [
{"shell": [ {"create": ["~/a"]},
{"command": "echo failure", "stdout": True}, {
]}, "shell": [
]) {"command": "echo failure", "stdout": True},
]
},
]
)
run_dotbot("--except", "create", "shell") run_dotbot("--except", "create", "shell")
assert not os.path.exists(os.path.join(home, "a")) assert not os.path.exists(os.path.join(home, "a"))
@ -55,11 +67,13 @@ def test_except_multiples(capfd, home, dotfiles, run_dotbot):
def test_exit_on_failure(capfd, home, dotfiles, run_dotbot): def test_exit_on_failure(capfd, home, dotfiles, run_dotbot):
"""Verify that processing can halt immediately on failures.""" """Verify that processing can halt immediately on failures."""
dotfiles.write_config([ dotfiles.write_config(
{"create": ["~/a"]}, [
{"shell": ["this_is_not_a_command"]}, {"create": ["~/a"]},
{"create": ["~/b"]}, {"shell": ["this_is_not_a_command"]},
]) {"create": ["~/b"]},
]
)
with pytest.raises(SystemExit): with pytest.raises(SystemExit):
run_dotbot("-x") run_dotbot("-x")
@ -70,10 +84,12 @@ def test_exit_on_failure(capfd, home, dotfiles, run_dotbot):
def test_only(capfd, home, dotfiles, run_dotbot): def test_only(capfd, home, dotfiles, run_dotbot):
"""Verify that `--only` works as intended.""" """Verify that `--only` works as intended."""
dotfiles.write_config([ dotfiles.write_config(
{"create": ["~/a"]}, [
{"shell": [{"command": "echo success", "stdout": True}]}, {"create": ["~/a"]},
]) {"shell": [{"command": "echo success", "stdout": True}]},
]
)
run_dotbot("--only", "shell") run_dotbot("--only", "shell")
assert not os.path.exists(os.path.join(home, "a")) assert not os.path.exists(os.path.join(home, "a"))
@ -84,11 +100,13 @@ def test_only(capfd, home, dotfiles, run_dotbot):
def test_only_with_defaults(capfd, home, dotfiles, run_dotbot): def test_only_with_defaults(capfd, home, dotfiles, run_dotbot):
"""Verify that `--only` does not suppress defaults.""" """Verify that `--only` does not suppress defaults."""
dotfiles.write_config([ dotfiles.write_config(
{"defaults": {"shell": {"stdout": True}}}, [
{"create": ["~/a"]}, {"defaults": {"shell": {"stdout": True}}},
{"shell": [{"command": "echo success"}]}, {"create": ["~/a"]},
]) {"shell": [{"command": "echo success"}]},
]
)
run_dotbot("--only", "shell") run_dotbot("--only", "shell")
assert not os.path.exists(os.path.join(home, "a")) assert not os.path.exists(os.path.join(home, "a"))
@ -99,11 +117,13 @@ def test_only_with_defaults(capfd, home, dotfiles, run_dotbot):
def test_only_with_multiples(capfd, home, dotfiles, run_dotbot): def test_only_with_multiples(capfd, home, dotfiles, run_dotbot):
"""Verify that `--only` works as intended.""" """Verify that `--only` works as intended."""
dotfiles.write_config([ dotfiles.write_config(
{"create": ["~/a"]}, [
{"shell": [{"command": "echo success", "stdout": True}]}, {"create": ["~/a"]},
{"link": ["~/.f"]} {"shell": [{"command": "echo success", "stdout": True}]},
]) {"link": ["~/.f"]},
]
)
run_dotbot("--only", "create", "shell") run_dotbot("--only", "create", "shell")
assert os.path.isdir(os.path.join(home, "a")) assert os.path.isdir(os.path.join(home, "a"))
@ -128,7 +148,9 @@ def test_plugin_loading_directory(home, dotfiles, run_dotbot):
"""Verify that plugins can be loaded from a directory.""" """Verify that plugins can be loaded from a directory."""
dotfiles.makedirs("plugins") dotfiles.makedirs("plugins")
plugin_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), "dotbot_plugin_directory.py") plugin_file = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "dotbot_plugin_directory.py"
)
shutil.copy(plugin_file, os.path.join(dotfiles.directory, "plugins", "directory.py")) shutil.copy(plugin_file, os.path.join(dotfiles.directory, "plugins", "directory.py"))
dotfiles.write_config([{"plugin_directory": "~"}]) dotfiles.write_config([{"plugin_directory": "~"}])
run_dotbot("--plugin-dir", os.path.join(dotfiles.directory, "plugins")) run_dotbot("--plugin-dir", os.path.join(dotfiles.directory, "plugins"))

View File

@ -24,10 +24,7 @@ def test_default_mode(home, dotfiles, run_dotbot):
""" """
read_only = 0o777 - stat.S_IWUSR - stat.S_IWGRP - stat.S_IWOTH read_only = 0o777 - stat.S_IWUSR - stat.S_IWGRP - stat.S_IWOTH
config = [ config = [{"defaults": {"create": {"mode": read_only}}}, {"create": ["~/a"]}]
{"defaults": {"create": {"mode": read_only}}},
{"create": ["~/a"]}
]
dotfiles.write_config(config) dotfiles.write_config(config)
run_dotbot() run_dotbot()

View File

@ -15,10 +15,7 @@ import pytest
# For these reasons, if the tests are running on Windows with Python < 3.8 # For these reasons, if the tests are running on Windows with Python < 3.8
# or with PyPy, the entire link test suite must be skipped. # or with PyPy, the entire link test suite must be skipped.
# #
if ( if sys.platform[:5] == "win32" and (sys.version_info < (3, 8) or "pypy" in sys.version.lower()):
sys.platform[:5] == "win32"
and (sys.version_info < (3, 8) or "pypy" in sys.version.lower())
):
reason = "It is impossible to perform link tests on this platform" reason = "It is impossible to perform link tests on this platform"
pytestmark = pytest.mark.skip(reason=reason) pytestmark = pytest.mark.skip(reason=reason)
@ -98,7 +95,9 @@ def test_link_environment_variable_expansion_source(monkeypatch, root, home, dot
assert file.read() == "grape" assert file.read() == "grape"
def test_link_environment_variable_expansion_source_extended(monkeypatch, root, home, dotfiles, run_dotbot): def test_link_environment_variable_expansion_source_extended(
monkeypatch, root, home, dotfiles, run_dotbot
):
"""Verify link expands environment variables in extended config syntax.""" """Verify link expands environment variables in extended config syntax."""
monkeypatch.setenv("APPLE", "h") monkeypatch.setenv("APPLE", "h")
@ -198,10 +197,12 @@ def test_link_glob_1(home, dotfiles, run_dotbot):
dotfiles.write("bin/a", "apple") dotfiles.write("bin/a", "apple")
dotfiles.write("bin/b", "banana") dotfiles.write("bin/b", "banana")
dotfiles.write("bin/c", "cherry") dotfiles.write("bin/c", "cherry")
dotfiles.write_config([ dotfiles.write_config(
{"defaults": {"link": {"glob": True, "create": True}}}, [
{"link": {"~/bin": "bin/*"}}, {"defaults": {"link": {"glob": True, "create": True}}},
]) {"link": {"~/bin": "bin/*"}},
]
)
run_dotbot() run_dotbot()
with open(os.path.join(home, "bin", "a")) as file: with open(os.path.join(home, "bin", "a")) as file:
@ -218,10 +219,12 @@ def test_link_glob_2(home, dotfiles, run_dotbot):
dotfiles.write("bin/a", "apple") dotfiles.write("bin/a", "apple")
dotfiles.write("bin/b", "banana") dotfiles.write("bin/b", "banana")
dotfiles.write("bin/c", "cherry") dotfiles.write("bin/c", "cherry")
dotfiles.write_config([ dotfiles.write_config(
{"defaults": {"link": {"glob": True, "create": True}}}, [
{"link": {"~/bin/": "bin/*"}}, {"defaults": {"link": {"glob": True, "create": True}}},
]) {"link": {"~/bin/": "bin/*"}},
]
)
run_dotbot() run_dotbot()
with open(os.path.join(home, "bin", "a")) as file: with open(os.path.join(home, "bin", "a")) as file:
@ -238,10 +241,12 @@ def test_link_glob_3(home, dotfiles, run_dotbot):
dotfiles.write("bin/.a", "dot-apple") dotfiles.write("bin/.a", "dot-apple")
dotfiles.write("bin/.b", "dot-banana") dotfiles.write("bin/.b", "dot-banana")
dotfiles.write("bin/.c", "dot-cherry") dotfiles.write("bin/.c", "dot-cherry")
dotfiles.write_config([ dotfiles.write_config(
{"defaults": {"link": {"glob": True, "create": True}}}, [
{"link": {"~/bin/": "bin/.*"}}, {"defaults": {"link": {"glob": True, "create": True}}},
]) {"link": {"~/bin/": "bin/.*"}},
]
)
run_dotbot() run_dotbot()
with open(os.path.join(home, "bin", ".a")) as file: with open(os.path.join(home, "bin", ".a")) as file:
@ -258,14 +263,18 @@ def test_link_glob_4(home, dotfiles, run_dotbot):
dotfiles.write(".a", "dot-apple") dotfiles.write(".a", "dot-apple")
dotfiles.write(".b", "dot-banana") dotfiles.write(".b", "dot-banana")
dotfiles.write(".c", "dot-cherry") dotfiles.write(".c", "dot-cherry")
dotfiles.write_config([{ dotfiles.write_config(
"link": { [
"~": { {
"path": ".*", "link": {
"glob": True, "~": {
}, "path": ".*",
}, "glob": True,
}]) },
},
}
]
)
run_dotbot() run_dotbot()
with open(os.path.join(home, ".a")) as file: with open(os.path.join(home, ".a")) as file:
@ -281,14 +290,18 @@ def test_link_glob_ambiguous_failure(path, home, dotfiles, run_dotbot):
"""Verify ambiguous link globbing fails.""" """Verify ambiguous link globbing fails."""
dotfiles.makedirs("foo") dotfiles.makedirs("foo")
dotfiles.write_config([{ dotfiles.write_config(
"link": { [
"~/foo/": { {
"path": path, "link": {
"glob": True, "~/foo/": {
"path": path,
"glob": True,
}
}
} }
} ]
}]) )
with pytest.raises(SystemExit): with pytest.raises(SystemExit):
run_dotbot() run_dotbot()
assert not os.path.exists(os.path.join(home, "foo")) assert not os.path.exists(os.path.join(home, "foo"))
@ -298,14 +311,18 @@ def test_link_glob_ambiguous_success(home, dotfiles, run_dotbot):
"""Verify the case where ambiguous link globbing succeeds.""" """Verify the case where ambiguous link globbing succeeds."""
dotfiles.makedirs("foo") dotfiles.makedirs("foo")
dotfiles.write_config([{ dotfiles.write_config(
"link": { [
"~/foo": { {
"path": "foo", "link": {
"glob": True, "~/foo": {
"path": "foo",
"glob": True,
}
}
} }
} ]
}]) )
run_dotbot() run_dotbot()
assert os.path.exists(os.path.join(home, "foo")) assert os.path.exists(os.path.join(home, "foo"))
@ -317,24 +334,26 @@ def test_link_glob_exclude_1(home, dotfiles, run_dotbot):
dotfiles.write("config/bar/b", "banana") dotfiles.write("config/bar/b", "banana")
dotfiles.write("config/bar/c", "cherry") dotfiles.write("config/bar/c", "cherry")
dotfiles.write("config/baz/d", "donut") dotfiles.write("config/baz/d", "donut")
dotfiles.write_config([ dotfiles.write_config(
{ [
"defaults": { {
"defaults": {
"link": {
"glob": True,
"create": True,
},
},
},
{
"link": { "link": {
"glob": True, "~/.config/": {
"create": True, "path": "config/*",
"exclude": ["config/baz"],
},
}, },
}, },
}, ]
{ )
"link": {
"~/.config/": {
"path": "config/*",
"exclude": ["config/baz"],
},
},
},
])
run_dotbot() run_dotbot()
assert not os.path.exists(os.path.join(home, ".config", "baz")) assert not os.path.exists(os.path.join(home, ".config", "baz"))
@ -358,24 +377,26 @@ def test_link_glob_exclude_2(home, dotfiles, run_dotbot):
dotfiles.write("config/bar/c", "cherry") dotfiles.write("config/bar/c", "cherry")
dotfiles.write("config/baz/d", "donut") dotfiles.write("config/baz/d", "donut")
dotfiles.write("config/baz/buzz/e", "egg") dotfiles.write("config/baz/buzz/e", "egg")
dotfiles.write_config([ dotfiles.write_config(
{ [
"defaults": { {
"defaults": {
"link": {
"glob": True,
"create": True,
},
},
},
{
"link": { "link": {
"glob": True, "~/.config/": {
"create": True, "path": "config/*/*",
"exclude": ["config/baz/*"],
},
}, },
}, },
}, ]
{ )
"link": {
"~/.config/": {
"path": "config/*/*",
"exclude": ["config/baz/*"],
},
},
},
])
run_dotbot() run_dotbot()
assert not os.path.exists(os.path.join(home, ".config", "baz")) assert not os.path.exists(os.path.join(home, ".config", "baz"))
@ -401,24 +422,26 @@ def test_link_glob_exclude_3(home, dotfiles, run_dotbot):
dotfiles.write("config/baz/d", "donut") dotfiles.write("config/baz/d", "donut")
dotfiles.write("config/baz/buzz/e", "egg") dotfiles.write("config/baz/buzz/e", "egg")
dotfiles.write("config/baz/bizz/g", "grape") dotfiles.write("config/baz/bizz/g", "grape")
dotfiles.write_config([ dotfiles.write_config(
{ [
"defaults": { {
"defaults": {
"link": {
"glob": True,
"create": True,
},
},
},
{
"link": { "link": {
"glob": True, "~/.config/": {
"create": True, "path": "config/*/*",
"exclude": ["config/baz/buzz"],
},
}, },
}, },
}, ]
{ )
"link": {
"~/.config/": {
"path": "config/*/*",
"exclude": ["config/baz/buzz"],
},
},
},
])
run_dotbot() run_dotbot()
assert not os.path.exists(os.path.join(home, ".config", "baz", "buzz")) assert not os.path.exists(os.path.join(home, ".config", "baz", "buzz"))
@ -451,24 +474,26 @@ def test_link_glob_exclude_4(home, dotfiles, run_dotbot):
dotfiles.write("config/baz/buzz/e", "egg") dotfiles.write("config/baz/buzz/e", "egg")
dotfiles.write("config/baz/bizz/g", "grape") dotfiles.write("config/baz/bizz/g", "grape")
dotfiles.write("config/fiz/f", "fig") dotfiles.write("config/fiz/f", "fig")
dotfiles.write_config([ dotfiles.write_config(
{ [
"defaults": { {
"defaults": {
"link": {
"glob": True,
"create": True,
},
},
},
{
"link": { "link": {
"glob": True, "~/.config/": {
"create": True, "path": "config/*/*",
"exclude": ["config/baz/*", "config/fiz/*"],
},
}, },
}, },
}, ]
{ )
"link": {
"~/.config/": {
"path": "config/*/*",
"exclude": ["config/baz/*", "config/fiz/*"],
},
},
},
])
run_dotbot() run_dotbot()
assert not os.path.exists(os.path.join(home, ".config", "baz")) assert not os.path.exists(os.path.join(home, ".config", "baz"))
@ -492,10 +517,12 @@ def test_link_glob_multi_star(home, dotfiles, run_dotbot):
dotfiles.write("config/foo/a", "apple") dotfiles.write("config/foo/a", "apple")
dotfiles.write("config/bar/b", "banana") dotfiles.write("config/bar/b", "banana")
dotfiles.write("config/bar/c", "cherry") dotfiles.write("config/bar/c", "cherry")
dotfiles.write_config([ dotfiles.write_config(
{"defaults": {"link": {"glob": True, "create": True}}}, [
{"link": {"~/.config/": "config/*/*"}}, {"defaults": {"link": {"glob": True, "create": True}}},
]) {"link": {"~/.config/": "config/*/*"}},
]
)
run_dotbot() run_dotbot()
assert not os.path.islink(os.path.join(home, ".config")) assert not os.path.islink(os.path.join(home, ".config"))
@ -518,7 +545,7 @@ def test_link_glob_multi_star(home, dotfiles, run_dotbot):
("conf/[bc]*", lambda fruit: fruit if fruit[0] in "bc" else None), ("conf/[bc]*", lambda fruit: fruit if fruit[0] in "bc" else None),
("conf/*e", lambda fruit: fruit if fruit[-1] == "e" else None), ("conf/*e", lambda fruit: fruit if fruit[-1] == "e" else None),
("conf/??r*", lambda fruit: fruit if fruit[2] == "r" else None), ("conf/??r*", lambda fruit: fruit if fruit[2] == "r" else None),
) ),
) )
def test_link_glob_patterns(pattern, expect_file, home, dotfiles, run_dotbot): def test_link_glob_patterns(pattern, expect_file, home, dotfiles, run_dotbot):
"""Verify link glob pattern matching.""" """Verify link glob pattern matching."""
@ -526,10 +553,12 @@ def test_link_glob_patterns(pattern, expect_file, home, dotfiles, run_dotbot):
fruits = ["apple", "apricot", "banana", "cherry", "currant", "cantalope"] fruits = ["apple", "apricot", "banana", "cherry", "currant", "cantalope"]
[dotfiles.write("conf/" + fruit, fruit) for fruit in fruits] [dotfiles.write("conf/" + fruit, fruit) for fruit in fruits]
[dotfiles.write("conf/." + fruit, "dot-" + fruit) for fruit in fruits] [dotfiles.write("conf/." + fruit, "dot-" + fruit) for fruit in fruits]
dotfiles.write_config([ dotfiles.write_config(
{"defaults": {"link": {"glob": True, "create": True}}}, [
{"link": {"~/globtest": pattern}}, {"defaults": {"link": {"glob": True, "create": True}}},
]) {"link": {"~/globtest": pattern}},
]
)
run_dotbot() run_dotbot()
for fruit in fruits: for fruit in fruits:
@ -554,10 +583,12 @@ def test_link_glob_recursive(home, dotfiles, run_dotbot):
dotfiles.write("config/foo/bar/a", "apple") dotfiles.write("config/foo/bar/a", "apple")
dotfiles.write("config/foo/bar/b", "banana") dotfiles.write("config/foo/bar/b", "banana")
dotfiles.write("config/foo/bar/c", "cherry") dotfiles.write("config/foo/bar/c", "cherry")
dotfiles.write_config([ dotfiles.write_config(
{"defaults": {"link": {"glob": True, "create": True}}}, [
{"link": {"~/.config/": {"path": "config/**", "exclude": ["config/**/b"]}}}, {"defaults": {"link": {"glob": True, "create": True}}},
]) {"link": {"~/.config/": {"path": "config/**", "exclude": ["config/**/b"]}}},
]
)
run_dotbot() run_dotbot()
assert not os.path.islink(os.path.join(home, ".config")) assert not os.path.islink(os.path.join(home, ".config"))
@ -581,14 +612,18 @@ def test_link_if(home, dotfiles, run_dotbot):
os.mkdir(os.path.join(home, "d")) os.mkdir(os.path.join(home, "d"))
dotfiles.write("f", "apple") dotfiles.write("f", "apple")
dotfiles.write_config([{ dotfiles.write_config(
"link": { [
"~/.f": {"path": "f", "if": "true"}, {
"~/.g": {"path": "f", "if": "false"}, "link": {
"~/.h": {"path": "f", "if": "[ -d ~/d ]"}, "~/.f": {"path": "f", "if": "true"},
"~/.i": {"path": "f", "if": "badcommand"}, "~/.g": {"path": "f", "if": "false"},
}, "~/.h": {"path": "f", "if": "[ -d ~/d ]"},
}]) "~/.i": {"path": "f", "if": "badcommand"},
},
}
]
)
run_dotbot() run_dotbot()
assert not os.path.exists(os.path.join(home, ".g")) assert not os.path.exists(os.path.join(home, ".g"))
@ -608,21 +643,23 @@ def test_link_if_defaults(home, dotfiles, run_dotbot):
os.mkdir(os.path.join(home, "d")) os.mkdir(os.path.join(home, "d"))
dotfiles.write("f", "apple") dotfiles.write("f", "apple")
dotfiles.write_config([ dotfiles.write_config(
{ [
"defaults": { {
"link": { "defaults": {
"if": "false", "link": {
"if": "false",
},
}, },
}, },
}, {
{ "link": {
"link": { "~/.j": {"path": "f", "if": "true"},
"~/.j": {"path": "f", "if": "true"}, "~/.k": {"path": "f"}, # default is false
"~/.k": {"path": "f"}, # default is false },
}, },
}, ]
]) )
run_dotbot() run_dotbot()
assert not os.path.exists(os.path.join(home, ".k")) assert not os.path.exists(os.path.join(home, ".k"))
@ -639,14 +676,18 @@ def test_link_if_windows(home, dotfiles, run_dotbot):
os.mkdir(os.path.join(home, "d")) os.mkdir(os.path.join(home, "d"))
dotfiles.write("f", "apple") dotfiles.write("f", "apple")
dotfiles.write_config([{ dotfiles.write_config(
"link": { [
"~/.f": {"path": "f", "if": 'cmd /c "exit 0"'}, {
"~/.g": {"path": "f", "if": 'cmd /c "exit 1"'}, "link": {
"~/.h": {"path": "f", "if": 'cmd /c "dir %USERPROFILE%\\d'}, "~/.f": {"path": "f", "if": 'cmd /c "exit 0"'},
"~/.i": {"path": "f", "if": 'cmd /c "badcommand"'}, "~/.g": {"path": "f", "if": 'cmd /c "exit 1"'},
}, "~/.h": {"path": "f", "if": 'cmd /c "dir %USERPROFILE%\\d'},
}]) "~/.i": {"path": "f", "if": 'cmd /c "badcommand"'},
},
}
]
)
run_dotbot() run_dotbot()
assert not os.path.exists(os.path.join(home, ".g")) assert not os.path.exists(os.path.join(home, ".g"))
@ -666,21 +707,23 @@ def test_link_if_defaults_windows(home, dotfiles, run_dotbot):
os.mkdir(os.path.join(home, "d")) os.mkdir(os.path.join(home, "d"))
dotfiles.write("f", "apple") dotfiles.write("f", "apple")
dotfiles.write_config([ dotfiles.write_config(
{ [
"defaults": { {
"link": { "defaults": {
"if": 'cmd /c "exit 1"', "link": {
"if": 'cmd /c "exit 1"',
},
}, },
}, },
}, {
{ "link": {
"link": { "~/.j": {"path": "f", "if": 'cmd /c "exit 0"'},
"~/.j": {"path": "f", "if": 'cmd /c "exit 0"'}, "~/.k": {"path": "f"}, # default is false
"~/.k": {"path": "f"}, # default is false },
}, },
}, ]
]) )
run_dotbot() run_dotbot()
assert not os.path.exists(os.path.join(home, ".k")) assert not os.path.exists(os.path.join(home, ".k"))
@ -692,14 +735,18 @@ def test_link_if_defaults_windows(home, dotfiles, run_dotbot):
def test_link_ignore_missing(ignore_missing, home, dotfiles, run_dotbot): def test_link_ignore_missing(ignore_missing, home, dotfiles, run_dotbot):
"""Verify link 'ignore_missing' is respected when the target is missing.""" """Verify link 'ignore_missing' is respected when the target is missing."""
dotfiles.write_config([{ dotfiles.write_config(
"link": { [
"~/missing_link": { {
"path": "missing", "link": {
"ignore-missing": ignore_missing, "~/missing_link": {
}, "path": "missing",
}, "ignore-missing": ignore_missing,
}]) },
},
}
]
)
if ignore_missing: if ignore_missing:
run_dotbot() run_dotbot()
@ -728,10 +775,7 @@ def test_link_no_canonicalize(key, home, dotfiles, run_dotbot):
"""Verify link canonicalization can be disabled.""" """Verify link canonicalization can be disabled."""
dotfiles.write("f", "apple") dotfiles.write("f", "apple")
dotfiles.write_config([ dotfiles.write_config([{"defaults": {"link": {key: False}}}, {"link": {"~/.f": {"path": "f"}}}])
{"defaults": {"link": {key: False}}},
{"link": {"~/.f": {"path": "f"}}}
])
try: try:
os.symlink( os.symlink(
dotfiles.directory, dotfiles.directory,
@ -759,15 +803,19 @@ def test_link_prefix(home, dotfiles, run_dotbot):
dotfiles.write("conf/a", "apple") dotfiles.write("conf/a", "apple")
dotfiles.write("conf/b", "banana") dotfiles.write("conf/b", "banana")
dotfiles.write("conf/c", "cherry") dotfiles.write("conf/c", "cherry")
dotfiles.write_config([{ dotfiles.write_config(
"link": { [
"~/": { {
"glob": True, "link": {
"path": "conf/*", "~/": {
"prefix": ".", "glob": True,
}, "path": "conf/*",
}, "prefix": ".",
}]) },
},
}
]
)
run_dotbot() run_dotbot()
with open(os.path.join(home, ".a")) as file: with open(os.path.join(home, ".a")) as file:
assert file.read() == "apple" assert file.read() == "apple"
@ -782,26 +830,30 @@ def test_link_relative(home, dotfiles, run_dotbot):
dotfiles.write("f", "apple") dotfiles.write("f", "apple")
dotfiles.write("d/e", "grape") dotfiles.write("d/e", "grape")
dotfiles.write_config([{ dotfiles.write_config(
"link": { [
"~/.f": { {
"path": "f", "link": {
}, "~/.f": {
"~/.frel": { "path": "f",
"path": "f", },
"relative": True, "~/.frel": {
}, "path": "f",
"~/nested/.frel": { "relative": True,
"path": "f", },
"relative": True, "~/nested/.frel": {
"create": True, "path": "f",
}, "relative": True,
"~/.d": { "create": True,
"path": "d", },
"relative": True, "~/.d": {
}, "path": "d",
}, "relative": True,
}]) },
},
}
]
)
run_dotbot() run_dotbot()
f = os.readlink(os.path.join(home, ".f")) f = os.readlink(os.path.join(home, ".f"))
@ -866,15 +918,17 @@ def test_link_relink_relative_leaves_file(home, dotfiles, run_dotbot):
dotfiles.write("f", "apple") dotfiles.write("f", "apple")
with open(os.path.join(home, ".f"), "w") as file: with open(os.path.join(home, ".f"), "w") as file:
file.write("grape") file.write("grape")
config = [{ config = [
"link": { {
"~/.folder/f": { "link": {
"path": "f", "~/.folder/f": {
"create": True, "path": "f",
"relative": True, "create": True,
"relative": True,
},
}, },
}, }
}] ]
dotfiles.write_config(config) dotfiles.write_config(config)
run_dotbot() run_dotbot()
@ -895,9 +949,13 @@ def test_link_defaults_1(home, dotfiles, run_dotbot):
file.write("grape") file.write("grape")
os.symlink(os.path.join(home, "f"), os.path.join(home, ".f")) os.symlink(os.path.join(home, "f"), os.path.join(home, ".f"))
dotfiles.write("f", "apple") dotfiles.write("f", "apple")
dotfiles.write_config([{ dotfiles.write_config(
"link": {"~/.f": "f"}, [
}]) {
"link": {"~/.f": "f"},
}
]
)
with pytest.raises(SystemExit): with pytest.raises(SystemExit):
run_dotbot() run_dotbot()
@ -912,10 +970,12 @@ def test_link_defaults_2(home, dotfiles, run_dotbot):
file.write("grape") file.write("grape")
os.symlink(os.path.join(home, "f"), os.path.join(home, ".f")) os.symlink(os.path.join(home, "f"), os.path.join(home, ".f"))
dotfiles.write("f", "apple") dotfiles.write("f", "apple")
dotfiles.write_config([ dotfiles.write_config(
{"defaults": {"link": {"relink": True}}}, [
{"link": {"~/.f": "f"}}, {"defaults": {"link": {"relink": True}}},
]) {"link": {"~/.f": "f"}},
]
)
run_dotbot() run_dotbot()
with open(os.path.join(home, ".f"), "r") as file: with open(os.path.join(home, ".f"), "r") as file:

View File

@ -1,12 +1,18 @@
def test_shell_allow_stdout(capfd, dotfiles, run_dotbot): def test_shell_allow_stdout(capfd, dotfiles, run_dotbot):
"""Verify shell command STDOUT works.""" """Verify shell command STDOUT works."""
dotfiles.write_config([{ dotfiles.write_config(
"shell": [{ [
"command": "echo apple", {
"stdout": True, "shell": [
}], {
}]) "command": "echo apple",
"stdout": True,
}
],
}
]
)
run_dotbot() run_dotbot()
output = capfd.readouterr() output = capfd.readouterr()
@ -36,10 +42,12 @@ def test_shell_cli_verbosity_overrides_2(capfd, dotfiles, run_dotbot):
def test_shell_cli_verbosity_overrides_3(capfd, dotfiles, run_dotbot): def test_shell_cli_verbosity_overrides_3(capfd, dotfiles, run_dotbot):
"""Verify that '-vv' overrides an explicit defaults:shell:stdout=False.""" """Verify that '-vv' overrides an explicit defaults:shell:stdout=False."""
dotfiles.write_config([ dotfiles.write_config(
{"defaults": {"shell": {"stdout": False}}}, [
{"shell": [{"command": "echo apple"}]}, {"defaults": {"shell": {"stdout": False}}},
]) {"shell": [{"command": "echo apple"}]},
]
)
run_dotbot("-vv") run_dotbot("-vv")
stdout = capfd.readouterr().out.splitlines() stdout = capfd.readouterr().out.splitlines()
@ -59,12 +67,18 @@ def test_shell_cli_verbosity_stderr(capfd, dotfiles, run_dotbot):
def test_shell_cli_verbosity_stderr_with_explicit_stdout_off(capfd, dotfiles, run_dotbot): def test_shell_cli_verbosity_stderr_with_explicit_stdout_off(capfd, dotfiles, run_dotbot):
"""Verify that commands can output to STDERR with STDOUT explicitly off.""" """Verify that commands can output to STDERR with STDOUT explicitly off."""
dotfiles.write_config([{ dotfiles.write_config(
"shell": [{ [
"command": "echo apple >&2", {
"stdout": False, "shell": [
}], {
}]) "command": "echo apple >&2",
"stdout": False,
}
],
}
]
)
run_dotbot("-vv") run_dotbot("-vv")
stderr = capfd.readouterr().err.splitlines() stderr = capfd.readouterr().err.splitlines()
@ -74,20 +88,22 @@ def test_shell_cli_verbosity_stderr_with_explicit_stdout_off(capfd, dotfiles, ru
def test_shell_cli_verbosity_stderr_with_defaults_stdout_off(capfd, dotfiles, run_dotbot): def test_shell_cli_verbosity_stderr_with_defaults_stdout_off(capfd, dotfiles, run_dotbot):
"""Verify that commands can output to STDERR with defaults:shell:stdout=False.""" """Verify that commands can output to STDERR with defaults:shell:stdout=False."""
dotfiles.write_config([ dotfiles.write_config(
{ [
"defaults": { {
"shell": { "defaults": {
"stdout": False, "shell": {
"stdout": False,
},
}, },
}, },
}, {
{ "shell": [
"shell": [ {"command": "echo apple >&2"},
{"command": "echo apple >&2"}, ],
], },
}, ]
]) )
run_dotbot("-vv") run_dotbot("-vv")
stderr = capfd.readouterr().err.splitlines() stderr = capfd.readouterr().err.splitlines()
@ -117,10 +133,12 @@ def test_shell_single_v_verbosity_stderr(capfd, dotfiles, run_dotbot):
def test_shell_compact_stdout_1(capfd, dotfiles, run_dotbot): def test_shell_compact_stdout_1(capfd, dotfiles, run_dotbot):
"""Verify that shell command stdout works in compact form.""" """Verify that shell command stdout works in compact form."""
dotfiles.write_config([ dotfiles.write_config(
{"defaults": {"shell": {"stdout": True}}}, [
{"shell": ["echo apple"]}, {"defaults": {"shell": {"stdout": True}}},
]) {"shell": ["echo apple"]},
]
)
run_dotbot() run_dotbot()
stdout = capfd.readouterr().out.splitlines() stdout = capfd.readouterr().out.splitlines()
@ -130,10 +148,12 @@ def test_shell_compact_stdout_1(capfd, dotfiles, run_dotbot):
def test_shell_compact_stdout_2(capfd, dotfiles, run_dotbot): def test_shell_compact_stdout_2(capfd, dotfiles, run_dotbot):
"""Verify that shell command stdout works in compact form.""" """Verify that shell command stdout works in compact form."""
dotfiles.write_config([ dotfiles.write_config(
{"defaults": {"shell": {"stdout": True}}}, [
{"shell": [["echo apple", "echoing message"]]}, {"defaults": {"shell": {"stdout": True}}},
]) {"shell": [["echo apple", "echoing message"]]},
]
)
run_dotbot() run_dotbot()
stdout = capfd.readouterr().out.splitlines() stdout = capfd.readouterr().out.splitlines()
@ -144,9 +164,13 @@ def test_shell_compact_stdout_2(capfd, dotfiles, run_dotbot):
def test_shell_stdout_disabled_by_default(capfd, dotfiles, run_dotbot): def test_shell_stdout_disabled_by_default(capfd, dotfiles, run_dotbot):
"""Verify that the shell command disables stdout by default.""" """Verify that the shell command disables stdout by default."""
dotfiles.write_config([{ dotfiles.write_config(
"shell": ["echo banana"], [
}]) {
"shell": ["echo banana"],
}
]
)
run_dotbot() run_dotbot()
stdout = capfd.readouterr().out.splitlines() stdout = capfd.readouterr().out.splitlines()
@ -156,10 +180,12 @@ def test_shell_stdout_disabled_by_default(capfd, dotfiles, run_dotbot):
def test_shell_can_override_defaults(capfd, dotfiles, run_dotbot): def test_shell_can_override_defaults(capfd, dotfiles, run_dotbot):
"""Verify that the shell command can override defaults.""" """Verify that the shell command can override defaults."""
dotfiles.write_config([ dotfiles.write_config(
{"defaults": {"shell": {"stdout": True}}}, [
{"shell": [{"command": "echo apple", "stdout": False}]}, {"defaults": {"shell": {"stdout": True}}},
]) {"shell": [{"command": "echo apple", "stdout": False}]},
]
)
run_dotbot() run_dotbot()
stdout = capfd.readouterr().out.splitlines() stdout = capfd.readouterr().out.splitlines()
@ -169,12 +195,18 @@ def test_shell_can_override_defaults(capfd, dotfiles, run_dotbot):
def test_shell_quiet_default(capfd, dotfiles, run_dotbot): def test_shell_quiet_default(capfd, dotfiles, run_dotbot):
"""Verify that quiet is off by default.""" """Verify that quiet is off by default."""
dotfiles.write_config([{ dotfiles.write_config(
"shell": [{ [
"command": "echo banana", {
"description": "echoing a thing...", "shell": [
}], {
}]) "command": "echo banana",
"description": "echoing a thing...",
}
],
}
]
)
run_dotbot() run_dotbot()
stdout = capfd.readouterr().out.splitlines() stdout = capfd.readouterr().out.splitlines()
@ -186,13 +218,19 @@ def test_shell_quiet_default(capfd, dotfiles, run_dotbot):
def test_shell_quiet_enabled_with_description(capfd, dotfiles, run_dotbot): def test_shell_quiet_enabled_with_description(capfd, dotfiles, run_dotbot):
"""Verify that only the description is shown when quiet is enabled.""" """Verify that only the description is shown when quiet is enabled."""
dotfiles.write_config([{ dotfiles.write_config(
"shell": [{ [
"command": "echo banana", {
"description": "echoing a thing...", "shell": [
"quiet": True, {
}], "command": "echo banana",
}]) "description": "echoing a thing...",
"quiet": True,
}
],
}
]
)
run_dotbot() run_dotbot()
stdout = capfd.readouterr().out.splitlines() stdout = capfd.readouterr().out.splitlines()
@ -204,12 +242,18 @@ def test_shell_quiet_enabled_with_description(capfd, dotfiles, run_dotbot):
def test_shell_quiet_enabled_without_description(capfd, dotfiles, run_dotbot): def test_shell_quiet_enabled_without_description(capfd, dotfiles, run_dotbot):
"""Verify the command is shown when quiet is enabled with no description.""" """Verify the command is shown when quiet is enabled with no description."""
dotfiles.write_config([{ dotfiles.write_config(
"shell": [{ [
"command": "echo banana", {
"quiet": True, "shell": [
}], {
}]) "command": "echo banana",
"quiet": True,
}
],
}
]
)
run_dotbot() run_dotbot()
stdout = capfd.readouterr().out.splitlines() stdout = capfd.readouterr().out.splitlines()

11
tox.ini
View File

@ -2,6 +2,7 @@
envlist = envlist =
py{27, 35, 36, 37, 38, 39, 310} py{27, 35, 36, 37, 38, 39, 310}
pypy{2, 3} pypy{2, 3}
py310-black
skip_missing_interpreters = true skip_missing_interpreters = true
@ -14,3 +15,13 @@ deps =
commands = commands =
pytest tests/ pytest tests/
[testenv:py310-black]
skip_install = true
skip_build = true
deps =
black
commands =
black --check dotbot/ tests/