We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
The aws_lambda patch module can't handle callable object event handlers
### Summary of problem
NOTE: I'll be cross-posting this in [datadog-lambda-python](https://github.com/DataDog/datadog-lambda-python) project because I'm not sure whether this is in the domain of the Lambda layer or this library itself. This could either be solved in this library by checking if the function being wrapped is a callable class instance, or by the Lambda layer passing the `__call__` function reference instead of the callable class.

The issue stems from ddtrace (specifically the `aws_lambda` module) trying to patch the outer most Lambda handler functions, which can be any callable that accepts an event and context arguments, so either a standard function or a callable object, like a class instance that defines `__call__`.

The internal wrapper is passed the class instance itself instead of the `__call__` function however, so as a result, it fails as a class instance isn't going to have a `__code__` property. The `patch_all` the Lambda layer is executing is in a try/catch block and only runs once, so there's no errors being raised, but on every cold start there's a stack trace popping up in the logs. 

Platform: AWS Lambda
Runtime: Python 3.9
Serverless Framework Version: 3.28.0
Serverless Plugin Version: 5.20.0
DataDog Lambda Layer Version: 68
ddtrace Version: 1.9.1

### Which version of dd-trace-py are you using?
ddtrace@1.9.1

### Which version of pip are you using?
pip@21.2.4

### Which libraries and their versions are you using?
Output of `poetry show --no-dev`, excluded some internal libraries irrelevant to this and added `chalice` since that's packaged manually using their internal packaging mechanism.

attrs@21.4.0
aws-lambda-context@1.1.0
boto3@1.24.57
botocore@1.27.57
bytecode@0.14.0
cattrs@22.2.0
certifi@2022.6.15
charset-normalizer@2.1.1
ddsketch@2.0.4
ddtrace@1.9.1
elasticsearch@7.9.1
elasticsearch-dsl@7.3.0
envier@0.4.0
exceptiongroup@1.1.0
idna@3.3
jmespath@0.10.0
jsonschema@3.2.0
marshmallow@3.15.0
packaging@23.0
protobuf@4.22.0
pyrsistent@0.18.1
python-dateutil@2.8.2
python-json-logger@2.0.4
requests@2.28.1
requests-aws4auth@1.0
s3transfer@0.6.0
simplejson@3.17.2
six@1.16.0
tenacity@8.2.2
typing-extensions@4.3.0
urllib3@1.26.12
xmltodict@0.13.0
chalice@1.26.6

### How can we reproduce your problem?
Deploy a lambda function where the event handler is a callable class instance, whether through the [chalice](https://github.com/aws/chalice) framework or something like this:
```
# app.py
from typing import Any

class LambdaHandler:
    def __call__(self, event: dict[str, Any], context: Any) -> dict[str, Any]:
        return {"hello": "world"}

app = LambdaHandler()
```
With `app.app` being the handler of the lambda function.

### What is the result that you get?
The following stack trace is thrown on every cold start as `patch_all` fails:
```
Traceback (most recent call last):
	File "/opt/python/lib/python3.9/site-packages/datadog_lambda/wrapper.py", line 177, in __init__
		patch_all()
	File "/opt/python/lib/python3.9/site-packages/datadog_lambda/patch.py", line 36, in patch_all
		patch_all_dd()
	File "/var/task/ddtrace/_monkey.py", line 190, in patch_all
		patch(raise_errors=False, **modules)
	File "/var/task/ddtrace/_monkey.py", line 230, in patch
		when_imported(module)(_on_import_factory(contrib, raise_errors=False))
	File "/var/task/ddtrace/vendor/wrapt/importer.py", line 284, in register
		register_post_import_hook(hook, name)
	File "/var/task/ddtrace/vendor/wrapt/decorators.py", line 470, in _synchronized
		return wrapped(*args, **kwargs)
	File "/var/task/ddtrace/vendor/wrapt/importer.py", line 82, in register_post_import_hook
		hook(module)
	File "/var/task/ddtrace/_monkey.py", line 155, in on_import
		imported_module.patch()
	File "/var/task/ddtrace/contrib/aws_lambda/patch.py", line 156, in patch
		wrap(handler, wrapper)
	File "/var/task/ddtrace/internal/wrapping.py", line 428, in wrap
		f.__code__,
	AttributeError: 'EventSourceHandler' object has no attribute '__code__'
```

### What is the result that you expected?
This operation should not fail, and either [ddtrace.internal.wrapping.wrap](https://github.com/DataDog/dd-trace-py/blob/1.x/ddtrace/internal/wrapping.py#L417) should account for callable classes, or `datadog_lambda` should be figuring out that the handler is a callable class instance and passing the `__call__` function reference instead of the class instance.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `ddtrace/contrib/aws_lambda/patch.py`
Content:
```
1 from importlib import import_module
2 import os
3 import signal
4 
5 from ddtrace import tracer
6 from ddtrace.constants import ERROR_MSG
7 from ddtrace.constants import ERROR_TYPE
8 from ddtrace.internal.logger import get_logger
9 from ddtrace.internal.serverless import in_aws_lambda
10 from ddtrace.internal.utils import get_argument_value
11 from ddtrace.internal.wrapping import unwrap
12 from ddtrace.internal.wrapping import wrap
13 
14 
15 log = get_logger(__name__)
16 
17 
18 class TimeoutChannel:
19     def __init__(self, context):
20         self.crashed = False
21         self.context = context
22 
23     def _handle_signal(self, sig, f):
24         """
25         Returns a signal of type `sig` with function `f`, if there are
26         no previously defined signals.
27 
28         Else, wraps the given signal with the previously defined one,
29         so no signals are overridden.
30         """
31         old_signal = signal.getsignal(sig)
32 
33         def wrap_signals(*args, **kwargs):
34             if old_signal is not None:
35                 old_signal(*args, **kwargs)
36             f(*args, **kwargs)
37 
38         # Return the incoming signal if any of the following cases happens:
39         # - old signal does not exist,
40         # - old signal is the same as the incoming, or
41         # - old signal is our wrapper.
42         # This avoids multiple signal calling and infinite wrapping.
43         if not callable(old_signal) or old_signal == f or old_signal == wrap_signals:
44             return signal.signal(sig, f)
45 
46         return signal.signal(sig, wrap_signals)
47 
48     def _start(self):
49         self._handle_signal(signal.SIGALRM, self._crash_flush)
50 
51         remaining_time_in_millis = self.context.get_remaining_time_in_millis()
52         apm_flush_deadline = int(os.environ.get("DD_APM_FLUSH_DEADLINE_MILLISECONDS", 100))
53         apm_flush_deadline = 100 if apm_flush_deadline < 0 else apm_flush_deadline
54 
55         # TODO: Update logic to calculate an approximate of how long it will
56         # take us to flush the spans on the queue.
57         remaining_time_in_seconds = max(((remaining_time_in_millis - apm_flush_deadline) / 1000), 0)
58         signal.setitimer(signal.ITIMER_REAL, remaining_time_in_seconds)
59 
60     def _crash_flush(self, _, __):
61         """
62         Tags the current root span with an Impending Timeout error.
63         Finishes spans with ancestors from the current span.
64         """
65         self._remove_alarm_signal()
66         self.crashed = True
67 
68         root_span = tracer.current_root_span()
69         if root_span is not None:
70             root_span.error = 1
71             root_span.set_tag_str(ERROR_MSG, "Datadog detected an Impending Timeout")
72             root_span.set_tag_str(ERROR_TYPE, "Impending Timeout")
73         else:
74             log.warning("An impending timeout was reached, but no root span was found. No error will be tagged.")
75 
76         current_span = tracer.current_span()
77         if current_span is not None:
78             current_span.finish_with_ancestors()
79 
80     def _remove_alarm_signal(self):
81         """Removes the handler set for the signal `SIGALRM`."""
82         signal.alarm(0)
83         signal.signal(signal.SIGALRM, signal.SIG_DFL)
84 
85     def stop(self):
86         self._remove_alarm_signal()
87 
88 
89 class DatadogInstrumentation(object):
90     """Patches an AWS Lambda handler function for Datadog instrumentation."""
91 
92     def __call__(self, func, args, kwargs):
93         self.func = func
94         self._before(args, kwargs)
95         try:
96             self.response = self.func(*args, **kwargs)
97             return self.response
98         except Exception:
99             raise
100         finally:
101             self._after()
102 
103     def _set_context(self, args, kwargs):
104         """Sets the context attribute."""
105         # The context is the second argument in a handler
106         # signature and it is always sent.
107         #
108         # note: AWS Lambda context is an object, the event is a dict.
109         # `get_remaining_time_in_millis` is guaranteed to be
110         # present in the context.
111         _context = get_argument_value(args, kwargs, 1, "context")
112         if hasattr(_context, "get_remaining_time_in_millis"):
113             self.context = _context
114         else:
115             # Handler was possibly manually wrapped, and the first
116             # argument is the `datadog-lambda` decorator object.
117             self.context = get_argument_value(args, kwargs, 2, "context")
118 
119     def _before(self, args, kwargs):
120         self._set_context(args, kwargs)
121         self.timeoutChannel = TimeoutChannel(self.context)
122 
123         self.timeoutChannel._start()
124 
125     def _after(self):
126         if not self.timeoutChannel.crashed:
127             self.timeoutChannel.stop()
128 
129 
130 def _modify_module_name(module_name):
131     """Returns a valid modified module to get imported."""
132     return ".".join(module_name.split("/"))
133 
134 
135 def _get_handler_and_module():
136     """Returns the user AWS Lambda handler and module."""
137     path = os.environ.get("DD_LAMBDA_HANDLER", None)
138     _datadog_instrumentation = DatadogInstrumentation()
139 
140     if path is None:
141         from datadog_lambda.wrapper import datadog_lambda_wrapper
142 
143         handler = getattr(datadog_lambda_wrapper, "__call__")
144 
145         return handler, datadog_lambda_wrapper, _datadog_instrumentation
146     else:
147         parts = path.rsplit(".", 1)
148         (mod_name, handler_name) = parts
149         modified_mod_name = _modify_module_name(mod_name)
150         handler_module = import_module(modified_mod_name)
151         handler = getattr(handler_module, handler_name)
152         return handler, handler_module, _datadog_instrumentation
153 
154 
155 def _has_patch_module():
156     """
157     Ensures that the `aws_lambda` integration can be patched.
158 
159     It checks either the user has the DD_LAMBDA_HANDLER set correctly.
160     Or if the `datadog_lambda` package is installed.
161     """
162     path = os.environ.get("DD_LAMBDA_HANDLER", None)
163     if path is None:
164         try:
165             import_module("datadog_lambda.wrapper")
166         except Exception:
167             return False
168     else:
169         parts = path.rsplit(".", 1)
170         if len(parts) != 2:
171             return False
172     return True
173 
174 
175 def patch():
176     """Patches an AWS Lambda using the `datadog-lambda-py` Lambda layer."""
177 
178     # It's expected to only patch only in AWS Lambda environments.
179     # The need to check if a patch module exists is to avoid patching
180     # when `ddtrace` is present but not `datadog-lambda`.
181     if not in_aws_lambda() and not _has_patch_module():
182         return
183 
184     try:
185         handler, handler_module, wrapper = _get_handler_and_module()
186 
187         if getattr(handler_module, "_datadog_patch", False):
188             return
189 
190         wrap(handler, wrapper)
191 
192         setattr(handler_module, "_datadog_patch", True)
193     except AttributeError:
194         # User code might contain `ddtrace.patch_all()` or `ddtrace.patch(aws_lambda=True)`
195         # which might cause a circular dependency. Skipping.
196         return
197     except Exception:
198         log.exception("Error patching AWS Lambda")
199         return
200 
201 
202 def unpatch():
203     if not in_aws_lambda() and not _has_patch_module():
204         return
205 
206     try:
207         handler, handler_module, wrapper = _get_handler_and_module()
208 
209         if not getattr(handler_module, "_datadog_patch", False):
210             return
211 
212         unwrap(handler, wrapper)
213 
214         setattr(handler_module, "_datadog_patch", False)
215     except AttributeError:
216         return
217     except Exception:
218         log.exception("Error unpatching AWS Lambda")
219         return
220
```
--- 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/ddtrace/contrib/aws_lambda/patch.py b/ddtrace/contrib/aws_lambda/patch.py
--- a/ddtrace/contrib/aws_lambda/patch.py
+++ b/ddtrace/contrib/aws_lambda/patch.py
@@ -140,16 +140,42 @@
     if path is None:
         from datadog_lambda.wrapper import datadog_lambda_wrapper
 
-        handler = getattr(datadog_lambda_wrapper, "__call__")
+        wrapper_module = datadog_lambda_wrapper
+        wrapper_handler = getattr(datadog_lambda_wrapper, "__call__")
 
-        return handler, datadog_lambda_wrapper, _datadog_instrumentation
+        return wrapper_handler, wrapper_module, _datadog_instrumentation
     else:
         parts = path.rsplit(".", 1)
         (mod_name, handler_name) = parts
         modified_mod_name = _modify_module_name(mod_name)
         handler_module = import_module(modified_mod_name)
         handler = getattr(handler_module, handler_name)
-        return handler, handler_module, _datadog_instrumentation
+
+        if callable(handler):
+            class_name = type(handler).__name__
+            is_function = not isinstance(handler, type) and hasattr(handler, "__code__") and class_name == "function"
+            # handler is a function
+            #
+            # note: this is a best effort to identify function based handlers
+            # this will not cover all cases
+            if is_function:
+                return handler, handler_module, _datadog_instrumentation
+
+            # handler must be either a class or an instance of a class
+            #
+            # note: if handler is a class instance with `__code__` defined,
+            # we will prioritize the `__call__` method, ignoring `__code__`.
+            class_module = getattr(handler_module, class_name)
+            class_handler = getattr(class_module, "__call__")
+
+            if isinstance(handler, type):
+                # class handler is a metaclass
+                if hasattr(class_handler, "__func__"):
+                    class_handler = class_handler.__func__
+
+            return class_handler, class_module, _datadog_instrumentation
+        else:
+            raise TypeError("Handler type is not supported to patch.")
 
 
 def _has_patch_module():

{"golden_diff": "diff --git a/ddtrace/contrib/aws_lambda/patch.py b/ddtrace/contrib/aws_lambda/patch.py\n--- a/ddtrace/contrib/aws_lambda/patch.py\n+++ b/ddtrace/contrib/aws_lambda/patch.py\n@@ -140,16 +140,42 @@\n     if path is None:\n         from datadog_lambda.wrapper import datadog_lambda_wrapper\n \n-        handler = getattr(datadog_lambda_wrapper, \"__call__\")\n+        wrapper_module = datadog_lambda_wrapper\n+        wrapper_handler = getattr(datadog_lambda_wrapper, \"__call__\")\n \n-        return handler, datadog_lambda_wrapper, _datadog_instrumentation\n+        return wrapper_handler, wrapper_module, _datadog_instrumentation\n     else:\n         parts = path.rsplit(\".\", 1)\n         (mod_name, handler_name) = parts\n         modified_mod_name = _modify_module_name(mod_name)\n         handler_module = import_module(modified_mod_name)\n         handler = getattr(handler_module, handler_name)\n-        return handler, handler_module, _datadog_instrumentation\n+\n+        if callable(handler):\n+            class_name = type(handler).__name__\n+            is_function = not isinstance(handler, type) and hasattr(handler, \"__code__\") and class_name == \"function\"\n+            # handler is a function\n+            #\n+            # note: this is a best effort to identify function based handlers\n+            # this will not cover all cases\n+            if is_function:\n+                return handler, handler_module, _datadog_instrumentation\n+\n+            # handler must be either a class or an instance of a class\n+            #\n+            # note: if handler is a class instance with `__code__` defined,\n+            # we will prioritize the `__call__` method, ignoring `__code__`.\n+            class_module = getattr(handler_module, class_name)\n+            class_handler = getattr(class_module, \"__call__\")\n+\n+            if isinstance(handler, type):\n+                # class handler is a metaclass\n+                if hasattr(class_handler, \"__func__\"):\n+                    class_handler = class_handler.__func__\n+\n+            return class_handler, class_module, _datadog_instrumentation\n+        else:\n+            raise TypeError(\"Handler type is not supported to patch.\")\n \n \n def _has_patch_module():\n", "issue": "The aws_lambda patch module can't handle callable object event handlers\n### Summary of problem\r\nNOTE: I'll be cross-posting this in [datadog-lambda-python](https://github.com/DataDog/datadog-lambda-python) project because I'm not sure whether this is in the domain of the Lambda layer or this library itself. This could either be solved in this library by checking if the function being wrapped is a callable class instance, or by the Lambda layer passing the `__call__` function reference instead of the callable class.\r\n\r\nThe issue stems from ddtrace (specifically the `aws_lambda` module) trying to patch the outer most Lambda handler functions, which can be any callable that accepts an event and context arguments, so either a standard function or a callable object, like a class instance that defines `__call__`.\r\n\r\nThe internal wrapper is passed the class instance itself instead of the `__call__` function however, so as a result, it fails as a class instance isn't going to have a `__code__` property. The `patch_all` the Lambda layer is executing is in a try/catch block and only runs once, so there's no errors being raised, but on every cold start there's a stack trace popping up in the logs. \r\n\r\nPlatform: AWS Lambda\r\nRuntime: Python 3.9\r\nServerless Framework Version: 3.28.0\r\nServerless Plugin Version: 5.20.0\r\nDataDog Lambda Layer Version: 68\r\nddtrace Version: 1.9.1\r\n\r\n### Which version of dd-trace-py are you using?\r\nddtrace@1.9.1\r\n\r\n### Which version of pip are you using?\r\npip@21.2.4\r\n\r\n### Which libraries and their versions are you using?\r\nOutput of `poetry show --no-dev`, excluded some internal libraries irrelevant to this and added `chalice` since that's packaged manually using their internal packaging mechanism.\r\n\r\nattrs@21.4.0\r\naws-lambda-context@1.1.0\r\nboto3@1.24.57\r\nbotocore@1.27.57\r\nbytecode@0.14.0\r\ncattrs@22.2.0\r\ncertifi@2022.6.15\r\ncharset-normalizer@2.1.1\r\nddsketch@2.0.4\r\nddtrace@1.9.1\r\nelasticsearch@7.9.1\r\nelasticsearch-dsl@7.3.0\r\nenvier@0.4.0\r\nexceptiongroup@1.1.0\r\nidna@3.3\r\njmespath@0.10.0\r\njsonschema@3.2.0\r\nmarshmallow@3.15.0\r\npackaging@23.0\r\nprotobuf@4.22.0\r\npyrsistent@0.18.1\r\npython-dateutil@2.8.2\r\npython-json-logger@2.0.4\r\nrequests@2.28.1\r\nrequests-aws4auth@1.0\r\ns3transfer@0.6.0\r\nsimplejson@3.17.2\r\nsix@1.16.0\r\ntenacity@8.2.2\r\ntyping-extensions@4.3.0\r\nurllib3@1.26.12\r\nxmltodict@0.13.0\r\nchalice@1.26.6\r\n\r\n### How can we reproduce your problem?\r\nDeploy a lambda function where the event handler is a callable class instance, whether through the [chalice](https://github.com/aws/chalice) framework or something like this:\r\n```\r\n# app.py\r\nfrom typing import Any\r\n\r\nclass LambdaHandler:\r\n    def __call__(self, event: dict[str, Any], context: Any) -> dict[str, Any]:\r\n        return {\"hello\": \"world\"}\r\n\r\napp = LambdaHandler()\r\n```\r\nWith `app.app` being the handler of the lambda function.\r\n\r\n### What is the result that you get?\r\nThe following stack trace is thrown on every cold start as `patch_all` fails:\r\n```\r\nTraceback (most recent call last):\r\n\tFile \"/opt/python/lib/python3.9/site-packages/datadog_lambda/wrapper.py\", line 177, in __init__\r\n\t\tpatch_all()\r\n\tFile \"/opt/python/lib/python3.9/site-packages/datadog_lambda/patch.py\", line 36, in patch_all\r\n\t\tpatch_all_dd()\r\n\tFile \"/var/task/ddtrace/_monkey.py\", line 190, in patch_all\r\n\t\tpatch(raise_errors=False, **modules)\r\n\tFile \"/var/task/ddtrace/_monkey.py\", line 230, in patch\r\n\t\twhen_imported(module)(_on_import_factory(contrib, raise_errors=False))\r\n\tFile \"/var/task/ddtrace/vendor/wrapt/importer.py\", line 284, in register\r\n\t\tregister_post_import_hook(hook, name)\r\n\tFile \"/var/task/ddtrace/vendor/wrapt/decorators.py\", line 470, in _synchronized\r\n\t\treturn wrapped(*args, **kwargs)\r\n\tFile \"/var/task/ddtrace/vendor/wrapt/importer.py\", line 82, in register_post_import_hook\r\n\t\thook(module)\r\n\tFile \"/var/task/ddtrace/_monkey.py\", line 155, in on_import\r\n\t\timported_module.patch()\r\n\tFile \"/var/task/ddtrace/contrib/aws_lambda/patch.py\", line 156, in patch\r\n\t\twrap(handler, wrapper)\r\n\tFile \"/var/task/ddtrace/internal/wrapping.py\", line 428, in wrap\r\n\t\tf.__code__,\r\n\tAttributeError: 'EventSourceHandler' object has no attribute '__code__'\r\n```\r\n\r\n### What is the result that you expected?\r\nThis operation should not fail, and either [ddtrace.internal.wrapping.wrap](https://github.com/DataDog/dd-trace-py/blob/1.x/ddtrace/internal/wrapping.py#L417) should account for callable classes, or `datadog_lambda` should be figuring out that the handler is a callable class instance and passing the `__call__` function reference instead of the class instance.\r\n\n", "before_files": [{"content": "from importlib import import_module\nimport os\nimport signal\n\nfrom ddtrace import tracer\nfrom ddtrace.constants import ERROR_MSG\nfrom ddtrace.constants import ERROR_TYPE\nfrom ddtrace.internal.logger import get_logger\nfrom ddtrace.internal.serverless import in_aws_lambda\nfrom ddtrace.internal.utils import get_argument_value\nfrom ddtrace.internal.wrapping import unwrap\nfrom ddtrace.internal.wrapping import wrap\n\n\nlog = get_logger(__name__)\n\n\nclass TimeoutChannel:\n    def __init__(self, context):\n        self.crashed = False\n        self.context = context\n\n    def _handle_signal(self, sig, f):\n        \"\"\"\n        Returns a signal of type `sig` with function `f`, if there are\n        no previously defined signals.\n\n        Else, wraps the given signal with the previously defined one,\n        so no signals are overridden.\n        \"\"\"\n        old_signal = signal.getsignal(sig)\n\n        def wrap_signals(*args, **kwargs):\n            if old_signal is not None:\n                old_signal(*args, **kwargs)\n            f(*args, **kwargs)\n\n        # Return the incoming signal if any of the following cases happens:\n        # - old signal does not exist,\n        # - old signal is the same as the incoming, or\n        # - old signal is our wrapper.\n        # This avoids multiple signal calling and infinite wrapping.\n        if not callable(old_signal) or old_signal == f or old_signal == wrap_signals:\n            return signal.signal(sig, f)\n\n        return signal.signal(sig, wrap_signals)\n\n    def _start(self):\n        self._handle_signal(signal.SIGALRM, self._crash_flush)\n\n        remaining_time_in_millis = self.context.get_remaining_time_in_millis()\n        apm_flush_deadline = int(os.environ.get(\"DD_APM_FLUSH_DEADLINE_MILLISECONDS\", 100))\n        apm_flush_deadline = 100 if apm_flush_deadline < 0 else apm_flush_deadline\n\n        # TODO: Update logic to calculate an approximate of how long it will\n        # take us to flush the spans on the queue.\n        remaining_time_in_seconds = max(((remaining_time_in_millis - apm_flush_deadline) / 1000), 0)\n        signal.setitimer(signal.ITIMER_REAL, remaining_time_in_seconds)\n\n    def _crash_flush(self, _, __):\n        \"\"\"\n        Tags the current root span with an Impending Timeout error.\n        Finishes spans with ancestors from the current span.\n        \"\"\"\n        self._remove_alarm_signal()\n        self.crashed = True\n\n        root_span = tracer.current_root_span()\n        if root_span is not None:\n            root_span.error = 1\n            root_span.set_tag_str(ERROR_MSG, \"Datadog detected an Impending Timeout\")\n            root_span.set_tag_str(ERROR_TYPE, \"Impending Timeout\")\n        else:\n            log.warning(\"An impending timeout was reached, but no root span was found. No error will be tagged.\")\n\n        current_span = tracer.current_span()\n        if current_span is not None:\n            current_span.finish_with_ancestors()\n\n    def _remove_alarm_signal(self):\n        \"\"\"Removes the handler set for the signal `SIGALRM`.\"\"\"\n        signal.alarm(0)\n        signal.signal(signal.SIGALRM, signal.SIG_DFL)\n\n    def stop(self):\n        self._remove_alarm_signal()\n\n\nclass DatadogInstrumentation(object):\n    \"\"\"Patches an AWS Lambda handler function for Datadog instrumentation.\"\"\"\n\n    def __call__(self, func, args, kwargs):\n        self.func = func\n        self._before(args, kwargs)\n        try:\n            self.response = self.func(*args, **kwargs)\n            return self.response\n        except Exception:\n            raise\n        finally:\n            self._after()\n\n    def _set_context(self, args, kwargs):\n        \"\"\"Sets the context attribute.\"\"\"\n        # The context is the second argument in a handler\n        # signature and it is always sent.\n        #\n        # note: AWS Lambda context is an object, the event is a dict.\n        # `get_remaining_time_in_millis` is guaranteed to be\n        # present in the context.\n        _context = get_argument_value(args, kwargs, 1, \"context\")\n        if hasattr(_context, \"get_remaining_time_in_millis\"):\n            self.context = _context\n        else:\n            # Handler was possibly manually wrapped, and the first\n            # argument is the `datadog-lambda` decorator object.\n            self.context = get_argument_value(args, kwargs, 2, \"context\")\n\n    def _before(self, args, kwargs):\n        self._set_context(args, kwargs)\n        self.timeoutChannel = TimeoutChannel(self.context)\n\n        self.timeoutChannel._start()\n\n    def _after(self):\n        if not self.timeoutChannel.crashed:\n            self.timeoutChannel.stop()\n\n\ndef _modify_module_name(module_name):\n    \"\"\"Returns a valid modified module to get imported.\"\"\"\n    return \".\".join(module_name.split(\"/\"))\n\n\ndef _get_handler_and_module():\n    \"\"\"Returns the user AWS Lambda handler and module.\"\"\"\n    path = os.environ.get(\"DD_LAMBDA_HANDLER\", None)\n    _datadog_instrumentation = DatadogInstrumentation()\n\n    if path is None:\n        from datadog_lambda.wrapper import datadog_lambda_wrapper\n\n        handler = getattr(datadog_lambda_wrapper, \"__call__\")\n\n        return handler, datadog_lambda_wrapper, _datadog_instrumentation\n    else:\n        parts = path.rsplit(\".\", 1)\n        (mod_name, handler_name) = parts\n        modified_mod_name = _modify_module_name(mod_name)\n        handler_module = import_module(modified_mod_name)\n        handler = getattr(handler_module, handler_name)\n        return handler, handler_module, _datadog_instrumentation\n\n\ndef _has_patch_module():\n    \"\"\"\n    Ensures that the `aws_lambda` integration can be patched.\n\n    It checks either the user has the DD_LAMBDA_HANDLER set correctly.\n    Or if the `datadog_lambda` package is installed.\n    \"\"\"\n    path = os.environ.get(\"DD_LAMBDA_HANDLER\", None)\n    if path is None:\n        try:\n            import_module(\"datadog_lambda.wrapper\")\n        except Exception:\n            return False\n    else:\n        parts = path.rsplit(\".\", 1)\n        if len(parts) != 2:\n            return False\n    return True\n\n\ndef patch():\n    \"\"\"Patches an AWS Lambda using the `datadog-lambda-py` Lambda layer.\"\"\"\n\n    # It's expected to only patch only in AWS Lambda environments.\n    # The need to check if a patch module exists is to avoid patching\n    # when `ddtrace` is present but not `datadog-lambda`.\n    if not in_aws_lambda() and not _has_patch_module():\n        return\n\n    try:\n        handler, handler_module, wrapper = _get_handler_and_module()\n\n        if getattr(handler_module, \"_datadog_patch\", False):\n            return\n\n        wrap(handler, wrapper)\n\n        setattr(handler_module, \"_datadog_patch\", True)\n    except AttributeError:\n        # User code might contain `ddtrace.patch_all()` or `ddtrace.patch(aws_lambda=True)`\n        # which might cause a circular dependency. Skipping.\n        return\n    except Exception:\n        log.exception(\"Error patching AWS Lambda\")\n        return\n\n\ndef unpatch():\n    if not in_aws_lambda() and not _has_patch_module():\n        return\n\n    try:\n        handler, handler_module, wrapper = _get_handler_and_module()\n\n        if not getattr(handler_module, \"_datadog_patch\", False):\n            return\n\n        unwrap(handler, wrapper)\n\n        setattr(handler_module, \"_datadog_patch\", False)\n    except AttributeError:\n        return\n    except Exception:\n        log.exception(\"Error unpatching AWS Lambda\")\n        return\n", "path": "ddtrace/contrib/aws_lambda/patch.py"}], "after_files": [{"content": "from importlib import import_module\nimport os\nimport signal\n\nfrom ddtrace import tracer\nfrom ddtrace.constants import ERROR_MSG\nfrom ddtrace.constants import ERROR_TYPE\nfrom ddtrace.internal.logger import get_logger\nfrom ddtrace.internal.serverless import in_aws_lambda\nfrom ddtrace.internal.utils import get_argument_value\nfrom ddtrace.internal.wrapping import unwrap\nfrom ddtrace.internal.wrapping import wrap\n\n\nlog = get_logger(__name__)\n\n\nclass TimeoutChannel:\n    def __init__(self, context):\n        self.crashed = False\n        self.context = context\n\n    def _handle_signal(self, sig, f):\n        \"\"\"\n        Returns a signal of type `sig` with function `f`, if there are\n        no previously defined signals.\n\n        Else, wraps the given signal with the previously defined one,\n        so no signals are overridden.\n        \"\"\"\n        old_signal = signal.getsignal(sig)\n\n        def wrap_signals(*args, **kwargs):\n            if old_signal is not None:\n                old_signal(*args, **kwargs)\n            f(*args, **kwargs)\n\n        # Return the incoming signal if any of the following cases happens:\n        # - old signal does not exist,\n        # - old signal is the same as the incoming, or\n        # - old signal is our wrapper.\n        # This avoids multiple signal calling and infinite wrapping.\n        if not callable(old_signal) or old_signal == f or old_signal == wrap_signals:\n            return signal.signal(sig, f)\n\n        return signal.signal(sig, wrap_signals)\n\n    def _start(self):\n        self._handle_signal(signal.SIGALRM, self._crash_flush)\n\n        remaining_time_in_millis = self.context.get_remaining_time_in_millis()\n        apm_flush_deadline = int(os.environ.get(\"DD_APM_FLUSH_DEADLINE_MILLISECONDS\", 100))\n        apm_flush_deadline = 100 if apm_flush_deadline < 0 else apm_flush_deadline\n\n        # TODO: Update logic to calculate an approximate of how long it will\n        # take us to flush the spans on the queue.\n        remaining_time_in_seconds = max(((remaining_time_in_millis - apm_flush_deadline) / 1000), 0)\n        signal.setitimer(signal.ITIMER_REAL, remaining_time_in_seconds)\n\n    def _crash_flush(self, _, __):\n        \"\"\"\n        Tags the current root span with an Impending Timeout error.\n        Finishes spans with ancestors from the current span.\n        \"\"\"\n        self._remove_alarm_signal()\n        self.crashed = True\n\n        root_span = tracer.current_root_span()\n        if root_span is not None:\n            root_span.error = 1\n            root_span.set_tag_str(ERROR_MSG, \"Datadog detected an Impending Timeout\")\n            root_span.set_tag_str(ERROR_TYPE, \"Impending Timeout\")\n        else:\n            log.warning(\"An impending timeout was reached, but no root span was found. No error will be tagged.\")\n\n        current_span = tracer.current_span()\n        if current_span is not None:\n            current_span.finish_with_ancestors()\n\n    def _remove_alarm_signal(self):\n        \"\"\"Removes the handler set for the signal `SIGALRM`.\"\"\"\n        signal.alarm(0)\n        signal.signal(signal.SIGALRM, signal.SIG_DFL)\n\n    def stop(self):\n        self._remove_alarm_signal()\n\n\nclass DatadogInstrumentation(object):\n    \"\"\"Patches an AWS Lambda handler function for Datadog instrumentation.\"\"\"\n\n    def __call__(self, func, args, kwargs):\n        self.func = func\n        self._before(args, kwargs)\n        try:\n            self.response = self.func(*args, **kwargs)\n            return self.response\n        except Exception:\n            raise\n        finally:\n            self._after()\n\n    def _set_context(self, args, kwargs):\n        \"\"\"Sets the context attribute.\"\"\"\n        # The context is the second argument in a handler\n        # signature and it is always sent.\n        #\n        # note: AWS Lambda context is an object, the event is a dict.\n        # `get_remaining_time_in_millis` is guaranteed to be\n        # present in the context.\n        _context = get_argument_value(args, kwargs, 1, \"context\")\n        if hasattr(_context, \"get_remaining_time_in_millis\"):\n            self.context = _context\n        else:\n            # Handler was possibly manually wrapped, and the first\n            # argument is the `datadog-lambda` decorator object.\n            self.context = get_argument_value(args, kwargs, 2, \"context\")\n\n    def _before(self, args, kwargs):\n        self._set_context(args, kwargs)\n        self.timeoutChannel = TimeoutChannel(self.context)\n\n        self.timeoutChannel._start()\n\n    def _after(self):\n        if not self.timeoutChannel.crashed:\n            self.timeoutChannel.stop()\n\n\ndef _modify_module_name(module_name):\n    \"\"\"Returns a valid modified module to get imported.\"\"\"\n    return \".\".join(module_name.split(\"/\"))\n\n\ndef _get_handler_and_module():\n    \"\"\"Returns the user AWS Lambda handler and module.\"\"\"\n    path = os.environ.get(\"DD_LAMBDA_HANDLER\", None)\n    _datadog_instrumentation = DatadogInstrumentation()\n\n    if path is None:\n        from datadog_lambda.wrapper import datadog_lambda_wrapper\n\n        wrapper_module = datadog_lambda_wrapper\n        wrapper_handler = getattr(datadog_lambda_wrapper, \"__call__\")\n\n        return wrapper_handler, wrapper_module, _datadog_instrumentation\n    else:\n        parts = path.rsplit(\".\", 1)\n        (mod_name, handler_name) = parts\n        modified_mod_name = _modify_module_name(mod_name)\n        handler_module = import_module(modified_mod_name)\n        handler = getattr(handler_module, handler_name)\n\n        if callable(handler):\n            class_name = type(handler).__name__\n            is_function = not isinstance(handler, type) and hasattr(handler, \"__code__\") and class_name == \"function\"\n            # handler is a function\n            #\n            # note: this is a best effort to identify function based handlers\n            # this will not cover all cases\n            if is_function:\n                return handler, handler_module, _datadog_instrumentation\n\n            # handler must be either a class or an instance of a class\n            #\n            # note: if handler is a class instance with `__code__` defined,\n            # we will prioritize the `__call__` method, ignoring `__code__`.\n            class_module = getattr(handler_module, class_name)\n            class_handler = getattr(class_module, \"__call__\")\n\n            if isinstance(handler, type):\n                # class handler is a metaclass\n                if hasattr(class_handler, \"__func__\"):\n                    class_handler = class_handler.__func__\n\n            return class_handler, class_module, _datadog_instrumentation\n        else:\n            raise TypeError(\"Handler type is not supported to patch.\")\n\n\ndef _has_patch_module():\n    \"\"\"\n    Ensures that the `aws_lambda` integration can be patched.\n\n    It checks either the user has the DD_LAMBDA_HANDLER set correctly.\n    Or if the `datadog_lambda` package is installed.\n    \"\"\"\n    path = os.environ.get(\"DD_LAMBDA_HANDLER\", None)\n    if path is None:\n        try:\n            import_module(\"datadog_lambda.wrapper\")\n        except Exception:\n            return False\n    else:\n        parts = path.rsplit(\".\", 1)\n        if len(parts) != 2:\n            return False\n    return True\n\n\ndef patch():\n    \"\"\"Patches an AWS Lambda using the `datadog-lambda-py` Lambda layer.\"\"\"\n\n    # It's expected to only patch only in AWS Lambda environments.\n    # The need to check if a patch module exists is to avoid patching\n    # when `ddtrace` is present but not `datadog-lambda`.\n    if not in_aws_lambda() and not _has_patch_module():\n        return\n\n    try:\n        handler, handler_module, wrapper = _get_handler_and_module()\n\n        if getattr(handler_module, \"_datadog_patch\", False):\n            return\n\n        wrap(handler, wrapper)\n\n        setattr(handler_module, \"_datadog_patch\", True)\n    except AttributeError:\n        # User code might contain `ddtrace.patch_all()` or `ddtrace.patch(aws_lambda=True)`\n        # which might cause a circular dependency. Skipping.\n        return\n    except Exception:\n        log.exception(\"Error patching AWS Lambda\")\n        return\n\n\ndef unpatch():\n    if not in_aws_lambda() and not _has_patch_module():\n        return\n\n    try:\n        handler, handler_module, wrapper = _get_handler_and_module()\n\n        if not getattr(handler_module, \"_datadog_patch\", False):\n            return\n\n        unwrap(handler, wrapper)\n\n        setattr(handler_module, \"_datadog_patch\", False)\n    except AttributeError:\n        return\n    except Exception:\n        log.exception(\"Error unpatching AWS Lambda\")\n        return\n", "path": "ddtrace/contrib/aws_lambda/patch.py"}]}