prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Fail legacy install if .egg-info directory was not created
  **What's the problem this feature will solve?**
  
  Currently on the legacy install code path ([here](https://github.com/pypa/pip/blob/e0239735a3d8a504e00133090dde2210b6564a38/src/pip/_internal/req/req_install.py#L997-L1004)) if execution of `setup.py install ...` didn't result in an `.egg-info` directory being created we trace a warning and continue.
  
  This is bad because we will have installed an uninstallable package and the only penalty is a small warning which is very easy to miss.
  
  **Describe the solution you'd like**
  
  Instead of tracing a warning we should throw an exception.
  
  This prevents additional packages from being installed and possibly hiding the warning from users.
  
  **Alternative Solutions**
  
  Continue tracing the warning as before.
  
  **Additional context**
  
  * [this support query](https://kite.trade/forum/discussion/4335/could-not-find-egg-info-directory-in-install-record-for-kiteconnect) for [this package](https://github.com/zerodhatech/pykiteconnect/blob/7af60a776fbfafb6c30cfbd8d4f6c192618c5af5/setup.py#L48) which does an egg install in its setup.py regardless of setuptools version.
  * [this query](https://stackoverflow.com/questions/26091641/what-does-a-could-not-find-egg-info-directory-in-install-record-from-pip-mean) which implicates having an older version of setuptools
  
  --- 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/operations/install/legacy.py`
  Content:
  ```
  1 """Legacy installation process, i.e. `setup.py install`.
  2 """
  3 
  4 import logging
  5 import os
  6 import sys
  7 from distutils.util import change_root
  8 
  9 from pip._internal.utils.deprecation import deprecated
  10 from pip._internal.utils.logging import indent_log
  11 from pip._internal.utils.misc import ensure_dir
  12 from pip._internal.utils.setuptools_build import make_setuptools_install_args
  13 from pip._internal.utils.subprocess import runner_with_spinner_message
  14 from pip._internal.utils.temp_dir import TempDirectory
  15 from pip._internal.utils.typing import MYPY_CHECK_RUNNING
  16 
  17 if MYPY_CHECK_RUNNING:
  18     from typing import List, Optional, Sequence
  19 
  20     from pip._internal.build_env import BuildEnvironment
  21     from pip._internal.models.scheme import Scheme
  22 
  23 
  24 logger = logging.getLogger(__name__)
  25 
  26 
  27 class LegacyInstallFailure(Exception):
  28     def __init__(self):
  29         # type: () -> None
  30         self.parent = sys.exc_info()
  31 
  32 
  33 def install(
  34     install_options,  # type: List[str]
  35     global_options,  # type: Sequence[str]
  36     root,  # type: Optional[str]
  37     home,  # type: Optional[str]
  38     prefix,  # type: Optional[str]
  39     use_user_site,  # type: bool
  40     pycompile,  # type: bool
  41     scheme,  # type: Scheme
  42     setup_py_path,  # type: str
  43     isolated,  # type: bool
  44     req_name,  # type: str
  45     build_env,  # type: BuildEnvironment
  46     unpacked_source_directory,  # type: str
  47     req_description,  # type: str
  48 ):
  49     # type: (...) -> bool
  50 
  51     header_dir = scheme.headers
  52 
  53     with TempDirectory(kind="record") as temp_dir:
  54         try:
  55             record_filename = os.path.join(temp_dir.path, 'install-record.txt')
  56             install_args = make_setuptools_install_args(
  57                 setup_py_path,
  58                 global_options=global_options,
  59                 install_options=install_options,
  60                 record_filename=record_filename,
  61                 root=root,
  62                 prefix=prefix,
  63                 header_dir=header_dir,
  64                 home=home,
  65                 use_user_site=use_user_site,
  66                 no_user_config=isolated,
  67                 pycompile=pycompile,
  68             )
  69 
  70             runner = runner_with_spinner_message(
  71                 "Running setup.py install for {}".format(req_name)
  72             )
  73             with indent_log(), build_env:
  74                 runner(
  75                     cmd=install_args,
  76                     cwd=unpacked_source_directory,
  77                 )
  78 
  79             if not os.path.exists(record_filename):
  80                 logger.debug('Record file %s not found', record_filename)
  81                 # Signal to the caller that we didn't install the new package
  82                 return False
  83 
  84         except Exception:
  85             # Signal to the caller that we didn't install the new package
  86             raise LegacyInstallFailure
  87 
  88         # At this point, we have successfully installed the requirement.
  89 
  90         # We intentionally do not use any encoding to read the file because
  91         # setuptools writes the file using distutils.file_util.write_file,
  92         # which does not specify an encoding.
  93         with open(record_filename) as f:
  94             record_lines = f.read().splitlines()
  95 
  96     def prepend_root(path):
  97         # type: (str) -> str
  98         if root is None or not os.path.isabs(path):
  99             return path
  100         else:
  101             return change_root(root, path)
  102 
  103     for line in record_lines:
  104         directory = os.path.dirname(line)
  105         if directory.endswith('.egg-info'):
  106             egg_info_dir = prepend_root(directory)
  107             break
  108     else:
  109         deprecated(
  110             reason=(
  111                 "{} did not indicate that it installed an "
  112                 ".egg-info directory. Only setup.py projects "
  113                 "generating .egg-info directories are supported."
  114             ).format(req_description),
  115             replacement=(
  116                 "for maintainers: updating the setup.py of {0}. "
  117                 "For users: contact the maintainers of {0} to let "
  118                 "them know to update their setup.py.".format(
  119                     req_name
  120                 )
  121             ),
  122             gone_in="20.2",
  123             issue=6998,
  124         )
  125         # FIXME: put the record somewhere
  126         return True
  127 
  128     new_lines = []
  129     for line in record_lines:
  130         filename = line.strip()
  131         if os.path.isdir(filename):
  132             filename += os.path.sep
  133         new_lines.append(
  134             os.path.relpath(prepend_root(filename), egg_info_dir)
  135         )
  136     new_lines.sort()
  137     ensure_dir(egg_info_dir)
  138     inst_files_path = os.path.join(egg_info_dir, 'installed-files.txt')
  139     with open(inst_files_path, 'w') as f:
  140         f.write('\n'.join(new_lines) + '\n')
  141 
  142     return True
  143
  ```
  --- 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()
  
  ```
  
golden_diff: diff --git a/src/pip/_internal/operations/install/legacy.py b/src/pip/_internal/operations/install/legacy.py
  --- a/src/pip/_internal/operations/install/legacy.py
  +++ b/src/pip/_internal/operations/install/legacy.py
  @@ -6,7 +6,7 @@
   import sys
   from distutils.util import change_root
   
  -from pip._internal.utils.deprecation import deprecated
  +from pip._internal.exceptions import InstallationError
   from pip._internal.utils.logging import indent_log
   from pip._internal.utils.misc import ensure_dir
   from pip._internal.utils.setuptools_build import make_setuptools_install_args
  @@ -106,24 +106,12 @@
               egg_info_dir = prepend_root(directory)
               break
       else:
  -        deprecated(
  -            reason=(
  -                "{} did not indicate that it installed an "
  -                ".egg-info directory. Only setup.py projects "
  -                "generating .egg-info directories are supported."
  -            ).format(req_description),
  -            replacement=(
  -                "for maintainers: updating the setup.py of {0}. "
  -                "For users: contact the maintainers of {0} to let "
  -                "them know to update their setup.py.".format(
  -                    req_name
  -                )
  -            ),
  -            gone_in="20.2",
  -            issue=6998,
  -        )
  -        # FIXME: put the record somewhere
  -        return True
  +        message = (
  +            "{} did not indicate that it installed an "
  +            ".egg-info directory. Only setup.py projects "
  +            "generating .egg-info directories are supported."
  +        ).format(req_description)
  +        raise InstallationError(message)
   
       new_lines = []
       for line in record_lines:
  
verification_info: {"golden_diff": "diff --git a/src/pip/_internal/operations/install/legacy.py b/src/pip/_internal/operations/install/legacy.py\n--- a/src/pip/_internal/operations/install/legacy.py\n+++ b/src/pip/_internal/operations/install/legacy.py\n@@ -6,7 +6,7 @@\n import sys\n from distutils.util import change_root\n \n-from pip._internal.utils.deprecation import deprecated\n+from pip._internal.exceptions import InstallationError\n from pip._internal.utils.logging import indent_log\n from pip._internal.utils.misc import ensure_dir\n from pip._internal.utils.setuptools_build import make_setuptools_install_args\n@@ -106,24 +106,12 @@\n             egg_info_dir = prepend_root(directory)\n             break\n     else:\n-        deprecated(\n-            reason=(\n-                \"{} did not indicate that it installed an \"\n-                \".egg-info directory. Only setup.py projects \"\n-                \"generating .egg-info directories are supported.\"\n-            ).format(req_description),\n-            replacement=(\n-                \"for maintainers: updating the setup.py of {0}. \"\n-                \"For users: contact the maintainers of {0} to let \"\n-                \"them know to update their setup.py.\".format(\n-                    req_name\n-                )\n-            ),\n-            gone_in=\"20.2\",\n-            issue=6998,\n-        )\n-        # FIXME: put the record somewhere\n-        return True\n+        message = (\n+            \"{} did not indicate that it installed an \"\n+            \".egg-info directory. Only setup.py projects \"\n+            \"generating .egg-info directories are supported.\"\n+        ).format(req_description)\n+        raise InstallationError(message)\n \n     new_lines = []\n     for line in record_lines:\n", "issue": "Fail legacy install if .egg-info directory was not created\n**What's the problem this feature will solve?**\r\n\r\nCurrently on the legacy install code path ([here](https://github.com/pypa/pip/blob/e0239735a3d8a504e00133090dde2210b6564a38/src/pip/_internal/req/req_install.py#L997-L1004)) if execution of `setup.py install ...` didn't result in an `.egg-info` directory being created we trace a warning and continue.\r\n\r\nThis is bad because we will have installed an uninstallable package and the only penalty is a small warning which is very easy to miss.\r\n\r\n**Describe the solution you'd like**\r\n\r\nInstead of tracing a warning we should throw an exception.\r\n\r\nThis prevents additional packages from being installed and possibly hiding the warning from users.\r\n\r\n**Alternative Solutions**\r\n\r\nContinue tracing the warning as before.\r\n\r\n**Additional context**\r\n\r\n* [this support query](https://kite.trade/forum/discussion/4335/could-not-find-egg-info-directory-in-install-record-for-kiteconnect) for [this package](https://github.com/zerodhatech/pykiteconnect/blob/7af60a776fbfafb6c30cfbd8d4f6c192618c5af5/setup.py#L48) which does an egg install in its setup.py regardless of setuptools version.\r\n* [this query](https://stackoverflow.com/questions/26091641/what-does-a-could-not-find-egg-info-directory-in-install-record-from-pip-mean) which implicates having an older version of setuptools\n", "before_files": [{"content": "\"\"\"Legacy installation process, i.e. `setup.py install`.\n\"\"\"\n\nimport logging\nimport os\nimport sys\nfrom distutils.util import change_root\n\nfrom pip._internal.utils.deprecation import deprecated\nfrom pip._internal.utils.logging import indent_log\nfrom pip._internal.utils.misc import ensure_dir\nfrom pip._internal.utils.setuptools_build import make_setuptools_install_args\nfrom pip._internal.utils.subprocess import runner_with_spinner_message\nfrom pip._internal.utils.temp_dir import TempDirectory\nfrom pip._internal.utils.typing import MYPY_CHECK_RUNNING\n\nif MYPY_CHECK_RUNNING:\n    from typing import List, Optional, Sequence\n\n    from pip._internal.build_env import BuildEnvironment\n    from pip._internal.models.scheme import Scheme\n\n\nlogger = logging.getLogger(__name__)\n\n\nclass LegacyInstallFailure(Exception):\n    def __init__(self):\n        # type: () -> None\n        self.parent = sys.exc_info()\n\n\ndef install(\n    install_options,  # type: List[str]\n    global_options,  # type: Sequence[str]\n    root,  # type: Optional[str]\n    home,  # type: Optional[str]\n    prefix,  # type: Optional[str]\n    use_user_site,  # type: bool\n    pycompile,  # type: bool\n    scheme,  # type: Scheme\n    setup_py_path,  # type: str\n    isolated,  # type: bool\n    req_name,  # type: str\n    build_env,  # type: BuildEnvironment\n    unpacked_source_directory,  # type: str\n    req_description,  # type: str\n):\n    # type: (...) -> bool\n\n    header_dir = scheme.headers\n\n    with TempDirectory(kind=\"record\") as temp_dir:\n        try:\n            record_filename = os.path.join(temp_dir.path, 'install-record.txt')\n            install_args = make_setuptools_install_args(\n                setup_py_path,\n                global_options=global_options,\n                install_options=install_options,\n                record_filename=record_filename,\n                root=root,\n                prefix=prefix,\n                header_dir=header_dir,\n                home=home,\n                use_user_site=use_user_site,\n                no_user_config=isolated,\n                pycompile=pycompile,\n            )\n\n            runner = runner_with_spinner_message(\n                \"Running setup.py install for {}\".format(req_name)\n            )\n            with indent_log(), build_env:\n                runner(\n                    cmd=install_args,\n                    cwd=unpacked_source_directory,\n                )\n\n            if not os.path.exists(record_filename):\n                logger.debug('Record file %s not found', record_filename)\n                # Signal to the caller that we didn't install the new package\n                return False\n\n        except Exception:\n            # Signal to the caller that we didn't install the new package\n            raise LegacyInstallFailure\n\n        # At this point, we have successfully installed the requirement.\n\n        # We intentionally do not use any encoding to read the file because\n        # setuptools writes the file using distutils.file_util.write_file,\n        # which does not specify an encoding.\n        with open(record_filename) as f:\n            record_lines = f.read().splitlines()\n\n    def prepend_root(path):\n        # type: (str) -> str\n        if root is None or not os.path.isabs(path):\n            return path\n        else:\n            return change_root(root, path)\n\n    for line in record_lines:\n        directory = os.path.dirname(line)\n        if directory.endswith('.egg-info'):\n            egg_info_dir = prepend_root(directory)\n            break\n    else:\n        deprecated(\n            reason=(\n                \"{} did not indicate that it installed an \"\n                \".egg-info directory. Only setup.py projects \"\n                \"generating .egg-info directories are supported.\"\n            ).format(req_description),\n            replacement=(\n                \"for maintainers: updating the setup.py of {0}. \"\n                \"For users: contact the maintainers of {0} to let \"\n                \"them know to update their setup.py.\".format(\n                    req_name\n                )\n            ),\n            gone_in=\"20.2\",\n            issue=6998,\n        )\n        # FIXME: put the record somewhere\n        return True\n\n    new_lines = []\n    for line in record_lines:\n        filename = line.strip()\n        if os.path.isdir(filename):\n            filename += os.path.sep\n        new_lines.append(\n            os.path.relpath(prepend_root(filename), egg_info_dir)\n        )\n    new_lines.sort()\n    ensure_dir(egg_info_dir)\n    inst_files_path = os.path.join(egg_info_dir, 'installed-files.txt')\n    with open(inst_files_path, 'w') as f:\n        f.write('\\n'.join(new_lines) + '\\n')\n\n    return True\n", "path": "src/pip/_internal/operations/install/legacy.py"}], "after_files": [{"content": "\"\"\"Legacy installation process, i.e. `setup.py install`.\n\"\"\"\n\nimport logging\nimport os\nimport sys\nfrom distutils.util import change_root\n\nfrom pip._internal.exceptions import InstallationError\nfrom pip._internal.utils.logging import indent_log\nfrom pip._internal.utils.misc import ensure_dir\nfrom pip._internal.utils.setuptools_build import make_setuptools_install_args\nfrom pip._internal.utils.subprocess import runner_with_spinner_message\nfrom pip._internal.utils.temp_dir import TempDirectory\nfrom pip._internal.utils.typing import MYPY_CHECK_RUNNING\n\nif MYPY_CHECK_RUNNING:\n    from typing import List, Optional, Sequence\n\n    from pip._internal.build_env import BuildEnvironment\n    from pip._internal.models.scheme import Scheme\n\n\nlogger = logging.getLogger(__name__)\n\n\nclass LegacyInstallFailure(Exception):\n    def __init__(self):\n        # type: () -> None\n        self.parent = sys.exc_info()\n\n\ndef install(\n    install_options,  # type: List[str]\n    global_options,  # type: Sequence[str]\n    root,  # type: Optional[str]\n    home,  # type: Optional[str]\n    prefix,  # type: Optional[str]\n    use_user_site,  # type: bool\n    pycompile,  # type: bool\n    scheme,  # type: Scheme\n    setup_py_path,  # type: str\n    isolated,  # type: bool\n    req_name,  # type: str\n    build_env,  # type: BuildEnvironment\n    unpacked_source_directory,  # type: str\n    req_description,  # type: str\n):\n    # type: (...) -> bool\n\n    header_dir = scheme.headers\n\n    with TempDirectory(kind=\"record\") as temp_dir:\n        try:\n            record_filename = os.path.join(temp_dir.path, 'install-record.txt')\n            install_args = make_setuptools_install_args(\n                setup_py_path,\n                global_options=global_options,\n                install_options=install_options,\n                record_filename=record_filename,\n                root=root,\n                prefix=prefix,\n                header_dir=header_dir,\n                home=home,\n                use_user_site=use_user_site,\n                no_user_config=isolated,\n                pycompile=pycompile,\n            )\n\n            runner = runner_with_spinner_message(\n                \"Running setup.py install for {}\".format(req_name)\n            )\n            with indent_log(), build_env:\n                runner(\n                    cmd=install_args,\n                    cwd=unpacked_source_directory,\n                )\n\n            if not os.path.exists(record_filename):\n                logger.debug('Record file %s not found', record_filename)\n                # Signal to the caller that we didn't install the new package\n                return False\n\n        except Exception:\n            # Signal to the caller that we didn't install the new package\n            raise LegacyInstallFailure\n\n        # At this point, we have successfully installed the requirement.\n\n        # We intentionally do not use any encoding to read the file because\n        # setuptools writes the file using distutils.file_util.write_file,\n        # which does not specify an encoding.\n        with open(record_filename) as f:\n            record_lines = f.read().splitlines()\n\n    def prepend_root(path):\n        # type: (str) -> str\n        if root is None or not os.path.isabs(path):\n            return path\n        else:\n            return change_root(root, path)\n\n    for line in record_lines:\n        directory = os.path.dirname(line)\n        if directory.endswith('.egg-info'):\n            egg_info_dir = prepend_root(directory)\n            break\n    else:\n        message = (\n            \"{} did not indicate that it installed an \"\n            \".egg-info directory. Only setup.py projects \"\n            \"generating .egg-info directories are supported.\"\n        ).format(req_description)\n        raise InstallationError(message)\n\n    new_lines = []\n    for line in record_lines:\n        filename = line.strip()\n        if os.path.isdir(filename):\n            filename += os.path.sep\n        new_lines.append(\n            os.path.relpath(prepend_root(filename), egg_info_dir)\n        )\n    new_lines.sort()\n    ensure_dir(egg_info_dir)\n    inst_files_path = os.path.join(egg_info_dir, 'installed-files.txt')\n    with open(inst_files_path, 'w') as f:\n        f.write('\\n'.join(new_lines) + '\\n')\n\n    return True\n", "path": "src/pip/_internal/operations/install/legacy.py"}]}