Repository: pypa/pip

Issue:
pip show fails silently if package not found
**Environment**
All

* pip version:
Up to 19.2.1

* Python version:
All

* OS:
All
<!-- Feel free to add more information about your environment here -->

**Description**
When you use `pip show` _somepackage_ if _somepackage_ is not found there is no output, this is not too bad when a single package is specified but when multiple ones are it is easy to fail to notice that one _or more_ is missing. _This was raised as a part of #5454 where trailing commas result in packages not being found._

**Expected behavior**
<!-- A clear and concise description of what you expected to happen. -->
I would expect `pip show` to give a clear indication of any package_(s)_ that it does not find, preferably either at the begining or end of output where it is less likely to be missed as mentioned by @chrahunt in his comments on #5454 - some thing like:

`The following package(s) are not installed: somepackage`

**How to Reproduce**
<!-- Describe the steps to reproduce this bug. -->
type: `pip show asdf asdfasdf gsdfgfg dfgd` _or any other random set of gibberish_ then try with 3 packages that you do have installed and one that you don't.
1. type: `pip show asdf asdfasdf gsdfgfg dfgd` _or any other random set of gibberish_ 
2. Then run `pip show` with some packages that you have and one that you don't _or gibberish_

**Output**

```
> python -mpip show asdf asdfasdf gsdfgfg dfgd 

> python -mpip show asdf requests requests-html
Name: requests
Version: 2.21.0
Summary: Python HTTP for Humans.
Home-page: http://python-requests.org
Author: Kenneth Reitz
Author-email: me@kennethreitz.org
License: Apache 2.0
Location: c:\python36_64\lib\site-packages
Requires: idna, chardet, certifi, urllib3
Required-by: yampy2, wikipedia, Sphinx, safety, requests-html, pypistats, plotly, pandas-datareader, osmnx, nbdime, moviepy, jupyterhub, jupyter-request
s, folium, cookiecutter, caniusepython3
---
Name: requests-html
Version: 0.10.0
Summary: HTML Parsing for Humans.
Home-page: https://github.com/kennethreitz/requests-html
Author: Kenneth Reitz
Author-email: me@kennethreitz.org
License: MIT
Location: c:\python36_64\lib\site-packagesRequires: pyppeteer, bs4, w3lib, requests, pyquery, fake-useragent, parseRequired-by:
```
Trying to spot the missing `asdf` in the above is not simple.

I agree with the sentiment that "Errors should never pass silently." and think that this is a case in point.

In the spirit of "Unless explicitly silenced." `-q` could suppress this message.


Discussion:
This seems reasonable to me. I think if we write to stderr then we don't necessarily need to implement a `-q`, which may be better evaluated in the context of all commands as part of #6099.
@chrahunt - I agree with leaving out the `-q` - Looking at #6758 it looks like the output should be to logging.info for consistency with the other code rather than just outputting directly to stderr.  _This will currently output to stdout and later switch to stderr as a part of #6758 _or some other resolution_.

Resolving patch:
diff --git a/src/pip/_internal/commands/show.py b/src/pip/_internal/commands/show.py
--- a/src/pip/_internal/commands/show.py
+++ b/src/pip/_internal/commands/show.py
@@ -60,6 +60,11 @@ def search_packages_info(query):
         installed[canonicalize_name(p.project_name)] = p
 
     query_names = [canonicalize_name(name) for name in query]
+    missing = sorted(
+        [name for name, pkg in zip(query, query_names) if pkg not in installed]
+    )
+    if missing:
+        logger.warning('Package(s) not found: %s', ', '.join(missing))
 
     for dist in [installed[pkg] for pkg in query_names if pkg in installed]:
         package = {


Test patch:
diff --git a/tests/functional/test_show.py b/tests/functional/test_show.py
--- a/tests/functional/test_show.py
+++ b/tests/functional/test_show.py
@@ -79,6 +79,34 @@ def test_find_package_not_found():
     assert len(list(result)) == 0
 
 
+def test_report_single_not_found(script):
+    """
+    Test passing one name and that isn't found.
+    """
+    # We choose a non-canonicalized name to test that the non-canonical
+    # form is logged.
+    # Also, the following should report an error as there are no results
+    # to print. Consequently, there is no need to pass
+    # allow_stderr_warning=True since this is implied by expect_error=True.
+    result = script.pip('show', 'Abcd-3', expect_error=True)
+    assert 'WARNING: Package(s) not found: Abcd-3' in result.stderr
+    assert not result.stdout.splitlines()
+
+
+def test_report_mixed_not_found(script):
+    """
+    Test passing a mixture of found and not-found names.
+    """
+    # We test passing non-canonicalized names.
+    result = script.pip(
+        'show', 'Abcd3', 'A-B-C', 'pip', allow_stderr_warning=True
+    )
+    assert 'WARNING: Package(s) not found: A-B-C, Abcd3' in result.stderr
+    lines = result.stdout.splitlines()
+    assert len(lines) == 10
+    assert 'Name: pip' in lines
+
+
 def test_search_any_case():
     """
     Search for a package in any case.
@@ -86,7 +114,7 @@ def test_search_any_case():
     """
     result = list(search_packages_info(['PIP']))
     assert len(result) == 1
-    assert 'pip' == result[0]['name']
+    assert result[0]['name'] == 'pip'
 
 
 def test_more_than_one_package():