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()

```

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

{"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"}]}