We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Not working with older boto version
Hello, we use it in Django==2.1.7 app and this row breaks the app.

https://github.com/getsentry/sentry-python/blob/cc08a6bed116e09db41c712c20ab63eb0a839e41/sentry_sdk/integrations/boto3.py#L36

For versions
boto3==1.7.45
botocore==1.10.84

this throws
`
AttributeError: 'str' object has no attribute 'hyphenize'`

I'm not sure the base of the integrations but I thought they must be enabled in settings, but this part of Boto3Integration is triggered even if we have not enabled it in django settings.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `sentry_sdk/integrations/boto3.py`
Content:
```
1 from __future__ import absolute_import
2 
3 from sentry_sdk import Hub
4 from sentry_sdk.integrations import Integration, DidNotEnable
5 from sentry_sdk.tracing import Span
6 
7 from sentry_sdk._functools import partial
8 from sentry_sdk._types import MYPY
9 
10 if MYPY:
11     from typing import Any
12     from typing import Dict
13     from typing import Optional
14     from typing import Type
15 
16 try:
17     from botocore.client import BaseClient  # type: ignore
18     from botocore.response import StreamingBody  # type: ignore
19     from botocore.awsrequest import AWSRequest  # type: ignore
20 except ImportError:
21     raise DidNotEnable("botocore is not installed")
22 
23 
24 class Boto3Integration(Integration):
25     identifier = "boto3"
26 
27     @staticmethod
28     def setup_once():
29         # type: () -> None
30         orig_init = BaseClient.__init__
31 
32         def sentry_patched_init(self, *args, **kwargs):
33             # type: (Type[BaseClient], *Any, **Any) -> None
34             orig_init(self, *args, **kwargs)
35             meta = self.meta
36             service_id = meta.service_model.service_id.hyphenize()
37             meta.events.register(
38                 "request-created",
39                 partial(_sentry_request_created, service_id=service_id),
40             )
41             meta.events.register("after-call", _sentry_after_call)
42             meta.events.register("after-call-error", _sentry_after_call_error)
43 
44         BaseClient.__init__ = sentry_patched_init
45 
46 
47 def _sentry_request_created(service_id, request, operation_name, **kwargs):
48     # type: (str, AWSRequest, str, **Any) -> None
49     hub = Hub.current
50     if hub.get_integration(Boto3Integration) is None:
51         return
52 
53     description = "aws.%s.%s" % (service_id, operation_name)
54     span = hub.start_span(
55         hub=hub,
56         op="aws.request",
57         description=description,
58     )
59     span.set_tag("aws.service_id", service_id)
60     span.set_tag("aws.operation_name", operation_name)
61     span.set_data("aws.request.url", request.url)
62 
63     # We do it in order for subsequent http calls/retries be
64     # attached to this span.
65     span.__enter__()
66 
67     # request.context is an open-ended data-structure
68     # where we can add anything useful in request life cycle.
69     request.context["_sentrysdk_span"] = span
70 
71 
72 def _sentry_after_call(context, parsed, **kwargs):
73     # type: (Dict[str, Any], Dict[str, Any], **Any) -> None
74     span = context.pop("_sentrysdk_span", None)  # type: Optional[Span]
75 
76     # Span could be absent if the integration is disabled.
77     if span is None:
78         return
79     span.__exit__(None, None, None)
80 
81     body = parsed.get("Body")
82     if not isinstance(body, StreamingBody):
83         return
84 
85     streaming_span = span.start_child(
86         op="aws.request.stream",
87         description=span.description,
88     )
89 
90     orig_read = body.read
91     orig_close = body.close
92 
93     def sentry_streaming_body_read(*args, **kwargs):
94         # type: (*Any, **Any) -> bytes
95         try:
96             ret = orig_read(*args, **kwargs)
97             if not ret:
98                 streaming_span.finish()
99             return ret
100         except Exception:
101             streaming_span.finish()
102             raise
103 
104     body.read = sentry_streaming_body_read
105 
106     def sentry_streaming_body_close(*args, **kwargs):
107         # type: (*Any, **Any) -> None
108         streaming_span.finish()
109         orig_close(*args, **kwargs)
110 
111     body.close = sentry_streaming_body_close
112 
113 
114 def _sentry_after_call_error(context, exception, **kwargs):
115     # type: (Dict[str, Any], Type[BaseException], **Any) -> None
116     span = context.pop("_sentrysdk_span", None)  # type: Optional[Span]
117 
118     # Span could be absent if the integration is disabled.
119     if span is None:
120         return
121     span.__exit__(type(exception), exception, None)
122
```
--- 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/sentry_sdk/integrations/boto3.py b/sentry_sdk/integrations/boto3.py
--- a/sentry_sdk/integrations/boto3.py
+++ b/sentry_sdk/integrations/boto3.py
@@ -14,6 +14,7 @@
     from typing import Type
 
 try:
+    from botocore import __version__ as BOTOCORE_VERSION  # type: ignore
     from botocore.client import BaseClient  # type: ignore
     from botocore.response import StreamingBody  # type: ignore
     from botocore.awsrequest import AWSRequest  # type: ignore
@@ -27,6 +28,14 @@
     @staticmethod
     def setup_once():
         # type: () -> None
+        try:
+            version = tuple(map(int, BOTOCORE_VERSION.split(".")[:3]))
+        except (ValueError, TypeError):
+            raise DidNotEnable(
+                "Unparsable botocore version: {}".format(BOTOCORE_VERSION)
+            )
+        if version < (1, 12):
+            raise DidNotEnable("Botocore 1.12 or newer is required.")
         orig_init = BaseClient.__init__
 
         def sentry_patched_init(self, *args, **kwargs):

{"golden_diff": "diff --git a/sentry_sdk/integrations/boto3.py b/sentry_sdk/integrations/boto3.py\n--- a/sentry_sdk/integrations/boto3.py\n+++ b/sentry_sdk/integrations/boto3.py\n@@ -14,6 +14,7 @@\n     from typing import Type\n \n try:\n+    from botocore import __version__ as BOTOCORE_VERSION  # type: ignore\n     from botocore.client import BaseClient  # type: ignore\n     from botocore.response import StreamingBody  # type: ignore\n     from botocore.awsrequest import AWSRequest  # type: ignore\n@@ -27,6 +28,14 @@\n     @staticmethod\n     def setup_once():\n         # type: () -> None\n+        try:\n+            version = tuple(map(int, BOTOCORE_VERSION.split(\".\")[:3]))\n+        except (ValueError, TypeError):\n+            raise DidNotEnable(\n+                \"Unparsable botocore version: {}\".format(BOTOCORE_VERSION)\n+            )\n+        if version < (1, 12):\n+            raise DidNotEnable(\"Botocore 1.12 or newer is required.\")\n         orig_init = BaseClient.__init__\n \n         def sentry_patched_init(self, *args, **kwargs):\n", "issue": "Not working with older boto version\nHello, we use it in Django==2.1.7 app and this row breaks the app.\r\n\r\nhttps://github.com/getsentry/sentry-python/blob/cc08a6bed116e09db41c712c20ab63eb0a839e41/sentry_sdk/integrations/boto3.py#L36\r\n\r\nFor versions\r\nboto3==1.7.45\r\nbotocore==1.10.84\r\n\r\nthis throws\r\n`\r\nAttributeError: 'str' object has no attribute 'hyphenize'`\r\n\r\nI'm not sure the base of the integrations but I thought they must be enabled in settings, but this part of Boto3Integration is triggered even if we have not enabled it in django settings.\r\n\n", "before_files": [{"content": "from __future__ import absolute_import\n\nfrom sentry_sdk import Hub\nfrom sentry_sdk.integrations import Integration, DidNotEnable\nfrom sentry_sdk.tracing import Span\n\nfrom sentry_sdk._functools import partial\nfrom sentry_sdk._types import MYPY\n\nif MYPY:\n    from typing import Any\n    from typing import Dict\n    from typing import Optional\n    from typing import Type\n\ntry:\n    from botocore.client import BaseClient  # type: ignore\n    from botocore.response import StreamingBody  # type: ignore\n    from botocore.awsrequest import AWSRequest  # type: ignore\nexcept ImportError:\n    raise DidNotEnable(\"botocore is not installed\")\n\n\nclass Boto3Integration(Integration):\n    identifier = \"boto3\"\n\n    @staticmethod\n    def setup_once():\n        # type: () -> None\n        orig_init = BaseClient.__init__\n\n        def sentry_patched_init(self, *args, **kwargs):\n            # type: (Type[BaseClient], *Any, **Any) -> None\n            orig_init(self, *args, **kwargs)\n            meta = self.meta\n            service_id = meta.service_model.service_id.hyphenize()\n            meta.events.register(\n                \"request-created\",\n                partial(_sentry_request_created, service_id=service_id),\n            )\n            meta.events.register(\"after-call\", _sentry_after_call)\n            meta.events.register(\"after-call-error\", _sentry_after_call_error)\n\n        BaseClient.__init__ = sentry_patched_init\n\n\ndef _sentry_request_created(service_id, request, operation_name, **kwargs):\n    # type: (str, AWSRequest, str, **Any) -> None\n    hub = Hub.current\n    if hub.get_integration(Boto3Integration) is None:\n        return\n\n    description = \"aws.%s.%s\" % (service_id, operation_name)\n    span = hub.start_span(\n        hub=hub,\n        op=\"aws.request\",\n        description=description,\n    )\n    span.set_tag(\"aws.service_id\", service_id)\n    span.set_tag(\"aws.operation_name\", operation_name)\n    span.set_data(\"aws.request.url\", request.url)\n\n    # We do it in order for subsequent http calls/retries be\n    # attached to this span.\n    span.__enter__()\n\n    # request.context is an open-ended data-structure\n    # where we can add anything useful in request life cycle.\n    request.context[\"_sentrysdk_span\"] = span\n\n\ndef _sentry_after_call(context, parsed, **kwargs):\n    # type: (Dict[str, Any], Dict[str, Any], **Any) -> None\n    span = context.pop(\"_sentrysdk_span\", None)  # type: Optional[Span]\n\n    # Span could be absent if the integration is disabled.\n    if span is None:\n        return\n    span.__exit__(None, None, None)\n\n    body = parsed.get(\"Body\")\n    if not isinstance(body, StreamingBody):\n        return\n\n    streaming_span = span.start_child(\n        op=\"aws.request.stream\",\n        description=span.description,\n    )\n\n    orig_read = body.read\n    orig_close = body.close\n\n    def sentry_streaming_body_read(*args, **kwargs):\n        # type: (*Any, **Any) -> bytes\n        try:\n            ret = orig_read(*args, **kwargs)\n            if not ret:\n                streaming_span.finish()\n            return ret\n        except Exception:\n            streaming_span.finish()\n            raise\n\n    body.read = sentry_streaming_body_read\n\n    def sentry_streaming_body_close(*args, **kwargs):\n        # type: (*Any, **Any) -> None\n        streaming_span.finish()\n        orig_close(*args, **kwargs)\n\n    body.close = sentry_streaming_body_close\n\n\ndef _sentry_after_call_error(context, exception, **kwargs):\n    # type: (Dict[str, Any], Type[BaseException], **Any) -> None\n    span = context.pop(\"_sentrysdk_span\", None)  # type: Optional[Span]\n\n    # Span could be absent if the integration is disabled.\n    if span is None:\n        return\n    span.__exit__(type(exception), exception, None)\n", "path": "sentry_sdk/integrations/boto3.py"}], "after_files": [{"content": "from __future__ import absolute_import\n\nfrom sentry_sdk import Hub\nfrom sentry_sdk.integrations import Integration, DidNotEnable\nfrom sentry_sdk.tracing import Span\n\nfrom sentry_sdk._functools import partial\nfrom sentry_sdk._types import MYPY\n\nif MYPY:\n    from typing import Any\n    from typing import Dict\n    from typing import Optional\n    from typing import Type\n\ntry:\n    from botocore import __version__ as BOTOCORE_VERSION  # type: ignore\n    from botocore.client import BaseClient  # type: ignore\n    from botocore.response import StreamingBody  # type: ignore\n    from botocore.awsrequest import AWSRequest  # type: ignore\nexcept ImportError:\n    raise DidNotEnable(\"botocore is not installed\")\n\n\nclass Boto3Integration(Integration):\n    identifier = \"boto3\"\n\n    @staticmethod\n    def setup_once():\n        # type: () -> None\n        try:\n            version = tuple(map(int, BOTOCORE_VERSION.split(\".\")[:3]))\n        except (ValueError, TypeError):\n            raise DidNotEnable(\n                \"Unparsable botocore version: {}\".format(BOTOCORE_VERSION)\n            )\n        if version < (1, 12):\n            raise DidNotEnable(\"Botocore 1.12 or newer is required.\")\n        orig_init = BaseClient.__init__\n\n        def sentry_patched_init(self, *args, **kwargs):\n            # type: (Type[BaseClient], *Any, **Any) -> None\n            orig_init(self, *args, **kwargs)\n            meta = self.meta\n            service_id = meta.service_model.service_id.hyphenize()\n            meta.events.register(\n                \"request-created\",\n                partial(_sentry_request_created, service_id=service_id),\n            )\n            meta.events.register(\"after-call\", _sentry_after_call)\n            meta.events.register(\"after-call-error\", _sentry_after_call_error)\n\n        BaseClient.__init__ = sentry_patched_init\n\n\ndef _sentry_request_created(service_id, request, operation_name, **kwargs):\n    # type: (str, AWSRequest, str, **Any) -> None\n    hub = Hub.current\n    if hub.get_integration(Boto3Integration) is None:\n        return\n\n    description = \"aws.%s.%s\" % (service_id, operation_name)\n    span = hub.start_span(\n        hub=hub,\n        op=\"aws.request\",\n        description=description,\n    )\n    span.set_tag(\"aws.service_id\", service_id)\n    span.set_tag(\"aws.operation_name\", operation_name)\n    span.set_data(\"aws.request.url\", request.url)\n\n    # We do it in order for subsequent http calls/retries be\n    # attached to this span.\n    span.__enter__()\n\n    # request.context is an open-ended data-structure\n    # where we can add anything useful in request life cycle.\n    request.context[\"_sentrysdk_span\"] = span\n\n\ndef _sentry_after_call(context, parsed, **kwargs):\n    # type: (Dict[str, Any], Dict[str, Any], **Any) -> None\n    span = context.pop(\"_sentrysdk_span\", None)  # type: Optional[Span]\n\n    # Span could be absent if the integration is disabled.\n    if span is None:\n        return\n    span.__exit__(None, None, None)\n\n    body = parsed.get(\"Body\")\n    if not isinstance(body, StreamingBody):\n        return\n\n    streaming_span = span.start_child(\n        op=\"aws.request.stream\",\n        description=span.description,\n    )\n\n    orig_read = body.read\n    orig_close = body.close\n\n    def sentry_streaming_body_read(*args, **kwargs):\n        # type: (*Any, **Any) -> bytes\n        try:\n            ret = orig_read(*args, **kwargs)\n            if not ret:\n                streaming_span.finish()\n            return ret\n        except Exception:\n            streaming_span.finish()\n            raise\n\n    body.read = sentry_streaming_body_read\n\n    def sentry_streaming_body_close(*args, **kwargs):\n        # type: (*Any, **Any) -> None\n        streaming_span.finish()\n        orig_close(*args, **kwargs)\n\n    body.close = sentry_streaming_body_close\n\n\ndef _sentry_after_call_error(context, exception, **kwargs):\n    # type: (Dict[str, Any], Type[BaseException], **Any) -> None\n    span = context.pop(\"_sentrysdk_span\", None)  # type: Optional[Span]\n\n    # Span could be absent if the integration is disabled.\n    if span is None:\n        return\n    span.__exit__(type(exception), exception, None)\n", "path": "sentry_sdk/integrations/boto3.py"}]}