def test_pathlib(temp_folder): subfolder = Path("subfolder") assert runez.to_path(subfolder) is subfolder assert not subfolder.is_dir() runez.ensure_folder(subfolder) assert subfolder.is_dir() with pytest.raises(Exception): runez.to_path("foo bar", no_spaces=Exception) with runez.CurrentFolder(subfolder, anchor=True): path = Path("foo") assert runez.short(path) == "foo" assert runez.short(path.absolute()) == "foo" assert runez.resolved_path(path) assert runez.parent_folder(path) == os.path.join(temp_folder, "subfolder") assert runez.touch(path) == 1 assert runez.copy(path, Path("bar")) == 1 assert runez.copy(Path("bar"), Path("baz")) == 1 foo_json = Path("foo.json") runez.write(path, '{"a": "b"}') runez.symlink(path, foo_json) assert runez.read_json(foo_json) == {"a": "b"} assert list(runez.readlines(foo_json)) == ['{"a": "b"}'] assert runez.basename(foo_json.absolute()) == "foo"
def create_symlinks(self, symlink, root=None, fatal=True): """ Use case: preparing a .tox/package/root folder to be packaged as a debian With a spec of "root:root/usr/local/bin", all executables produced under ./root will be symlinked to /usr/local/bin :param str symlink: A specification of the form "root:root/usr/local/bin" :param str root: Optionally, 'root' prefix if used :param bool fatal: Abort execution on failure if True :return int: 1 if effectively done, 0 if no-op, -1 on failure """ if not symlink or not self.executables: return 0 base, _, target = symlink.partition(":") if not target: return runez.abort("Invalid symlink specification '%s'", symlink, fatal=(fatal, -1)) base = runez.resolved_path(base) target = runez.resolved_path(target) for path in self.executables: if path and root: path = runez.resolved_path(root + path) if not path.startswith(base) or len(path) <= len(base): return runez.abort("Symlink base '%s' does not cover '%s'", base, path, fatal=(fatal, -1)) source = path[len(base):] basename = os.path.basename(path) destination = os.path.join(target, basename) runez.symlink(source, destination, must_exist=False, fatal=fatal, logger=LOG.info) return 1 if self.executables else 0
def test_edge_cases(): # Don't crash for no-ops 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.delete("non-existing") == 0 assert runez.touch(None) == 0 assert not runez.file.is_younger("", None) assert not runez.file.is_younger("", 1) assert not runez.file.is_younger("/dev/null/not-there", 1)
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 apply(self, exe): dest = os.path.join(self.target, os.path.basename(exe)) if os.path.exists(exe): r = runez.symlink(exe, dest, must_exist=False) if r > 0: inform("Symlinked %s -> %s" % (runez.short(dest), runez.short(exe))) else: LOG.debug("'%s' does not exist, skipping symlink" % exe)
def test_facultative(cli): runez.save_json({"pinned": { "virtualenv": { "facultative": True } }}, dot_meta("config.json")) # Empty file -> proceed with install as if it wasn't there runez.touch("virtualenv") cli.expect_success("-n install virtualenv", "Would state: Installed virtualenv") # Simulate pickley wrapper runez.write("virtualenv", "echo installed by pickley") runez.make_executable("virtualenv") cli.expect_success("-n install virtualenv", "Would state: Installed virtualenv") # Unknown executable -> skip pickley installation (since facultative) runez.write("virtualenv", "echo foo") runez.make_executable("virtualenv") cli.expect_success( "-n install virtualenv", "Skipping installation of virtualenv: not installed by pickley") cli.expect_success("-n check virtualenv", "skipped, not installed by pickley") # --force ignores 'facultative' setting cli.expect_failure("-n install --force virtualenv", "Can't automatically uninstall virtualenv") # Simulate pickley symlink delivery dummy_target = dot_meta("foo") runez.touch(dummy_target) runez.symlink(dummy_target, "virtualenv") cli.expect_success("-n install virtualenv", "Would state: Installed virtualenv")
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 test_depot(temp_folder, monkeypatch, logged): # Create some pyenv-style python installation mocks (using version 8 so it sorts above any real version...) mk_python("8.6.1") mk_python("8.7.2") runez.symlink("8.6.1", ".pyenv/versions/8.6", must_exist=False, logger=None) # Verify that if invoker is one of the pyenv-installations, it is properly detected depot = mocked_invoker(pyenv=".pyenv", base_prefix=".pyenv/versions/8.6.1", version_info=(8, 6, 1)) p8 = depot.find_python("8") p86 = depot.find_python("8.6") assert str( p8 ) == ".pyenv/versions/8.7.2 [cpython:8.7.2]" # Latest version 8 (invoker doesn't take precedence) assert p8.folder == runez.to_path(".pyenv/versions/8.7.2/bin").absolute() assert p86 is depot.invoker assert str(depot) == "2 scanned" assert depot.scanned == [p8, p86] assert depot.from_path == [] assert not logged mk_python("8.8.3", executable=False) mk_python("8.9.0") mk_python("miniconda3-4.7.12") # Create some PATH-style python installation mocks (using version 9 so it sorts higher than pyenv ones) mk_python("python", folder="path1", version="9.5.1") mk_python("python3", folder="path1", content=[ "foo" ]) # Invalid: mocked _pv.py does not return the right number of lines mk_python("python", folder="path2", content=["foo; bar"]) # --version fails mk_python("some-other-python-exe-name", folder="path3", version="8.5.0") mk_python("python3", folder="path3") # Invalid version with runez.CurrentFolder("path3/bin"): runez.symlink("some-other-python-exe-name", "python", logger=None) monkeypatch.setenv("PATH", "bar:path1/bin:path2/bin:path3/bin") scanner = PythonInstallationScanner(".pyenv") assert str(scanner) == "portable python [.pyenv]" depot = PythonDepot(scanner=scanner, use_path=True) assert str(depot) == "4 scanned, 2 from PATH" r = depot.representation() assert "Installed portable python:" in r assert "Available pythons from PATH:" in r depot.find_preferred_python("8.7.2,8.9.0", "8.7", "8.10") assert depot.preferred_python.version == "8.7.2" depot.find_preferred_python("8.7.2,8.9.0", "8.7", "8.8") assert depot.preferred_python.version == "8.9.0" assert depot.find_python(None) is depot.preferred_python assert depot.find_python("8") is depot.preferred_python depot.find_preferred_python("8.7.2,8.9.0", "10.7", "10.8") assert depot.preferred_python is None depot.find_preferred_python("") assert depot.preferred_python is None assert len(depot.from_path) == 2 assert len(depot.scanned) == 4 assert depot.scan_path_env_var( ) is None # Already scanned to try and find invoker p95 = depot.find_python("9.5.1") assert str(p95) == "path1/bin/python [cpython:9.5.1]" check_find_python(depot, "9", "path1/bin/python [cpython:9.5.1]") check_find_python(depot, "42.4", "42.4 [not available]") check_find_python(depot, "foo", "foo [not available]") check_find_python(depot, "python:43.0.0", "python:43.0.0 [not available]") with pytest.raises(runez.system.AbortException): depot.find_python("/bar", fatal=True) pbar = depot.find_python("/bar") assert str(pbar) == "/bar [not an executable]" assert pbar.problem assert not pbar.satisfies(depot.spec_from_text("python")) p8 = depot.find_python("8") p8a = depot.find_python(PythonSpec("8")) assert p8a is p8 p86 = depot.find_python("8.6") p87 = depot.find_python("8.7") p88 = depot.find_python("8.8") p89 = depot.find_python("8.9") c = depot.find_python("conda") c47 = depot.find_python("conda:4.7") assert c47 is c assert depot.find_python(PythonSpec("conda47")) is c47 assert depot.scanned == [p89, p87, p86, c47] assert p8.major == 8 assert p88.major == 8 assert str(p8) == ".pyenv/versions/8.9.0 [cpython:8.9.0]" assert str(p88) == "8.8 [not available]" assert str(c47) == ".pyenv/versions/miniconda3-4.7.12 [conda:4.7.12]" assert p8 is p89 assert p8 == p89 assert p8 != p88 assert p8 != pbar assert p8.satisfies(PythonSpec("python")) assert p8.satisfies(PythonSpec("python8")) assert p8.satisfies(PythonSpec("py8.9.0")) assert not p8.satisfies(PythonSpec("py8.9.1")) assert c47.satisfies(PythonSpec("conda47")) assert len({p8, p89}) == 1 assert len({p8, p89, p88}) == 2
def test_file_inspection(temp_folder, logged): assert runez.touch("sample") == 1 assert runez.delete("sample") == 1 assert "Deleted sample" in logged.pop() assert runez.ensure_folder("sample") == 1 assert runez.delete("sample") == 1 assert "Deleted sample" in logged.pop() sample = runez.DEV.tests_path("sample.txt") assert len(list(runez.readlines(sample))) == 4 assert len(list(runez.readlines(sample, first=1))) == 1 assert not logged cc = "%s\n" % "\n".join(runez.readlines(sample)) assert runez.write("sample", cc, fatal=False, logger=logging.debug) == 1 cc2 = "%s\n" % "\n".join(runez.readlines("sample")) assert cc2 == cc assert "bytes to sample" in logged.pop() # Wrote 13 bytes on linux... but 14 on windows... assert list(runez.readlines("sample", first=2)) == ["", "Fred"] assert runez.file.is_younger("sample", age=10) assert not runez.file.is_younger("sample", age=-1) # Verify that readlines() can ignore encoding errors with io.open("not-a-text-file", "wb") as fh: fh.write(b"\x89 hello\nworld") assert list(runez.readlines("not-a-text-file", first=1)) == [" hello"] assert not logged 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("s/bar", "s/baz", fatal=False, must_exist=False) == 1 assert "Symlink s/bar <- s/baz" in logged.pop() assert os.path.islink("s/baz") assert not os.path.exists("s/baz") runez.touch("s/bar") assert os.path.exists("s/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/z1") == 1 assert os.path.exists("x/z1/sample") assert os.path.exists("x/z1/sample2") assert os.path.exists("x/z1/sample3") assert os.path.islink("x/z1/sample3") assert runez.copy("x/y", "x/z2", ignore={"sample2"}) == 1 assert os.path.exists("x/z2/sample") assert not os.path.exists("x/z2/sample2") assert os.path.exists("x/z2/sample3") assert os.path.islink("x/z2/sample3") def should_ignore(src, dest): if src == "x/y" and "sample3" in dest: return {"sample3"} assert runez.copy("x/y", "x/z3", ignore=should_ignore) == 1 assert os.path.exists("x/z3/sample") assert os.path.exists("x/z3/sample2") assert not os.path.exists("x/z3/sample3") assert runez.copy("x/y", "x/z2") == 1 assert os.path.exists("x/z2/sample2") # Copy a folder over an existing file runez.touch("x2") assert not os.path.exists("x2/z2/sample2") assert runez.copy("x", "x2") == 1 assert os.path.exists("x2/z2/sample2")