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 __enter__(self): """Acquire lock""" if CFG.base: runez.Anchored.add(CFG.base.path) cutoff = time.time() + self.give_up holder_args = self._locked_by() while holder_args: if time.time() >= cutoff: lock = runez.bold(runez.short(self.lock_path)) holder_args = runez.bold(holder_args) raise SoftLockException( "Can't grab lock %s, giving up\nIt is being held by: pickley %s" % (lock, holder_args)) time.sleep(1) holder_args = self._locked_by() # We got the soft lock if runez.DRYRUN: print("Would acquire %s" % runez.short(self.lock_path)) else: runez.log.trace("Acquired %s" % runez.short(self.lock_path)) runez.write(self.lock_path, "%s\n%s\n" % (os.getpid(), runez.quoted(sys.argv[1:])), logger=False) self.pspec.resolve() return self
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_bogus_config(temp_folder, logged): cfg = grab_sample("bogus-config") assert cfg.pyenv() == "/dev/null" # from custom.json assert cfg.resolved_bundle("") == [] assert cfg.resolved_bundle("foo") == ["foo"] assert cfg.resolved_bundle("bundle:dev") == ["tox", "mgit"] assert cfg.resolved_bundle("bundle:dev2") == ["tox", "mgit", "pipenv"] actual = cfg.represented().strip() expected = SAMPLE_CONFIG.strip().format( base=runez.short(cfg.base), meta=runez.short(cfg.meta), DEFAULT_PYTHONS=DEFAULT_PYTHONS, ) assert actual == expected p = cfg.find_python(pspec=None, fatal=False) assert p.executable == "/dev/null/foo" assert p.problem == "not an executable" assert "skipped: not an executable" in logged.pop() assert not logged with pytest.raises(SystemExit): # Fails to resolve due to desired python configured to be /dev/null PackageSpec(cfg, "mgit") assert "No suitable python" in logged.pop()
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 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_anchored(): user_path = runez.resolved_path("~/some-folder/bar") current_path = runez.resolved_path("./some-folder/bar") assert user_path != "~/some-folder/bar" assert runez.short(user_path) == "~/some-folder/bar" assert runez.short(current_path) != "some-folder/bar" with runez.Anchored(os.getcwd()): assert runez.short(current_path) == "some-folder/bar"
def __exit__(self, *_): """Release lock""" if runez.DRYRUN: print("Would release %s" % runez.short(self.lock_path)) else: runez.log.trace("Released %s" % runez.short(self.lock_path)) if CFG.base: runez.Anchored.pop(CFG.base.path) runez.delete(self.lock_path, logger=False)
def represented(cls, value, size=runez.UNSET, stringify=runez.stringified, dt=str): if isinstance(value, NotImplementedError): if size is None: return {"_error": "not implemented"} return runez.orange("not implemented") if isinstance(value, Exception): if size is None: return {"_error": runez.short(value, size=256)} return runez.red(runez.short(value, size=size)) return runez.represented_json(value, stringify=stringify, dt=dt, keep_none=True, none_key="-null-")
def resolve(self): if not os.path.isdir(self.folder): abort("Folder %s does not exist" % runez.red(runez.short(self.folder))) req = self.requirements if not req: default_req = runez.resolved_path("requirements.txt", base=self.folder) if os.path.exists(default_req): req = [default_req] if req: req = [("-r", runez.resolved_path(r, base=self.folder)) for r in req] req = runez.flattened(req, shellify=True) req.append(self.folder) self.requirements = req self.pspec = PackageSpec(CFG, self.folder) LOG.info("Using python: %s" % self.pspec.python) if self.dist.startswith("root/"): # Special case: we're targeting 'root/...' probably for a debian, use target in that case to avoid venv relocation issues target = self.dist[4:] if os.path.isdir(target): LOG.debug("debian mode: %s -> %s", self.dist, target) self.dist = target parts = self.dist.split("/") if len(parts) <= 2: # Auto-add package name to targets of the form root/subfolder (most typical case) self.dist = os.path.join(self.dist, self.pspec.dashed)
def find_python(self, pspec=None, fatal=True): """ Args: pspec (PackageSpec | None): Package spec, when applicable fatal (bool): If True, abort execution is no valid python could be found Returns: (runez.pyenv.PythonInstallation): Object representing python installation """ desired = self.get_value("python", pspec=pspec) desired = runez.flattened(desired, split=",") if not desired: # Edge case: configured empty python... just use invoker in that case return self.available_pythons.invoker issues = [] python = None for d in desired: python = self.available_pythons.find_python(d) if not python.problem: return python issues.append( "Python '%s' skipped: %s" % (runez.bold(runez.short(d)), runez.red(python.problem))) for i in issues: # Warn only if no python could be found at all LOG.warning(i) if fatal: abort("No suitable python installation found") return python
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 __init__(self, path): self.path = os.path.abspath(path) self.basename = os.path.basename(self.path) self.basename, _, self.extension = self.basename.rpartition( os.path.extsep) self.folder = os.path.dirname(self.path) self.name = runez.short(self.path) self.category = os.path.dirname(self.name) self.key = self.name if "/" in self.name else "./%s" % self.name self._expected = {}
def represented(self): """str: Human readable representation of this configuration""" result = ["%s:" % runez.bold(runez.short(self.source))] if self.values: self._add_dict_representation(result, self.values) else: result[0] += runez.dim(" # empty") result.append("") return "\n".join(result)
def version_check(programs): """Check that programs are present with a minimum version""" if not programs: runez.abort("Specify at least one program to check") specs = [] for program_spec in programs: program, _, min_version = program_spec.partition(":") min_version = Version(min_version) if not program or not min_version.is_valid: runez.abort( "Invalid argument '%s', expecting format <program>:<version>" % program_spec) specs.append((program, min_version)) overview = [] for program, min_version in specs: if runez.DRYRUN: runez.run(program, "--version") continue full_path = runez.which(program) if not full_path: runez.abort("%s is not installed" % program) r = runez.run(full_path, "--version", fatal=False, logger=None) if not r.succeeded: runez.abort("%s --version failed: %s" % (runez.short(full_path), runez.short(r.full_output))) version = Version.from_text(r.full_output) if not version or version < min_version: runez.abort("%s version too low: %s (need %s+)" % (runez.short(full_path), version, min_version)) overview.append("%s %s" % (program, version)) print(runez.short(runez.joined(overview, delimiter=" ; ")))
def _add_dict_representation(self, result, data, indent=1): """ Args: result (list): Where to add lines representing 'data' data (dict): Data to represent indent (int): Indentation to use """ padding = " " * indent for key, value in sorted(data.items()): key = self.parent.colored_key(key, indent) if isinstance(value, dict): result.append("%s%s:" % (padding, key)) self._add_dict_representation(result, value, indent=indent + 1) elif isinstance(value, list): result.append("%s%s:" % (padding, key)) for item in value: result.append("%s- %s" % (padding + " ", runez.short(item))) else: result.append("%s%s: %s" % (padding, key, runez.short(value)))
def short(path, meta=True): """ :param path: Path to represent in its short form :param bool meta: If True, shorten paths relatively to SYSTEM.meta as well :return str: Short form, using '~' if applicable """ if not path: return path if not meta: runez.Anchored.pop(SETTINGS.meta.path) result = runez.short(str(path)) if not meta: runez.Anchored.add(SETTINGS.meta.path) return result
def load(self): path = self._path old_path = None if path is None and self._suffix == "current": # pragma: no cover if self._package_spec.multi_named and not os.path.exists(self._path): # Temporary fix: pickley <v1.8 didn't standardize on dashed package name p = system.SETTINGS.meta.full_path(self._package_spec.pythonified, ".%s.json" % self._suffix) if os.path.exists(p): path = p old_path = self._path data = runez.read_json(path, default={}, fatal=False) self.set_from_dict(data, source=runez.short(path)) if old_path: # pragma: no cover self._path = old_path
def get_target(path, **kwargs): """ :param str path: Path to target :param kwargs: Optional preferences """ prefs = MgitPreferences(**kwargs) actual_path = find_actual_path(path) if not actual_path or not os.path.isdir(actual_path): runez.abort("No folder '%s'" % runez.short(actual_path)) if os.path.isdir(os.path.join(actual_path, ".git")): return GitCheckout(actual_path, prefs=prefs) return ProjectDir(actual_path, prefs=prefs)
def test_run_description(): short_py = runez.short(sys.executable) audit = RunAudit(sys.executable, ["-mpip", "--help"], {}) assert str(audit) == "pip --help" assert audit.run_description() == "pip --help" assert audit.run_description( short_exe=None) == "%s -mpip --help" % short_py assert audit.run_description( short_exe=False) == "%s -mpip --help" % short_py assert audit.run_description(short_exe=True) == "pip --help" assert audit.run_description(short_exe="foo") == "foo -mpip --help" audit = RunAudit(sys.executable, ["-m", "pip", "--help"], {}) assert audit.run_description() == "pip --help" assert audit.run_description( short_exe=None) == "%s -m pip --help" % short_py assert audit.run_description( short_exe=False) == "%s -m pip --help" % short_py assert audit.run_description(short_exe=True) == "pip --help" assert audit.run_description(short_exe="foo") == "foo -m pip --help" audit = RunAudit(sys.executable, ["bin/pip/__main__.py", "--help"], {}) assert audit.run_description() == "pip --help" assert audit.run_description( short_exe=None) == "%s bin/pip/__main__.py --help" % short_py assert audit.run_description( short_exe=False) == "%s bin/pip/__main__.py --help" % short_py assert audit.run_description(short_exe=True) == "pip --help" assert audit.run_description( short_exe="foo") == "foo bin/pip/__main__.py --help" audit = RunAudit("foo/python3", ["-mpip", "--help"], {}) assert audit.run_description() == "foo/python3 -mpip --help" assert audit.run_description(short_exe=None) == "foo/python3 -mpip --help" assert audit.run_description(short_exe=False) == "foo/python3 -mpip --help" assert audit.run_description(short_exe=True) == "pip --help" assert audit.run_description(short_exe="foo") == "foo -mpip --help" audit = RunAudit("foo/bar", ["-mpip", "--help"], {}) assert audit.run_description() == "foo/bar -mpip --help" assert audit.run_description(short_exe=None) == "foo/bar -mpip --help" assert audit.run_description(short_exe=False) == "foo/bar -mpip --help" assert audit.run_description(short_exe=True) == "foo/bar -mpip --help" assert audit.run_description(short_exe="foo") == "foo -mpip --help" cmd = runez.to_path(runez.SYS_INFO.venv_bin_path("foo")) audit = RunAudit(cmd, ["--help"], {}) assert str(audit) == "foo --help" assert audit.run_description() == "foo --help"
def header(self): result = "%s:" % runez.purple(runez.short(self.path)) if not self.projects: return "%s %s" % (result, runez.orange("no git folders")) if self.predominant: result += runez.bold(" %s %s" % (len(self.projects[self.predominant]), self.predominant)) else: result += runez.orange(" no predominant project") if self.additional: result += " (%s)" % runez.purple(", ".join("+%s %s" % (len(self.projects[project]), project) for project in self.additional)) return result
def deserialized(self, kind): try: if kind == TestSamples.K_TOKEN: tokens = tokens_from_path(self.path) actual = [str(t) for t in tokens] else: actual = load_path(self.path) actual = runez.serialize.json_sanitized(actual, stringify=decode, none_key="-null-") except Exception as e: actual = {"_error": runez.short(e)} return actual
def _git_command(self, args): """ :param list|tuple args: Git command + args to use :return list, str: Full git invocation + human friendly representation """ cmd = ["git"] if args and args[0] == "clone": args_represented = "git %s" % " ".join(args) else: args_represented = "git -C %s %s" % (runez.short( self.path), " ".join(args)) cmd.extend(["-C", self.path]) cmd.extend(args) return cmd, args_represented
def main(debug, log, lines, profile, stacktrace): """Troubleshooting commands, useful for iterating on this library""" TestSettings.line_numbers = lines TestSettings.stacktrace = stacktrace runez.log.setup(debug=debug, console_level=logging.INFO, file_location=log, locations=None) logging.debug("Running with %s, v%s", runez.short(sys.executable), ".".join(str(s) for s in sys.version_info[:3])) runez.Anchored.add([runez.DEV.project_folder, TestSamples.SAMPLE_FOLDER]) if profile: import atexit import cProfile TestSettings.profiler = cProfile.Profile() TestSettings.profiler.enable() atexit.register(TestSettings.stop_profiler)
def install(self, pspec, venv, entry_points): """ Args: pspec (pickley.PackageSpec): Package spec this installation is for venv (pickley.package.PythonVenv): Virtual env where executables reside (DOT_META/<package>/...) entry_points (dict | list): Full path of executable to deliver (<base>/<entry_point>) """ if not pspec.is_clear_for_installation(): auto_uninstall(pspec.exe_path(pspec.dashed)) try: prev_manifest = pspec.get_manifest() for name in entry_points: src = venv.bin_path(name) dest = pspec.exe_path(name) if runez.DRYRUN: print("Would %s %s -> %s" % (self.short_name, short(dest), short(src))) continue if not os.path.exists(src): abort( "Can't %s %s -> %s: source does not exist" % (self.short_name, short(dest), runez.red(short(src)))) LOG.debug("%s %s -> %s" % (self.action, short(dest), short(src))) self._install(pspec, dest, src) manifest = pspec.save_manifest(entry_points) if not runez.DRYRUN and prev_manifest and prev_manifest.entrypoints: for old_ep in prev_manifest.entrypoints: if old_ep and old_ep not in entry_points: # Remove old entry points that are not in new manifest any more runez.delete(pspec.exe_path(old_ep)) if self.ping: # Touch the .ping file since this is a fresh install (no need to check for upgrades right away) runez.touch(pspec.ping_path) return manifest except Exception as e: abort("Failed to %s %s: %s" % (self.short_name, short(pspec), runez.red(e)))
def my_group(debug): # By default, --config value is NOT exposed, the global runez.config.CONFIG is altered runez.system.AbortException = SystemExit runez.log.setup( debug=debug, console_format="%(levelname)s %(message)s", console_level=logging.INFO, locations=None, greetings=":: {argv}", ) config = runez.config.CONFIG cd = config.get("g.cd") if cd: logging.info("Changed folder to %s" % runez.short(cd)) runez.ensure_folder(cd) os.chdir(cd) assert len(config.providers) == 1 assert "--config: " in config.overview() assert config.provider_by_name("--config") is not None
def package(build, dist, symlink, no_compile, sanity_check, project, requirement): """Package a project from source checkout""" started = time.time() runez.log.spec.default_logger = LOG.info CFG.set_base(runez.resolved_path(build)) finalizer = PackageFinalizer(project, dist, symlink) finalizer.sanity_check = sanity_check finalizer.requirements = requirement finalizer.compile = not no_compile finalizer.resolve() report = finalizer.finalize() if report: inform("") inform(report) inform("") elapsed = "in %s" % runez.represented_duration(time.time() - started) inform("Packaged %s successfully %s" % (runez.bold(runez.short(project)), runez.dim(elapsed)))
def auto_uninstall(target): """ Args: target (str): Path to executable to auto-uninstall if needed Returns: Aborts if uninstallation was not possible """ brew, name = find_brew_name(target) if brew and name: result = runez.run(brew, "uninstall", "-f", name, fatal=False, logger=LOG.info) if result.succeeded: LOG.info("Auto-uninstalled brew formula '%s'" % name) return # command = "%s uninstall %s" % (brew, name) abort("'%s' failed, please check" % runez.bold(command)) abort("Can't automatically uninstall %s" % runez.short(target))
def __repr__(self): return runez.short(self.path)
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