prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  set user-agent to something pex-identifiable
  Donald did some interesting research into where downloads were coming from on PyPI.  Realized that we have no custom user-agent to identify pex.
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `pex/fetcher.py`
  Content:
  ```
  1 # Copyright 2021 Pants project contributors (see CONTRIBUTORS.md).
  2 # Licensed under the Apache License, Version 2.0 (see LICENSE).
  3 
  4 from __future__ import absolute_import
  5 
  6 import ssl
  7 import time
  8 from contextlib import closing, contextmanager
  9 
  10 from pex.compatibility import FileHandler, HTTPError, HTTPSHandler, ProxyHandler, build_opener
  11 from pex.network_configuration import NetworkConfiguration
  12 from pex.typing import TYPE_CHECKING, cast
  13 
  14 if TYPE_CHECKING:
  15     from typing import BinaryIO, Dict, Iterator, Optional, Text
  16 else:
  17     BinaryIO = None
  18 
  19 
  20 class URLFetcher(object):
  21     def __init__(
  22         self,
  23         network_configuration=None,  # type: Optional[NetworkConfiguration]
  24         handle_file_urls=False,  # type: bool
  25     ):
  26         # type: (...) -> None
  27         network_configuration = network_configuration or NetworkConfiguration()
  28 
  29         self._timeout = network_configuration.timeout
  30         self._max_retries = network_configuration.retries
  31 
  32         ssl_context = ssl.create_default_context(cafile=network_configuration.cert)
  33         if network_configuration.client_cert:
  34             ssl_context.load_cert_chain(network_configuration.client_cert)
  35 
  36         proxies = None  # type: Optional[Dict[str, str]]
  37         if network_configuration.proxy:
  38             proxies = {protocol: network_configuration.proxy for protocol in ("http", "https")}
  39 
  40         handlers = [ProxyHandler(proxies), HTTPSHandler(context=ssl_context)]
  41         if handle_file_urls:
  42             handlers.append(FileHandler())
  43         self._handlers = tuple(handlers)
  44 
  45     @contextmanager
  46     def get_body_stream(self, url):
  47         # type: (Text) -> Iterator[BinaryIO]
  48         retries = 0
  49         retry_delay_secs = 0.1
  50         last_error = None  # type: Optional[Exception]
  51         while retries <= self._max_retries:
  52             if retries > 0:
  53                 time.sleep(retry_delay_secs)
  54                 retry_delay_secs *= 2
  55 
  56             opener = build_opener(*self._handlers)
  57             # The fp is typed as Optional[...] for Python 2 only in the typeshed. A `None`
  58             # can only be returned if a faulty custom handler is installed and we only
  59             # install stdlib handlers.
  60             fp = cast(BinaryIO, opener.open(url, timeout=self._timeout))
  61             try:
  62                 with closing(fp) as body_stream:
  63                     yield body_stream
  64                     return
  65             except HTTPError as e:
  66                 # See: https://tools.ietf.org/html/rfc2616#page-39
  67                 if e.code not in (
  68                     408,  # Request Time-out
  69                     500,  # Internal Server Error
  70                     503,  # Service Unavailable
  71                     504,  # Gateway Time-out
  72                 ):
  73                     raise e
  74                 last_error = e
  75             except (IOError, OSError) as e:
  76                 # Unfortunately errors are overly broad at this point. We can get either OSError or
  77                 # URLError (a subclass of OSError) which at times indicates retryable socket level
  78                 # errors. Since retrying a non-retryable socket level error just wastes local
  79                 # machine resources we err towards always retrying.
  80                 last_error = e
  81             finally:
  82                 retries += 1
  83 
  84         raise cast(Exception, last_error)
  85 
  86     @contextmanager
  87     def get_body_iter(self, url):
  88         # type: (Text) -> Iterator[Iterator[Text]]
  89         with self.get_body_stream(url) as body_stream:
  90             yield (line.decode("utf-8") for line in body_stream.readlines())
  91
  ```
  --- 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/pex/fetcher.py b/pex/fetcher.py
  --- a/pex/fetcher.py
  +++ b/pex/fetcher.py
  @@ -7,9 +7,17 @@
   import time
   from contextlib import closing, contextmanager
   
  -from pex.compatibility import FileHandler, HTTPError, HTTPSHandler, ProxyHandler, build_opener
  +from pex.compatibility import (
  +    FileHandler,
  +    HTTPError,
  +    HTTPSHandler,
  +    ProxyHandler,
  +    Request,
  +    build_opener,
  +)
   from pex.network_configuration import NetworkConfiguration
   from pex.typing import TYPE_CHECKING, cast
  +from pex.version import __version__
   
   if TYPE_CHECKING:
       from typing import BinaryIO, Dict, Iterator, Optional, Text
  @@ -18,6 +26,8 @@
   
   
   class URLFetcher(object):
  +    USER_AGENT = "pex/{version}".format(version=__version__)
  +
       def __init__(
           self,
           network_configuration=None,  # type: Optional[NetworkConfiguration]
  @@ -54,10 +64,16 @@
                   retry_delay_secs *= 2
   
               opener = build_opener(*self._handlers)
  +            request = Request(
  +                # N.B.: MyPy incorrectly thinks url must be a str in Python 2 where a unicode url
  +                # actually works fine.
  +                url,  # type: ignore[arg-type]
  +                headers={"User-Agent": self.USER_AGENT},
  +            )
               # The fp is typed as Optional[...] for Python 2 only in the typeshed. A `None`
               # can only be returned if a faulty custom handler is installed and we only
               # install stdlib handlers.
  -            fp = cast(BinaryIO, opener.open(url, timeout=self._timeout))
  +            fp = cast(BinaryIO, opener.open(request, timeout=self._timeout))
               try:
                   with closing(fp) as body_stream:
                       yield body_stream
  
verification_info: {"golden_diff": "diff --git a/pex/fetcher.py b/pex/fetcher.py\n--- a/pex/fetcher.py\n+++ b/pex/fetcher.py\n@@ -7,9 +7,17 @@\n import time\n from contextlib import closing, contextmanager\n \n-from pex.compatibility import FileHandler, HTTPError, HTTPSHandler, ProxyHandler, build_opener\n+from pex.compatibility import (\n+    FileHandler,\n+    HTTPError,\n+    HTTPSHandler,\n+    ProxyHandler,\n+    Request,\n+    build_opener,\n+)\n from pex.network_configuration import NetworkConfiguration\n from pex.typing import TYPE_CHECKING, cast\n+from pex.version import __version__\n \n if TYPE_CHECKING:\n     from typing import BinaryIO, Dict, Iterator, Optional, Text\n@@ -18,6 +26,8 @@\n \n \n class URLFetcher(object):\n+    USER_AGENT = \"pex/{version}\".format(version=__version__)\n+\n     def __init__(\n         self,\n         network_configuration=None,  # type: Optional[NetworkConfiguration]\n@@ -54,10 +64,16 @@\n                 retry_delay_secs *= 2\n \n             opener = build_opener(*self._handlers)\n+            request = Request(\n+                # N.B.: MyPy incorrectly thinks url must be a str in Python 2 where a unicode url\n+                # actually works fine.\n+                url,  # type: ignore[arg-type]\n+                headers={\"User-Agent\": self.USER_AGENT},\n+            )\n             # The fp is typed as Optional[...] for Python 2 only in the typeshed. A `None`\n             # can only be returned if a faulty custom handler is installed and we only\n             # install stdlib handlers.\n-            fp = cast(BinaryIO, opener.open(url, timeout=self._timeout))\n+            fp = cast(BinaryIO, opener.open(request, timeout=self._timeout))\n             try:\n                 with closing(fp) as body_stream:\n                     yield body_stream\n", "issue": "set user-agent to something pex-identifiable\nDonald did some interesting research into where downloads were coming from on PyPI.  Realized that we have no custom user-agent to identify pex.\n\n", "before_files": [{"content": "# Copyright 2021 Pants project contributors (see CONTRIBUTORS.md).\n# Licensed under the Apache License, Version 2.0 (see LICENSE).\n\nfrom __future__ import absolute_import\n\nimport ssl\nimport time\nfrom contextlib import closing, contextmanager\n\nfrom pex.compatibility import FileHandler, HTTPError, HTTPSHandler, ProxyHandler, build_opener\nfrom pex.network_configuration import NetworkConfiguration\nfrom pex.typing import TYPE_CHECKING, cast\n\nif TYPE_CHECKING:\n    from typing import BinaryIO, Dict, Iterator, Optional, Text\nelse:\n    BinaryIO = None\n\n\nclass URLFetcher(object):\n    def __init__(\n        self,\n        network_configuration=None,  # type: Optional[NetworkConfiguration]\n        handle_file_urls=False,  # type: bool\n    ):\n        # type: (...) -> None\n        network_configuration = network_configuration or NetworkConfiguration()\n\n        self._timeout = network_configuration.timeout\n        self._max_retries = network_configuration.retries\n\n        ssl_context = ssl.create_default_context(cafile=network_configuration.cert)\n        if network_configuration.client_cert:\n            ssl_context.load_cert_chain(network_configuration.client_cert)\n\n        proxies = None  # type: Optional[Dict[str, str]]\n        if network_configuration.proxy:\n            proxies = {protocol: network_configuration.proxy for protocol in (\"http\", \"https\")}\n\n        handlers = [ProxyHandler(proxies), HTTPSHandler(context=ssl_context)]\n        if handle_file_urls:\n            handlers.append(FileHandler())\n        self._handlers = tuple(handlers)\n\n    @contextmanager\n    def get_body_stream(self, url):\n        # type: (Text) -> Iterator[BinaryIO]\n        retries = 0\n        retry_delay_secs = 0.1\n        last_error = None  # type: Optional[Exception]\n        while retries <= self._max_retries:\n            if retries > 0:\n                time.sleep(retry_delay_secs)\n                retry_delay_secs *= 2\n\n            opener = build_opener(*self._handlers)\n            # The fp is typed as Optional[...] for Python 2 only in the typeshed. A `None`\n            # can only be returned if a faulty custom handler is installed and we only\n            # install stdlib handlers.\n            fp = cast(BinaryIO, opener.open(url, timeout=self._timeout))\n            try:\n                with closing(fp) as body_stream:\n                    yield body_stream\n                    return\n            except HTTPError as e:\n                # See: https://tools.ietf.org/html/rfc2616#page-39\n                if e.code not in (\n                    408,  # Request Time-out\n                    500,  # Internal Server Error\n                    503,  # Service Unavailable\n                    504,  # Gateway Time-out\n                ):\n                    raise e\n                last_error = e\n            except (IOError, OSError) as e:\n                # Unfortunately errors are overly broad at this point. We can get either OSError or\n                # URLError (a subclass of OSError) which at times indicates retryable socket level\n                # errors. Since retrying a non-retryable socket level error just wastes local\n                # machine resources we err towards always retrying.\n                last_error = e\n            finally:\n                retries += 1\n\n        raise cast(Exception, last_error)\n\n    @contextmanager\n    def get_body_iter(self, url):\n        # type: (Text) -> Iterator[Iterator[Text]]\n        with self.get_body_stream(url) as body_stream:\n            yield (line.decode(\"utf-8\") for line in body_stream.readlines())\n", "path": "pex/fetcher.py"}], "after_files": [{"content": "# Copyright 2021 Pants project contributors (see CONTRIBUTORS.md).\n# Licensed under the Apache License, Version 2.0 (see LICENSE).\n\nfrom __future__ import absolute_import\n\nimport ssl\nimport time\nfrom contextlib import closing, contextmanager\n\nfrom pex.compatibility import (\n    FileHandler,\n    HTTPError,\n    HTTPSHandler,\n    ProxyHandler,\n    Request,\n    build_opener,\n)\nfrom pex.network_configuration import NetworkConfiguration\nfrom pex.typing import TYPE_CHECKING, cast\nfrom pex.version import __version__\n\nif TYPE_CHECKING:\n    from typing import BinaryIO, Dict, Iterator, Optional, Text\nelse:\n    BinaryIO = None\n\n\nclass URLFetcher(object):\n    USER_AGENT = \"pex/{version}\".format(version=__version__)\n\n    def __init__(\n        self,\n        network_configuration=None,  # type: Optional[NetworkConfiguration]\n        handle_file_urls=False,  # type: bool\n    ):\n        # type: (...) -> None\n        network_configuration = network_configuration or NetworkConfiguration()\n\n        self._timeout = network_configuration.timeout\n        self._max_retries = network_configuration.retries\n\n        ssl_context = ssl.create_default_context(cafile=network_configuration.cert)\n        if network_configuration.client_cert:\n            ssl_context.load_cert_chain(network_configuration.client_cert)\n\n        proxies = None  # type: Optional[Dict[str, str]]\n        if network_configuration.proxy:\n            proxies = {protocol: network_configuration.proxy for protocol in (\"http\", \"https\")}\n\n        handlers = [ProxyHandler(proxies), HTTPSHandler(context=ssl_context)]\n        if handle_file_urls:\n            handlers.append(FileHandler())\n        self._handlers = tuple(handlers)\n\n    @contextmanager\n    def get_body_stream(self, url):\n        # type: (Text) -> Iterator[BinaryIO]\n        retries = 0\n        retry_delay_secs = 0.1\n        last_error = None  # type: Optional[Exception]\n        while retries <= self._max_retries:\n            if retries > 0:\n                time.sleep(retry_delay_secs)\n                retry_delay_secs *= 2\n\n            opener = build_opener(*self._handlers)\n            request = Request(\n                # N.B.: MyPy incorrectly thinks url must be a str in Python 2 where a unicode url\n                # actually works fine.\n                url,  # type: ignore[arg-type]\n                headers={\"User-Agent\": self.USER_AGENT},\n            )\n            # The fp is typed as Optional[...] for Python 2 only in the typeshed. A `None`\n            # can only be returned if a faulty custom handler is installed and we only\n            # install stdlib handlers.\n            fp = cast(BinaryIO, opener.open(request, timeout=self._timeout))\n            try:\n                with closing(fp) as body_stream:\n                    yield body_stream\n                    return\n            except HTTPError as e:\n                # See: https://tools.ietf.org/html/rfc2616#page-39\n                if e.code not in (\n                    408,  # Request Time-out\n                    500,  # Internal Server Error\n                    503,  # Service Unavailable\n                    504,  # Gateway Time-out\n                ):\n                    raise e\n                last_error = e\n            except (IOError, OSError) as e:\n                # Unfortunately errors are overly broad at this point. We can get either OSError or\n                # URLError (a subclass of OSError) which at times indicates retryable socket level\n                # errors. Since retrying a non-retryable socket level error just wastes local\n                # machine resources we err towards always retrying.\n                last_error = e\n            finally:\n                retries += 1\n\n        raise cast(Exception, last_error)\n\n    @contextmanager\n    def get_body_iter(self, url):\n        # type: (Text) -> Iterator[Iterator[Text]]\n        with self.get_body_stream(url) as body_stream:\n            yield (line.decode(\"utf-8\") for line in body_stream.readlines())\n", "path": "pex/fetcher.py"}]}