def test_run(temp_folder): assert runez.program.added_env_paths(None) is None ls = runez.which("ls") with runez.CaptureOutput(dryrun=True) as logged: assert "Would run: /dev/null" in runez.run("/dev/null", fatal=False) assert "Would run: /dev/null" in logged.pop() assert "Would run:" in runez.run(ls, "--invalid-flag", None, ".") assert "Would run: %s ." % ls in logged.pop() with runez.CaptureOutput() as logged: assert runez.run("/dev/null", fatal=False) is False assert "/dev/null is not installed" in logged.pop() assert runez.touch("sample") == 1 assert runez.run(ls, "--invalid-flag", None, ".", path_env={"PATH": ":."}) == "sample" assert "Running: %s ." % ls in logged.pop() assert runez.run(ls, "some-file", fatal=False) is False assert "Running: %s some-file" % ls in logged assert "exited with code" in logged assert "No such file" in logged.pop()
def test_temp(): cwd = os.getcwd() with runez.CaptureOutput(anchors=["/tmp", "/etc"]) as logged: with runez.TempFolder() as tmp: assert os.path.isdir(tmp) assert tmp != runez.convert.SYMBOLIC_TMP assert not os.path.isdir(tmp) assert os.getcwd() == cwd assert runez.short("/tmp/some-file") == "some-file" assert runez.short("/etc/some-file") == "some-file" assert not logged symbolic = "%s/some-file" % runez.convert.SYMBOLIC_TMP with runez.CaptureOutput(dryrun=True) as logged: assert os.getcwd() == cwd with runez.TempFolder() as tmp: assert tmp == runez.convert.SYMBOLIC_TMP assert runez.short(symbolic) == "some-file" assert os.getcwd() == cwd with runez.TempFolder(anchor=False) as tmp: assert tmp == runez.convert.SYMBOLIC_TMP assert runez.short(symbolic) == symbolic assert not logged assert os.getcwd() == cwd
def test_temp_folder(): cwd = os.getcwd() with runez.CaptureOutput( anchors=[os.path.join("/tmp"), os.path.join("/etc")]) as logged: with runez.TempFolder() as tmp: assert os.path.isdir(tmp) assert tmp != runez.system.SYMBOLIC_TMP assert not os.path.isdir(tmp) assert os.getcwd() == cwd assert runez.short(os.path.join("/tmp", "some-file")) == "some-file" assert runez.short(os.path.join("/etc", "some-file")) == "some-file" assert not logged symbolic = os.path.join(runez.system.SYMBOLIC_TMP, "some-file") with runez.CaptureOutput(dryrun=True) as logged: assert os.getcwd() == cwd with runez.TempFolder() as tmp: assert tmp == runez.system.SYMBOLIC_TMP assert runez.short(symbolic) == "some-file" assert os.getcwd() == cwd with runez.TempFolder(anchor=False) as tmp: assert tmp == runez.system.SYMBOLIC_TMP assert runez.short(symbolic) == symbolic assert not logged assert os.getcwd() == cwd
def test_json(temp_folder, monkeypatch): assert runez.read_json(None) is None assert runez.represented_json(None) == "null\n" assert runez.represented_json([]) == "[]\n" assert runez.represented_json({}) == "{}\n" assert runez.represented_json("foo") == '"foo"\n' assert runez.represented_json({None: 2}, none=True) == '{\n "null": 2\n}\n' assert runez.represented_json({None: 2}, none="None") == '{\n "None": 2\n}\n' assert runez.represented_json({None: None}, none=True) == '{\n "null": null\n}\n' assert runez.represented_json({None: 1, "foo": None}, none="_null") == '{\n "_null": 1,\n "foo": null\n}\n' with pytest.raises(TypeError): # py3 stdlib can't sort with None key... runez.represented_json({None: 2, "foo": "bar"}, none=True) data = {"a": "x", "b": "y"} assert runez.represented_json(data) == '{\n "a": "x",\n "b": "y"\n}\n' assert runez.represented_json(data, indent=None) == '{"a": "x", "b": "y"}' assert runez.save_json(None, None, fatal=False) == 0 assert not runez.DRYRUN with runez.CaptureOutput(dryrun=True) as logged: assert runez.save_json(data, "sample.json") == 1 assert "Would save" in logged.pop() assert not runez.DRYRUN with runez.CaptureOutput() as logged: with pytest.raises(runez.system.AbortException) as exc: runez.read_json(None, fatal=True) assert "Can't read None" in str(exc) assert "Can't read None" in logged.pop() assert runez.read_json("sample.json") is None assert not logged assert runez.read_json("sample.json", default={}, logger=None) == {} assert not logged with monkeypatch.context() as m: m.setattr(runez.serialize, "open", runez.conftest.exception_raiser(), raising=False) assert runez.save_json(data, "sample.json", fatal=False) == -1 assert "Can't save" in logged.pop() assert runez.save_json(data, "sample.json", logger=logging.debug) == 1 assert "Saved " in logged.pop() with monkeypatch.context() as m: m.setattr(io, "open", runez.conftest.exception_raiser()) with pytest.raises(runez.system.AbortException) as exc: runez.read_json("sample.json", fatal=True, logger=None) assert "Can't read sample.json" in str(exc) assert runez.read_json("sample.json") is None assert not logged
def test_scope(): with runez.CaptureOutput() as logged: print("on stdout") sys.stderr.write("on stderr") assert "on stdout" in logged.stdout assert "on stderr" in logged.stderr with runez.CaptureOutput(stderr=False) as logged: print("on stdout") sys.stderr.write("on stderr") # Verify that stderr was not captured, but stdout was assert "on stdout" in logged.stdout assert "on stderr" not in logged assert logged.stderr is None
def test_executable(temp_folder): with runez.CaptureOutput(dryrun=True) as logged: assert runez.make_executable("some-file") == 1 assert "Would make some-file executable" in logged assert runez.touch("some-file") == 1 assert runez.make_executable("some-file") == 1 assert runez.is_executable("some-file") assert runez.make_executable("some-file") == 0 assert runez.delete("some-file") == 1 assert not runez.is_executable("some-file") with runez.CaptureOutput() as logged: assert runez.make_executable("/dev/null/some-file", fatal=False) == -1 assert "does not exist, can't make it executable" in logged
def test_json(temp_folder): assert runez.read_json(None, fatal=False) is None assert runez.save_json(None, None, fatal=False) == 0 data = {"a": "b"} assert not runez.DRYRUN with runez.CaptureOutput(dryrun=True) as logged: assert runez.save_json(data, "sample.json") == 1 assert "Would save" in logged.pop() assert not runez.DRYRUN with runez.CaptureOutput() as logged: assert runez.read_json("sample.json", fatal=False) is None assert "No file" in logged.pop() assert runez.read_json("sample.json", default={}, fatal=False) == {} assert not logged with patch("runez.serialize.open", side_effect=Exception): assert runez.save_json(data, "sample.json", fatal=False) == -1 assert "Couldn't save" in logged.pop() assert runez.save_json(data, "sample.json", logger=logging.debug) == 1 assert "Saved " in logged.pop() with patch("io.open", side_effect=Exception): assert runez.read_json("sample.json", fatal=False) is None assert "Couldn't read" in logged.pop() assert runez.read_json("sample.json", logger=logging.debug) == data assert "Read " in logged.pop() assert runez.read_json("sample.json", default=[], fatal=False) == [] assert "Wrong type" in logged.pop() with runez.CaptureOutput() as logged: # Try with an object that isn't directly serializable, but has a to_dict() function obj = SomeRecord() obj.to_dict = lambda *_: data assert runez.save_json(obj, "sample2.json", logger=logging.debug) == 1 assert "Saved " in logged.pop() assert runez.read_json("sample2.json", logger=logging.debug) == data assert "Read " in logged.pop()
def check_protected_main(exit_code, exception, *messages, **kwargs): with runez.CaptureOutput() as logged: with pytest.raises(SystemExit) as x: runez.click.protected_main(exception_raiser(exception), **kwargs) assert x.value.code == exit_code for message in messages: assert message in logged return logged
def test_channel(*_): p = PACKAGERS.get(system.VENV_PACKAGER)("foo") p.refresh_desired() assert p.desired.representation( verbose=True ) == "foo 1.0 (as venv wrap, channel: stable, source: test:channel.stable.foo)" with runez.CaptureOutput(dryrun=True) as logged: p.executables = ["foo/bar"] assert p.create_symlinks("foo:baz", fatal=False) == 1 assert "Would symlink /bar <- baz/bar" in logged.pop()
def test_to_dict(temp_folder): with runez.CaptureOutput() as logged: # Try with an object that isn't directly serializable, but has a to_dict() function data = {"a": "b"} obj = SomeRecord() obj.to_dict = lambda *_: data assert runez.save_json(obj, "sample2.json", logger=logging.debug) == 1 assert "Saved " in logged.pop() assert runez.read_json("sample2.json") == data assert not logged
def test_get_version(): with runez.CaptureOutput() as logged: expected = runez.get_version(runez) assert expected assert expected != "0.0.0" assert expected == runez.get_version(runez.__name__) assert expected == runez.get_version("runez") assert expected == runez.get_version("runez.system") assert not logged with runez.CaptureOutput() as logged: assert runez.get_version(None) is None assert runez.get_version( ["foo"], default="0.0.0", logger=logging.debug ) is None # Ignore if given name is not a string or module assert runez.get_version(__name__) == VERSION assert not logged assert runez.get_version("foo", logger=logging.debug) == "0.0.0" assert "Can't determine version" in logged.pop()
def test_samples(isolated_log_setup): runez.log.setup() runez.log.enable_trace(True) with runez.CaptureOutput() as output: config = runez.config.Configuration() config.add(runez.config.PropsfsProvider(SAMPLES)) assert str(config) == "propsfs" assert "Adding config provider propsfs" in output.pop() assert config.get_str("non-existent") is None assert not output assert config.get_str("some-string") == "hello there" assert "Using some-string='hello there' from propsfs" in output.pop() assert config.get_int("some-string") is None assert config.get_float("some-string") is None assert config.get_bool("some-string") is False assert config.get_bytesize("some-string") is None assert config.get_str("some-string", default="foo") == "hello there" assert config.get_int("some-string", default=5) == 5 assert config.get_float("some-string", default=5.1) == 5.1 assert config.get_bool("some-string", default=False) is False assert config.get_bytesize("some-string", default=5) == 5 assert config.get_str("some-int") == "123" assert config.get_int("some-int") == 123 assert config.get_float("some-int") == 123 assert config.get_bool("some-int") is True assert config.get_bytesize("some-int") == 123 assert config.get_json("sample.json") == { "some-key": "some-value", "some-int": 51 } assert config.get_json("some-string") is None assert config.get_json("some-string", default={"a": "b"}) == {"a": "b"} assert config.get_json("some-string", default='{"a": "b"}') == { "a": "b" } additional = runez.config.DictProvider({ "some-string": "foo", "x": "y" }) config.add(additional, front=True) values = config.values assert len(values) == 4 assert values["sample.json"] assert values["some-int"] == "123" assert values["some-string"] == "foo" assert values["x"] == "y"
def test_stacked(): with runez.CaptureOutput(stdout=True, stderr=True) as logged1: # Capture both stdout and stderr print("print1") sys.stderr.write("err1\n") assert "print1" in logged1.stdout assert "err1" in logged1.stderr with runez.CaptureOutput(stdout=True, stderr=False) as logged2: # Capture only stdout on 2nd level print("print2") sys.stderr.write("err2\n") # Verify that we did capture, and are isolated from prev level assert "print1" not in logged2.stdout assert "print2" in logged2.stdout with runez.CaptureOutput(stdout=False, stderr=True) as logged3: # Capture only stderr on 3rd level print("print3") sys.stderr.write("err3\n") # Verify that we did capture, and are isolated from prev level assert "err1" not in logged3.stderr assert "err2" not in logged3.stderr assert "err3" in logged3.stderr # Verify that 1st level was not impacted by the others assert "print1" in logged1.stdout assert "err1" in logged1.stderr # err2 should have passed through as we weren't capturing stderr in logged2 assert "err2" in logged1.stderr assert "print2" not in logged1.stdout assert "print3" not in logged1.stdout assert "err3" not in logged1.stderr
def test_failed_read(*_): with runez.CaptureOutput() as logged: assert runez.get_lines("bar", fatal=False) is None assert "Can't read" in logged.pop() assert runez.write("bar", "some content", fatal=False) assert "Can't write" in logged.pop() assert runez.copy("some-file", "bar", fatal=False) == -1 assert "Can't delete" in logged assert "Can't copy" in logged.pop() assert runez.make_executable("some-file", fatal=False) == -1 assert "Can't chmod" in logged.pop()
def test_executable(temp_folder): with runez.CaptureOutput(dryrun=True) as logged: assert runez.make_executable("some-file") == 1 assert "Would make some-file executable" in logged.pop() assert runez.make_executable("some-file", logger=False) == 1 assert not logged with runez.CaptureOutput() as logged: assert runez.touch("some-file") == 1 assert "Touched some-file" in logged.pop() assert runez.delete("some-file") == 1 assert "Deleted some-file" in logged.pop() assert runez.touch("some-file", logger=logging.debug) == 1 assert "Touched some-file" in logged.pop() assert runez.make_executable("some-file", logger=logging.debug) == 1 assert "Made 'some-file' executable" in logged.pop() assert runez.is_executable("some-file") assert runez.make_executable("some-file") == 0 assert not logged assert runez.touch("some-file", logger=False) == 1 assert runez.delete("some-file", logger=False) == 1 assert not runez.is_executable("some-file") assert not logged assert runez.make_executable("/dev/null/some-file", fatal=False) == -1 assert "does not exist, can't make it executable" in logged.pop() assert runez.make_executable("/dev/null/some-file", fatal=False, logger=None) == -1 # Don't log anything assert not logged assert runez.make_executable("/dev/null/some-file", fatal=False, logger=False) == -1 # Log errors only assert "does not exist, can't make it executable" in logged.pop()
def test_file_operations(temp_folder): runez.symlink("foo", "dangling-symlink", must_exist=False) runez.move("dangling-symlink", "dangling-symlink2") assert os.path.islink("dangling-symlink2") runez.write("README.md", "hello") runez.copy("README.md", "sample1/README.md") runez.copy("sample1", "sample2") runez.move("sample1/README.md", "sample1/foo") # overwrite=None "merges" dir contents runez.copy("sample1", "sample2", overwrite=None) assert dir_contents("sample2") == {"README.md": ["hello"], "foo": ["hello"]} # overwrite=True replaces dir runez.copy("sample1", "sample2", overwrite=True) assert dir_contents("sample2") == {"foo": ["hello"]} # overwrite=None, source is a dir, existing destination file gets replaced by source directory runez.copy("sample1", "sample2/foo", overwrite=None) assert dir_contents("sample2") == {"foo": {"foo": ["hello"]}} with runez.CaptureOutput(dryrun=True) as logged: assert runez.ensure_folder("some-folder", fatal=False) == 1 assert "Would create" in logged.pop() assert runez.touch("some-file", logger=logging.debug) == 1 assert "Would touch some-file" in logged.pop() assert runez.copy("some-file", "bar") == 1 assert "Would copy some-file -> bar" in logged.pop() assert runez.move("some-file", "bar") == 1 assert "Would move some-file -> bar" in logged.pop() assert runez.symlink("some-file", "bar") == 1 assert "Would symlink some-file <- bar" in logged.pop() assert runez.delete(temp_folder) == 1 assert "Would delete" in logged.pop() assert runez.copy("some-folder/bar", "some-folder", fatal=False) == -1 assert "source contained in destination" in logged.pop() assert runez.move("some-folder/bar/baz", "some-folder", fatal=False) == -1 assert "source contained in destination" in logged.pop() assert runez.symlink("some-folder/bar/baz", "some-folder", fatal=False) == -1 assert "source contained in destination" in logged.pop()
def test_uninstall_brew(*_): with runez.CaptureOutput() as logged: with patch("runez.run", return_value=runez.program.RunResult(code=0)): # Simulate successful uninstall auto_uninstall("%s/tox" % BREW_INSTALL) assert "Auto-uninstalled brew formula 'tox'" in logged.pop() with patch("runez.run", return_value=runez.program.RunResult(code=1)): # Simulate failed uninstall with pytest.raises(SystemExit): auto_uninstall("%s/twine" % BREW_INSTALL) assert "brew uninstall twine' failed, please check" in logged.pop() with pytest.raises(SystemExit): auto_uninstall("%s/wget" % BREW_INSTALL) assert "Can't automatically uninstall" in logged.pop()
def test_relocate_venv_successfully(temp_base): with runez.CaptureOutput() as logged: original = "line 1: source\nline 2\n" runez.write("foo/bar/bin/baz", original, logger=logging.debug) runez.write("foo/bar/bin/empty", "", logger=logging.debug) runez.write("foo/bar/bin/python", "", logger=logging.debug) runez.make_executable("foo/bar/bin/baz") runez.make_executable("foo/bar/bin/empty") runez.make_executable("foo/bar/bin/python") assert "Created" in logged.pop() # Simulate already seen expected = ["line 1: source\n", "line 2\n"] assert system.relocate_venv("foo", "source", "dest", fatal=False, _seen={"foo"}) == 0 assert runez.get_lines("foo/bar/bin/baz") == expected assert not logged # Simulate failure to write with patch("runez.write", return_value=-1): assert system.relocate_venv("foo", "source", "dest", fatal=False) == -1 assert runez.get_lines("foo/bar/bin/baz") == expected assert not logged # Simulate effective relocation, by folder expected = ["line 1: dest\n", "line 2\n"] assert system.relocate_venv("foo", "source", "dest", fatal=False) == 1 assert runez.get_lines("foo/bar/bin/baz") == expected assert "Relocated " in logged # Second relocation is a no-op assert system.relocate_venv("foo", "source", "dest", fatal=False) == 0 # Test relocating a single file runez.write("foo/bar/bin/baz", original, logger=logging.debug) assert system.relocate_venv("foo/bar/bin/baz", "source", "dest", fatal=False) == 1 assert "Relocated " in logged
def run(self, *args, **kwargs): """ Args: *args: Command line args **kwargs: If provided, format each arg with given `kwargs` """ if kwargs: args = [runez.formatted(a, **kwargs) for a in args] if len(args) == 1 and hasattr(args[0], "split"): # Convenience: allow to provide full command as one string argument args = args[0].split() self.args = runez.flattened(args, split=runez.SHELL) with IsolatedLogSetup(adjust_tmp=False): with runez.CaptureOutput(dryrun=runez.DRYRUN) as logged: self.logged = logged runner = ClickWrapper.get_runner() assert bool(self.main), "No main provided" result = runner.invoke(self.main, args=self.args) if result.output: logged.stdout.buffer.write(result.output) if result.exception and not isinstance(result.exception, SystemExit): try: raise result.exception except BaseException: LOG.exception("Exited with stacktrace:") self.exit_code = result.exit_code if self.logged: handler = WrappedHandler.find_wrapper() if handler: handler.reset() title = runez.header("Captured output for: %s" % runez.represented_args(self.args), border="==") LOG.info("\n%s\nmain: %s\nexit_code: %s\n%s\n", title, self.main, self.exit_code, self.logged)
def test_entry_points(temp_cfg): venv = PythonVenv(folder="", cfg=temp_cfg) with runez.CaptureOutput(dryrun=True): pspec = PackageSpec(temp_cfg, "mgit") contents = PackageContents(venv, pspec) assert str(contents) == "mgit [None]" assert str(contents.bin) == "bin [1 files]" assert contents.entry_points == {"mgit": "dryrun"} runez.write( "ansible.dist-info/metadata.json", '{"extensions": {"python.commands": {"wrap_console": ["ansible"]}}}') with patch("runez.run", side_effect=simulated_run): pspec = PackageSpec(temp_cfg, "ansible") # Used to trigger ansible edge case contents = PackageContents(venv, pspec) assert str(contents) == "ansible [.]" assert str(contents.bin) == "bin [0 files]" assert str(contents.completers) == "bin [1 files]" assert str(contents.dist_info) == "ansible.dist-info [1 files]" assert contents.entry_points == ["ansible"] assert str(contents.files) == " [1 files]" assert contents.files.files.get("foo/bar.py") assert contents.info == { "Name": "ansible", "Version": "1.0.0", "Location": "." } assert contents.location == "." assert contents.pspec is pspec assert contents.venv is venv contents = PackageContents(venv, PackageSpec(temp_cfg, "no-location")) assert contents.files is None assert contents.entry_points is None contents = PackageContents(venv, PackageSpec(temp_cfg, "no-such-package")) assert contents.files is None assert contents.entry_points is None
def test_failure(monkeypatch): monkeypatch.setattr(io, "open", runez.conftest.exception_raiser()) monkeypatch.setattr(os, "unlink", runez.conftest.exception_raiser("bad unlink")) monkeypatch.setattr(shutil, "copy", runez.conftest.exception_raiser()) monkeypatch.setattr(os.path, "exists", lambda _: True) monkeypatch.setattr(os.path, "isfile", lambda _: True) monkeypatch.setattr(os.path, "getsize", lambda _: 10) with runez.CaptureOutput() as logged: with patch("runez.file._do_delete"): with patch("pathlib.Path.exists", return_value=True): assert runez.copy("some-file", "bar", fatal=False) == -1 assert "Can't copy" in logged.pop() assert runez.delete("some-file", fatal=False) == -1 assert "Can't delete" in logged assert "bad unlink" in logged.pop() assert runez.write("bar", "some content", fatal=False) assert "Can't write" in logged.pop() if not runez.SYS_INFO.platform_id.is_windows: assert runez.make_executable("some-file", fatal=False) == -1 assert "Can't chmod" in logged.pop()
def test_rest(): with runez.CaptureOutput(trace=True) as logged: session = RestClient("https://example.com", headers={"test": "testing"}) assert len(session.headers) == 2 assert session.headers["test"] == "testing" assert session.headers["User-Agent"] assert session.url_exists("foo-bar") is True assert session.delete("foo-bar").ok assert "DELETE https://example.com/foo-bar [200]" in logged.pop() assert session.purge("foo-bar").ok assert "PURGE https://example.com/foo-bar [200]" in logged.pop() assert session.get("foo-bar") == {"foo": "bar"} assert session.get("https://example.com/foo-bar") == {"foo": "bar"} assert "GET https://example.com/foo-bar [200]" in logged.pop() assert session.post("foo-bar").ok assert "POST https://example.com/foo-bar [200]" in logged.pop() assert session.put("foo-bar").ok assert "PUT https://example.com/foo-bar [200]" in logged.pop() assert not session.post("bad-request", fatal=False).ok assert "POST https://example.com/bad-request [400] oops" in logged.pop( ) # Status 500 in mock spec does NOT impact dryrun assert session.post("server-crashed", dryrun=True).ok assert "Would POST" in logged.pop() # But does impact actual (no dryrun) run with pytest.raises(runez.system.AbortException): session.get("server-crashed", fatal=True) assert "GET https://example.com/server-crashed [500]" in logged.pop() r = session.get_response("server-crashed", fatal=False, logger=None) assert not r.ok assert r.status_code == 500 assert session.url_exists("") is False assert session.url_exists("not-found") is False assert session.get("not-found", fatal=False, logger=None) is None assert session.head("not-found", fatal=False, logger=None).status_code == 404 logged.pop() assert str(session.get_response("dynamic-a", logger=None)) == "<Response [201]>" r = session.get_response("dynamic-b", logger=None) assert r.description(size=12) == "GET https://..." assert not logged r = session.put("explicit") assert r.method == "PUT" assert str(r) == "<Response [202]>" with pytest.raises(Exception) as exc: session.get("fail1") assert "Simulated crash" in str(exc) with pytest.raises(Exception) as exc: session.get("fail2") assert "oops" in str(exc)
def test_capture(monkeypatch): with runez.CurrentFolder(os.path.dirname(CHATTER)): # Check which finds programs in current folder assert runez.which("chatter") == CHATTER assert runez.shell("chatter hello") == "hello" with runez.CaptureOutput(dryrun=True) as logged: # Dryrun mode doesn't fail (since it doesn't actually run the program) r = runez.run(CHATTER, "silent-fail", fatal=True) assert r.succeeded assert "[dryrun] " in r.output assert r.error == "" assert "Would run:" in logged.pop() r = runez.run(CHATTER, "silent-fail", stdout=None, stderr=None, fatal=True) assert r.succeeded assert r.output is None assert r.error is None assert "Would run:" in logged.pop() with runez.CaptureOutput(seed_logging=True) as logged: # Test success assert runez.run(CHATTER, "hello", fatal=False) == RunResult("hello", "", 0) assert runez.run(CHATTER, "hello", fatal=True) == RunResult("hello", "", 0) assert "chatter hello" in logged.pop() assert runez.run(CHATTER, stdout=None) == RunResult(None, "", 0) assert "Running:" in logged.pop() crasher = CrashingWrite() assert runez.run(CHATTER, "hello", fatal=True, passthrough=crasher) == RunResult("hello", "", 0) assert crasher.crash_counter assert "hello" in logged.pop() # Test no-wait r = runez.run(CHATTER, "hello", fatal=None, stdout=None, stderr=None) assert r.exit_code is None # We don't know exit code because we didn't wait assert r.pid r = runez.run(CHATTER, stdout=None, stderr=None) assert r assert str(r) == "RunResult(exit_code=0)" assert r.succeeded assert r.output is None assert r.error is None assert r.full_output is None r = runez.run(CHATTER, "hello", path_env={ "PATH": ":.", "CPPFLAGS": " -I/usr/local/opt/openssl/include" }) assert str(r) == "RunResult(exit_code=0)" assert r.succeeded assert r.output == "hello" assert r.error == "" assert r.full_output == "hello" # Test stderr r = runez.run(CHATTER, "complain") assert r.succeeded assert r.output == "" assert r.error == "complaining" assert r.full_output == "complaining" logged.pop() # Test failure with pytest.raises(Exception): runez.run(CHATTER, "fail") assert "Run failed:" in logged.pop() r = runez.run(CHATTER, "silent-fail", fatal=False) assert str(r) == "RunResult(exit_code=1)" assert r.failed assert r.error == "" assert r.output == "" assert r.full_output == r.error if hasattr(subprocess.Popen, "__enter__"): # Simulate an EIO with patch("runez.program._read_data", side_effect=simulate_os_error(errno.EIO)): r = runez.run(CHATTER, "fail", fatal=False, passthrough=True) assert r.failed assert r.exc_info is None assert r.output == "" assert r.error == "" # Simulate an OSError with patch("runez.program._read_data", side_effect=simulate_os_error(errno.EINTR)): r = runez.run(CHATTER, "fail", fatal=False, passthrough=True) assert r.failed assert r.output is None assert "failed: OSError(" in r.error # Verify no extra "exited with code ..." message is added when pass-through had some output logged.clear() with pytest.raises(SystemExit): runez.run(CHATTER, "fail", fatal=SystemExit, passthrough=True) assert "exited with code" not in logged.pop() with pytest.raises(runez.system.AbortException): runez.run(CHATTER, "fail", fatal=True, passthrough=True) assert "exited with code" not in logged.pop() # Verify that silent pass-through gets at least mention of exit code with pytest.raises(SystemExit): runez.run(CHATTER, "silent-fail", fatal=SystemExit, passthrough=True) assert "exited with code" in logged.pop() with pytest.raises(runez.system.AbortException): runez.run(CHATTER, "silent-fail", fatal=True, passthrough=True) assert "exited with code" in logged.pop() r = runez.run(CHATTER, "fail", fatal=False, passthrough=True) assert r.failed assert r.error == "failed" assert r.output == "hello there" assert r.full_output == "failed\nhello there" r = runez.run("foo/bar", fatal=False) assert r.exit_code == 1 assert "foo/bar is not an executable" in r.error r = runez.run("foo-bar-no-such-program", fatal=False) assert r.exit_code == 1 assert "is not installed (PATH=" in r.error with monkeypatch.context() as m: m.setattr(subprocess, "Popen", exception_raiser(OSError("testing"))) r = runez.run("python", "--version", fatal=False) assert not r assert r.failed assert "python failed: OSError(" in r.error assert r.output is None with pytest.raises(OSError): runez.run("python", "--version") # Test convenience arg None filtering logged.clear() assert runez.run(CHATTER, "hello", "-a", 0, "-b", None, 1, 2, None, "foo bar") == RunResult("hello -a 0 1 2 foo bar", "", 0) assert 'chatter hello -a 0 1 2 "foo bar"' in logged.pop()
def test_paths(temp_folder): assert runez.resolved_path(None) is None assert runez.resolved_path("some-file") == os.path.join(temp_folder, "some-file") assert runez.resolved_path("some-file", base="bar") == os.path.join(temp_folder, "bar", "some-file") assert runez.short(None) is None assert runez.short("") == "" assert runez.short(temp_folder) == temp_folder assert runez.short(temp_folder + "/some-file") == "some-file" assert runez.short(temp_folder + "/some-file") == "some-file" assert runez.parent_folder(None) is None assert runez.parent_folder(temp_folder + "/some-file") == temp_folder assert runez.represented_args(["ls", temp_folder + "/some-file bar", "-a"]) == 'ls "some-file bar" -a' # Don't crash for no-ops assert runez.ensure_folder(None) == 0 assert runez.ensure_folder("") == 0 assert runez.copy(None, None) == 0 assert runez.move(None, None) == 0 assert runez.symlink(None, None) == 0 assert runez.copy("some-file", "some-file") == 0 assert runez.move("some-file", "some-file") == 0 assert runez.symlink("some-file", "some-file") == 0 assert runez.ensure_folder("some-folder") == 0 # 'some-folder' would be in temp_folder, which already exists with runez.CaptureOutput(dryrun=True) as logged: assert runez.ensure_folder("some-folder", folder=True, fatal=False) == 1 assert "Would create" in logged.pop() assert runez.touch("some-file", logger=logging.debug) == 1 assert "Would touch some-file" in logged.pop() assert runez.copy("some-file", "bar") == 1 assert "Would copy some-file -> bar" in logged.pop() assert runez.move("some-file", "bar") == 1 assert "Would move some-file -> bar" in logged.pop() assert runez.symlink("some-file", "bar") == 1 assert "Would symlink some-file <- bar" in logged.pop() assert runez.delete(temp_folder) == 1 assert "Would delete" in logged.pop() assert runez.copy("some-folder/bar/baz", "some-folder", fatal=False) == -1 assert "source contained in destination" in logged.pop() assert runez.move("some-folder/bar/baz", "some-folder", fatal=False) == -1 assert "source contained in destination" in logged.pop() assert runez.symlink("some-folder/bar/baz", "some-folder", fatal=False) == -1 assert "source contained in destination" in logged.pop() with runez.CaptureOutput(): assert runez.touch("sample") == 1 assert "Can't create folder" in runez.verify_abort(runez.ensure_folder, "sample", folder=True) custom = runez.verify_abort(runez.ensure_folder, "sample", folder=True, fatal=SystemExit, expected_exception=SystemExit) assert "Can't create folder" in custom with pytest.raises(AssertionError): assert runez.verify_abort(runez.ensure_folder, None) assert runez.delete("sample") == 1 assert runez.ensure_folder("sample", folder=True) == 1 assert os.getcwd() == temp_folder with runez.CurrentFolder("sample", anchor=False): cwd = os.getcwd() sample = os.path.join(temp_folder, "sample") assert cwd == sample assert runez.short(os.path.join(cwd, "some-file")) == "sample/some-file" with runez.CurrentFolder("sample", anchor=True): cwd = os.getcwd() sample = os.path.join(temp_folder, "sample") assert cwd == sample assert runez.short(os.path.join(cwd, "some-file")) == "some-file" assert os.getcwd() == temp_folder assert runez.delete("sample") == 1 with runez.CaptureOutput() as logged: sample = os.path.join(os.path.dirname(__file__), "sample.txt") content = runez.get_lines(sample) assert runez.write("sample", "".join(content), fatal=False, logger=logging.debug) == 1 assert runez.get_lines("sample") == content assert "Writing 13 bytes" in logged.pop() assert runez.first_line("sample") == "Fred" assert runez.is_younger("sample", age=10) assert not runez.is_younger("sample", age=-1) assert runez.copy("bar", "baz", fatal=False) == -1 assert "does not exist" in logged.pop() assert runez.move("bar", "baz", fatal=False) == -1 assert "does not exist" in logged.pop() assert runez.symlink("bar", "baz", fatal=False) == -1 assert "does not exist" in logged.pop() # Creating dangling symlinks is possible assert runez.symlink("bar", "baz", fatal=False, must_exist=False) == 1 assert "Symlink bar <- baz" in logged.pop() assert os.path.islink("baz") assert not os.path.exists("baz") assert runez.copy("sample", "x/y/sample") == 1 assert runez.symlink("sample", "x/y/sample3", fatal=False) == 1 assert os.path.exists("sample") assert runez.move("sample", "x/y/sample2") == 1 assert not os.path.exists("sample") assert runez.copy("x/y", "x/z") == 1 assert os.path.exists("x/z/sample") assert os.path.exists("x/z/sample2") assert os.path.exists("x/z/sample3") assert os.path.islink("x/z/sample3") assert runez.touch(None) == 0 assert not runez.is_younger(None, 1) assert not runez.is_younger("/dev/null/not-there", 1) assert runez.first_line("/dev/null/not-there") is None assert runez.get_lines(None) is None
def verify_abort(func, *args, **kwargs): exception = kwargs.pop('exception', SystemExit) with runez.CaptureOutput() as logged: with pytest.raises(exception): func(*args, **kwargs) return str(logged)
def logged(): with runez.CaptureOutput() as logged: yield logged
def temp_log(): with IsolatedLogSetup(): with runez.CaptureOutput() as tracked: yield TempLog(os.getcwd(), tracked)
def test_versions(_, __, temp_base): p = PACKAGERS.get("pex")("foo") p.pip_wheel = lambda *_: None assert p.specced_command() == "pex" p.implementation_version = "1.4.5" assert p.specced_command() == "pex==1.4.5" p.refresh_desired() assert p.desired.representation( verbose=True ) == "foo: can't determine latest version from pypi (channel: latest, source: pypi)" with patch("pickley.package.latest_pypi_version", return_value="error: test failed"): p.refresh_desired() assert p.desired.representation() == "foo: test failed" system.SETTINGS.cli.contents["channel"] = "stable" p.refresh_desired() assert p.desired.representation() == "foo: can't determine stable version" p.version = None assert "can't determine stable version" in verify_abort(p.install) # Without a build folder assert p.get_entry_points() is None # With an empty build fodler runez.ensure_folder(p.build_folder, folder=True) assert p.get_entry_points() is None # With a bogus wheel with runez.CaptureOutput() as logged: p.version = "0.0.0" whl = os.path.join(p.build_folder, "foo-0.0.0-py2.py3-none-any.whl") runez.touch(whl) assert p.get_entry_points() is None assert "Can't read wheel" in logged runez.delete(whl) p.refresh_desired() assert p.desired.channel == "adhoc" assert p.desired.source == "cli" assert p.desired.version == "0.0.0" assert not p.desired.problem p.version = None p.refresh_desired() assert p.desired.problem # Ambiguous package() call assert "Need either source_folder or version in order to package" in verify_abort( p.package) # Package bogus folder without a setup.py p.source_folder = temp_base assert "No setup.py" in verify_abort(p.package) # Package with a bogus setup.py setup_py = os.path.join(temp_base, "setup.py") runez.touch(setup_py) assert "Could not determine version" in verify_abort(p.package) # Provide a minimal setup.py runez.write( setup_py, "from setuptools import setup\nsetup(name='foo', version='0.0.0')\n") # Package project without entry points p.get_entry_points = lambda *_: None assert "is not a CLI" in verify_abort(p.required_entry_points)