def list_packages(venv_container: VenvContainer, include_injected: bool) -> None: dirs: Collection[Path] = sorted(venv_container.iter_venv_dirs()) if not dirs: print(f"nothing has been installed with pipx {sleep}") return print(f"venvs are in {bold(str(venv_container))}") print(f"apps are exposed on your $PATH at {bold(str(constants.LOCAL_BIN_DIR))}") venv_container.verify_shared_libs() if Pool: p = Pool() try: for package_summary in p.map( partial(get_package_summary, include_injected=include_injected), dirs, ): print(package_summary) finally: p.close() p.join() else: for package_summary in map( partial(get_package_summary, include_injected=include_injected), dirs, ): print(package_summary)
def list_packages(venv_container: VenvContainer, include_injected: bool) -> ExitCode: """Returns pipx exit code.""" dirs: Collection[Path] = sorted(venv_container.iter_venv_dirs()) if not dirs: print(f"nothing has been installed with pipx {sleep}") return EXIT_CODE_OK print(f"venvs are in {bold(str(venv_container))}") print( f"apps are exposed on your $PATH at {bold(str(constants.LOCAL_BIN_DIR))}" ) venv_container.verify_shared_libs() all_venv_problems = VenvProblems() if Pool: p = Pool() try: for package_summary, venv_problems in p.map( partial(get_package_summary, include_injected=include_injected), dirs): print(package_summary) all_venv_problems.or_(venv_problems) finally: p.close() p.join() else: for package_summary, venv_problems in map( partial(get_package_summary, include_injected=include_injected), dirs): print(package_summary) all_venv_problems.or_(venv_problems) if all_venv_problems.bad_venv_name: print( "\nOne or more packages contain out-of-date internal data installed from a\n" "previous pipx version and need to be updated.\n" " To fix, execute: pipx reinstall-all") if all_venv_problems.invalid_interpreter: print("\nOne or more packages have a missing python interpreter.\n" " To fix, execute: pipx reinstall-all") if all_venv_problems.missing_metadata: print( "\nOne or more packages have a missing internal pipx metadata.\n" " They were likely installed using a pipx version before 0.15.0.0.\n" " Please uninstall and install these package(s) to fix.") if all_venv_problems.not_installed: print("\nOne or more packages are not installed properly.\n" " Please uninstall and install these package(s) to fix.") if all_venv_problems.any_(): print() return EXIT_CODE_LIST_PROBLEM return EXIT_CODE_OK
def list_packages(venv_container: VenvContainer): dirs = list(sorted(venv_container.iter_venv_dirs())) if not dirs: print(f"nothing has been installed with pipx {sleep}") return print(f"venvs are in {bold(str(venv_container))}") print(f"apps are exposed on your $PATH at {bold(str(constants.LOCAL_BIN_DIR))}") venv_container.verify_shared_libs() with Pool() as p: for package_summary in p.map(_get_package_summary, dirs): print(package_summary)
def reinstall_all( venv_container: VenvContainer, local_bin_dir: Path, python: str, verbose: bool, *, skip: List[str], ) -> int: """Returns pipx shell exit code""" failed: List[str] = [] for venv_dir in venv_container.iter_venv_dirs(): if venv_dir.name in skip: continue try: reinstall( venv_dir=venv_dir, local_bin_dir=local_bin_dir, python=python, verbose=verbose, ) except PipxError as e: print(e, file=sys.stderr) failed.append(venv_dir.name) if len(failed) > 0: raise PipxError( f"The following package(s) failed to reinstall: {', '.join(failed)}" ) return 0
def get_command_parser() -> argparse.ArgumentParser: venv_container = VenvContainer(constants.PIPX_LOCAL_VENVS) completer_venvs = InstalledVenvsCompleter(venv_container) parser = argparse.ArgumentParser( formatter_class=LineWrapRawTextHelpFormatter, description=PIPX_DESCRIPTION) subparsers = parser.add_subparsers( dest="command", description="Get help for commands with pipx COMMAND --help") _add_install(subparsers) _add_inject(subparsers, completer_venvs.use) _add_upgrade(subparsers, completer_venvs.use) _add_upgrade_all(subparsers) _add_uninstall(subparsers, completer_venvs.use) _add_uninstall_all(subparsers) _add_reinstall(subparsers, completer_venvs.use) _add_reinstall_all(subparsers) _add_list(subparsers) _add_run(subparsers) _add_runpip(subparsers, completer_venvs.use) _add_ensurepath(subparsers) parser.add_argument("--version", action="store_true", help="Print version and exit") subparsers.add_parser( "completions", help="Print instructions on enabling shell completions for pipx", description="Print instructions on enabling shell completions for pipx", ) return parser
def upgrade_all(venv_container: VenvContainer, verbose: bool, *, skip: List[str], force: bool): venv_error = False venvs_upgraded = 0 for venv_dir in venv_container.iter_venv_dirs(): venv = Venv(venv_dir, verbose=verbose) if (venv_dir.name in skip or "--editable" in venv.pipx_metadata.main_package.pip_args): continue try: venvs_upgraded += upgrade( venv_dir, venv.pipx_metadata.main_package.pip_args, verbose, upgrading_all=True, force=force, ) except Exception: venv_error = True logging.error(f"Error encountered when upgrading {venv_dir.name}") if venvs_upgraded == 0: print( f"Versions did not change after running 'pip upgrade' for each package {sleep}" ) if venv_error: raise PipxError("Some packages encountered errors during upgrade. " "See specific error messages above.")
def reinstall_all( venv_container: VenvContainer, local_bin_dir: Path, python: str, verbose: bool, *, skip: Sequence[str], ) -> ExitCode: """Returns pipx exit code.""" pipx.shared_libs.shared_libs.upgrade(verbose=verbose) failed: List[str] = [] for venv_dir in venv_container.iter_venv_dirs(): if venv_dir.name in skip: continue try: package_exit = reinstall( venv_dir=venv_dir, local_bin_dir=local_bin_dir, python=python, verbose=verbose, ) except PipxError as e: print(e, file=sys.stderr) failed.append(venv_dir.name) if package_exit != 0: failed.append(venv_dir.name) if len(failed) > 0: raise PipxError( f"The following package(s) failed to reinstall: {', '.join(failed)}" ) # Any failure to install will raise PipxError, otherwise success return EXIT_CODE_OK
def reinstall_all( venv_container: VenvContainer, local_bin_dir: Path, python: str, pip_args: List[str], venv_args: List[str], verbose: bool, include_dependencies: bool, *, skip: List[str], ): for venv_dir in venv_container.iter_venv_dirs(): package = venv_dir.name if package in skip: continue uninstall(venv_dir, package, local_bin_dir, verbose) package_or_url = package install( venv_dir, package, package_or_url, local_bin_dir, python, pip_args, venv_args, verbose, force=True, include_dependencies=include_dependencies, )
def upgrade_all(venv_container: VenvContainer, verbose: bool, *, skip: List[str], force: bool): packages_upgraded = 0 num_packages = 0 for venv_dir in venv_container.iter_venv_dirs(): num_packages += 1 package = venv_dir.name venv = Venv(venv_dir, verbose=verbose) if package in skip or "--editable" in venv.pipx_metadata.main_package.pip_args: continue try: packages_upgraded += upgrade( venv_dir, package, venv.pipx_metadata.main_package.pip_args, verbose, upgrading_all=True, force=force, ) except Exception: logging.error(f"Error encountered when upgrading {package}") if packages_upgraded == 0: print( f"Versions did not change after running 'pip upgrade' for each package {sleep}" )
def list_packages( venv_container: VenvContainer, include_injected: bool, json_format: bool, short_format: bool, ) -> ExitCode: """Returns pipx exit code.""" venv_dirs: Collection[Path] = sorted(venv_container.iter_venv_dirs()) if not venv_dirs: print(f"nothing has been installed with pipx {sleep}", file=sys.stderr) venv_container.verify_shared_libs() if json_format: all_venv_problems = list_json(venv_dirs) elif short_format: all_venv_problems = list_short(venv_dirs) else: if not venv_dirs: return EXIT_CODE_OK all_venv_problems = list_text(venv_dirs, include_injected, str(venv_container)) if all_venv_problems.bad_venv_name: logger.warning( "\nOne or more packages contain out-of-date internal data installed from a\n" "previous pipx version and need to be updated.\n" " To fix, execute: pipx reinstall-all") if all_venv_problems.invalid_interpreter: logger.warning( "\nOne or more packages have a missing python interpreter.\n" " To fix, execute: pipx reinstall-all") if all_venv_problems.missing_metadata: logger.warning( "\nOne or more packages have a missing internal pipx metadata.\n" " They were likely installed using a pipx version before 0.15.0.0.\n" " Please uninstall and install these package(s) to fix.") if all_venv_problems.not_installed: logger.warning( "\nOne or more packages are not installed properly.\n" " Please uninstall and install these package(s) to fix.") if all_venv_problems.any_(): print("", file=sys.stderr) return EXIT_CODE_LIST_PROBLEM return EXIT_CODE_OK
def uninstall_all(venv_container: VenvContainer, local_bin_dir: Path, verbose: bool) -> ExitCode: """Returns pipx exit code.""" all_success = True for venv_dir in venv_container.iter_venv_dirs(): return_val = uninstall(venv_dir, local_bin_dir, verbose) all_success &= return_val == 0 return EXIT_CODE_OK if all_success else EXIT_CODE_UNINSTALL_ERROR
def reinstall_all( venv_container: VenvContainer, local_bin_dir: Path, python: str, verbose: bool, *, skip: List[str], ): for venv_dir in venv_container.iter_venv_dirs(): package = venv_dir.name if package in skip: continue venv = Venv(venv_dir, verbose=verbose) if venv.pipx_metadata.main_package.package_or_url is not None: package_or_url = venv.pipx_metadata.main_package.package_or_url else: package_or_url = package uninstall(venv_dir, package, local_bin_dir, verbose) # install main package first install( venv_dir, package, package_or_url, local_bin_dir, python, venv.pipx_metadata.main_package.pip_args, venv.pipx_metadata.venv_args, verbose, force=True, include_dependencies=venv.pipx_metadata.main_package. include_dependencies, ) # now install injected packages for ( injected_name, injected_package, ) in venv.pipx_metadata.injected_packages.items(): if injected_package.package_or_url is None: # This should never happen, but package_or_url is type # Optional[str] so mypy thinks it could be None raise PipxError("Internal Error injecting package") inject( venv_dir, injected_name, injected_package.package_or_url, injected_package.pip_args, verbose=verbose, include_apps=injected_package.include_apps, include_dependencies=injected_package.include_dependencies, force=True, )
def upgrade_all( venv_container: VenvContainer, verbose: bool, *, include_injected: bool, skip: Sequence[str], force: bool, ) -> ExitCode: """Returns pipx exit code.""" venv_error = False venvs_upgraded = 0 for venv_dir in venv_container.iter_venv_dirs(): venv = Venv(venv_dir, verbose=verbose) if ( venv_dir.name in skip or "--editable" in venv.pipx_metadata.main_package.pip_args ): continue try: venvs_upgraded += _upgrade_venv( venv_dir, venv.pipx_metadata.main_package.pip_args, verbose, include_injected=include_injected, upgrading_all=True, force=force, ) except PipxError as e: venv_error = True logger.error(f"Error encountered when upgrading {venv_dir.name}:") logger.error(f"{e}\n") if venvs_upgraded == 0: print( f"Versions did not change after running 'pip upgrade' for each package {sleep}" ) if venv_error: raise PipxError( "\nSome packages encountered errors during upgrade.\n" " See specific error messages above." ) return EXIT_CODE_OK
def upgrade_all( venv_container: VenvContainer, pip_args: List[str], verbose: bool, *, include_dependencies: bool, skip: List[str], force: bool, ): packages_upgraded = 0 num_packages = 0 for venv_dir in venv_container.iter_venv_dirs(): num_packages += 1 package = venv_dir.name if package in skip: continue if package == "pipx": package_or_url = PIPX_PACKAGE_NAME else: package_or_url = package try: packages_upgraded += upgrade( venv_dir, package, package_or_url, pip_args, verbose, upgrading_all=True, include_dependencies=include_dependencies, force=force, ) except Exception: logging.error(f"Error encountered when upgrading {package}") if packages_upgraded == 0: print( f"Versions did not change after running 'pip upgrade' for each package {sleep}" )
def install( venv_dir: Optional[Path], package_name: Optional[str], package_spec: str, local_bin_dir: Path, python: str, pip_args: List[str], venv_args: List[str], verbose: bool, *, force: bool, include_dependencies: bool, suffix: str = "", ): # package_spec is anything pip-installable, including package_name, vcs spec, # zip file, or tar.gz file. if package_name is None: package_name = package_name_from_spec(package_spec, python, pip_args=pip_args, verbose=verbose) if venv_dir is None: venv_container = VenvContainer(constants.PIPX_LOCAL_VENVS) venv_dir = venv_container.get_venv_dir(package_name) if suffix != "": venv_dir = venv_dir.parent / f"{venv_dir.stem}{suffix}" try: exists = venv_dir.exists() and next(venv_dir.iterdir()) except StopIteration: exists = False if exists: if force: print(f"Installing to existing directory {str(venv_dir)!r}") else: print(f"{venv_dir.name!r} already seems to be installed. " f"Not modifying existing installation in {str(venv_dir)!r}. " "Pass '--force' to force installation.") return venv = Venv(venv_dir, python=python, verbose=verbose) try: venv.create_venv(venv_args, pip_args) venv.install_package( package=package_name, package_or_url=package_spec, pip_args=pip_args, include_dependencies=include_dependencies, include_apps=True, is_main_package=True, suffix=suffix, ) run_post_install_actions( venv, package_name, local_bin_dir, venv_dir, include_dependencies, force=force, ) except (Exception, KeyboardInterrupt): print("") venv.remove_venv() raise
def run_pipx_command(args: argparse.Namespace) -> ExitCode: # noqa: C901 verbose = args.verbose if "verbose" in args else False pip_args = get_pip_args(vars(args)) venv_args = get_venv_args(vars(args)) venv_container = VenvContainer(constants.PIPX_LOCAL_VENVS) if "package" in args: package = args.package if urllib.parse.urlparse(package).scheme: raise PipxError("Package cannot be a url") if "spec" in args and args.spec is not None: if urllib.parse.urlparse(args.spec).scheme: if "#egg=" not in args.spec: args.spec = args.spec + f"#egg={package}" venv_dir = venv_container.get_venv_dir(package) logger.info(f"Virtual Environment location is {venv_dir}") if "skip" in args: skip_list = [canonicalize_name(x) for x in args.skip] if args.command == "run": package_or_url = (args.spec if ("spec" in args and args.spec is not None) else args.app_with_args[0]) use_cache = not args.no_cache commands.run( args.app_with_args[0], package_or_url, args.app_with_args[1:], args.python, pip_args, venv_args, args.pypackages, verbose, use_cache, ) # We should never reach here because run() is NoReturn. return ExitCode(1) elif args.command == "install": return commands.install( None, None, args.package_spec, constants.LOCAL_BIN_DIR, args.python, pip_args, venv_args, verbose, force=args.force, include_dependencies=args.include_deps, suffix=args.suffix, ) elif args.command == "inject": return commands.inject( venv_dir, None, args.dependencies, pip_args, verbose=verbose, include_apps=args.include_apps, include_dependencies=args.include_deps, force=args.force, ) elif args.command == "upgrade": return commands.upgrade( venv_dir, pip_args, verbose, include_injected=args.include_injected, force=args.force, ) elif args.command == "upgrade-all": return commands.upgrade_all( venv_container, verbose, include_injected=args.include_injected, skip=skip_list, force=args.force, ) elif args.command == "list": return commands.list_packages(venv_container, args.include_injected) elif args.command == "uninstall": return commands.uninstall(venv_dir, constants.LOCAL_BIN_DIR, verbose) elif args.command == "uninstall-all": return commands.uninstall_all(venv_container, constants.LOCAL_BIN_DIR, verbose) elif args.command == "reinstall": return commands.reinstall( venv_dir=venv_dir, local_bin_dir=constants.LOCAL_BIN_DIR, python=args.python, verbose=verbose, ) elif args.command == "reinstall-all": return commands.reinstall_all( venv_container, constants.LOCAL_BIN_DIR, args.python, verbose, skip=skip_list, ) elif args.command == "runpip": if not venv_dir: raise PipxError("Developer error: venv_dir is not defined.") return commands.run_pip(package, venv_dir, args.pipargs, args.verbose) elif args.command == "ensurepath": try: return commands.ensure_pipx_paths(force=args.force) except Exception as e: logger.debug("Uncaught Exception:", exc_info=True) raise PipxError(str(e), wrap_message=False) elif args.command == "completions": print(constants.completion_instructions) return ExitCode(0) else: raise PipxError(f"Unknown command {args.command}")
def __init__(self, venv_container: VenvContainer) -> None: self.packages = [ str(p.name) for p in sorted(venv_container.iter_venv_dirs()) ]
def uninstall_all(venv_container: VenvContainer, local_bin_dir: Path, verbose: bool): for venv_dir in venv_container.iter_venv_dirs(): package = venv_dir.name uninstall(venv_dir, package, local_bin_dir, verbose)
def install( venv_dir: Optional[Path], package_name: Optional[str], package_spec: str, local_bin_dir: Path, python: str, pip_args: List[str], venv_args: List[str], verbose: bool, *, force: bool, include_dependencies: bool, suffix: str = "", ) -> ExitCode: """Returns pipx exit code.""" # package_spec is anything pip-installable, including package_name, vcs spec, # zip file, or tar.gz file. if package_name is None: package_name = package_name_from_spec(package_spec, python, pip_args=pip_args, verbose=verbose) if venv_dir is None: venv_container = VenvContainer(constants.PIPX_LOCAL_VENVS) venv_dir = venv_container.get_venv_dir(f"{package_name}{suffix}") try: exists = venv_dir.exists() and bool(next(venv_dir.iterdir())) except StopIteration: exists = False venv = Venv(venv_dir, python=python, verbose=verbose) if exists: if force: print(f"Installing to existing venv {venv.name!r}") else: print( pipx_wrap(f""" {venv.name!r} already seems to be installed. Not modifying existing installation in {str(venv_dir)!r}. Pass '--force' to force installation. """)) return EXIT_CODE_INSTALL_VENV_EXISTS try: venv.create_venv(venv_args, pip_args) venv.install_package( package_name=package_name, package_or_url=package_spec, pip_args=pip_args, include_dependencies=include_dependencies, include_apps=True, is_main_package=True, suffix=suffix, ) run_post_install_actions( venv, package_name, local_bin_dir, venv_dir, include_dependencies, force=force, ) except (Exception, KeyboardInterrupt): print() venv.remove_venv() raise # Any failure to install will raise PipxError, otherwise success return EXIT_CODE_OK