We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Opentracing contrib: `DroppedSpan has no attribute transaction`
**Describe the bug**: ...
When using Elastic APM with Opentracing, the program crashes on `AttributeError: DroppedSpan object has no attribute transaction` exception if `span.finish()` is called. 
 It turns out to be this line:
https://github.com/elastic/apm-agent-python/blob/74cd1fa56f15a3149b367bd65ea4721a6fd95615/elasticapm/contrib/opentracing/span.py#L129
It will access `transaction` attribute which does NOT exist for `DroppedSpan`.

**Expected behavior**: ...
It should be expected to behave normally even if it's `DroppedSpan`.

**Environment (please complete the following information)**
- OS: [e.g. Linux] Linux
- Python version: 2.7
- Framework and version [e.g. Django 2.1]: N/A
- APM Server version: 7.4.0
- Agent version: 5.3.2




--- 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/contrib/opentracing/span.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 from opentracing.span import Span as OTSpanBase
32 from opentracing.span import SpanContext as OTSpanContextBase
33 
34 from elasticapm import traces
35 from elasticapm.utils import compat, get_url_dict
36 from elasticapm.utils.logging import get_logger
37 
38 try:
39     # opentracing-python 2.1+
40     from opentracing import tags
41     from opentracing import logs as ot_logs
42 except ImportError:
43     # opentracing-python <2.1
44     from opentracing.ext import tags
45 
46     ot_logs = None
47 
48 
49 logger = get_logger("elasticapm.contrib.opentracing")
50 
51 
52 class OTSpan(OTSpanBase):
53     def __init__(self, tracer, context, elastic_apm_ref):
54         super(OTSpan, self).__init__(tracer, context)
55         self.elastic_apm_ref = elastic_apm_ref
56         self.is_transaction = isinstance(elastic_apm_ref, traces.Transaction)
57         if not context.span:
58             context.span = self
59 
60     def log_kv(self, key_values, timestamp=None):
61         exc_type, exc_val, exc_tb = None, None, None
62         if "python.exception.type" in key_values:
63             exc_type = key_values["python.exception.type"]
64             exc_val = key_values.get("python.exception.val")
65             exc_tb = key_values.get("python.exception.tb")
66         elif ot_logs and key_values.get(ot_logs.EVENT) == tags.ERROR:
67             exc_type = key_values[ot_logs.ERROR_KIND]
68             exc_val = key_values.get(ot_logs.ERROR_OBJECT)
69             exc_tb = key_values.get(ot_logs.STACK)
70         else:
71             logger.debug("Can't handle non-exception type opentracing logs")
72         if exc_type:
73             agent = self.tracer._agent
74             agent.capture_exception(exc_info=(exc_type, exc_val, exc_tb))
75         return self
76 
77     def set_operation_name(self, operation_name):
78         self.elastic_apm_ref.name = operation_name
79         return self
80 
81     def set_tag(self, key, value):
82         if self.is_transaction:
83             if key == "type":
84                 self.elastic_apm_ref.transaction_type = value
85             elif key == "result":
86                 self.elastic_apm_ref.result = value
87             elif key == tags.HTTP_STATUS_CODE:
88                 self.elastic_apm_ref.result = "HTTP {}xx".format(compat.text_type(value)[0])
89                 traces.set_context({"status_code": value}, "response")
90             elif key == "user.id":
91                 traces.set_user_context(user_id=value)
92             elif key == "user.username":
93                 traces.set_user_context(username=value)
94             elif key == "user.email":
95                 traces.set_user_context(email=value)
96             elif key == tags.HTTP_URL:
97                 traces.set_context({"url": get_url_dict(value)}, "request")
98             elif key == tags.HTTP_METHOD:
99                 traces.set_context({"method": value}, "request")
100             elif key == tags.COMPONENT:
101                 traces.set_context({"framework": {"name": value}}, "service")
102             else:
103                 self.elastic_apm_ref.label(**{key: value})
104         else:
105             if key.startswith("db."):
106                 span_context = self.elastic_apm_ref.context or {}
107                 if "db" not in span_context:
108                     span_context["db"] = {}
109                 if key == tags.DATABASE_STATEMENT:
110                     span_context["db"]["statement"] = value
111                 elif key == tags.DATABASE_USER:
112                     span_context["db"]["user"] = value
113                 elif key == tags.DATABASE_TYPE:
114                     span_context["db"]["type"] = value
115                     self.elastic_apm_ref.type = "db." + value
116                 else:
117                     self.elastic_apm_ref.label(**{key: value})
118                 self.elastic_apm_ref.context = span_context
119             elif key == tags.SPAN_KIND:
120                 self.elastic_apm_ref.type = value
121             else:
122                 self.elastic_apm_ref.label(**{key: value})
123         return self
124 
125     def finish(self, finish_time=None):
126         if self.is_transaction:
127             self.tracer._agent.end_transaction()
128         else:
129             self.elastic_apm_ref.transaction.end_span()
130 
131 
132 class OTSpanContext(OTSpanContextBase):
133     def __init__(self, trace_parent, span=None):
134         self.trace_parent = trace_parent
135         self.span = span
136
```
--- 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/elasticapm/contrib/opentracing/span.py b/elasticapm/contrib/opentracing/span.py
--- a/elasticapm/contrib/opentracing/span.py
+++ b/elasticapm/contrib/opentracing/span.py
@@ -54,6 +54,7 @@
         super(OTSpan, self).__init__(tracer, context)
         self.elastic_apm_ref = elastic_apm_ref
         self.is_transaction = isinstance(elastic_apm_ref, traces.Transaction)
+        self.is_dropped = isinstance(elastic_apm_ref, traces.DroppedSpan)
         if not context.span:
             context.span = self
 
@@ -101,7 +102,7 @@
                 traces.set_context({"framework": {"name": value}}, "service")
             else:
                 self.elastic_apm_ref.label(**{key: value})
-        else:
+        elif not self.is_dropped:
             if key.startswith("db."):
                 span_context = self.elastic_apm_ref.context or {}
                 if "db" not in span_context:
@@ -125,7 +126,7 @@
     def finish(self, finish_time=None):
         if self.is_transaction:
             self.tracer._agent.end_transaction()
-        else:
+        elif not self.is_dropped:
             self.elastic_apm_ref.transaction.end_span()

{"golden_diff": "diff --git a/elasticapm/contrib/opentracing/span.py b/elasticapm/contrib/opentracing/span.py\n--- a/elasticapm/contrib/opentracing/span.py\n+++ b/elasticapm/contrib/opentracing/span.py\n@@ -54,6 +54,7 @@\n         super(OTSpan, self).__init__(tracer, context)\n         self.elastic_apm_ref = elastic_apm_ref\n         self.is_transaction = isinstance(elastic_apm_ref, traces.Transaction)\n+        self.is_dropped = isinstance(elastic_apm_ref, traces.DroppedSpan)\n         if not context.span:\n             context.span = self\n \n@@ -101,7 +102,7 @@\n                 traces.set_context({\"framework\": {\"name\": value}}, \"service\")\n             else:\n                 self.elastic_apm_ref.label(**{key: value})\n-        else:\n+        elif not self.is_dropped:\n             if key.startswith(\"db.\"):\n                 span_context = self.elastic_apm_ref.context or {}\n                 if \"db\" not in span_context:\n@@ -125,7 +126,7 @@\n     def finish(self, finish_time=None):\n         if self.is_transaction:\n             self.tracer._agent.end_transaction()\n-        else:\n+        elif not self.is_dropped:\n             self.elastic_apm_ref.transaction.end_span()\n", "issue": "Opentracing contrib: `DroppedSpan has no attribute transaction`\n**Describe the bug**: ...\r\nWhen using Elastic APM with Opentracing, the program crashes on `AttributeError: DroppedSpan object has no attribute transaction` exception if `span.finish()` is called. \r\n It turns out to be this line:\r\nhttps://github.com/elastic/apm-agent-python/blob/74cd1fa56f15a3149b367bd65ea4721a6fd95615/elasticapm/contrib/opentracing/span.py#L129\r\nIt will access `transaction` attribute which does NOT exist for `DroppedSpan`.\r\n\r\n**Expected behavior**: ...\r\nIt should be expected to behave normally even if it's `DroppedSpan`.\r\n\r\n**Environment (please complete the following information)**\r\n- OS: [e.g. Linux] Linux\r\n- Python version: 2.7\r\n- Framework and version [e.g. Django 2.1]: N/A\r\n- APM Server version: 7.4.0\r\n- Agent version: 5.3.2\r\n\r\n\r\n\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\nfrom opentracing.span import Span as OTSpanBase\nfrom opentracing.span import SpanContext as OTSpanContextBase\n\nfrom elasticapm import traces\nfrom elasticapm.utils import compat, get_url_dict\nfrom elasticapm.utils.logging import get_logger\n\ntry:\n    # opentracing-python 2.1+\n    from opentracing import tags\n    from opentracing import logs as ot_logs\nexcept ImportError:\n    # opentracing-python <2.1\n    from opentracing.ext import tags\n\n    ot_logs = None\n\n\nlogger = get_logger(\"elasticapm.contrib.opentracing\")\n\n\nclass OTSpan(OTSpanBase):\n    def __init__(self, tracer, context, elastic_apm_ref):\n        super(OTSpan, self).__init__(tracer, context)\n        self.elastic_apm_ref = elastic_apm_ref\n        self.is_transaction = isinstance(elastic_apm_ref, traces.Transaction)\n        if not context.span:\n            context.span = self\n\n    def log_kv(self, key_values, timestamp=None):\n        exc_type, exc_val, exc_tb = None, None, None\n        if \"python.exception.type\" in key_values:\n            exc_type = key_values[\"python.exception.type\"]\n            exc_val = key_values.get(\"python.exception.val\")\n            exc_tb = key_values.get(\"python.exception.tb\")\n        elif ot_logs and key_values.get(ot_logs.EVENT) == tags.ERROR:\n            exc_type = key_values[ot_logs.ERROR_KIND]\n            exc_val = key_values.get(ot_logs.ERROR_OBJECT)\n            exc_tb = key_values.get(ot_logs.STACK)\n        else:\n            logger.debug(\"Can't handle non-exception type opentracing logs\")\n        if exc_type:\n            agent = self.tracer._agent\n            agent.capture_exception(exc_info=(exc_type, exc_val, exc_tb))\n        return self\n\n    def set_operation_name(self, operation_name):\n        self.elastic_apm_ref.name = operation_name\n        return self\n\n    def set_tag(self, key, value):\n        if self.is_transaction:\n            if key == \"type\":\n                self.elastic_apm_ref.transaction_type = value\n            elif key == \"result\":\n                self.elastic_apm_ref.result = value\n            elif key == tags.HTTP_STATUS_CODE:\n                self.elastic_apm_ref.result = \"HTTP {}xx\".format(compat.text_type(value)[0])\n                traces.set_context({\"status_code\": value}, \"response\")\n            elif key == \"user.id\":\n                traces.set_user_context(user_id=value)\n            elif key == \"user.username\":\n                traces.set_user_context(username=value)\n            elif key == \"user.email\":\n                traces.set_user_context(email=value)\n            elif key == tags.HTTP_URL:\n                traces.set_context({\"url\": get_url_dict(value)}, \"request\")\n            elif key == tags.HTTP_METHOD:\n                traces.set_context({\"method\": value}, \"request\")\n            elif key == tags.COMPONENT:\n                traces.set_context({\"framework\": {\"name\": value}}, \"service\")\n            else:\n                self.elastic_apm_ref.label(**{key: value})\n        else:\n            if key.startswith(\"db.\"):\n                span_context = self.elastic_apm_ref.context or {}\n                if \"db\" not in span_context:\n                    span_context[\"db\"] = {}\n                if key == tags.DATABASE_STATEMENT:\n                    span_context[\"db\"][\"statement\"] = value\n                elif key == tags.DATABASE_USER:\n                    span_context[\"db\"][\"user\"] = value\n                elif key == tags.DATABASE_TYPE:\n                    span_context[\"db\"][\"type\"] = value\n                    self.elastic_apm_ref.type = \"db.\" + value\n                else:\n                    self.elastic_apm_ref.label(**{key: value})\n                self.elastic_apm_ref.context = span_context\n            elif key == tags.SPAN_KIND:\n                self.elastic_apm_ref.type = value\n            else:\n                self.elastic_apm_ref.label(**{key: value})\n        return self\n\n    def finish(self, finish_time=None):\n        if self.is_transaction:\n            self.tracer._agent.end_transaction()\n        else:\n            self.elastic_apm_ref.transaction.end_span()\n\n\nclass OTSpanContext(OTSpanContextBase):\n    def __init__(self, trace_parent, span=None):\n        self.trace_parent = trace_parent\n        self.span = span\n", "path": "elasticapm/contrib/opentracing/span.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\nfrom opentracing.span import Span as OTSpanBase\nfrom opentracing.span import SpanContext as OTSpanContextBase\n\nfrom elasticapm import traces\nfrom elasticapm.utils import compat, get_url_dict\nfrom elasticapm.utils.logging import get_logger\n\ntry:\n    # opentracing-python 2.1+\n    from opentracing import tags\n    from opentracing import logs as ot_logs\nexcept ImportError:\n    # opentracing-python <2.1\n    from opentracing.ext import tags\n\n    ot_logs = None\n\n\nlogger = get_logger(\"elasticapm.contrib.opentracing\")\n\n\nclass OTSpan(OTSpanBase):\n    def __init__(self, tracer, context, elastic_apm_ref):\n        super(OTSpan, self).__init__(tracer, context)\n        self.elastic_apm_ref = elastic_apm_ref\n        self.is_transaction = isinstance(elastic_apm_ref, traces.Transaction)\n        self.is_dropped = isinstance(elastic_apm_ref, traces.DroppedSpan)\n        if not context.span:\n            context.span = self\n\n    def log_kv(self, key_values, timestamp=None):\n        exc_type, exc_val, exc_tb = None, None, None\n        if \"python.exception.type\" in key_values:\n            exc_type = key_values[\"python.exception.type\"]\n            exc_val = key_values.get(\"python.exception.val\")\n            exc_tb = key_values.get(\"python.exception.tb\")\n        elif ot_logs and key_values.get(ot_logs.EVENT) == tags.ERROR:\n            exc_type = key_values[ot_logs.ERROR_KIND]\n            exc_val = key_values.get(ot_logs.ERROR_OBJECT)\n            exc_tb = key_values.get(ot_logs.STACK)\n        else:\n            logger.debug(\"Can't handle non-exception type opentracing logs\")\n        if exc_type:\n            agent = self.tracer._agent\n            agent.capture_exception(exc_info=(exc_type, exc_val, exc_tb))\n        return self\n\n    def set_operation_name(self, operation_name):\n        self.elastic_apm_ref.name = operation_name\n        return self\n\n    def set_tag(self, key, value):\n        if self.is_transaction:\n            if key == \"type\":\n                self.elastic_apm_ref.transaction_type = value\n            elif key == \"result\":\n                self.elastic_apm_ref.result = value\n            elif key == tags.HTTP_STATUS_CODE:\n                self.elastic_apm_ref.result = \"HTTP {}xx\".format(compat.text_type(value)[0])\n                traces.set_context({\"status_code\": value}, \"response\")\n            elif key == \"user.id\":\n                traces.set_user_context(user_id=value)\n            elif key == \"user.username\":\n                traces.set_user_context(username=value)\n            elif key == \"user.email\":\n                traces.set_user_context(email=value)\n            elif key == tags.HTTP_URL:\n                traces.set_context({\"url\": get_url_dict(value)}, \"request\")\n            elif key == tags.HTTP_METHOD:\n                traces.set_context({\"method\": value}, \"request\")\n            elif key == tags.COMPONENT:\n                traces.set_context({\"framework\": {\"name\": value}}, \"service\")\n            else:\n                self.elastic_apm_ref.label(**{key: value})\n        elif not self.is_dropped:\n            if key.startswith(\"db.\"):\n                span_context = self.elastic_apm_ref.context or {}\n                if \"db\" not in span_context:\n                    span_context[\"db\"] = {}\n                if key == tags.DATABASE_STATEMENT:\n                    span_context[\"db\"][\"statement\"] = value\n                elif key == tags.DATABASE_USER:\n                    span_context[\"db\"][\"user\"] = value\n                elif key == tags.DATABASE_TYPE:\n                    span_context[\"db\"][\"type\"] = value\n                    self.elastic_apm_ref.type = \"db.\" + value\n                else:\n                    self.elastic_apm_ref.label(**{key: value})\n                self.elastic_apm_ref.context = span_context\n            elif key == tags.SPAN_KIND:\n                self.elastic_apm_ref.type = value\n            else:\n                self.elastic_apm_ref.label(**{key: value})\n        return self\n\n    def finish(self, finish_time=None):\n        if self.is_transaction:\n            self.tracer._agent.end_transaction()\n        elif not self.is_dropped:\n            self.elastic_apm_ref.transaction.end_span()\n\n\nclass OTSpanContext(OTSpanContextBase):\n    def __init__(self, trace_parent, span=None):\n        self.trace_parent = trace_parent\n        self.span = span\n", "path": "elasticapm/contrib/opentracing/span.py"}]}