def resolved_name_version(self): if not self.folder: if self.version: return self.original, self.version return despecced(self.original) setup_py = os.path.join(self.folder, "setup.py") if not os.path.exists(setup_py): abort("No setup.py in %s" % self.folder) with runez.CurrentFolder(self.folder): # Some setup.py's assume current folder is the one with their setup.py r = runez.run(sys.executable, "setup.py", "--name", dryrun=False, fatal=False, logger=False) package_name = r.output if r.failed or not package_name: abort("Could not determine package name from %s" % setup_py) validate_pypi_name(package_name) r = runez.run(sys.executable, "setup.py", "--version", dryrun=False, fatal=False, logger=False) package_version = r.output if r.failed or not package_version: abort("Could not determine package version from setup.py") return package_name, package_version
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 upgrade(self): self.stop() with runez.CurrentFolder(self.target): runez.run("docker-compose", "pull") runez.run("docker-compose", "prune", "-f") self.sync() self.start()
def package(build, dist, symlink, relocatable, sanity_check, folder): """ Package a project from source checkout """ build = runez.resolved_path(build) root = None target_dist = dist if target_dist.startswith("root/"): # Special case: we're targeting 'root/...' probably for a debian, use target in that case to avoid venv relocation issues target = target_dist[4:] if os.path.isdir(target): root = target_dist[:4] target_dist = target LOG.debug("debian mode: %s -> %s", dist, target) folder = runez.resolved_path(folder) if not os.path.isdir(folder): sys.exit("Folder %s does not exist" % short(folder)) system.SETTINGS.set_base(build) setup_py = os.path.join(folder, "setup.py") if not os.path.exists(setup_py): sys.exit("No setup.py in %s" % short(folder)) with runez.CurrentFolder(folder): # Some setup.py's assume their working folder is the folder where they're in result = system.run_python(setup_py, "--name", fatal=False, dryrun=False) name = result.output if result.failed or not name: sys.exit("Could not determine package name from %s" % short(setup_py)) package_spec = system.PackageSpec(name) runez.Anchored.add(folder) p = PACKAGERS.resolved(package_spec) p.build_folder = build p.dist_folder = runez.resolved_path(target_dist) p.relocatable = relocatable p.source_folder = folder p.package() p.create_symlinks(symlink, root=root) p.sanity_check(sanity_check) if p.executables: overview = "produced: %s" % runez.quoted(p.executables) else: overview = "package has no entry-points" print("Packaged %s successfully, %s" % (short(folder), overview)) runez.Anchored.pop(folder)
def test_current_folder(temp_folder): sample = os.path.join(temp_folder, "sample") assert os.getcwd() == temp_folder assert runez.ensure_folder("sample") == 1 with runez.CurrentFolder("sample", anchor=False): cwd = os.getcwd() assert cwd == sample assert runez.short(os.path.join(cwd, "some-file")) == os.path.join( "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
def test_background_run(logged): with runez.CurrentFolder(os.path.dirname(CHATTER)): r = runez.run(CHATTER, "hello", background=True, dryrun=True, logger=True) assert r.succeeded assert "chatter hello &" in logged.pop() r = runez.run(CHATTER, "hello", background=True, dryrun=False) assert r.succeeded assert r.pid assert r.output is None assert r.error is None assert "chatter hello &" in logged.pop()
def test_status(cli): # Note: using explicit lists below, to support case where used directory path may have a space in it # [wouldn't work if args passed as string, due to naive split in run()] # Status on a non-existing folder should fail cli.expect_failure("foo", "No folder 'foo'") # Status on this test folder should succeed and report no git folders found cli.expect_success(cli.tests_folder, "no git folders") # Status on project folder should succeed (we're not calling fetch) project = runez.DEV.project_folder cli.expect_success(project, "mgit") with runez.CurrentFolder(project): cli.run() assert cli.succeeded assert "%s:" % os.path.basename(project) in cli.logged.stdout cli.expect_success("-cs")
def package(build, dist, symlink, relocatable, sanity_check, folder): """ Package a project from source checkout """ build = runez.resolved_path(build) dist = runez.resolved_path(dist) folder = runez.resolved_path(folder) system.SETTINGS.meta = meta_folder(build) if not os.path.isdir(folder): sys.exit("Folder %s does not exist" % short(folder)) setup_py = os.path.join(folder, "setup.py") if not os.path.exists(setup_py): sys.exit("No setup.py in %s" % short(folder)) with runez.CurrentFolder(folder): # Some setup.py's assume their working folder is the folder where they're in name = system.run_python(setup_py, "--name", fatal=False, dryrun=False) if not name: sys.exit("Could not determine package name from %s" % short(setup_py)) runez.Anchored.add(folder) p = PACKAGERS.resolved(name) p.build_folder = build p.dist_folder = dist p.relocatable = relocatable p.source_folder = folder p.package() p.create_symlinks(symlink) p.sanity_check(sanity_check) print("Packaged %s successfully, produced: %s" % (short(folder), runez.represented_args(p.executables))) runez.Anchored.pop(folder)
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_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 stop(self): self.validate() with runez.CurrentFolder(self.target): runez.run("docker-compose", "stop")
def start(self): self.validate() with runez.CurrentFolder(self.target): runez.run("docker-compose", "up", "-d")
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