Example #1
0
    def __init__(self, lock, venv_python):
        """
        :param SoftLock lock: Acquired lock
        """
        self.venv_python = venv_python
        self.lock = lock
        self.folder = lock.folder
        self.bin = os.path.join(self.folder, "bin")
        self.python = os.path.join(self.bin, "python")
        self._frozen = None
        if runez.file.is_younger(self.python, self.lock.keep):
            return
        runez.delete(self.folder)

        is_py2 = runez.PY2
        if venv_python:
            is_py2 = runez.to_int(venv_python.major, default=2) < 3

        if is_py2:
            venv = virtualenv_path()
            if not venv:
                runez.abort("Can't determine path to virtualenv.py")
            runez.run(self.venv_python.executable, venv, self.folder)

        else:
            runez.run(self.venv_python.executable, "-mvenv", self.folder)
            runez.run(self.python, "-mpip", "install", "wheel")
Example #2
0
    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
Example #3
0
    def package(self):
        """Package pypi module with 'self.name'"""
        if not self.version and not self.source_folder:
            return runez.abort(
                "Need either source_folder or version in order to package",
                fatal=(True, []))

        if not self.version:
            setup_py = os.path.join(self.source_folder, "setup.py")
            if not os.path.isfile(setup_py):
                return runez.abort("No setup.py in %s",
                                   short(self.source_folder),
                                   fatal=(True, []))
            self.version = system.run_python(setup_py,
                                             "--version",
                                             dryrun=False,
                                             fatal=False,
                                             package_name=self.name)
            if not self.version:
                return runez.abort("Could not determine version from %s",
                                   short(setup_py),
                                   fatal=(True, []))

        self.pip_wheel()

        self.refresh_entry_points()
        runez.ensure_folder(self.dist_folder, folder=True)
        template = "{name}" if self.source_folder else "{name}-{version}"
        self.packaged = []
        self.effective_package(template)
Example #4
0
    def install(self, force=False):
        """
        :param bool force: If True, re-install even if package is already installed
        """
        try:
            self.internal_install(force=force)

        except SoftLockException as e:
            LOG.error("%s is currently being installed by another process" % self.package_spec)
            runez.abort("If that is incorrect, please delete %s.lock", short(e.folder))
Example #5
0
File: cli.py Project: zsimic/mgit
def run_git(target, fatal, *args):
    """Run git command on target, abort if command exits with error code"""
    error = target.git.run_raw_git_command(*args)
    if error.has_problems:
        if fatal:
            runez.abort(error.representation())

        print(error.representation())
        return 0

    return 1
Example #6
0
 def required_entry_points(self):
     """
     :return list: Entry points, abort execution if there aren't any
     """
     ep = self.entry_points
     if not ep:
         runez.delete(system.SETTINGS.meta.full_path(self.name))
         runez.abort(
             "'%s' is not a CLI, it has no console_scripts entry points",
             self.name)
     return ep
Example #7
0
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)
Example #8
0
File: cli.py Project: zsimic/mgit
def handle_clean(target, what):
    if isinstance(target, GitCheckout):
        handle_single_clean(target, what)
        return

    if what in "remote reset":
        runez.abort(
            "Only '--clean show' and '--clean local' supported for multiple git checkouts for now"
        )

    target.prefs.name_size = None
    target.prefs.set_short(True)
    for sub_target in target.checkouts:
        handle_single_clean(sub_target, what)
        print("----")
Example #9
0
File: cli.py Project: zsimic/mgit
def clean_reset(target):
    """
    :param GitCheckout target: Target to reset
    """
    fallback = target.git.fallback_branch()
    if not fallback:
        runez.abort("Can't determine a branch that can be used for reset")

    run_git(target, True, "reset", "--hard", "HEAD")
    run_git(target, True, "clean", "-fdx")
    if fallback != target.git.branches.current:
        run_git(target, True, "checkout", fallback)

    run_git(target, True, "pull")
    target.git.reset_cached_properties()
    print(target.header())
Example #10
0
def test_abort(logged):
    assert runez.abort("aborted",
                       fatal=(False, "some-return")) == "some-return"
    assert "aborted" in logged.pop()

    assert runez.abort("aborted", fatal=(False, "some-return"),
                       code=0) == "some-return"
    assert "aborted" in logged
    assert "ERROR" not in logged.pop()

    assert runez.abort("aborted", fatal=(None, "some-return")) == "some-return"
    assert not logged
    assert "stderr: oops" in runez.verify_abort(failed_function, "oops")

    with patch("runez.system.AbortException", side_effect=str):
        assert runez.abort("oops", logger=None) == "1"
Example #11
0
    def resolved(self, package_spec, default=None):
        """
        :param system.PackageSpec package_spec: Pypi package spec
        :param default: Optional default value (takes precedence over system.SETTINGS.defaults only)
        :return: Corresponding implementation to use
        """
        name = self.resolved_name(package_spec, default=default)
        name, version = system.despecced(name)
        if not name:
            runez.abort("No %s type configured for %s", self.key, package_spec)

        implementation = self.get(name)
        if not implementation:
            runez.abort("Unknown %s type '%s'", self.key, name)

        imp = implementation(package_spec)
        imp.implementation_version = version
        return imp
Example #12
0
    def internal_install(self, force=False, verbose=True):
        """
        :param bool force: If True, re-install even if package is already installed
        :param bool verbose: If True, show more extensive info
        """
        with SoftLock(self.dist_folder, timeout=system.SETTINGS.install_timeout):
            self.refresh_desired(force=force)
            if not self.desired.valid:
                system.setup_audit_log()
                return runez.abort("Can't install %s: %s", self.package_spec, self.desired.problem)

            if not force and self.current.equivalent(self.desired):
                system.inform(self.desired.representation(verbose=verbose, note="is already installed"))
                self.cleanup()
                return

            system.setup_audit_log()
            prev_entry_points = self.entry_points
            self.effective_install()

            new_entry_points = self.entry_points
            removed = set(prev_entry_points).difference(new_entry_points)
            if removed:
                old_removed = runez.read_json(self.removed_entry_points_path, default=[], fatal=False)
                removed = sorted(removed.union(old_removed))
                runez.save_json(removed, self.removed_entry_points_path, fatal=False)

            # Delete wrapper/symlinks of removed entry points immediately
            for name in removed:
                runez.delete(system.SETTINGS.base.full_path(name))

            self.cleanup()
            if self.package_spec.multi_named:
                # Clean up old installations with underscore in name
                runez.delete(system.SETTINGS.meta.full_path(self.package_spec.pythonified))

            if not self.entry_points:
                runez.abort("'%s' is not a CLI, it has no console_scripts entry points", self.package_spec.dashed)

            self.current.set_from(self.desired)
            self.current.save()

            msg = "Would install" if runez.DRYRUN else "Installed"
            system.inform("%s %s" % (msg, self.desired.representation(verbose=verbose)))
Example #13
0
    def __init__(self, lock, venv_python):
        """
        :param SoftLock lock: Acquired lock
        """
        self.venv_python = venv_python
        self.lock = lock
        self.folder = lock.folder
        self.bin = os.path.join(self.folder, "bin")
        self.python = os.path.join(self.bin, "python")
        self.pip = os.path.join(self.bin, "pip")
        self._frozen = None
        if runez.is_younger(self.python, self.lock.keep):
            return
        runez.delete(self.folder)
        venv = system.virtualenv_path()
        if not venv:
            runez.abort("Can't determine path to virtualenv.py")

        runez.run(self.venv_python.executable, venv, self.folder)
Example #14
0
def uninstall_existing(target, fatal=True):
    """
    :param str target: Path to executable to auto-uninstall if needed
    :param bool|None fatal: Abort execution on failure if True
    :return int: 1 if successfully uninstalled, 0 if nothing to do, -1 if failed
    """
    handler = find_uninstaller(target)
    if handler:
        return handler(target, fatal=fatal)

    return runez.abort("Can't automatically uninstall %s", short(target), fatal=(fatal, -1))
Example #15
0
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=" ; ")))
Example #16
0
    def package(self):
        """Package given python project"""
        if not self.desired.version and not self.source_folder:
            return runez.abort("Need either source_folder or version in order to package", fatal=(True, []))

        if not self.desired.version:
            setup_py = os.path.join(self.source_folder, "setup.py")
            if not os.path.isfile(setup_py):
                return runez.abort("No setup.py in %s", short(self.source_folder), fatal=(True, []))
            self.desired.version = None
            result = system.run_python(setup_py, "--version", dryrun=False, fatal=False, package_spec=self.package_spec)
            if result.succeeded:
                self.desired.version = result.output

            if not self.desired.version:
                return runez.abort("Could not determine version from %s", short(setup_py), fatal=(True, []))

        self.pip_wheel()

        self.refresh_entry_points()
        self.packaged = []
        template = "{name}" if self.source_folder else "{name}-{version}"
        self.effective_package(template)
Example #17
0
    def install(self, target, source):
        """
        :param str target: Full path of executable to deliver (<base>/<entry_point>)
        :param str source: Path to original executable being delivered (.pickley/<package>/...)
        """
        runez.delete(target)
        if runez.DRYRUN:
            LOG.debug("Would %s %s (source: %s)", self.implementation_name,
                      short(target), short(source))
            return

        if not os.path.exists(source):
            runez.abort("Can't %s, source %s does not exist",
                        self.implementation_name, short(source))

        try:
            LOG.debug("Delivering %s %s -> %s", self.implementation_name,
                      short(target), short(source))
            self._install(target, source)

        except Exception as e:
            runez.abort("Failed %s %s: %s", self.implementation_name,
                        short(target), e)
Example #18
0
def target_python(desired=None, package_name=None, fatal=True):
    """
    :param str|None desired: Desired python (overrides anything else configured)
    :param str|None package_name: Target pypi package
    :param bool|None fatal: If True, abort execution if python invalid
    :return PythonInstallation: Python installation to use
    """
    if not desired:
        desired = DESIRED_PYTHON or SETTINGS.resolved_value(
            "python", package_name=package_name) or INVOKER
    python = PythonInstallation(desired)
    if not python.is_valid:
        return runez.abort(python.problem, fatal=(fatal, python))

    return python
Example #19
0
def brew_uninstall(target, fatal=False):
    """
    :param str target: Path of file to uninstall
    :param bool fatal: Abort if True
    :return int: 1 if successfully uninstalled, 0 if nothing to do, -1 if failed
    """
    brew, name = find_brew_name(target)
    if not brew or not name:
        return -1

    result = runez.run(brew, "uninstall", "-f", name, fatal=False, logger=LOG.info)
    if result.failed:
        # Failed brew uninstall
        return runez.abort("'%s uninstall %s' failed, please check", brew, name, fatal=(fatal, -1))

    # All good
    return 1
Example #20
0
    def find_wheel(self, folder, fatal=True):
        """list[str]: Wheel for this package found in 'folder', if any"""
        if runez.DRYRUN:
            return ["%s-%s-py3-none-any.whl" % (self.wheelified, self.version)]

        result = []
        if folder and os.path.isdir(folder):
            prefix = "%s-" % self.wheelified
            for fname in os.listdir(folder):
                if fname.startswith(prefix):
                    result.append(os.path.join(folder, fname))

            if len(result) == 1:
                return result[0]

        return runez.abort("Expecting 1 wheel, found: %s" % (result or "None"),
                           fatal=fatal,
                           return_value=None)
Example #21
0
File: cli.py Project: zsimic/mgit
def handle_single_clean(target, what):
    """
    :param GitCheckout target: Single checkout to clean
    :param str what: Operation
    """
    report = target.git.fetch()
    if report.has_problems:
        if what != "reset":
            what = "clean"

        print(
            target.header(
                GitRunReport(report).add(problem="<can't %s" % what)))
        runez.abort("")

    if what == "reset":
        return clean_reset(target)

    if what == "show":
        return clean_show(target)

    total_cleaned = 0
    print(target.header())

    if what in "remote all":
        if not target.git.remote_cleanable_branches:
            print("  No remote branches can be cleaned")

        else:
            total = len(target.git.remote_cleanable_branches)
            cleaned = 0
            for branch in target.git.remote_cleanable_branches:
                remote, _, name = branch.partition("/")
                if not remote and name:
                    raise Exception("Unknown branch spec '%s'" % branch)

                if run_git(target, False, "branch", "--delete", "--remotes",
                           branch):
                    cleaned += run_git(target, False, "push", "--delete",
                                       remote, name)

            total_cleaned += cleaned
            if cleaned == total:
                print("%s cleaned" % runez.plural(cleaned, "remote branch"))

            else:
                print("%s/%s remote branches cleaned" % (cleaned, total))

            target.git.reset_cached_properties()
            if what == "all":
                # Fetch to update remote branches (and correctly detect new dangling local)
                target.git.fetch()

    if what in "local all":
        if not target.git.local_cleanable_branches:
            print("  No local branches can be cleaned")

        else:
            total = len(target.git.local_cleanable_branches)
            cleaned = 0
            for branch in target.git.local_cleanable_branches:
                if branch == target.git.branches.current:
                    fallback = target.git.fallback_branch()
                    if not fallback:
                        print(
                            "Skipping branch '%s', can't determine fallback branch"
                            % target.git.branches.current)
                        continue

                    run_git(target, True, "checkout", fallback)
                    run_git(target, True, "pull")

                cleaned += run_git(target, False, "branch", "--delete", branch)

            total_cleaned += cleaned
            if cleaned == total:
                print(
                    runez.bold("%s cleaned" %
                               runez.plural(cleaned, "local branch")))

            else:
                print(
                    runez.orange("%s/%s local branches cleaned" %
                                 (cleaned, total)))

            target.git.reset_cached_properties()

    if total_cleaned:
        print(target.header())
Example #22
0
def failed_function(*args):
    with patch("runez.system.logging.root") as root:
        root.handlers = None
        runez.abort(*args)
Example #23
0
def test_abort(logged, monkeypatch):
    assert runez.abort("aborted", fatal=False) is None
    assert "aborted" in logged.pop()

    assert runez.abort("aborted", fatal=None) is None
    assert not logged

    assert runez.abort("aborted", return_value="some-return",
                       fatal=False) == "some-return"
    assert "ERROR" in logged
    assert "aborted" in logged.pop()

    # User wants their own logger called
    assert runez.abort("aborted",
                       return_value="some-return",
                       fatal=False,
                       logger=logging.debug) == "some-return"
    assert "ERROR" not in logged
    assert "DEBUG" in logged
    assert "aborted" in logged.pop()

    assert runez.abort("aborted",
                       return_value="some-return",
                       fatal=False,
                       logger=print) == "some-return"
    assert not logged.stderr
    assert logged.pop().strip() == "aborted"

    def on_log(message):
        print(message)

    assert runez.abort("aborted",
                       return_value="some-return",
                       fatal=False,
                       logger=on_log,
                       exc_info=Exception("oops")) == "some-return"
    assert not logged.stderr
    assert logged.pop().strip() == "aborted: oops"

    assert runez.abort("aborted",
                       return_value="some-return",
                       fatal=False,
                       logger=on_log) == "some-return"
    assert not logged.stderr
    assert logged.pop().strip() == "aborted"

    assert runez.abort("aborted", return_value="some-return",
                       fatal=None) == "some-return"
    assert not logged

    monkeypatch.setattr(runez.system.logging.root, "handlers", [])
    with pytest.raises(Exception):
        # logger is UNSET -> log failure
        runez.abort("oops")
    assert "oops" in logged.pop()

    with pytest.raises(Exception) as exc:
        # Message not logged, but part of raised exception
        runez.abort("oops", logger=None)
    assert "oops" in str(exc)
    assert not logged

    with pytest.raises(SystemExit):
        # Failure logged anyway due to sys.exit()
        runez.abort("oops", fatal=SystemExit, logger=None)
    assert "oops" in logged.pop()

    # Verify we still log failure when we're about to sys.exit(), even when logger given is explicitly None
    monkeypatch.setattr(runez.system, "AbortException", SystemExit)
    with pytest.raises(SystemExit):
        runez.abort("oops", logger=None)
    assert "oops" in logged.pop()