We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
object has no attribute '__qualname__'
I am getting the following error when trying to enable the scout APM on a starlette application:
```
Traceback (most recent call last):
  File "/app/.heroku/python/lib/python3.8/site-packages/uvicorn/protocols/http/httptools_impl.py", line 391, in run_asgi
    result = await app(self.scope, self.receive, self.send)
  File "/app/.heroku/python/lib/python3.8/site-packages/uvicorn/middleware/proxy_headers.py", line 45, in __call__
    return await self.app(scope, receive, send)
  File "/app/.heroku/python/lib/python3.8/site-packages/starlette/applications.py", line 111, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/app/.heroku/python/lib/python3.8/site-packages/starlette/middleware/errors.py", line 181, in __call__
    raise exc from None
  File "/app/.heroku/python/lib/python3.8/site-packages/starlette/middleware/errors.py", line 159, in __call__
    await self.app(scope, receive, _send)
  File "/app/.heroku/python/lib/python3.8/site-packages/scout_apm/async_/starlette.py", line 67, in __call__
    grab_extra_data()
  File "/app/.heroku/python/lib/python3.8/site-packages/scout_apm/async_/starlette.py", line 35, in grab_extra_data
    endpoint.__module__, endpoint.__qualname__
AttributeError: 'GraphQLWithBackground' object has no attribute '__qualname__'
```

`GraphQLWithBackground` is a subclass of `ariadne.asgi.GraphQL`.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/scout_apm/async_/starlette.py`
Content:
```
1 # coding=utf-8
2 from __future__ import absolute_import, division, print_function, unicode_literals
3 
4 import wrapt
5 from starlette.background import BackgroundTask
6 
7 import scout_apm.core
8 from scout_apm.core.tracked_request import TrackedRequest
9 from scout_apm.core.web_requests import asgi_track_request_data
10 
11 
12 class ScoutMiddleware:
13     def __init__(self, app):
14         self.app = app
15         installed = scout_apm.core.install()
16         self._do_nothing = not installed
17         if installed:
18             install_background_instrumentation()
19 
20     async def __call__(self, scope, receive, send):
21         if self._do_nothing or scope["type"] != "http":
22             return await self.app(scope, receive, send)
23 
24         tracked_request = TrackedRequest.instance()
25         # Can't name controller until post-routing - see final clause
26         controller_span = tracked_request.start_span(operation="Controller/Unknown")
27 
28         asgi_track_request_data(scope, tracked_request)
29 
30         def grab_extra_data():
31             if "endpoint" in scope:
32                 # Rename top span
33                 endpoint = scope["endpoint"]
34                 controller_span.operation = "Controller/{}.{}".format(
35                     endpoint.__module__, endpoint.__qualname__
36                 )
37                 tracked_request.is_real_request = True
38 
39             # From AuthenticationMiddleware - bypass request.user because it
40             # throws AssertionError if 'user' is not in Scope, and we need a
41             # try/except already
42             try:
43                 username = scope["user"].display_name
44             except (KeyError, AttributeError):
45                 pass
46             else:
47                 tracked_request.tag("username", username)
48 
49         async def wrapped_send(data):
50             type_ = data.get("type", None)
51             if type_ == "http.response.start" and 500 <= data.get("status", 200) <= 599:
52                 tracked_request.tag("error", "true")
53             elif type_ == "http.response.body" and not data.get("more_body", False):
54                 # Finish HTTP span when body finishes sending, not later (e.g.
55                 # after background tasks)
56                 grab_extra_data()
57                 tracked_request.stop_span()
58             return await send(data)
59 
60         try:
61             await self.app(scope, receive, wrapped_send)
62         except Exception as exc:
63             tracked_request.tag("error", "true")
64             raise exc
65         finally:
66             if tracked_request.end_time is None:
67                 grab_extra_data()
68                 tracked_request.stop_span()
69 
70 
71 background_instrumentation_installed = False
72 
73 
74 def install_background_instrumentation():
75     global background_instrumentation_installed
76     if background_instrumentation_installed:
77         return
78     background_instrumentation_installed = True
79 
80     @wrapt.decorator
81     async def wrapped_background_call(wrapped, instance, args, kwargs):
82         tracked_request = TrackedRequest.instance()
83         tracked_request.is_real_request = True
84 
85         with tracked_request.span(
86             operation="Job/{}.{}".format(
87                 instance.func.__module__, instance.func.__qualname__
88             )
89         ):
90             return await wrapped(*args, **kwargs)
91 
92     BackgroundTask.__call__ = wrapped_background_call(BackgroundTask.__call__)
93
```
--- 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/src/scout_apm/async_/starlette.py b/src/scout_apm/async_/starlette.py
--- a/src/scout_apm/async_/starlette.py
+++ b/src/scout_apm/async_/starlette.py
@@ -31,8 +31,11 @@
             if "endpoint" in scope:
                 # Rename top span
                 endpoint = scope["endpoint"]
+                if not hasattr(endpoint, "__qualname__"):
+                    endpoint = endpoint.__class__
                 controller_span.operation = "Controller/{}.{}".format(
-                    endpoint.__module__, endpoint.__qualname__
+                    endpoint.__module__,
+                    endpoint.__qualname__,
                 )
                 tracked_request.is_real_request = True

{"golden_diff": "diff --git a/src/scout_apm/async_/starlette.py b/src/scout_apm/async_/starlette.py\n--- a/src/scout_apm/async_/starlette.py\n+++ b/src/scout_apm/async_/starlette.py\n@@ -31,8 +31,11 @@\n             if \"endpoint\" in scope:\n                 # Rename top span\n                 endpoint = scope[\"endpoint\"]\n+                if not hasattr(endpoint, \"__qualname__\"):\n+                    endpoint = endpoint.__class__\n                 controller_span.operation = \"Controller/{}.{}\".format(\n-                    endpoint.__module__, endpoint.__qualname__\n+                    endpoint.__module__,\n+                    endpoint.__qualname__,\n                 )\n                 tracked_request.is_real_request = True\n", "issue": "object has no attribute '__qualname__'\nI am getting the following error when trying to enable the scout APM on a starlette application:\r\n```\r\nTraceback (most recent call last):\r\n  File \"/app/.heroku/python/lib/python3.8/site-packages/uvicorn/protocols/http/httptools_impl.py\", line 391, in run_asgi\r\n    result = await app(self.scope, self.receive, self.send)\r\n  File \"/app/.heroku/python/lib/python3.8/site-packages/uvicorn/middleware/proxy_headers.py\", line 45, in __call__\r\n    return await self.app(scope, receive, send)\r\n  File \"/app/.heroku/python/lib/python3.8/site-packages/starlette/applications.py\", line 111, in __call__\r\n    await self.middleware_stack(scope, receive, send)\r\n  File \"/app/.heroku/python/lib/python3.8/site-packages/starlette/middleware/errors.py\", line 181, in __call__\r\n    raise exc from None\r\n  File \"/app/.heroku/python/lib/python3.8/site-packages/starlette/middleware/errors.py\", line 159, in __call__\r\n    await self.app(scope, receive, _send)\r\n  File \"/app/.heroku/python/lib/python3.8/site-packages/scout_apm/async_/starlette.py\", line 67, in __call__\r\n    grab_extra_data()\r\n  File \"/app/.heroku/python/lib/python3.8/site-packages/scout_apm/async_/starlette.py\", line 35, in grab_extra_data\r\n    endpoint.__module__, endpoint.__qualname__\r\nAttributeError: 'GraphQLWithBackground' object has no attribute '__qualname__'\r\n```\r\n\r\n`GraphQLWithBackground` is a subclass of `ariadne.asgi.GraphQL`.\n", "before_files": [{"content": "# coding=utf-8\nfrom __future__ import absolute_import, division, print_function, unicode_literals\n\nimport wrapt\nfrom starlette.background import BackgroundTask\n\nimport scout_apm.core\nfrom scout_apm.core.tracked_request import TrackedRequest\nfrom scout_apm.core.web_requests import asgi_track_request_data\n\n\nclass ScoutMiddleware:\n    def __init__(self, app):\n        self.app = app\n        installed = scout_apm.core.install()\n        self._do_nothing = not installed\n        if installed:\n            install_background_instrumentation()\n\n    async def __call__(self, scope, receive, send):\n        if self._do_nothing or scope[\"type\"] != \"http\":\n            return await self.app(scope, receive, send)\n\n        tracked_request = TrackedRequest.instance()\n        # Can't name controller until post-routing - see final clause\n        controller_span = tracked_request.start_span(operation=\"Controller/Unknown\")\n\n        asgi_track_request_data(scope, tracked_request)\n\n        def grab_extra_data():\n            if \"endpoint\" in scope:\n                # Rename top span\n                endpoint = scope[\"endpoint\"]\n                controller_span.operation = \"Controller/{}.{}\".format(\n                    endpoint.__module__, endpoint.__qualname__\n                )\n                tracked_request.is_real_request = True\n\n            # From AuthenticationMiddleware - bypass request.user because it\n            # throws AssertionError if 'user' is not in Scope, and we need a\n            # try/except already\n            try:\n                username = scope[\"user\"].display_name\n            except (KeyError, AttributeError):\n                pass\n            else:\n                tracked_request.tag(\"username\", username)\n\n        async def wrapped_send(data):\n            type_ = data.get(\"type\", None)\n            if type_ == \"http.response.start\" and 500 <= data.get(\"status\", 200) <= 599:\n                tracked_request.tag(\"error\", \"true\")\n            elif type_ == \"http.response.body\" and not data.get(\"more_body\", False):\n                # Finish HTTP span when body finishes sending, not later (e.g.\n                # after background tasks)\n                grab_extra_data()\n                tracked_request.stop_span()\n            return await send(data)\n\n        try:\n            await self.app(scope, receive, wrapped_send)\n        except Exception as exc:\n            tracked_request.tag(\"error\", \"true\")\n            raise exc\n        finally:\n            if tracked_request.end_time is None:\n                grab_extra_data()\n                tracked_request.stop_span()\n\n\nbackground_instrumentation_installed = False\n\n\ndef install_background_instrumentation():\n    global background_instrumentation_installed\n    if background_instrumentation_installed:\n        return\n    background_instrumentation_installed = True\n\n    @wrapt.decorator\n    async def wrapped_background_call(wrapped, instance, args, kwargs):\n        tracked_request = TrackedRequest.instance()\n        tracked_request.is_real_request = True\n\n        with tracked_request.span(\n            operation=\"Job/{}.{}\".format(\n                instance.func.__module__, instance.func.__qualname__\n            )\n        ):\n            return await wrapped(*args, **kwargs)\n\n    BackgroundTask.__call__ = wrapped_background_call(BackgroundTask.__call__)\n", "path": "src/scout_apm/async_/starlette.py"}], "after_files": [{"content": "# coding=utf-8\nfrom __future__ import absolute_import, division, print_function, unicode_literals\n\nimport wrapt\nfrom starlette.background import BackgroundTask\n\nimport scout_apm.core\nfrom scout_apm.core.tracked_request import TrackedRequest\nfrom scout_apm.core.web_requests import asgi_track_request_data\n\n\nclass ScoutMiddleware:\n    def __init__(self, app):\n        self.app = app\n        installed = scout_apm.core.install()\n        self._do_nothing = not installed\n        if installed:\n            install_background_instrumentation()\n\n    async def __call__(self, scope, receive, send):\n        if self._do_nothing or scope[\"type\"] != \"http\":\n            return await self.app(scope, receive, send)\n\n        tracked_request = TrackedRequest.instance()\n        # Can't name controller until post-routing - see final clause\n        controller_span = tracked_request.start_span(operation=\"Controller/Unknown\")\n\n        asgi_track_request_data(scope, tracked_request)\n\n        def grab_extra_data():\n            if \"endpoint\" in scope:\n                # Rename top span\n                endpoint = scope[\"endpoint\"]\n                if not hasattr(endpoint, \"__qualname__\"):\n                    endpoint = endpoint.__class__\n                controller_span.operation = \"Controller/{}.{}\".format(\n                    endpoint.__module__,\n                    endpoint.__qualname__,\n                )\n                tracked_request.is_real_request = True\n\n            # From AuthenticationMiddleware - bypass request.user because it\n            # throws AssertionError if 'user' is not in Scope, and we need a\n            # try/except already\n            try:\n                username = scope[\"user\"].display_name\n            except (KeyError, AttributeError):\n                pass\n            else:\n                tracked_request.tag(\"username\", username)\n\n        async def wrapped_send(data):\n            type_ = data.get(\"type\", None)\n            if type_ == \"http.response.start\" and 500 <= data.get(\"status\", 200) <= 599:\n                tracked_request.tag(\"error\", \"true\")\n            elif type_ == \"http.response.body\" and not data.get(\"more_body\", False):\n                # Finish HTTP span when body finishes sending, not later (e.g.\n                # after background tasks)\n                grab_extra_data()\n                tracked_request.stop_span()\n            return await send(data)\n\n        try:\n            await self.app(scope, receive, wrapped_send)\n        except Exception as exc:\n            tracked_request.tag(\"error\", \"true\")\n            raise exc\n        finally:\n            if tracked_request.end_time is None:\n                grab_extra_data()\n                tracked_request.stop_span()\n\n\nbackground_instrumentation_installed = False\n\n\ndef install_background_instrumentation():\n    global background_instrumentation_installed\n    if background_instrumentation_installed:\n        return\n    background_instrumentation_installed = True\n\n    @wrapt.decorator\n    async def wrapped_background_call(wrapped, instance, args, kwargs):\n        tracked_request = TrackedRequest.instance()\n        tracked_request.is_real_request = True\n\n        with tracked_request.span(\n            operation=\"Job/{}.{}\".format(\n                instance.func.__module__, instance.func.__qualname__\n            )\n        ):\n            return await wrapped(*args, **kwargs)\n\n    BackgroundTask.__call__ = wrapped_background_call(BackgroundTask.__call__)\n", "path": "src/scout_apm/async_/starlette.py"}]}