def install( venv_dir: Path, package: str, package_or_url: str, local_bin_dir: Path, python: str, pip_args: List[str], venv_args: List[str], verbose: bool, *, force: bool, include_dependencies: bool, ): 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"{package!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) try: venv.install_package( package=package, package_or_url=package_or_url, pip_args=pip_args, include_dependencies=include_dependencies, include_apps=True, is_main_package=True, ) except PackageInstallFailureError: venv.remove_venv() raise PipxError( f"Could not install package {package}. Is the name or spec correct?" ) _run_post_install_actions(venv, package, local_bin_dir, venv_dir, include_dependencies, force=force) except (Exception, KeyboardInterrupt): print("") venv.remove_venv() raise
def run_post_install_actions( venv: Venv, package: str, local_bin_dir: Path, venv_dir: Path, include_dependencies: bool, *, force: bool, suffix: Optional[str] = None, ): package_metadata = venv.package_metadata[package] if not package_metadata.app_paths and not include_dependencies: # No apps associated with this package and we aren't including dependencies. # This package has nothing for pipx to use, so this is an error. for dep, dependent_apps in package_metadata.app_paths_of_dependencies.items( ): print( f"Note: Dependent package '{dep}' contains {len(dependent_apps)} apps" ) for app in dependent_apps: print(f" - {app.name}") if venv.safe_to_remove(): venv.remove_venv() if package_metadata.app_paths_of_dependencies: raise PipxError( f"No apps associated with package {package}. " "Try again with '--include-deps' to include apps of dependent packages, " "which are listed above. " "If you are attempting to install a library, pipx should not be used. " "Consider using pip or a similar tool instead.") else: raise PipxError( f"No apps associated with package {package}. " "If you are attempting to install a library, pipx should not be used. " "Consider using pip or a similar tool instead.") if package_metadata.apps: pass elif package_metadata.apps_of_dependencies and include_dependencies: pass else: # No apps associated with this package and we aren't including dependencies. # This package has nothing for pipx to use, so this is an error. if venv.safe_to_remove(): venv.remove_venv() raise PipxError( f"No apps associated with package {package} or its dependencies." "If you are attempting to install a library, pipx should not be used. " "Consider using pip or a similar tool instead.") if suffix is None: suffix = "" expose_apps_globally(local_bin_dir, package_metadata.app_paths, package, force=force, suffix=suffix) if include_dependencies: for _, app_paths in package_metadata.app_paths_of_dependencies.items(): expose_apps_globally(local_bin_dir, app_paths, package, force=force, suffix=suffix) print( get_package_summary(venv_dir, package=package, new_install=True, suffix=suffix)) warn_if_not_on_path(local_bin_dir) print(f"done! {stars}", file=sys.stderr)
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_post_install_actions( venv: Venv, package_name: str, local_bin_dir: Path, venv_dir: Path, include_dependencies: bool, *, force: bool, ) -> None: package_metadata = venv.package_metadata[package_name] display_name = f"{package_name}{package_metadata.suffix}" if not package_metadata.apps: if not package_metadata.apps_of_dependencies: if venv.safe_to_remove(): venv.remove_venv() raise PipxError( f""" No apps associated with package {display_name} or its dependencies. If you are attempting to install a library, pipx should not be used. Consider using pip or a similar tool instead. """ ) if package_metadata.apps_of_dependencies and not include_dependencies: for ( dep, dependent_apps, ) in package_metadata.app_paths_of_dependencies.items(): print( f"Note: Dependent package '{dep}' contains {len(dependent_apps)} apps" ) for app in dependent_apps: print(f" - {app.name}") if venv.safe_to_remove(): venv.remove_venv() raise PipxError( f""" No apps associated with package {display_name}. Try again with '--include-deps' to include apps of dependent packages, which are listed above. If you are attempting to install a library, pipx should not be used. Consider using pip or a similar tool instead." """ ) expose_apps_globally( local_bin_dir, package_metadata.app_paths, force=force, suffix=package_metadata.suffix, ) if include_dependencies: for _, app_paths in package_metadata.app_paths_of_dependencies.items(): expose_apps_globally( local_bin_dir, app_paths, force=force, suffix=package_metadata.suffix ) package_summary, _ = get_venv_summary( venv_dir, package_name=package_name, new_install=True ) print(package_summary) warn_if_not_on_path(local_bin_dir) print(f"done! {stars}", file=sys.stderr)
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