From d1e8b04b6b2c5092fe8d0d01daf41e1db1d939c9 Mon Sep 17 00:00:00 2001 From: Chris Barnes Date: Tue, 24 Sep 2019 16:32:25 -0400 Subject: [PATCH] pipx list --freeze option If `--freeze` is supplied, print package information in a format compatible with requirements files, like `pip freeze`. Note that all pipx packages are listed, regardless of their associated python version. Like `pip freeze`, development packages and those installed from non-pypi sources will simply have their name and version listed. --- docs/docs.md | 4 +++- pipx/commands.py | 21 +++++++++++++++++++++ pipx/main.py | 16 +++++++++++----- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/docs/docs.md b/docs/docs.md index 7a8b5d2eb9..e252fb1011 100644 --- a/docs/docs.md +++ b/docs/docs.md @@ -347,12 +347,14 @@ optional arguments: ``` pipx list --help -usage: pipx list [-h] [--verbose] +usage: pipx list [-h] [--freeze] [--verbose] List packages and apps installed with pipx optional arguments: -h, --help show this help message and exit + --freeze Print package information in `pip freeze`-like format. Unlike + `pip freeze`, dependencies are not included. --verbose ``` diff --git a/pipx/commands.py b/pipx/commands.py index 77c58178e2..82f40306a1 100644 --- a/pipx/commands.py +++ b/pipx/commands.py @@ -648,6 +648,27 @@ def list_packages(venv_container: VenvContainer): print(package_summary) +def _get_package_spec(path: Path, *, package: str = None) -> str: + venv = Venv(path) + if package is None: + package = path.name + metadata = venv.get_venv_metadata_for_package(package) + return f"{package}=={metadata.package_version}" + + +def freeze_packages(venv_container: VenvContainer): + dirs = list(sorted(venv_container.iter_venv_dirs())) + if not dirs: + print("") + return + + venv_container.verify_shared_libs() + + with multiprocessing.Pool() as p: + for package_spec in p.map(_get_package_spec, dirs): + print(package_spec) + + def _get_exposed_app_paths_for_package( venv_bin_path: Path, package_binary_names: List[str], local_bin_dir: Path ): diff --git a/pipx/main.py b/pipx/main.py index 12c4295f8f..056d5eb149 100644 --- a/pipx/main.py +++ b/pipx/main.py @@ -2,8 +2,6 @@ # PYTHON_ARGCOMPLETE_OK """The command line interface to pipx""" -from pathlib import Path - import argcomplete # type: ignore import argparse import functools @@ -197,7 +195,10 @@ def run_pipx_command(args): # noqa: C901 force=args.force, ) elif args.command == "list": - commands.list_packages(venv_container) + if args.freeze: + commands.freeze_packages(venv_container) + else: + commands.list_packages(venv_container) elif args.command == "uninstall": commands.uninstall(venv_dir, package, LOCAL_BIN_DIR, verbose) elif args.command == "uninstall-all": @@ -417,6 +418,12 @@ def _add_list(subparsers): help="List installed packages", description="List packages and apps installed with pipx", ) + p.add_argument( + "--freeze", + action="store_true", + help="Print package information in `pip freeze`-like format. " + "Unlike `pip freeze`, dependencies are not included.", + ) p.add_argument("--verbose", action="store_true") @@ -539,8 +546,7 @@ def get_command_parser(): 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", + "completions", help="Print instructions on enabling shell completions for pipx" ) return parser