prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  urllib3 v2.0 compatibility
  urllib3 v2.0 came out during PyConUS 2023 and is resulting in hanging tests on windows, and test failures on Linux. We need to update our code/tests to ensure compatibility for this new version.
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `elasticapm/instrumentation/packages/urllib3.py`
  Content:
  ```
  1 #  BSD 3-Clause License
  2 #
  3 #  Copyright (c) 2019, Elasticsearch BV
  4 #  All rights reserved.
  5 #
  6 #  Redistribution and use in source and binary forms, with or without
  7 #  modification, are permitted provided that the following conditions are met:
  8 #
  9 #  * Redistributions of source code must retain the above copyright notice, this
  10 #    list of conditions and the following disclaimer.
  11 #
  12 #  * Redistributions in binary form must reproduce the above copyright notice,
  13 #    this list of conditions and the following disclaimer in the documentation
  14 #    and/or other materials provided with the distribution.
  15 #
  16 #  * Neither the name of the copyright holder nor the names of its
  17 #    contributors may be used to endorse or promote products derived from
  18 #    this software without specific prior written permission.
  19 #
  20 #  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  21 #  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  22 #  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  23 #  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  24 #  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  25 #  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  26 #  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  27 #  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  28 #  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  29 #  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30 
  31 import itertools
  32 
  33 from elasticapm.conf import constants
  34 from elasticapm.instrumentation.packages.base import AbstractInstrumentedModule
  35 from elasticapm.traces import DroppedSpan, capture_span, execution_context
  36 from elasticapm.utils import default_ports
  37 from elasticapm.utils.disttracing import TracingOptions
  38 
  39 
  40 def _set_disttracing_headers(headers, trace_parent, transaction):
  41     trace_parent_str = trace_parent.to_string()
  42     headers[constants.TRACEPARENT_HEADER_NAME] = trace_parent_str
  43     if transaction.tracer.config.use_elastic_traceparent_header:
  44         headers[constants.TRACEPARENT_LEGACY_HEADER_NAME] = trace_parent_str
  45     if trace_parent.tracestate:
  46         headers[constants.TRACESTATE_HEADER_NAME] = trace_parent.tracestate
  47 
  48 
  49 def update_headers(args, kwargs, instance, transaction, trace_parent):
  50     """
  51     The headers might be in 3 different places: as 4th positional argument, as "headers" keyword argument,
  52     or, if none of the former two are provided, as instance variable on the HTTPConnection object.
  53 
  54     If the headers are in the positional arguments tuple, a new tuple with updated headers will be returned.
  55     If they are in the keyword arguments or on the instance, an updated kwargs dict will be returned
  56 
  57     :param args: list of positional arguments
  58     :param kwargs: dict of keyword arguments
  59     :param instance: the HTTPConnection instance
  60     :param transaction: the Transaction object
  61     :param trace_parent: the TraceParent object
  62     :return: an (args, kwargs) tuple
  63     """
  64     if len(args) >= 4 and args[3]:
  65         headers = args[3].copy()
  66         args = tuple(itertools.chain((args[:3]), (headers,), args[4:]))
  67     elif "headers" in kwargs and kwargs["headers"]:
  68         headers = kwargs["headers"].copy()
  69         kwargs["headers"] = headers
  70     else:
  71         headers = instance.headers.copy() if instance.headers else {}
  72         # we don't want to change the instance headers, so we'll cheat and
  73         # set the headers as keywords. This slightly changes how the wrapped
  74         # method is called compared to uninstrumented code.
  75         kwargs["headers"] = headers
  76     _set_disttracing_headers(headers, trace_parent, transaction)
  77     return args, kwargs
  78 
  79 
  80 class Urllib3Instrumentation(AbstractInstrumentedModule):
  81     name = "urllib3"
  82 
  83     instrument_list = [
  84         ("urllib3.connectionpool", "HTTPConnectionPool.urlopen"),
  85         # packages that vendor or vendored urllib3 in the past
  86         ("requests.packages.urllib3.connectionpool", "HTTPConnectionPool.urlopen"),
  87         ("botocore.vendored.requests.packages.urllib3.connectionpool", "HTTPConnectionPool.urlopen"),
  88     ]
  89 
  90     def call(self, module, method, wrapped, instance, args, kwargs):
  91         if "method" in kwargs:
  92             method = kwargs["method"]
  93         else:
  94             method = args[0]
  95 
  96         host = instance.host
  97 
  98         if instance.port != default_ports.get(instance.scheme):
  99             host += ":" + str(instance.port)
  100 
  101         if "url" in kwargs:
  102             url = kwargs["url"]
  103         else:
  104             url = args[1]
  105 
  106         signature = method.upper() + " " + host
  107 
  108         if url.startswith("/"):
  109             url = "%s://%s%s" % (instance.scheme, host, url)
  110 
  111         transaction = execution_context.get_transaction()
  112 
  113         with capture_span(
  114             signature,
  115             span_type="external",
  116             span_subtype="http",
  117             extra={"http": {"url": url}},
  118             leaf=True,
  119         ) as span:
  120             # if urllib3 has been called in a leaf span, this span might be a DroppedSpan.
  121             leaf_span = span
  122             while isinstance(leaf_span, DroppedSpan):
  123                 leaf_span = leaf_span.parent
  124 
  125             parent_id = leaf_span.id if leaf_span else transaction.id
  126             trace_parent = transaction.trace_parent.copy_from(
  127                 span_id=parent_id, trace_options=TracingOptions(recorded=True)
  128             )
  129             args, kwargs = update_headers(args, kwargs, instance, transaction, trace_parent)
  130             if leaf_span:
  131                 leaf_span.dist_tracing_propagated = True
  132             response = wrapped(*args, **kwargs)
  133             if response:
  134                 if span.context:
  135                     span.context["http"]["status_code"] = response.status
  136                 span.set_success() if response.status < 400 else span.set_failure()
  137             return response
  138 
  139     def mutate_unsampled_call_args(self, module, method, wrapped, instance, args, kwargs, transaction):
  140         # since we don't have a span, we set the span id to the transaction id
  141         trace_parent = transaction.trace_parent.copy_from(
  142             span_id=transaction.id, trace_options=TracingOptions(recorded=False)
  143         )
  144         return update_headers(args, kwargs, instance, transaction, trace_parent)
  145
  ```
  --- 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/elasticapm/instrumentation/packages/urllib3.py b/elasticapm/instrumentation/packages/urllib3.py
  --- a/elasticapm/instrumentation/packages/urllib3.py
  +++ b/elasticapm/instrumentation/packages/urllib3.py
  @@ -61,7 +61,12 @@
       :param trace_parent: the TraceParent object
       :return: an (args, kwargs) tuple
       """
  -    if len(args) >= 4 and args[3]:
  +    from urllib3._version import __version__ as urllib3_version
  +
  +    if urllib3_version.startswith("2") and len(args) >= 5 and args[4]:
  +        headers = args[4].copy()
  +        args = tuple(itertools.chain((args[:4]), (headers,), args[5:]))
  +    elif len(args) >= 4 and args[3]:
           headers = args[3].copy()
           args = tuple(itertools.chain((args[:3]), (headers,), args[4:]))
       elif "headers" in kwargs and kwargs["headers"]:
  
verification_info: {"golden_diff": "diff --git a/elasticapm/instrumentation/packages/urllib3.py b/elasticapm/instrumentation/packages/urllib3.py\n--- a/elasticapm/instrumentation/packages/urllib3.py\n+++ b/elasticapm/instrumentation/packages/urllib3.py\n@@ -61,7 +61,12 @@\n     :param trace_parent: the TraceParent object\n     :return: an (args, kwargs) tuple\n     \"\"\"\n-    if len(args) >= 4 and args[3]:\n+    from urllib3._version import __version__ as urllib3_version\n+\n+    if urllib3_version.startswith(\"2\") and len(args) >= 5 and args[4]:\n+        headers = args[4].copy()\n+        args = tuple(itertools.chain((args[:4]), (headers,), args[5:]))\n+    elif len(args) >= 4 and args[3]:\n         headers = args[3].copy()\n         args = tuple(itertools.chain((args[:3]), (headers,), args[4:]))\n     elif \"headers\" in kwargs and kwargs[\"headers\"]:\n", "issue": "urllib3 v2.0 compatibility\nurllib3 v2.0 came out during PyConUS 2023 and is resulting in hanging tests on windows, and test failures on Linux. We need to update our code/tests to ensure compatibility for this new version.\n", "before_files": [{"content": "#  BSD 3-Clause License\n#\n#  Copyright (c) 2019, Elasticsearch BV\n#  All rights reserved.\n#\n#  Redistribution and use in source and binary forms, with or without\n#  modification, are permitted provided that the following conditions are met:\n#\n#  * Redistributions of source code must retain the above copyright notice, this\n#    list of conditions and the following disclaimer.\n#\n#  * Redistributions in binary form must reproduce the above copyright notice,\n#    this list of conditions and the following disclaimer in the documentation\n#    and/or other materials provided with the distribution.\n#\n#  * Neither the name of the copyright holder nor the names of its\n#    contributors may be used to endorse or promote products derived from\n#    this software without specific prior written permission.\n#\n#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n#  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n#  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n#  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n#  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n#  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n#  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n#  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nimport itertools\n\nfrom elasticapm.conf import constants\nfrom elasticapm.instrumentation.packages.base import AbstractInstrumentedModule\nfrom elasticapm.traces import DroppedSpan, capture_span, execution_context\nfrom elasticapm.utils import default_ports\nfrom elasticapm.utils.disttracing import TracingOptions\n\n\ndef _set_disttracing_headers(headers, trace_parent, transaction):\n    trace_parent_str = trace_parent.to_string()\n    headers[constants.TRACEPARENT_HEADER_NAME] = trace_parent_str\n    if transaction.tracer.config.use_elastic_traceparent_header:\n        headers[constants.TRACEPARENT_LEGACY_HEADER_NAME] = trace_parent_str\n    if trace_parent.tracestate:\n        headers[constants.TRACESTATE_HEADER_NAME] = trace_parent.tracestate\n\n\ndef update_headers(args, kwargs, instance, transaction, trace_parent):\n    \"\"\"\n    The headers might be in 3 different places: as 4th positional argument, as \"headers\" keyword argument,\n    or, if none of the former two are provided, as instance variable on the HTTPConnection object.\n\n    If the headers are in the positional arguments tuple, a new tuple with updated headers will be returned.\n    If they are in the keyword arguments or on the instance, an updated kwargs dict will be returned\n\n    :param args: list of positional arguments\n    :param kwargs: dict of keyword arguments\n    :param instance: the HTTPConnection instance\n    :param transaction: the Transaction object\n    :param trace_parent: the TraceParent object\n    :return: an (args, kwargs) tuple\n    \"\"\"\n    if len(args) >= 4 and args[3]:\n        headers = args[3].copy()\n        args = tuple(itertools.chain((args[:3]), (headers,), args[4:]))\n    elif \"headers\" in kwargs and kwargs[\"headers\"]:\n        headers = kwargs[\"headers\"].copy()\n        kwargs[\"headers\"] = headers\n    else:\n        headers = instance.headers.copy() if instance.headers else {}\n        # we don't want to change the instance headers, so we'll cheat and\n        # set the headers as keywords. This slightly changes how the wrapped\n        # method is called compared to uninstrumented code.\n        kwargs[\"headers\"] = headers\n    _set_disttracing_headers(headers, trace_parent, transaction)\n    return args, kwargs\n\n\nclass Urllib3Instrumentation(AbstractInstrumentedModule):\n    name = \"urllib3\"\n\n    instrument_list = [\n        (\"urllib3.connectionpool\", \"HTTPConnectionPool.urlopen\"),\n        # packages that vendor or vendored urllib3 in the past\n        (\"requests.packages.urllib3.connectionpool\", \"HTTPConnectionPool.urlopen\"),\n        (\"botocore.vendored.requests.packages.urllib3.connectionpool\", \"HTTPConnectionPool.urlopen\"),\n    ]\n\n    def call(self, module, method, wrapped, instance, args, kwargs):\n        if \"method\" in kwargs:\n            method = kwargs[\"method\"]\n        else:\n            method = args[0]\n\n        host = instance.host\n\n        if instance.port != default_ports.get(instance.scheme):\n            host += \":\" + str(instance.port)\n\n        if \"url\" in kwargs:\n            url = kwargs[\"url\"]\n        else:\n            url = args[1]\n\n        signature = method.upper() + \" \" + host\n\n        if url.startswith(\"/\"):\n            url = \"%s://%s%s\" % (instance.scheme, host, url)\n\n        transaction = execution_context.get_transaction()\n\n        with capture_span(\n            signature,\n            span_type=\"external\",\n            span_subtype=\"http\",\n            extra={\"http\": {\"url\": url}},\n            leaf=True,\n        ) as span:\n            # if urllib3 has been called in a leaf span, this span might be a DroppedSpan.\n            leaf_span = span\n            while isinstance(leaf_span, DroppedSpan):\n                leaf_span = leaf_span.parent\n\n            parent_id = leaf_span.id if leaf_span else transaction.id\n            trace_parent = transaction.trace_parent.copy_from(\n                span_id=parent_id, trace_options=TracingOptions(recorded=True)\n            )\n            args, kwargs = update_headers(args, kwargs, instance, transaction, trace_parent)\n            if leaf_span:\n                leaf_span.dist_tracing_propagated = True\n            response = wrapped(*args, **kwargs)\n            if response:\n                if span.context:\n                    span.context[\"http\"][\"status_code\"] = response.status\n                span.set_success() if response.status < 400 else span.set_failure()\n            return response\n\n    def mutate_unsampled_call_args(self, module, method, wrapped, instance, args, kwargs, transaction):\n        # since we don't have a span, we set the span id to the transaction id\n        trace_parent = transaction.trace_parent.copy_from(\n            span_id=transaction.id, trace_options=TracingOptions(recorded=False)\n        )\n        return update_headers(args, kwargs, instance, transaction, trace_parent)\n", "path": "elasticapm/instrumentation/packages/urllib3.py"}], "after_files": [{"content": "#  BSD 3-Clause License\n#\n#  Copyright (c) 2019, Elasticsearch BV\n#  All rights reserved.\n#\n#  Redistribution and use in source and binary forms, with or without\n#  modification, are permitted provided that the following conditions are met:\n#\n#  * Redistributions of source code must retain the above copyright notice, this\n#    list of conditions and the following disclaimer.\n#\n#  * Redistributions in binary form must reproduce the above copyright notice,\n#    this list of conditions and the following disclaimer in the documentation\n#    and/or other materials provided with the distribution.\n#\n#  * Neither the name of the copyright holder nor the names of its\n#    contributors may be used to endorse or promote products derived from\n#    this software without specific prior written permission.\n#\n#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n#  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n#  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n#  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n#  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n#  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n#  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n#  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nimport itertools\n\nfrom elasticapm.conf import constants\nfrom elasticapm.instrumentation.packages.base import AbstractInstrumentedModule\nfrom elasticapm.traces import DroppedSpan, capture_span, execution_context\nfrom elasticapm.utils import default_ports\nfrom elasticapm.utils.disttracing import TracingOptions\n\n\ndef _set_disttracing_headers(headers, trace_parent, transaction):\n    trace_parent_str = trace_parent.to_string()\n    headers[constants.TRACEPARENT_HEADER_NAME] = trace_parent_str\n    if transaction.tracer.config.use_elastic_traceparent_header:\n        headers[constants.TRACEPARENT_LEGACY_HEADER_NAME] = trace_parent_str\n    if trace_parent.tracestate:\n        headers[constants.TRACESTATE_HEADER_NAME] = trace_parent.tracestate\n\n\ndef update_headers(args, kwargs, instance, transaction, trace_parent):\n    \"\"\"\n    The headers might be in 3 different places: as 4th positional argument, as \"headers\" keyword argument,\n    or, if none of the former two are provided, as instance variable on the HTTPConnection object.\n\n    If the headers are in the positional arguments tuple, a new tuple with updated headers will be returned.\n    If they are in the keyword arguments or on the instance, an updated kwargs dict will be returned\n\n    :param args: list of positional arguments\n    :param kwargs: dict of keyword arguments\n    :param instance: the HTTPConnection instance\n    :param transaction: the Transaction object\n    :param trace_parent: the TraceParent object\n    :return: an (args, kwargs) tuple\n    \"\"\"\n    from urllib3._version import __version__ as urllib3_version\n\n    if urllib3_version.startswith(\"2\") and len(args) >= 5 and args[4]:\n        headers = args[4].copy()\n        args = tuple(itertools.chain((args[:4]), (headers,), args[5:]))\n    elif len(args) >= 4 and args[3]:\n        headers = args[3].copy()\n        args = tuple(itertools.chain((args[:3]), (headers,), args[4:]))\n    elif \"headers\" in kwargs and kwargs[\"headers\"]:\n        headers = kwargs[\"headers\"].copy()\n        kwargs[\"headers\"] = headers\n    else:\n        headers = instance.headers.copy() if instance.headers else {}\n        # we don't want to change the instance headers, so we'll cheat and\n        # set the headers as keywords. This slightly changes how the wrapped\n        # method is called compared to uninstrumented code.\n        kwargs[\"headers\"] = headers\n    _set_disttracing_headers(headers, trace_parent, transaction)\n    return args, kwargs\n\n\nclass Urllib3Instrumentation(AbstractInstrumentedModule):\n    name = \"urllib3\"\n\n    instrument_list = [\n        (\"urllib3.connectionpool\", \"HTTPConnectionPool.urlopen\"),\n        # packages that vendor or vendored urllib3 in the past\n        (\"requests.packages.urllib3.connectionpool\", \"HTTPConnectionPool.urlopen\"),\n        (\"botocore.vendored.requests.packages.urllib3.connectionpool\", \"HTTPConnectionPool.urlopen\"),\n    ]\n\n    def call(self, module, method, wrapped, instance, args, kwargs):\n        if \"method\" in kwargs:\n            method = kwargs[\"method\"]\n        else:\n            method = args[0]\n\n        host = instance.host\n\n        if instance.port != default_ports.get(instance.scheme):\n            host += \":\" + str(instance.port)\n\n        if \"url\" in kwargs:\n            url = kwargs[\"url\"]\n        else:\n            url = args[1]\n\n        signature = method.upper() + \" \" + host\n\n        if url.startswith(\"/\"):\n            url = \"%s://%s%s\" % (instance.scheme, host, url)\n\n        transaction = execution_context.get_transaction()\n\n        with capture_span(\n            signature,\n            span_type=\"external\",\n            span_subtype=\"http\",\n            extra={\"http\": {\"url\": url}},\n            leaf=True,\n        ) as span:\n            # if urllib3 has been called in a leaf span, this span might be a DroppedSpan.\n            leaf_span = span\n            while isinstance(leaf_span, DroppedSpan):\n                leaf_span = leaf_span.parent\n\n            parent_id = leaf_span.id if leaf_span else transaction.id\n            trace_parent = transaction.trace_parent.copy_from(\n                span_id=parent_id, trace_options=TracingOptions(recorded=True)\n            )\n            args, kwargs = update_headers(args, kwargs, instance, transaction, trace_parent)\n            if leaf_span:\n                leaf_span.dist_tracing_propagated = True\n            response = wrapped(*args, **kwargs)\n            if response:\n                if span.context:\n                    span.context[\"http\"][\"status_code\"] = response.status\n                span.set_success() if response.status < 400 else span.set_failure()\n            return response\n\n    def mutate_unsampled_call_args(self, module, method, wrapped, instance, args, kwargs, transaction):\n        # since we don't have a span, we set the span id to the transaction id\n        trace_parent = transaction.trace_parent.copy_from(\n            span_id=transaction.id, trace_options=TracingOptions(recorded=False)\n        )\n        return update_headers(args, kwargs, instance, transaction, trace_parent)\n", "path": "elasticapm/instrumentation/packages/urllib3.py"}]}