We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Increase the usage of augmented assignment statements
This issue corresponds with [this one](https://github.com/open-telemetry/opentelemetry-python/issues/2258).

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py`
Content:
```
1 # Copyright The OpenTelemetry Authors
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 
15 """
16 The opentelemetry-instrumentation-asgi package provides an ASGI middleware that can be used
17 on any ASGI framework (such as Django-channels / Quart) to track requests
18 timing through OpenTelemetry.
19 
20 Usage (Quart)
21 -------------
22 
23 .. code-block:: python
24 
25     from quart import Quart
26     from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware
27 
28     app = Quart(__name__)
29     app.asgi_app = OpenTelemetryMiddleware(app.asgi_app)
30 
31     @app.route("/")
32     async def hello():
33         return "Hello!"
34 
35     if __name__ == "__main__":
36         app.run(debug=True)
37 
38 
39 Usage (Django 3.0)
40 ------------------
41 
42 Modify the application's ``asgi.py`` file as shown below.
43 
44 .. code-block:: python
45 
46     import os
47     from django.core.asgi import get_asgi_application
48     from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware
49 
50     os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'asgi_example.settings')
51 
52     application = get_asgi_application()
53     application = OpenTelemetryMiddleware(application)
54 
55 
56 Usage (Raw ASGI)
57 ----------------
58 
59 .. code-block:: python
60 
61     from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware
62 
63     app = ...  # An ASGI application.
64     app = OpenTelemetryMiddleware(app)
65 
66 
67 Configuration
68 -------------
69 
70 Request/Response hooks
71 **********************
72 
73 Utilize request/reponse hooks to execute custom logic to be performed before/after performing a request. The server request hook takes in a server span and ASGI
74 scope object for every incoming request. The client request hook is called with the internal span and an ASGI scope which is sent as a dictionary for when the method recieve is called.
75 The client response hook is called with the internal span and an ASGI event which is sent as a dictionary for when the method send is called.
76 
77 .. code-block:: python
78 
79     def server_request_hook(span: Span, scope: dict):
80         if span and span.is_recording():
81             span.set_attribute("custom_user_attribute_from_request_hook", "some-value")
82 
83     def client_request_hook(span: Span, scope: dict):
84         if span and span.is_recording():
85             span.set_attribute("custom_user_attribute_from_client_request_hook", "some-value")
86 
87     def client_response_hook(span: Span, message: dict):
88         if span and span.is_recording():
89             span.set_attribute("custom_user_attribute_from_response_hook", "some-value")
90 
91    OpenTelemetryMiddleware().(application, server_request_hook=server_request_hook, client_request_hook=client_request_hook, client_response_hook=client_response_hook)
92 
93 API
94 ---
95 """
96 
97 import typing
98 import urllib
99 from functools import wraps
100 from typing import Tuple
101 
102 from asgiref.compatibility import guarantee_single_callable
103 
104 from opentelemetry import context, trace
105 from opentelemetry.instrumentation.asgi.version import __version__  # noqa
106 from opentelemetry.instrumentation.utils import http_status_to_status_code
107 from opentelemetry.propagate import extract
108 from opentelemetry.propagators.textmap import Getter
109 from opentelemetry.semconv.trace import SpanAttributes
110 from opentelemetry.trace import Span
111 from opentelemetry.trace.status import Status, StatusCode
112 from opentelemetry.util.http import remove_url_credentials
113 
114 _ServerRequestHookT = typing.Optional[typing.Callable[[Span, dict], None]]
115 _ClientRequestHookT = typing.Optional[typing.Callable[[Span, dict], None]]
116 _ClientResponseHookT = typing.Optional[typing.Callable[[Span, dict], None]]
117 
118 
119 class ASGIGetter(Getter):
120     def get(
121         self, carrier: dict, key: str
122     ) -> typing.Optional[typing.List[str]]:
123         """Getter implementation to retrieve a HTTP header value from the ASGI
124         scope.
125 
126         Args:
127             carrier: ASGI scope object
128             key: header name in scope
129         Returns:
130             A list with a single string with the header value if it exists,
131                 else None.
132         """
133         headers = carrier.get("headers")
134         if not headers:
135             return None
136 
137         # asgi header keys are in lower case
138         key = key.lower()
139         decoded = [
140             _value.decode("utf8")
141             for (_key, _value) in headers
142             if _key.decode("utf8") == key
143         ]
144         if not decoded:
145             return None
146         return decoded
147 
148     def keys(self, carrier: dict) -> typing.List[str]:
149         return list(carrier.keys())
150 
151 
152 asgi_getter = ASGIGetter()
153 
154 
155 def collect_request_attributes(scope):
156     """Collects HTTP request attributes from the ASGI scope and returns a
157     dictionary to be used as span creation attributes."""
158     server_host, port, http_url = get_host_port_url_tuple(scope)
159     query_string = scope.get("query_string")
160     if query_string and http_url:
161         if isinstance(query_string, bytes):
162             query_string = query_string.decode("utf8")
163         http_url = http_url + ("?" + urllib.parse.unquote(query_string))
164 
165     result = {
166         SpanAttributes.HTTP_SCHEME: scope.get("scheme"),
167         SpanAttributes.HTTP_HOST: server_host,
168         SpanAttributes.NET_HOST_PORT: port,
169         SpanAttributes.HTTP_FLAVOR: scope.get("http_version"),
170         SpanAttributes.HTTP_TARGET: scope.get("path"),
171         SpanAttributes.HTTP_URL: remove_url_credentials(http_url),
172     }
173     http_method = scope.get("method")
174     if http_method:
175         result[SpanAttributes.HTTP_METHOD] = http_method
176 
177     http_host_value_list = asgi_getter.get(scope, "host")
178     if http_host_value_list:
179         result[SpanAttributes.HTTP_SERVER_NAME] = ",".join(
180             http_host_value_list
181         )
182     http_user_agent = asgi_getter.get(scope, "user-agent")
183     if http_user_agent:
184         result[SpanAttributes.HTTP_USER_AGENT] = http_user_agent[0]
185 
186     if "client" in scope and scope["client"] is not None:
187         result[SpanAttributes.NET_PEER_IP] = scope.get("client")[0]
188         result[SpanAttributes.NET_PEER_PORT] = scope.get("client")[1]
189 
190     # remove None values
191     result = {k: v for k, v in result.items() if v is not None}
192 
193     return result
194 
195 
196 def get_host_port_url_tuple(scope):
197     """Returns (host, port, full_url) tuple."""
198     server = scope.get("server") or ["0.0.0.0", 80]
199     port = server[1]
200     server_host = server[0] + (":" + str(port) if str(port) != "80" else "")
201     full_path = scope.get("root_path", "") + scope.get("path", "")
202     http_url = scope.get("scheme", "http") + "://" + server_host + full_path
203     return server_host, port, http_url
204 
205 
206 def set_status_code(span, status_code):
207     """Adds HTTP response attributes to span using the status_code argument."""
208     if not span.is_recording():
209         return
210     try:
211         status_code = int(status_code)
212     except ValueError:
213         span.set_status(
214             Status(
215                 StatusCode.ERROR,
216                 "Non-integer HTTP status: " + repr(status_code),
217             )
218         )
219     else:
220         span.set_attribute(SpanAttributes.HTTP_STATUS_CODE, status_code)
221         span.set_status(
222             Status(http_status_to_status_code(status_code, server_span=True))
223         )
224 
225 
226 def get_default_span_details(scope: dict) -> Tuple[str, dict]:
227     """Default implementation for get_default_span_details
228     Args:
229         scope: the asgi scope dictionary
230     Returns:
231         a tuple of the span name, and any attributes to attach to the span.
232     """
233     span_name = (
234         scope.get("path", "").strip()
235         or f"HTTP {scope.get('method', '').strip()}"
236     )
237 
238     return span_name, {}
239 
240 
241 class OpenTelemetryMiddleware:
242     """The ASGI application middleware.
243 
244     This class is an ASGI middleware that starts and annotates spans for any
245     requests it is invoked with.
246 
247     Args:
248         app: The ASGI application callable to forward requests to.
249         default_span_details: Callback which should return a string and a tuple, representing the desired default span name and a
250                       dictionary with any additional span attributes to set.
251                       Optional: Defaults to get_default_span_details.
252         server_request_hook: Optional callback which is called with the server span and ASGI
253                       scope object for every incoming request.
254         client_request_hook: Optional callback which is called with the internal span and an ASGI
255                       scope which is sent as a dictionary for when the method recieve is called.
256         client_response_hook: Optional callback which is called with the internal span and an ASGI
257                       event which is sent as a dictionary for when the method send is called.
258         tracer_provider: The optional tracer provider to use. If omitted
259             the current globally configured one is used.
260     """
261 
262     def __init__(
263         self,
264         app,
265         excluded_urls=None,
266         default_span_details=None,
267         server_request_hook: _ServerRequestHookT = None,
268         client_request_hook: _ClientRequestHookT = None,
269         client_response_hook: _ClientResponseHookT = None,
270         tracer_provider=None,
271     ):
272         self.app = guarantee_single_callable(app)
273         self.tracer = trace.get_tracer(__name__, __version__, tracer_provider)
274         self.excluded_urls = excluded_urls
275         self.default_span_details = (
276             default_span_details or get_default_span_details
277         )
278         self.server_request_hook = server_request_hook
279         self.client_request_hook = client_request_hook
280         self.client_response_hook = client_response_hook
281 
282     async def __call__(self, scope, receive, send):
283         """The ASGI application
284 
285         Args:
286             scope: A ASGI environment.
287             receive: An awaitable callable yielding dictionaries
288             send: An awaitable callable taking a single dictionary as argument.
289         """
290         if scope["type"] not in ("http", "websocket"):
291             return await self.app(scope, receive, send)
292 
293         _, _, url = get_host_port_url_tuple(scope)
294         if self.excluded_urls and self.excluded_urls.url_disabled(url):
295             return await self.app(scope, receive, send)
296 
297         token = context.attach(extract(scope, getter=asgi_getter))
298         span_name, additional_attributes = self.default_span_details(scope)
299 
300         try:
301             with self.tracer.start_as_current_span(
302                 span_name,
303                 kind=trace.SpanKind.SERVER,
304             ) as span:
305                 if span.is_recording():
306                     attributes = collect_request_attributes(scope)
307                     attributes.update(additional_attributes)
308                     for key, value in attributes.items():
309                         span.set_attribute(key, value)
310 
311                 if callable(self.server_request_hook):
312                     self.server_request_hook(span, scope)
313 
314                 @wraps(receive)
315                 async def wrapped_receive():
316                     with self.tracer.start_as_current_span(
317                         " ".join((span_name, scope["type"], "receive"))
318                     ) as receive_span:
319                         if callable(self.client_request_hook):
320                             self.client_request_hook(receive_span, scope)
321                         message = await receive()
322                         if receive_span.is_recording():
323                             if message["type"] == "websocket.receive":
324                                 set_status_code(receive_span, 200)
325                             receive_span.set_attribute("type", message["type"])
326                     return message
327 
328                 @wraps(send)
329                 async def wrapped_send(message):
330                     with self.tracer.start_as_current_span(
331                         " ".join((span_name, scope["type"], "send"))
332                     ) as send_span:
333                         if callable(self.client_response_hook):
334                             self.client_response_hook(send_span, message)
335                         if send_span.is_recording():
336                             if message["type"] == "http.response.start":
337                                 status_code = message["status"]
338                                 set_status_code(span, status_code)
339                                 set_status_code(send_span, status_code)
340                             elif message["type"] == "websocket.send":
341                                 set_status_code(span, 200)
342                                 set_status_code(send_span, 200)
343                             send_span.set_attribute("type", message["type"])
344                         await send(message)
345 
346                 await self.app(scope, wrapped_receive, wrapped_send)
347         finally:
348             context.detach(token)
349
```
--- 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/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py b/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py
--- a/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py
+++ b/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py
@@ -160,7 +160,7 @@
     if query_string and http_url:
         if isinstance(query_string, bytes):
             query_string = query_string.decode("utf8")
-        http_url = http_url + ("?" + urllib.parse.unquote(query_string))
+        http_url += "?" + urllib.parse.unquote(query_string)
 
     result = {
         SpanAttributes.HTTP_SCHEME: scope.get("scheme"),

{"golden_diff": "diff --git a/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py b/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py\n--- a/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py\n+++ b/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py\n@@ -160,7 +160,7 @@\n     if query_string and http_url:\n         if isinstance(query_string, bytes):\n             query_string = query_string.decode(\"utf8\")\n-        http_url = http_url + (\"?\" + urllib.parse.unquote(query_string))\n+        http_url += \"?\" + urllib.parse.unquote(query_string)\n \n     result = {\n         SpanAttributes.HTTP_SCHEME: scope.get(\"scheme\"),\n", "issue": "Increase the usage of augmented assignment statements\nThis issue corresponds with [this one](https://github.com/open-telemetry/opentelemetry-python/issues/2258).\n", "before_files": [{"content": "# Copyright The OpenTelemetry Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nThe opentelemetry-instrumentation-asgi package provides an ASGI middleware that can be used\non any ASGI framework (such as Django-channels / Quart) to track requests\ntiming through OpenTelemetry.\n\nUsage (Quart)\n-------------\n\n.. code-block:: python\n\n    from quart import Quart\n    from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware\n\n    app = Quart(__name__)\n    app.asgi_app = OpenTelemetryMiddleware(app.asgi_app)\n\n    @app.route(\"/\")\n    async def hello():\n        return \"Hello!\"\n\n    if __name__ == \"__main__\":\n        app.run(debug=True)\n\n\nUsage (Django 3.0)\n------------------\n\nModify the application's ``asgi.py`` file as shown below.\n\n.. code-block:: python\n\n    import os\n    from django.core.asgi import get_asgi_application\n    from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware\n\n    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'asgi_example.settings')\n\n    application = get_asgi_application()\n    application = OpenTelemetryMiddleware(application)\n\n\nUsage (Raw ASGI)\n----------------\n\n.. code-block:: python\n\n    from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware\n\n    app = ...  # An ASGI application.\n    app = OpenTelemetryMiddleware(app)\n\n\nConfiguration\n-------------\n\nRequest/Response hooks\n**********************\n\nUtilize request/reponse hooks to execute custom logic to be performed before/after performing a request. The server request hook takes in a server span and ASGI\nscope object for every incoming request. The client request hook is called with the internal span and an ASGI scope which is sent as a dictionary for when the method recieve is called.\nThe client response hook is called with the internal span and an ASGI event which is sent as a dictionary for when the method send is called.\n\n.. code-block:: python\n\n    def server_request_hook(span: Span, scope: dict):\n        if span and span.is_recording():\n            span.set_attribute(\"custom_user_attribute_from_request_hook\", \"some-value\")\n\n    def client_request_hook(span: Span, scope: dict):\n        if span and span.is_recording():\n            span.set_attribute(\"custom_user_attribute_from_client_request_hook\", \"some-value\")\n\n    def client_response_hook(span: Span, message: dict):\n        if span and span.is_recording():\n            span.set_attribute(\"custom_user_attribute_from_response_hook\", \"some-value\")\n\n   OpenTelemetryMiddleware().(application, server_request_hook=server_request_hook, client_request_hook=client_request_hook, client_response_hook=client_response_hook)\n\nAPI\n---\n\"\"\"\n\nimport typing\nimport urllib\nfrom functools import wraps\nfrom typing import Tuple\n\nfrom asgiref.compatibility import guarantee_single_callable\n\nfrom opentelemetry import context, trace\nfrom opentelemetry.instrumentation.asgi.version import __version__  # noqa\nfrom opentelemetry.instrumentation.utils import http_status_to_status_code\nfrom opentelemetry.propagate import extract\nfrom opentelemetry.propagators.textmap import Getter\nfrom opentelemetry.semconv.trace import SpanAttributes\nfrom opentelemetry.trace import Span\nfrom opentelemetry.trace.status import Status, StatusCode\nfrom opentelemetry.util.http import remove_url_credentials\n\n_ServerRequestHookT = typing.Optional[typing.Callable[[Span, dict], None]]\n_ClientRequestHookT = typing.Optional[typing.Callable[[Span, dict], None]]\n_ClientResponseHookT = typing.Optional[typing.Callable[[Span, dict], None]]\n\n\nclass ASGIGetter(Getter):\n    def get(\n        self, carrier: dict, key: str\n    ) -> typing.Optional[typing.List[str]]:\n        \"\"\"Getter implementation to retrieve a HTTP header value from the ASGI\n        scope.\n\n        Args:\n            carrier: ASGI scope object\n            key: header name in scope\n        Returns:\n            A list with a single string with the header value if it exists,\n                else None.\n        \"\"\"\n        headers = carrier.get(\"headers\")\n        if not headers:\n            return None\n\n        # asgi header keys are in lower case\n        key = key.lower()\n        decoded = [\n            _value.decode(\"utf8\")\n            for (_key, _value) in headers\n            if _key.decode(\"utf8\") == key\n        ]\n        if not decoded:\n            return None\n        return decoded\n\n    def keys(self, carrier: dict) -> typing.List[str]:\n        return list(carrier.keys())\n\n\nasgi_getter = ASGIGetter()\n\n\ndef collect_request_attributes(scope):\n    \"\"\"Collects HTTP request attributes from the ASGI scope and returns a\n    dictionary to be used as span creation attributes.\"\"\"\n    server_host, port, http_url = get_host_port_url_tuple(scope)\n    query_string = scope.get(\"query_string\")\n    if query_string and http_url:\n        if isinstance(query_string, bytes):\n            query_string = query_string.decode(\"utf8\")\n        http_url = http_url + (\"?\" + urllib.parse.unquote(query_string))\n\n    result = {\n        SpanAttributes.HTTP_SCHEME: scope.get(\"scheme\"),\n        SpanAttributes.HTTP_HOST: server_host,\n        SpanAttributes.NET_HOST_PORT: port,\n        SpanAttributes.HTTP_FLAVOR: scope.get(\"http_version\"),\n        SpanAttributes.HTTP_TARGET: scope.get(\"path\"),\n        SpanAttributes.HTTP_URL: remove_url_credentials(http_url),\n    }\n    http_method = scope.get(\"method\")\n    if http_method:\n        result[SpanAttributes.HTTP_METHOD] = http_method\n\n    http_host_value_list = asgi_getter.get(scope, \"host\")\n    if http_host_value_list:\n        result[SpanAttributes.HTTP_SERVER_NAME] = \",\".join(\n            http_host_value_list\n        )\n    http_user_agent = asgi_getter.get(scope, \"user-agent\")\n    if http_user_agent:\n        result[SpanAttributes.HTTP_USER_AGENT] = http_user_agent[0]\n\n    if \"client\" in scope and scope[\"client\"] is not None:\n        result[SpanAttributes.NET_PEER_IP] = scope.get(\"client\")[0]\n        result[SpanAttributes.NET_PEER_PORT] = scope.get(\"client\")[1]\n\n    # remove None values\n    result = {k: v for k, v in result.items() if v is not None}\n\n    return result\n\n\ndef get_host_port_url_tuple(scope):\n    \"\"\"Returns (host, port, full_url) tuple.\"\"\"\n    server = scope.get(\"server\") or [\"0.0.0.0\", 80]\n    port = server[1]\n    server_host = server[0] + (\":\" + str(port) if str(port) != \"80\" else \"\")\n    full_path = scope.get(\"root_path\", \"\") + scope.get(\"path\", \"\")\n    http_url = scope.get(\"scheme\", \"http\") + \"://\" + server_host + full_path\n    return server_host, port, http_url\n\n\ndef set_status_code(span, status_code):\n    \"\"\"Adds HTTP response attributes to span using the status_code argument.\"\"\"\n    if not span.is_recording():\n        return\n    try:\n        status_code = int(status_code)\n    except ValueError:\n        span.set_status(\n            Status(\n                StatusCode.ERROR,\n                \"Non-integer HTTP status: \" + repr(status_code),\n            )\n        )\n    else:\n        span.set_attribute(SpanAttributes.HTTP_STATUS_CODE, status_code)\n        span.set_status(\n            Status(http_status_to_status_code(status_code, server_span=True))\n        )\n\n\ndef get_default_span_details(scope: dict) -> Tuple[str, dict]:\n    \"\"\"Default implementation for get_default_span_details\n    Args:\n        scope: the asgi scope dictionary\n    Returns:\n        a tuple of the span name, and any attributes to attach to the span.\n    \"\"\"\n    span_name = (\n        scope.get(\"path\", \"\").strip()\n        or f\"HTTP {scope.get('method', '').strip()}\"\n    )\n\n    return span_name, {}\n\n\nclass OpenTelemetryMiddleware:\n    \"\"\"The ASGI application middleware.\n\n    This class is an ASGI middleware that starts and annotates spans for any\n    requests it is invoked with.\n\n    Args:\n        app: The ASGI application callable to forward requests to.\n        default_span_details: Callback which should return a string and a tuple, representing the desired default span name and a\n                      dictionary with any additional span attributes to set.\n                      Optional: Defaults to get_default_span_details.\n        server_request_hook: Optional callback which is called with the server span and ASGI\n                      scope object for every incoming request.\n        client_request_hook: Optional callback which is called with the internal span and an ASGI\n                      scope which is sent as a dictionary for when the method recieve is called.\n        client_response_hook: Optional callback which is called with the internal span and an ASGI\n                      event which is sent as a dictionary for when the method send is called.\n        tracer_provider: The optional tracer provider to use. If omitted\n            the current globally configured one is used.\n    \"\"\"\n\n    def __init__(\n        self,\n        app,\n        excluded_urls=None,\n        default_span_details=None,\n        server_request_hook: _ServerRequestHookT = None,\n        client_request_hook: _ClientRequestHookT = None,\n        client_response_hook: _ClientResponseHookT = None,\n        tracer_provider=None,\n    ):\n        self.app = guarantee_single_callable(app)\n        self.tracer = trace.get_tracer(__name__, __version__, tracer_provider)\n        self.excluded_urls = excluded_urls\n        self.default_span_details = (\n            default_span_details or get_default_span_details\n        )\n        self.server_request_hook = server_request_hook\n        self.client_request_hook = client_request_hook\n        self.client_response_hook = client_response_hook\n\n    async def __call__(self, scope, receive, send):\n        \"\"\"The ASGI application\n\n        Args:\n            scope: A ASGI environment.\n            receive: An awaitable callable yielding dictionaries\n            send: An awaitable callable taking a single dictionary as argument.\n        \"\"\"\n        if scope[\"type\"] not in (\"http\", \"websocket\"):\n            return await self.app(scope, receive, send)\n\n        _, _, url = get_host_port_url_tuple(scope)\n        if self.excluded_urls and self.excluded_urls.url_disabled(url):\n            return await self.app(scope, receive, send)\n\n        token = context.attach(extract(scope, getter=asgi_getter))\n        span_name, additional_attributes = self.default_span_details(scope)\n\n        try:\n            with self.tracer.start_as_current_span(\n                span_name,\n                kind=trace.SpanKind.SERVER,\n            ) as span:\n                if span.is_recording():\n                    attributes = collect_request_attributes(scope)\n                    attributes.update(additional_attributes)\n                    for key, value in attributes.items():\n                        span.set_attribute(key, value)\n\n                if callable(self.server_request_hook):\n                    self.server_request_hook(span, scope)\n\n                @wraps(receive)\n                async def wrapped_receive():\n                    with self.tracer.start_as_current_span(\n                        \" \".join((span_name, scope[\"type\"], \"receive\"))\n                    ) as receive_span:\n                        if callable(self.client_request_hook):\n                            self.client_request_hook(receive_span, scope)\n                        message = await receive()\n                        if receive_span.is_recording():\n                            if message[\"type\"] == \"websocket.receive\":\n                                set_status_code(receive_span, 200)\n                            receive_span.set_attribute(\"type\", message[\"type\"])\n                    return message\n\n                @wraps(send)\n                async def wrapped_send(message):\n                    with self.tracer.start_as_current_span(\n                        \" \".join((span_name, scope[\"type\"], \"send\"))\n                    ) as send_span:\n                        if callable(self.client_response_hook):\n                            self.client_response_hook(send_span, message)\n                        if send_span.is_recording():\n                            if message[\"type\"] == \"http.response.start\":\n                                status_code = message[\"status\"]\n                                set_status_code(span, status_code)\n                                set_status_code(send_span, status_code)\n                            elif message[\"type\"] == \"websocket.send\":\n                                set_status_code(span, 200)\n                                set_status_code(send_span, 200)\n                            send_span.set_attribute(\"type\", message[\"type\"])\n                        await send(message)\n\n                await self.app(scope, wrapped_receive, wrapped_send)\n        finally:\n            context.detach(token)\n", "path": "instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py"}], "after_files": [{"content": "# Copyright The OpenTelemetry Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nThe opentelemetry-instrumentation-asgi package provides an ASGI middleware that can be used\non any ASGI framework (such as Django-channels / Quart) to track requests\ntiming through OpenTelemetry.\n\nUsage (Quart)\n-------------\n\n.. code-block:: python\n\n    from quart import Quart\n    from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware\n\n    app = Quart(__name__)\n    app.asgi_app = OpenTelemetryMiddleware(app.asgi_app)\n\n    @app.route(\"/\")\n    async def hello():\n        return \"Hello!\"\n\n    if __name__ == \"__main__\":\n        app.run(debug=True)\n\n\nUsage (Django 3.0)\n------------------\n\nModify the application's ``asgi.py`` file as shown below.\n\n.. code-block:: python\n\n    import os\n    from django.core.asgi import get_asgi_application\n    from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware\n\n    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'asgi_example.settings')\n\n    application = get_asgi_application()\n    application = OpenTelemetryMiddleware(application)\n\n\nUsage (Raw ASGI)\n----------------\n\n.. code-block:: python\n\n    from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware\n\n    app = ...  # An ASGI application.\n    app = OpenTelemetryMiddleware(app)\n\n\nConfiguration\n-------------\n\nRequest/Response hooks\n**********************\n\nUtilize request/reponse hooks to execute custom logic to be performed before/after performing a request. The server request hook takes in a server span and ASGI\nscope object for every incoming request. The client request hook is called with the internal span and an ASGI scope which is sent as a dictionary for when the method recieve is called.\nThe client response hook is called with the internal span and an ASGI event which is sent as a dictionary for when the method send is called.\n\n.. code-block:: python\n\n    def server_request_hook(span: Span, scope: dict):\n        if span and span.is_recording():\n            span.set_attribute(\"custom_user_attribute_from_request_hook\", \"some-value\")\n\n    def client_request_hook(span: Span, scope: dict):\n        if span and span.is_recording():\n            span.set_attribute(\"custom_user_attribute_from_client_request_hook\", \"some-value\")\n\n    def client_response_hook(span: Span, message: dict):\n        if span and span.is_recording():\n            span.set_attribute(\"custom_user_attribute_from_response_hook\", \"some-value\")\n\n   OpenTelemetryMiddleware().(application, server_request_hook=server_request_hook, client_request_hook=client_request_hook, client_response_hook=client_response_hook)\n\nAPI\n---\n\"\"\"\n\nimport typing\nimport urllib\nfrom functools import wraps\nfrom typing import Tuple\n\nfrom asgiref.compatibility import guarantee_single_callable\n\nfrom opentelemetry import context, trace\nfrom opentelemetry.instrumentation.asgi.version import __version__  # noqa\nfrom opentelemetry.instrumentation.utils import http_status_to_status_code\nfrom opentelemetry.propagate import extract\nfrom opentelemetry.propagators.textmap import Getter\nfrom opentelemetry.semconv.trace import SpanAttributes\nfrom opentelemetry.trace import Span\nfrom opentelemetry.trace.status import Status, StatusCode\nfrom opentelemetry.util.http import remove_url_credentials\n\n_ServerRequestHookT = typing.Optional[typing.Callable[[Span, dict], None]]\n_ClientRequestHookT = typing.Optional[typing.Callable[[Span, dict], None]]\n_ClientResponseHookT = typing.Optional[typing.Callable[[Span, dict], None]]\n\n\nclass ASGIGetter(Getter):\n    def get(\n        self, carrier: dict, key: str\n    ) -> typing.Optional[typing.List[str]]:\n        \"\"\"Getter implementation to retrieve a HTTP header value from the ASGI\n        scope.\n\n        Args:\n            carrier: ASGI scope object\n            key: header name in scope\n        Returns:\n            A list with a single string with the header value if it exists,\n                else None.\n        \"\"\"\n        headers = carrier.get(\"headers\")\n        if not headers:\n            return None\n\n        # asgi header keys are in lower case\n        key = key.lower()\n        decoded = [\n            _value.decode(\"utf8\")\n            for (_key, _value) in headers\n            if _key.decode(\"utf8\") == key\n        ]\n        if not decoded:\n            return None\n        return decoded\n\n    def keys(self, carrier: dict) -> typing.List[str]:\n        return list(carrier.keys())\n\n\nasgi_getter = ASGIGetter()\n\n\ndef collect_request_attributes(scope):\n    \"\"\"Collects HTTP request attributes from the ASGI scope and returns a\n    dictionary to be used as span creation attributes.\"\"\"\n    server_host, port, http_url = get_host_port_url_tuple(scope)\n    query_string = scope.get(\"query_string\")\n    if query_string and http_url:\n        if isinstance(query_string, bytes):\n            query_string = query_string.decode(\"utf8\")\n        http_url += \"?\" + urllib.parse.unquote(query_string)\n\n    result = {\n        SpanAttributes.HTTP_SCHEME: scope.get(\"scheme\"),\n        SpanAttributes.HTTP_HOST: server_host,\n        SpanAttributes.NET_HOST_PORT: port,\n        SpanAttributes.HTTP_FLAVOR: scope.get(\"http_version\"),\n        SpanAttributes.HTTP_TARGET: scope.get(\"path\"),\n        SpanAttributes.HTTP_URL: remove_url_credentials(http_url),\n    }\n    http_method = scope.get(\"method\")\n    if http_method:\n        result[SpanAttributes.HTTP_METHOD] = http_method\n\n    http_host_value_list = asgi_getter.get(scope, \"host\")\n    if http_host_value_list:\n        result[SpanAttributes.HTTP_SERVER_NAME] = \",\".join(\n            http_host_value_list\n        )\n    http_user_agent = asgi_getter.get(scope, \"user-agent\")\n    if http_user_agent:\n        result[SpanAttributes.HTTP_USER_AGENT] = http_user_agent[0]\n\n    if \"client\" in scope and scope[\"client\"] is not None:\n        result[SpanAttributes.NET_PEER_IP] = scope.get(\"client\")[0]\n        result[SpanAttributes.NET_PEER_PORT] = scope.get(\"client\")[1]\n\n    # remove None values\n    result = {k: v for k, v in result.items() if v is not None}\n\n    return result\n\n\ndef get_host_port_url_tuple(scope):\n    \"\"\"Returns (host, port, full_url) tuple.\"\"\"\n    server = scope.get(\"server\") or [\"0.0.0.0\", 80]\n    port = server[1]\n    server_host = server[0] + (\":\" + str(port) if str(port) != \"80\" else \"\")\n    full_path = scope.get(\"root_path\", \"\") + scope.get(\"path\", \"\")\n    http_url = scope.get(\"scheme\", \"http\") + \"://\" + server_host + full_path\n    return server_host, port, http_url\n\n\ndef set_status_code(span, status_code):\n    \"\"\"Adds HTTP response attributes to span using the status_code argument.\"\"\"\n    if not span.is_recording():\n        return\n    try:\n        status_code = int(status_code)\n    except ValueError:\n        span.set_status(\n            Status(\n                StatusCode.ERROR,\n                \"Non-integer HTTP status: \" + repr(status_code),\n            )\n        )\n    else:\n        span.set_attribute(SpanAttributes.HTTP_STATUS_CODE, status_code)\n        span.set_status(\n            Status(http_status_to_status_code(status_code, server_span=True))\n        )\n\n\ndef get_default_span_details(scope: dict) -> Tuple[str, dict]:\n    \"\"\"Default implementation for get_default_span_details\n    Args:\n        scope: the asgi scope dictionary\n    Returns:\n        a tuple of the span name, and any attributes to attach to the span.\n    \"\"\"\n    span_name = (\n        scope.get(\"path\", \"\").strip()\n        or f\"HTTP {scope.get('method', '').strip()}\"\n    )\n\n    return span_name, {}\n\n\nclass OpenTelemetryMiddleware:\n    \"\"\"The ASGI application middleware.\n\n    This class is an ASGI middleware that starts and annotates spans for any\n    requests it is invoked with.\n\n    Args:\n        app: The ASGI application callable to forward requests to.\n        default_span_details: Callback which should return a string and a tuple, representing the desired default span name and a\n                      dictionary with any additional span attributes to set.\n                      Optional: Defaults to get_default_span_details.\n        server_request_hook: Optional callback which is called with the server span and ASGI\n                      scope object for every incoming request.\n        client_request_hook: Optional callback which is called with the internal span and an ASGI\n                      scope which is sent as a dictionary for when the method recieve is called.\n        client_response_hook: Optional callback which is called with the internal span and an ASGI\n                      event which is sent as a dictionary for when the method send is called.\n        tracer_provider: The optional tracer provider to use. If omitted\n            the current globally configured one is used.\n    \"\"\"\n\n    def __init__(\n        self,\n        app,\n        excluded_urls=None,\n        default_span_details=None,\n        server_request_hook: _ServerRequestHookT = None,\n        client_request_hook: _ClientRequestHookT = None,\n        client_response_hook: _ClientResponseHookT = None,\n        tracer_provider=None,\n    ):\n        self.app = guarantee_single_callable(app)\n        self.tracer = trace.get_tracer(__name__, __version__, tracer_provider)\n        self.excluded_urls = excluded_urls\n        self.default_span_details = (\n            default_span_details or get_default_span_details\n        )\n        self.server_request_hook = server_request_hook\n        self.client_request_hook = client_request_hook\n        self.client_response_hook = client_response_hook\n\n    async def __call__(self, scope, receive, send):\n        \"\"\"The ASGI application\n\n        Args:\n            scope: A ASGI environment.\n            receive: An awaitable callable yielding dictionaries\n            send: An awaitable callable taking a single dictionary as argument.\n        \"\"\"\n        if scope[\"type\"] not in (\"http\", \"websocket\"):\n            return await self.app(scope, receive, send)\n\n        _, _, url = get_host_port_url_tuple(scope)\n        if self.excluded_urls and self.excluded_urls.url_disabled(url):\n            return await self.app(scope, receive, send)\n\n        token = context.attach(extract(scope, getter=asgi_getter))\n        span_name, additional_attributes = self.default_span_details(scope)\n\n        try:\n            with self.tracer.start_as_current_span(\n                span_name,\n                kind=trace.SpanKind.SERVER,\n            ) as span:\n                if span.is_recording():\n                    attributes = collect_request_attributes(scope)\n                    attributes.update(additional_attributes)\n                    for key, value in attributes.items():\n                        span.set_attribute(key, value)\n\n                if callable(self.server_request_hook):\n                    self.server_request_hook(span, scope)\n\n                @wraps(receive)\n                async def wrapped_receive():\n                    with self.tracer.start_as_current_span(\n                        \" \".join((span_name, scope[\"type\"], \"receive\"))\n                    ) as receive_span:\n                        if callable(self.client_request_hook):\n                            self.client_request_hook(receive_span, scope)\n                        message = await receive()\n                        if receive_span.is_recording():\n                            if message[\"type\"] == \"websocket.receive\":\n                                set_status_code(receive_span, 200)\n                            receive_span.set_attribute(\"type\", message[\"type\"])\n                    return message\n\n                @wraps(send)\n                async def wrapped_send(message):\n                    with self.tracer.start_as_current_span(\n                        \" \".join((span_name, scope[\"type\"], \"send\"))\n                    ) as send_span:\n                        if callable(self.client_response_hook):\n                            self.client_response_hook(send_span, message)\n                        if send_span.is_recording():\n                            if message[\"type\"] == \"http.response.start\":\n                                status_code = message[\"status\"]\n                                set_status_code(span, status_code)\n                                set_status_code(send_span, status_code)\n                            elif message[\"type\"] == \"websocket.send\":\n                                set_status_code(span, 200)\n                                set_status_code(send_span, 200)\n                            send_span.set_attribute(\"type\", message[\"type\"])\n                        await send(message)\n\n                await self.app(scope, wrapped_receive, wrapped_send)\n        finally:\n            context.detach(token)\n", "path": "instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py"}]}