We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Add pre-released package upgrade hints
Before this commit when querying for an installed package using pip if
the package that was returned contained a pre-released version of the
package it was not intuitive that the pre-released version of the
package could not be directly upgraded to without the `--pre` flag.

This commit shows additional information on how to upgrade to
pre-released versions of a package if one is found in the search.

Fixes #5169.

<!---
Thank you for your soon to be pull request. Before you submit this, please
double check to make sure that you've added a news file fragment. In pip we
generate our NEWS.rst from multiple news fragment files, and all pull requests
require either a news file fragment or a marker to indicate they don't require
one.

To read more about adding a news file fragment for your PR, please check out
our documentation at: https://pip.pypa.io/en/latest/development/#adding-a-news-entry
-->

Display pre-release version hint in package version output of "pip search" (and others)
* Pip version: pip 9.0.3 from c:\python36\lib\site-packages (python 3.6)
* Python version: Python 3.6.2
* Operating system: Windows 10

### Description:

In #5168 I learned that version numbers like "2018.04.04a" are interpreted as pre-release versions. Therefore I could not install them without adding the parameter `--pre`.

However, this is _implicit_ knowledge the user must have about interpretations of version numbers and related pip behavior in order to understand the behavior of pip. Otherwise, the user is irritated, just like I was until a couple of minutes ago.

Current situation where 2018.04.04a is not installable when `--pre` is not given:

```
C:\Users\karl.voit>pip search filetags
filetags (2018.04.04a)  - Management of simple tags within file names
  INSTALLED: 2018.3.19
  LATEST:    2018.04.04a

C:\Users\karl.voit>
```

Shouldn't there at least be a hint such as following suggestion?

```
C:\Users\karl.voit>pip search filetags
filetags (2018.04.04a)  - Management of simple tags within file names
  INSTALLED: 2018.3.19
  LATEST:    2018.04.04a (pre-release; install with "pip install --pre")

C:\Users\karl.voit>
```

Maybe this applies for multiple stdout messages.

### What I've run:

See #5168 to give you some background.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/pip/_internal/commands/search.py`
Content:
```
1 from __future__ import absolute_import
2 
3 import logging
4 import sys
5 import textwrap
6 from collections import OrderedDict
7 
8 from pip._vendor import pkg_resources
9 from pip._vendor.packaging.version import parse as parse_version
10 # NOTE: XMLRPC Client is not annotated in typeshed as on 2017-07-17, which is
11 #       why we ignore the type on this import
12 from pip._vendor.six.moves import xmlrpc_client  # type: ignore
13 
14 from pip._internal.cli.base_command import Command
15 from pip._internal.cli.status_codes import NO_MATCHES_FOUND, SUCCESS
16 from pip._internal.download import PipXmlrpcTransport
17 from pip._internal.exceptions import CommandError
18 from pip._internal.models.index import PyPI
19 from pip._internal.utils.compat import get_terminal_size
20 from pip._internal.utils.logging import indent_log
21 
22 logger = logging.getLogger(__name__)
23 
24 
25 class SearchCommand(Command):
26     """Search for PyPI packages whose name or summary contains <query>."""
27     name = 'search'
28     usage = """
29       %prog [options] <query>"""
30     summary = 'Search PyPI for packages.'
31     ignore_require_venv = True
32 
33     def __init__(self, *args, **kw):
34         super(SearchCommand, self).__init__(*args, **kw)
35         self.cmd_opts.add_option(
36             '-i', '--index',
37             dest='index',
38             metavar='URL',
39             default=PyPI.pypi_url,
40             help='Base URL of Python Package Index (default %default)')
41 
42         self.parser.insert_option_group(0, self.cmd_opts)
43 
44     def run(self, options, args):
45         if not args:
46             raise CommandError('Missing required argument (search query).')
47         query = args
48         pypi_hits = self.search(query, options)
49         hits = transform_hits(pypi_hits)
50 
51         terminal_width = None
52         if sys.stdout.isatty():
53             terminal_width = get_terminal_size()[0]
54 
55         print_results(hits, terminal_width=terminal_width)
56         if pypi_hits:
57             return SUCCESS
58         return NO_MATCHES_FOUND
59 
60     def search(self, query, options):
61         index_url = options.index
62         with self._build_session(options) as session:
63             transport = PipXmlrpcTransport(index_url, session)
64             pypi = xmlrpc_client.ServerProxy(index_url, transport)
65             hits = pypi.search({'name': query, 'summary': query}, 'or')
66             return hits
67 
68 
69 def transform_hits(hits):
70     """
71     The list from pypi is really a list of versions. We want a list of
72     packages with the list of versions stored inline. This converts the
73     list from pypi into one we can use.
74     """
75     packages = OrderedDict()
76     for hit in hits:
77         name = hit['name']
78         summary = hit['summary']
79         version = hit['version']
80 
81         if name not in packages.keys():
82             packages[name] = {
83                 'name': name,
84                 'summary': summary,
85                 'versions': [version],
86             }
87         else:
88             packages[name]['versions'].append(version)
89 
90             # if this is the highest version, replace summary and score
91             if version == highest_version(packages[name]['versions']):
92                 packages[name]['summary'] = summary
93 
94     return list(packages.values())
95 
96 
97 def print_results(hits, name_column_width=None, terminal_width=None):
98     if not hits:
99         return
100     if name_column_width is None:
101         name_column_width = max([
102             len(hit['name']) + len(highest_version(hit.get('versions', ['-'])))
103             for hit in hits
104         ]) + 4
105 
106     installed_packages = [p.project_name for p in pkg_resources.working_set]
107     for hit in hits:
108         name = hit['name']
109         summary = hit['summary'] or ''
110         latest = highest_version(hit.get('versions', ['-']))
111         if terminal_width is not None:
112             target_width = terminal_width - name_column_width - 5
113             if target_width > 10:
114                 # wrap and indent summary to fit terminal
115                 summary = textwrap.wrap(summary, target_width)
116                 summary = ('\n' + ' ' * (name_column_width + 3)).join(summary)
117 
118         line = '%-*s - %s' % (name_column_width,
119                               '%s (%s)' % (name, latest), summary)
120         try:
121             logger.info(line)
122             if name in installed_packages:
123                 dist = pkg_resources.get_distribution(name)
124                 with indent_log():
125                     if dist.version == latest:
126                         logger.info('INSTALLED: %s (latest)', dist.version)
127                     else:
128                         logger.info('INSTALLED: %s', dist.version)
129                         logger.info('LATEST:    %s', latest)
130         except UnicodeEncodeError:
131             pass
132 
133 
134 def highest_version(versions):
135     return max(versions, key=parse_version)
136
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/pip/_internal/commands/search.py b/src/pip/_internal/commands/search.py
--- a/src/pip/_internal/commands/search.py
+++ b/src/pip/_internal/commands/search.py
@@ -126,7 +126,11 @@
                         logger.info('INSTALLED: %s (latest)', dist.version)
                     else:
                         logger.info('INSTALLED: %s', dist.version)
-                        logger.info('LATEST:    %s', latest)
+                        if parse_version(latest).pre:
+                            logger.info('LATEST:    %s (pre-release; install'
+                                        ' with "pip install --pre")', latest)
+                        else:
+                            logger.info('LATEST:    %s', latest)
         except UnicodeEncodeError:
             pass

{"golden_diff": "diff --git a/src/pip/_internal/commands/search.py b/src/pip/_internal/commands/search.py\n--- a/src/pip/_internal/commands/search.py\n+++ b/src/pip/_internal/commands/search.py\n@@ -126,7 +126,11 @@\n                         logger.info('INSTALLED: %s (latest)', dist.version)\n                     else:\n                         logger.info('INSTALLED: %s', dist.version)\n-                        logger.info('LATEST:    %s', latest)\n+                        if parse_version(latest).pre:\n+                            logger.info('LATEST:    %s (pre-release; install'\n+                                        ' with \"pip install --pre\")', latest)\n+                        else:\n+                            logger.info('LATEST:    %s', latest)\n         except UnicodeEncodeError:\n             pass\n", "issue": "Add pre-released package upgrade hints\nBefore this commit when querying for an installed package using pip if\r\nthe package that was returned contained a pre-released version of the\r\npackage it was not intuitive that the pre-released version of the\r\npackage could not be directly upgraded to without the `--pre` flag.\r\n\r\nThis commit shows additional information on how to upgrade to\r\npre-released versions of a package if one is found in the search.\r\n\r\nFixes #5169.\r\n\r\n<!---\r\nThank you for your soon to be pull request. Before you submit this, please\r\ndouble check to make sure that you've added a news file fragment. In pip we\r\ngenerate our NEWS.rst from multiple news fragment files, and all pull requests\r\nrequire either a news file fragment or a marker to indicate they don't require\r\none.\r\n\r\nTo read more about adding a news file fragment for your PR, please check out\r\nour documentation at: https://pip.pypa.io/en/latest/development/#adding-a-news-entry\r\n-->\r\n\nDisplay pre-release version hint in package version output of \"pip search\" (and others)\n* Pip version: pip 9.0.3 from c:\\python36\\lib\\site-packages (python 3.6)\r\n* Python version: Python 3.6.2\r\n* Operating system: Windows 10\r\n\r\n### Description:\r\n\r\nIn #5168 I learned that version numbers like \"2018.04.04a\" are interpreted as pre-release versions. Therefore I could not install them without adding the parameter `--pre`.\r\n\r\nHowever, this is _implicit_ knowledge the user must have about interpretations of version numbers and related pip behavior in order to understand the behavior of pip. Otherwise, the user is irritated, just like I was until a couple of minutes ago.\r\n\r\nCurrent situation where 2018.04.04a is not installable when `--pre` is not given:\r\n\r\n```\r\nC:\\Users\\karl.voit>pip search filetags\r\nfiletags (2018.04.04a)  - Management of simple tags within file names\r\n  INSTALLED: 2018.3.19\r\n  LATEST:    2018.04.04a\r\n\r\nC:\\Users\\karl.voit>\r\n```\r\n\r\nShouldn't there at least be a hint such as following suggestion?\r\n\r\n```\r\nC:\\Users\\karl.voit>pip search filetags\r\nfiletags (2018.04.04a)  - Management of simple tags within file names\r\n  INSTALLED: 2018.3.19\r\n  LATEST:    2018.04.04a (pre-release; install with \"pip install --pre\")\r\n\r\nC:\\Users\\karl.voit>\r\n```\r\n\r\nMaybe this applies for multiple stdout messages.\r\n\r\n### What I've run:\r\n\r\nSee #5168 to give you some background.\n", "before_files": [{"content": "from __future__ import absolute_import\n\nimport logging\nimport sys\nimport textwrap\nfrom collections import OrderedDict\n\nfrom pip._vendor import pkg_resources\nfrom pip._vendor.packaging.version import parse as parse_version\n# NOTE: XMLRPC Client is not annotated in typeshed as on 2017-07-17, which is\n#       why we ignore the type on this import\nfrom pip._vendor.six.moves import xmlrpc_client  # type: ignore\n\nfrom pip._internal.cli.base_command import Command\nfrom pip._internal.cli.status_codes import NO_MATCHES_FOUND, SUCCESS\nfrom pip._internal.download import PipXmlrpcTransport\nfrom pip._internal.exceptions import CommandError\nfrom pip._internal.models.index import PyPI\nfrom pip._internal.utils.compat import get_terminal_size\nfrom pip._internal.utils.logging import indent_log\n\nlogger = logging.getLogger(__name__)\n\n\nclass SearchCommand(Command):\n    \"\"\"Search for PyPI packages whose name or summary contains <query>.\"\"\"\n    name = 'search'\n    usage = \"\"\"\n      %prog [options] <query>\"\"\"\n    summary = 'Search PyPI for packages.'\n    ignore_require_venv = True\n\n    def __init__(self, *args, **kw):\n        super(SearchCommand, self).__init__(*args, **kw)\n        self.cmd_opts.add_option(\n            '-i', '--index',\n            dest='index',\n            metavar='URL',\n            default=PyPI.pypi_url,\n            help='Base URL of Python Package Index (default %default)')\n\n        self.parser.insert_option_group(0, self.cmd_opts)\n\n    def run(self, options, args):\n        if not args:\n            raise CommandError('Missing required argument (search query).')\n        query = args\n        pypi_hits = self.search(query, options)\n        hits = transform_hits(pypi_hits)\n\n        terminal_width = None\n        if sys.stdout.isatty():\n            terminal_width = get_terminal_size()[0]\n\n        print_results(hits, terminal_width=terminal_width)\n        if pypi_hits:\n            return SUCCESS\n        return NO_MATCHES_FOUND\n\n    def search(self, query, options):\n        index_url = options.index\n        with self._build_session(options) as session:\n            transport = PipXmlrpcTransport(index_url, session)\n            pypi = xmlrpc_client.ServerProxy(index_url, transport)\n            hits = pypi.search({'name': query, 'summary': query}, 'or')\n            return hits\n\n\ndef transform_hits(hits):\n    \"\"\"\n    The list from pypi is really a list of versions. We want a list of\n    packages with the list of versions stored inline. This converts the\n    list from pypi into one we can use.\n    \"\"\"\n    packages = OrderedDict()\n    for hit in hits:\n        name = hit['name']\n        summary = hit['summary']\n        version = hit['version']\n\n        if name not in packages.keys():\n            packages[name] = {\n                'name': name,\n                'summary': summary,\n                'versions': [version],\n            }\n        else:\n            packages[name]['versions'].append(version)\n\n            # if this is the highest version, replace summary and score\n            if version == highest_version(packages[name]['versions']):\n                packages[name]['summary'] = summary\n\n    return list(packages.values())\n\n\ndef print_results(hits, name_column_width=None, terminal_width=None):\n    if not hits:\n        return\n    if name_column_width is None:\n        name_column_width = max([\n            len(hit['name']) + len(highest_version(hit.get('versions', ['-'])))\n            for hit in hits\n        ]) + 4\n\n    installed_packages = [p.project_name for p in pkg_resources.working_set]\n    for hit in hits:\n        name = hit['name']\n        summary = hit['summary'] or ''\n        latest = highest_version(hit.get('versions', ['-']))\n        if terminal_width is not None:\n            target_width = terminal_width - name_column_width - 5\n            if target_width > 10:\n                # wrap and indent summary to fit terminal\n                summary = textwrap.wrap(summary, target_width)\n                summary = ('\\n' + ' ' * (name_column_width + 3)).join(summary)\n\n        line = '%-*s - %s' % (name_column_width,\n                              '%s (%s)' % (name, latest), summary)\n        try:\n            logger.info(line)\n            if name in installed_packages:\n                dist = pkg_resources.get_distribution(name)\n                with indent_log():\n                    if dist.version == latest:\n                        logger.info('INSTALLED: %s (latest)', dist.version)\n                    else:\n                        logger.info('INSTALLED: %s', dist.version)\n                        logger.info('LATEST:    %s', latest)\n        except UnicodeEncodeError:\n            pass\n\n\ndef highest_version(versions):\n    return max(versions, key=parse_version)\n", "path": "src/pip/_internal/commands/search.py"}], "after_files": [{"content": "from __future__ import absolute_import\n\nimport logging\nimport sys\nimport textwrap\nfrom collections import OrderedDict\n\nfrom pip._vendor import pkg_resources\nfrom pip._vendor.packaging.version import parse as parse_version\n# NOTE: XMLRPC Client is not annotated in typeshed as on 2017-07-17, which is\n#       why we ignore the type on this import\nfrom pip._vendor.six.moves import xmlrpc_client  # type: ignore\n\nfrom pip._internal.cli.base_command import Command\nfrom pip._internal.cli.status_codes import NO_MATCHES_FOUND, SUCCESS\nfrom pip._internal.download import PipXmlrpcTransport\nfrom pip._internal.exceptions import CommandError\nfrom pip._internal.models.index import PyPI\nfrom pip._internal.utils.compat import get_terminal_size\nfrom pip._internal.utils.logging import indent_log\n\nlogger = logging.getLogger(__name__)\n\n\nclass SearchCommand(Command):\n    \"\"\"Search for PyPI packages whose name or summary contains <query>.\"\"\"\n    name = 'search'\n    usage = \"\"\"\n      %prog [options] <query>\"\"\"\n    summary = 'Search PyPI for packages.'\n    ignore_require_venv = True\n\n    def __init__(self, *args, **kw):\n        super(SearchCommand, self).__init__(*args, **kw)\n        self.cmd_opts.add_option(\n            '-i', '--index',\n            dest='index',\n            metavar='URL',\n            default=PyPI.pypi_url,\n            help='Base URL of Python Package Index (default %default)')\n\n        self.parser.insert_option_group(0, self.cmd_opts)\n\n    def run(self, options, args):\n        if not args:\n            raise CommandError('Missing required argument (search query).')\n        query = args\n        pypi_hits = self.search(query, options)\n        hits = transform_hits(pypi_hits)\n\n        terminal_width = None\n        if sys.stdout.isatty():\n            terminal_width = get_terminal_size()[0]\n\n        print_results(hits, terminal_width=terminal_width)\n        if pypi_hits:\n            return SUCCESS\n        return NO_MATCHES_FOUND\n\n    def search(self, query, options):\n        index_url = options.index\n        with self._build_session(options) as session:\n            transport = PipXmlrpcTransport(index_url, session)\n            pypi = xmlrpc_client.ServerProxy(index_url, transport)\n            hits = pypi.search({'name': query, 'summary': query}, 'or')\n            return hits\n\n\ndef transform_hits(hits):\n    \"\"\"\n    The list from pypi is really a list of versions. We want a list of\n    packages with the list of versions stored inline. This converts the\n    list from pypi into one we can use.\n    \"\"\"\n    packages = OrderedDict()\n    for hit in hits:\n        name = hit['name']\n        summary = hit['summary']\n        version = hit['version']\n\n        if name not in packages.keys():\n            packages[name] = {\n                'name': name,\n                'summary': summary,\n                'versions': [version],\n            }\n        else:\n            packages[name]['versions'].append(version)\n\n            # if this is the highest version, replace summary and score\n            if version == highest_version(packages[name]['versions']):\n                packages[name]['summary'] = summary\n\n    return list(packages.values())\n\n\ndef print_results(hits, name_column_width=None, terminal_width=None):\n    if not hits:\n        return\n    if name_column_width is None:\n        name_column_width = max([\n            len(hit['name']) + len(highest_version(hit.get('versions', ['-'])))\n            for hit in hits\n        ]) + 4\n\n    installed_packages = [p.project_name for p in pkg_resources.working_set]\n    for hit in hits:\n        name = hit['name']\n        summary = hit['summary'] or ''\n        latest = highest_version(hit.get('versions', ['-']))\n        if terminal_width is not None:\n            target_width = terminal_width - name_column_width - 5\n            if target_width > 10:\n                # wrap and indent summary to fit terminal\n                summary = textwrap.wrap(summary, target_width)\n                summary = ('\\n' + ' ' * (name_column_width + 3)).join(summary)\n\n        line = '%-*s - %s' % (name_column_width,\n                              '%s (%s)' % (name, latest), summary)\n        try:\n            logger.info(line)\n            if name in installed_packages:\n                dist = pkg_resources.get_distribution(name)\n                with indent_log():\n                    if dist.version == latest:\n                        logger.info('INSTALLED: %s (latest)', dist.version)\n                    else:\n                        logger.info('INSTALLED: %s', dist.version)\n                        if parse_version(latest).pre:\n                            logger.info('LATEST:    %s (pre-release; install'\n                                        ' with \"pip install --pre\")', latest)\n                        else:\n                            logger.info('LATEST:    %s', latest)\n        except UnicodeEncodeError:\n            pass\n\n\ndef highest_version(versions):\n    return max(versions, key=parse_version)\n", "path": "src/pip/_internal/commands/search.py"}]}