diff --git a/news/949.feature b/news/949.feature new file mode 100644 index 00000000000..e33819b881e --- /dev/null +++ b/news/949.feature @@ -0,0 +1,3 @@ +Added a way to distinguish between pip installed packages and those from +the system package manager in 'pip list'. Specifically, 'pip list -v' also +shows the installer of package if it has that meta data. diff --git a/pip/commands/list.py b/pip/commands/list.py index 0fe0b5bc1cd..2758626e04f 100644 --- a/pip/commands/list.py +++ b/pip/commands/list.py @@ -16,6 +16,7 @@ from pip.utils import ( get_installed_distributions, dist_is_editable) from pip.utils.deprecation import RemovedInPip11Warning +from pip.utils.packaging import get_installer from pip.cmdoptions import make_option_group, index_group logger = logging.getLogger(__name__) @@ -210,7 +211,14 @@ def iter_packages_latest_infos(self, packages, options): yield dist def output_legacy(self, dist, options): - if options.verbose >= 1 or dist_is_editable(dist): + if options.verbose >= 1: + return '%s (%s, %s, %s)' % ( + dist.project_name, + dist.version, + dist.location, + get_installer(dist), + ) + elif dist_is_editable(dist): return '%s (%s, %s)' % ( dist.project_name, dist.version, @@ -298,6 +306,8 @@ def format_for_columns(pkgs, options): data = [] if options.verbose >= 1 or any(dist_is_editable(x) for x in pkgs): header.append("Location") + if options.verbose >= 1: + header.append("Installer") for proj in pkgs: # if we're working on the 'outdated' list, separate out the @@ -310,6 +320,8 @@ def format_for_columns(pkgs, options): if options.verbose >= 1 or dist_is_editable(proj): row.append(proj.location) + if options.verbose >= 1: + row.append(get_installer(proj)) data.append(row) @@ -325,6 +337,7 @@ def format_for_json(packages, options): } if options.verbose >= 1: info['location'] = dist.location + info['installer'] = get_installer(dist) if options.outdated: info['latest_version'] = six.text_type(dist.latest_version) info['latest_filetype'] = dist.latest_filetype diff --git a/pip/utils/packaging.py b/pip/utils/packaging.py index e93b20d158d..5464dd13336 100644 --- a/pip/utils/packaging.py +++ b/pip/utils/packaging.py @@ -61,3 +61,11 @@ def check_dist_requires_python(dist): "Package %s has an invalid Requires-Python entry %s - %s" % ( dist.project_name, requires_python, e)) return + + +def get_installer(dist): + if dist.has_metadata('INSTALLER'): + for line in dist.get_metadata_lines('INSTALLER'): + if line.strip(): + return line.strip() + return '' diff --git a/tests/functional/test_list.py b/tests/functional/test_list.py index 25b891d5c4c..e3ddc69936f 100644 --- a/tests/functional/test_list.py +++ b/tests/functional/test_list.py @@ -25,10 +25,11 @@ def test_verbose_flag(script, data): 'install', '-f', data.find_links, '--no-index', 'simple==1.0', 'simple2==3.0', ) - result = script.pip('list', '-v') + result = script.pip('list', '-v', '--format=columns') assert 'Package' in result.stdout, str(result) assert 'Version' in result.stdout, str(result) assert 'Location' in result.stdout, str(result) + assert 'Installer' in result.stdout, str(result) assert 'simple 1.0' in result.stdout, str(result) assert 'simple2 3.0' in result.stdout, str(result)