Exemple #1
0
    def get_venv_metadata_for_package(self, package: str) -> VenvMetadata:
        data = json.loads(
            run_subprocess(
                [
                    self.python_path,
                    "-c",
                    VENV_METADATA_INSPECTOR,
                    package,
                    self.bin_path,
                ],
                capture_stderr=False,
                log_cmd_str=" ".join([
                    str(self.python_path),
                    "-c",
                    "<contents of venv_metadata_inspector.py>",
                    package,
                    str(self.bin_path),
                ]),
            ).stdout)

        venv_metadata_traceback = data.pop("exception_traceback", None)
        if venv_metadata_traceback is not None:
            logger.error("Internal error with venv metadata inspection.")
            logger.info(
                f"venv_metadata_inspector.py Traceback:\n{venv_metadata_traceback}"
            )

        data["app_paths"] = [Path(p) for p in data["app_paths"]]
        for dep in data["app_paths_of_dependencies"]:
            data["app_paths_of_dependencies"][dep] = [
                Path(p) for p in data["app_paths_of_dependencies"][dep]
            ]

        return VenvMetadata(**data)
Exemple #2
0
 def pip_search(self, search_term: str, pip_search_args: List[str]) -> str:
     cmd_run = run_subprocess(
         [str(self.python_path), "-m", "pip", "search"]
         + pip_search_args
         + [search_term]
     )
     return cmd_run.stdout.strip()
Exemple #3
0
def fetch_info_in_venv(
        venv_python_path: Path) -> Tuple[List[str], Dict[str, str], str]:
    command_str = textwrap.dedent("""
        import json
        import os
        import platform
        import sys

        impl_ver = sys.implementation.version
        implementation_version = "{0.major}.{0.minor}.{0.micro}".format(impl_ver)
        if impl_ver.releaselevel != "final":
            implementation_version = "{}{}{}".format(
                implementation_version,
                impl_ver.releaselevel[0],
                impl_ver.serial,
            )

        sys_path = sys.path
        try:
            sys_path.remove("")
        except ValueError:
            pass

        print(
            json.dumps(
                {
                    "sys_path": sys_path,
                    "python_version": "{0.major}.{0.minor}.{0.micro}".format(sys.version_info),
                    "environment": {
                        "implementation_name": sys.implementation.name,
                        "implementation_version": implementation_version,
                        "os_name": os.name,
                        "platform_machine": platform.machine(),
                        "platform_release": platform.release(),
                        "platform_system": platform.system(),
                        "platform_version": platform.version(),
                        "python_full_version": platform.python_version(),
                        "platform_python_implementation": platform.python_implementation(),
                        "python_version": ".".join(platform.python_version_tuple()[:2]),
                        "sys_platform": sys.platform,
                    },
                }
            )
        )
        """)
    venv_info = json.loads(
        run_subprocess(
            [venv_python_path, "-c", command_str],
            capture_stderr=False,
            log_cmd_str="<fetch_info_in_venv commands>",
        ).stdout)
    return (
        venv_info["sys_path"],
        venv_info["environment"],
        f"Python {venv_info['python_version']}",
    )
Exemple #4
0
    def create(self, verbose: bool = False) -> None:
        if not self.is_valid:
            with animate("creating shared libraries", not verbose):
                create_process = run_subprocess(
                    [DEFAULT_PYTHON, "-m", "venv", "--clear", self.root])
            subprocess_post_check(create_process)

            # ignore installed packages to ensure no unexpected patches from the OS vendor
            # are used
            self.upgrade(pip_args=["--force-reinstall"], verbose=verbose)
Exemple #5
0
    def install_package(
        self,
        package_name: str,
        package_or_url: str,
        pip_args: List[str],
        include_dependencies: bool,
        include_apps: bool,
        is_main_package: bool,
        suffix: str = "",
    ) -> None:
        # package_name in package specifier can mismatch URL due to user error
        package_or_url = fix_package_name(package_or_url, package_name)

        # check syntax and clean up spec and pip_args
        (package_or_url,
         pip_args) = parse_specifier_for_install(package_or_url, pip_args)

        with animate(
                f"installing {full_package_description(package_name, package_or_url)}",
                self.do_animation,
        ):
            # do not use -q with `pip install` so subprocess_post_check_pip_errors
            #   has more information to analyze in case of failure.
            cmd = ([str(self.python_path), "-m", "pip", "install"] + pip_args +
                   [package_or_url])
            # no logging because any errors will be specially logged by
            #   subprocess_post_check_handle_pip_error()
            pip_process = run_subprocess(cmd,
                                         log_stdout=False,
                                         log_stderr=False)
        subprocess_post_check_handle_pip_error(pip_process)
        if pip_process.returncode:
            raise PipxError(
                f"Error installing {full_package_description(package_name, package_or_url)}."
            )

        self._update_package_metadata(
            package_name=package_name,
            package_or_url=package_or_url,
            pip_args=pip_args,
            include_dependencies=include_dependencies,
            include_apps=include_apps,
            is_main_package=is_main_package,
            suffix=suffix,
        )

        # Verify package installed ok
        if self.package_metadata[package_name].package_version is None:
            raise PipxError(
                f"Unable to install "
                f"{full_package_description(package_name, package_or_url)}.\n"
                f"Check the name or spec for errors, and verify that it can "
                f"be installed with pip.",
                wrap_message=False,
            )
Exemple #6
0
 def run_pip_get_exit_code(self, cmd: List[str]) -> ExitCode:
     cmd = [str(self.python_path), "-m", "pip"] + cmd
     if not self.verbose:
         cmd.append("-q")
     returncode = run_subprocess(cmd,
                                 capture_stdout=False,
                                 capture_stderr=False).returncode
     if returncode:
         cmd_str = " ".join(str(c) for c in cmd)
         logger.error(f"{cmd_str!r} failed")
     return ExitCode(returncode)
Exemple #7
0
    def upgrade(self,
                *,
                pip_args: Optional[List[str]] = None,
                verbose: bool = False) -> None:
        if not self.is_valid:
            self.create(verbose=verbose)
            return

        # Don't try to upgrade multiple times per run
        if self.has_been_updated_this_run:
            logger.info(f"Already upgraded libraries in {self.root}")
            return

        if pip_args is None:
            pip_args = []

        logger.info(f"Upgrading shared libraries in {self.root}")

        ignored_args = ["--editable"]
        _pip_args = [arg for arg in pip_args if arg not in ignored_args]
        if not verbose:
            _pip_args.append("-q")
        try:
            with animate("upgrading shared libraries", not verbose):
                upgrade_process = run_subprocess([
                    self.python_path,
                    "-m",
                    "pip",
                    "--disable-pip-version-check",
                    "install",
                    *_pip_args,
                    "--upgrade",
                    "pip",
                    "setuptools",
                    "wheel",
                ])
            subprocess_post_check(upgrade_process)

            self.has_been_updated_this_run = True
            self.pip_path.touch()

        except Exception:
            logger.error("Failed to upgrade shared libraries", exc_info=True)
Exemple #8
0
    def create_venv(self, venv_args: List[str], pip_args: List[str]) -> None:
        with animate("creating virtual environment", self.do_animation):
            cmd = [self.python, "-m", "venv", "--without-pip"]
            venv_process = run_subprocess(cmd + venv_args + [str(self.root)])
        subprocess_post_check(venv_process)

        shared_libs.create(self.verbose)
        pipx_pth = get_site_packages(self.python_path) / PIPX_SHARED_PTH
        # write path pointing to the shared libs site-packages directory
        # example pipx_pth location:
        #   ~/.local/pipx/venvs/black/lib/python3.8/site-packages/pipx_shared.pth
        # example shared_libs.site_packages location:
        #   ~/.local/pipx/shared/lib/python3.6/site-packages
        #
        # https://docs.python.org/3/library/site.html
        # A path configuration file is a file whose name has the form 'name.pth'.
        # its contents are additional items (one per line) to be added to sys.path
        pipx_pth.write_text(f"{shared_libs.site_packages}\n", encoding="utf-8")

        self.pipx_metadata.venv_args = venv_args
        self.pipx_metadata.python_version = self.get_python_version()
Exemple #9
0
 def list_installed_packages(self) -> Set[str]:
     cmd_run = run_subprocess(
         [str(self.python_path), "-m", "pip", "list", "--format=json"])
     pip_list = json.loads(cmd_run.stdout.strip())
     return set([x["name"] for x in pip_list])
Exemple #10
0
 def get_python_version(self) -> str:
     return run_subprocess([str(self.python_path),
                            "--version"]).stdout.strip()
Exemple #11
0
 def _run_pip(self, cmd: List[str]) -> CompletedProcess:
     cmd = [str(self.python_path), "-m", "pip"] + cmd
     if not self.verbose:
         cmd.append("-q")
     return run_subprocess(cmd)