Repository: pypa/pip
Pull request: Fixed issue #979
Fixes #979

Problem statement:
pip list verbose to show install locations
Hello!

Currently `
for i in $( pip-2.7 freeze | cut -d'=' -f1) ; do echo -n " $i " ;  pip-2.7 show $i | grep Location | cut -d':' -f2 ; done
` does a somewhat okay job of showing which package is installed where; but it's already buggy (since there is no pip list without version numbers; the quiet -q switch just mutes it completely, and the verbose -v just produces no effect), so it'd be good to have a really barebones pip list, and a verbose one that one can easily grep.

Thanks. (And yes, I know, pull requests are welcome; though probably that bash one-liner is enough for most people, if they can find it.)

Hints from issue discussion:
Another great -vv option would be to list the install date/time

Now that `pip list` has a `columns` with a `Location` column, this could be quite easy to implement.

Commit message: Added a warning when both --verbose and --quiet options used simultaneously

Patch to review:
diff --git a/AUTHORS.txt b/AUTHORS.txt
index 7859b7c3109..a0c3d18bf2b 100644
--- a/AUTHORS.txt
+++ b/AUTHORS.txt
@@ -172,6 +172,7 @@ Lincoln de Sousa <lincoln@comum.org>
 Lipis <lipiridis@gmail.com>
 Ludovic Gasc <gmludo@gmail.com>
 Luke Macken <lmacken@redhat.com>
+Luo Jiebin <luo.jiebin@foxmail.com>
 Marc Abramowitz <marc@marc-abramowitz.com>
 Marc Tamlyn <marc.tamlyn@gmail.com>
 Marcus Smith <qwcode@gmail.com>
diff --git a/pip/basecommand.py b/pip/basecommand.py
index 6ba36069558..2cf8e3c85a1 100644
--- a/pip/basecommand.py
+++ b/pip/basecommand.py
@@ -105,6 +105,12 @@ def parse_args(self, args):
     def main(self, args):
         options, args = self.parse_args(args)
 
+        if options.quiet and options.verbose:
+            warnings.warn(
+                "--verbose and --quiet options used simultaneously."
+                "Ignoring --verbose."
+            )
+
         if options.quiet:
             if options.quiet == 1:
                 level = "WARNING"
diff --git a/pip/commands/list.py b/pip/commands/list.py
index 9d31bb65ba7..a8d6666b90b 100644
--- a/pip/commands/list.py
+++ b/pip/commands/list.py
@@ -209,8 +209,8 @@ def iter_packages_latest_infos(self, packages, options):
                 dist.latest_filetype = typ
                 yield dist
 
-    def output_legacy(self, dist):
-        if dist_is_editable(dist):
+    def output_legacy(self, dist, options):
+        if options.count == 1 or dist_is_editable(dist):
             return '%s (%s, %s)' % (
                 dist.project_name,
                 dist.version,
@@ -219,9 +219,9 @@ def output_legacy(self, dist):
         else:
             return '%s (%s)' % (dist.project_name, dist.version)
 
-    def output_legacy_latest(self, dist):
+    def output_legacy_latest(self, dist, options):
         return '%s - Latest: %s [%s]' % (
-            self.output_legacy(dist),
+            self.output_legacy(dist, options),
             dist.latest_version,
             dist.latest_filetype,
         )
@@ -236,15 +236,19 @@ def output_package_listing(self, packages, options):
             self.output_package_listing_columns(data, header)
         elif options.list_format == 'freeze':
             for dist in packages:
-                logger.info("%s==%s", dist.project_name, dist.version)
+                if options.count == 1:
+                    logger.info("%s==%s (%s)", dist.project_name,
+                                dist.version, dist.location)
+                else:
+                    logger.info("%s==%s", dist.project_name, dist.version)
         elif options.list_format == 'json':
             logger.info(format_for_json(packages, options))
         elif options.list_format == "legacy":
             for dist in packages:
                 if options.outdated:
-                    logger.info(self.output_legacy_latest(dist))
+                    logger.info(self.output_legacy_latest(dist, options))
                 else:
-                    logger.info(self.output_legacy(dist))
+                    logger.info(self.output_legacy(dist, options))
 
     def output_package_listing_columns(self, data, header):
         # insert the header first: we need to know the size of column names
@@ -292,7 +296,7 @@ def format_for_columns(pkgs, options):
         header = ["Package", "Version"]
 
     data = []
-    if any(dist_is_editable(x) for x in pkgs):
+    if options.count == 1 or any(dist_is_editable(x) for x in pkgs):
         header.append("Location")
 
     for proj in pkgs:
@@ -304,7 +308,7 @@ def format_for_columns(pkgs, options):
             row.append(proj.latest_version)
             row.append(proj.latest_filetype)
 
-        if dist_is_editable(proj):
+        if options.count == 1 or dist_is_editable(proj):
             row.append(proj.location)
 
         data.append(row)
@@ -319,6 +323,8 @@ def format_for_json(packages, options):
             'name': dist.project_name,
             'version': six.text_type(dist.version),
         }
+        if options.count == 1:
+            info['location'] = dist.location
         if options.outdated:
             info['latest_version'] = six.text_type(dist.latest_version)
             info['latest_filetype'] = dist.latest_filetype
diff --git a/tests/functional/test_list.py b/tests/functional/test_list.py
index 7b4169683bf..25b891d5c4c 100644
--- a/tests/functional/test_list.py
+++ b/tests/functional/test_list.py
@@ -17,6 +17,22 @@ def test_list_command(script, data):
     assert 'simple2    3.0' in result.stdout, str(result)
 
 
+def test_verbose_flag(script, data):
+    """
+    Test the list command with the '-v' option
+    """
+    script.pip(
+        'install', '-f', data.find_links, '--no-index', 'simple==1.0',
+        'simple2==3.0',
+    )
+    result = script.pip('list', '-v')
+    assert 'Package' in result.stdout, str(result)
+    assert 'Version' in result.stdout, str(result)
+    assert 'Location' in result.stdout, str(result)
+    assert 'simple     1.0' in result.stdout, str(result)
+    assert 'simple2    3.0' in result.stdout, str(result)
+
+
 def test_columns_flag(script, data):
     """
     Test the list command with the '--format=columns' option


Code review comments:
diff_hunk: @@ -105,6 +105,12 @@ def parse_args(self, args):
     def main(self, args):
         options, args = self.parse_args(args)
 
+        if options.quiet and options.verbose:
path: pip/basecommand.py
text: @user1:
I think rather than doing this, it'd be better to do something like:

```python
verbosity = options.verbose - options.quiet
if verbosity >= 1:
    level = "DEBUG"
elif verbosity == -1:
    level = "WARNING"
elif verbosity == -2:
    level = "ERROR"
elif verbosity <= -3:
    level = "CRITICAL"
else:
    level = "INFO"
```

This eliminates the warning (which kind of defeat the purpose of the ``-q`` flag) and allows combinations of ``-q`` and ``-v`` to act sanely (each use of ``-q`` lowers the verbosity level until it gets max quiet, each use of ``-v`` raises it until it gets max noisy, combinations of ``-q`` and ``-v`` end up being combined to determine the output level).

@author:
Good idea. I will improve it.

@user2:
Agreed. You'd also need to then pass the "net verbosity" to then list command below. Although at that point I wonder whether triggering the extra output off the ```-v``` option makes sense - should ```pip list -v -q``` add the extra info or not? Maybe it would be better to have an explicit option for that information.