We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
APM agent crashes if the SQL query is too large
**Describe the bug**: ...
Same as https://github.com/elastic/apm-agent-python/issues/827, but with `psycopg2` library instead of Django ORM.

**To Reproduce**

1. use any of `psycopg2` extra utilities, for example `execute_values`, with a query that is sufficiently long.

**Environment (please complete the following information)**
- OS: mac
- Python version: 3.7
- APM Server version: ?
- Agent version: 5.10.0


**Additional context**

The issue is that `psycopg2` is passing the sql as bytes object (bytes array). The agent sql instrumentation [code](https://github.com/elastic/apm-agent-python/blob/master/elasticapm/instrumentation/packages/dbapi2.py#L222-L231) is calling the [`shorten()`](https://github.com/elastic/apm-agent-python/blob/f8977bf5c5d9921f83eed174b94cc48922bc72b2/elasticapm/utils/encoding.py#L182-L217) function which does not support bytes object.

I'm not sure if same behavior applies to `psycopg2` core `execute()` function, or only in the "extras" the query is passed as bytes object.
APM agent crashes if the SQL query is too large
**Describe the bug**: ...
Same as https://github.com/elastic/apm-agent-python/issues/827, but with `psycopg2` library instead of Django ORM.

**To Reproduce**

1. use any of `psycopg2` extra utilities, for example `execute_values`, with a query that is sufficiently long.

**Environment (please complete the following information)**
- OS: mac
- Python version: 3.7
- APM Server version: ?
- Agent version: 5.10.0


**Additional context**

The issue is that `psycopg2` is passing the sql as bytes object (bytes array). The agent sql instrumentation [code](https://github.com/elastic/apm-agent-python/blob/master/elasticapm/instrumentation/packages/dbapi2.py#L222-L231) is calling the [`shorten()`](https://github.com/elastic/apm-agent-python/blob/f8977bf5c5d9921f83eed174b94cc48922bc72b2/elasticapm/utils/encoding.py#L182-L217) function which does not support bytes object.

I'm not sure if same behavior applies to `psycopg2` core `execute()` function, or only in the "extras" the query is passed as bytes object.

--- 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/instrumentation/packages/psycopg2.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 elasticapm.instrumentation.packages.dbapi2 import (
32     ConnectionProxy,
33     CursorProxy,
34     DbApi2Instrumentation,
35     extract_signature,
36 )
37 from elasticapm.traces import capture_span
38 from elasticapm.utils import compat, default_ports
39 
40 
41 class PGCursorProxy(CursorProxy):
42     provider_name = "postgresql"
43 
44     def _bake_sql(self, sql):
45         # if this is a Composable object, use its `as_string` method
46         # see http://initd.org/psycopg/docs/sql.html
47         if hasattr(sql, "as_string"):
48             return sql.as_string(self.__wrapped__)
49         return sql
50 
51     def extract_signature(self, sql):
52         return extract_signature(sql)
53 
54     def __enter__(self):
55         return PGCursorProxy(self.__wrapped__.__enter__(), destination_info=self._self_destination_info)
56 
57 
58 class PGConnectionProxy(ConnectionProxy):
59     cursor_proxy = PGCursorProxy
60 
61     def __enter__(self):
62         return PGConnectionProxy(self.__wrapped__.__enter__(), destination_info=self._self_destination_info)
63 
64 
65 class Psycopg2Instrumentation(DbApi2Instrumentation):
66     name = "psycopg2"
67 
68     instrument_list = [("psycopg2", "connect")]
69 
70     def call(self, module, method, wrapped, instance, args, kwargs):
71         signature = "psycopg2.connect"
72 
73         host = kwargs.get("host")
74         if host:
75             signature += " " + compat.text_type(host)
76 
77             port = kwargs.get("port")
78             if port:
79                 port = str(port)
80                 if int(port) != default_ports.get("postgresql"):
81                     host += ":" + port
82             signature += " " + compat.text_type(host)
83         else:
84             # Parse connection string and extract host/port
85             pass
86         destination_info = {
87             "address": kwargs.get("host", "localhost"),
88             "port": int(kwargs.get("port", default_ports.get("postgresql"))),
89             "service": {"name": "postgresql", "resource": "postgresql", "type": "db"},
90         }
91         with capture_span(
92             signature,
93             span_type="db",
94             span_subtype="postgresql",
95             span_action="connect",
96             extra={"destination": destination_info},
97         ):
98             return PGConnectionProxy(wrapped(*args, **kwargs), destination_info=destination_info)
99 
100 
101 class Psycopg2ExtensionsInstrumentation(DbApi2Instrumentation):
102     """
103     Some extensions do a type check on the Connection/Cursor in C-code, which our
104     proxy fails. For these extensions, we need to ensure that the unwrapped
105     Connection/Cursor is passed.
106     """
107 
108     name = "psycopg2"
109 
110     instrument_list = [
111         ("psycopg2.extensions", "register_type"),
112         # specifically instrument `register_json` as it bypasses `register_type`
113         ("psycopg2._json", "register_json"),
114         ("psycopg2.extensions", "quote_ident"),
115         ("psycopg2.extensions", "encrypt_password"),
116     ]
117 
118     def call(self, module, method, wrapped, instance, args, kwargs):
119         if "conn_or_curs" in kwargs and hasattr(kwargs["conn_or_curs"], "__wrapped__"):
120             kwargs["conn_or_curs"] = kwargs["conn_or_curs"].__wrapped__
121         # register_type takes the connection as second argument
122         elif len(args) == 2 and hasattr(args[1], "__wrapped__"):
123             args = (args[0], args[1].__wrapped__)
124         # register_json takes the connection as first argument, and can have
125         # several more arguments
126         elif method == "register_json":
127             if args and hasattr(args[0], "__wrapped__"):
128                 args = (args[0].__wrapped__,) + args[1:]
129 
130         elif method == "encrypt_password":
131             # connection/cursor is either 3rd argument, or "scope" keyword argument
132             if len(args) >= 3 and hasattr(args[2], "__wrapped__"):
133                 args = args[:2] + (args[2].__wrapped__,) + args[3:]
134             elif "scope" in kwargs and hasattr(kwargs["scope"], "__wrapped__"):
135                 kwargs["scope"] = kwargs["scope"].__wrapped__
136 
137         return wrapped(*args, **kwargs)
138
```
--- 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/instrumentation/packages/psycopg2.py b/elasticapm/instrumentation/packages/psycopg2.py
--- a/elasticapm/instrumentation/packages/psycopg2.py
+++ b/elasticapm/instrumentation/packages/psycopg2.py
@@ -27,6 +27,7 @@
 #  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 #  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 #  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+from __future__ import absolute_import
 
 from elasticapm.instrumentation.packages.dbapi2 import (
     ConnectionProxy,
@@ -42,10 +43,15 @@
     provider_name = "postgresql"
 
     def _bake_sql(self, sql):
+        from psycopg2 import extensions as psycopg2_extensions
+
         # if this is a Composable object, use its `as_string` method
         # see http://initd.org/psycopg/docs/sql.html
         if hasattr(sql, "as_string"):
-            return sql.as_string(self.__wrapped__)
+            sql = sql.as_string(self.__wrapped__)
+        # if the sql string is already a byte string, we need to decode it using the connection encoding
+        if isinstance(sql, compat.binary_type):
+            sql = sql.decode(psycopg2_extensions.encodings[self.__wrapped__.connection.encoding])
         return sql
 
     def extract_signature(self, sql):

{"golden_diff": "diff --git a/elasticapm/instrumentation/packages/psycopg2.py b/elasticapm/instrumentation/packages/psycopg2.py\n--- a/elasticapm/instrumentation/packages/psycopg2.py\n+++ b/elasticapm/instrumentation/packages/psycopg2.py\n@@ -27,6 +27,7 @@\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+from __future__ import absolute_import\n \n from elasticapm.instrumentation.packages.dbapi2 import (\n     ConnectionProxy,\n@@ -42,10 +43,15 @@\n     provider_name = \"postgresql\"\n \n     def _bake_sql(self, sql):\n+        from psycopg2 import extensions as psycopg2_extensions\n+\n         # if this is a Composable object, use its `as_string` method\n         # see http://initd.org/psycopg/docs/sql.html\n         if hasattr(sql, \"as_string\"):\n-            return sql.as_string(self.__wrapped__)\n+            sql = sql.as_string(self.__wrapped__)\n+        # if the sql string is already a byte string, we need to decode it using the connection encoding\n+        if isinstance(sql, compat.binary_type):\n+            sql = sql.decode(psycopg2_extensions.encodings[self.__wrapped__.connection.encoding])\n         return sql\n \n     def extract_signature(self, sql):\n", "issue": "APM agent crashes if the SQL query is too large\n**Describe the bug**: ...\r\nSame as https://github.com/elastic/apm-agent-python/issues/827, but with `psycopg2` library instead of Django ORM.\r\n\r\n**To Reproduce**\r\n\r\n1. use any of `psycopg2` extra utilities, for example `execute_values`, with a query that is sufficiently long.\r\n\r\n**Environment (please complete the following information)**\r\n- OS: mac\r\n- Python version: 3.7\r\n- APM Server version: ?\r\n- Agent version: 5.10.0\r\n\r\n\r\n**Additional context**\r\n\r\nThe issue is that `psycopg2` is passing the sql as bytes object (bytes array). The agent sql instrumentation [code](https://github.com/elastic/apm-agent-python/blob/master/elasticapm/instrumentation/packages/dbapi2.py#L222-L231) is calling the [`shorten()`](https://github.com/elastic/apm-agent-python/blob/f8977bf5c5d9921f83eed174b94cc48922bc72b2/elasticapm/utils/encoding.py#L182-L217) function which does not support bytes object.\r\n\r\nI'm not sure if same behavior applies to `psycopg2` core `execute()` function, or only in the \"extras\" the query is passed as bytes object.\nAPM agent crashes if the SQL query is too large\n**Describe the bug**: ...\r\nSame as https://github.com/elastic/apm-agent-python/issues/827, but with `psycopg2` library instead of Django ORM.\r\n\r\n**To Reproduce**\r\n\r\n1. use any of `psycopg2` extra utilities, for example `execute_values`, with a query that is sufficiently long.\r\n\r\n**Environment (please complete the following information)**\r\n- OS: mac\r\n- Python version: 3.7\r\n- APM Server version: ?\r\n- Agent version: 5.10.0\r\n\r\n\r\n**Additional context**\r\n\r\nThe issue is that `psycopg2` is passing the sql as bytes object (bytes array). The agent sql instrumentation [code](https://github.com/elastic/apm-agent-python/blob/master/elasticapm/instrumentation/packages/dbapi2.py#L222-L231) is calling the [`shorten()`](https://github.com/elastic/apm-agent-python/blob/f8977bf5c5d9921f83eed174b94cc48922bc72b2/elasticapm/utils/encoding.py#L182-L217) function which does not support bytes object.\r\n\r\nI'm not sure if same behavior applies to `psycopg2` core `execute()` function, or only in the \"extras\" the query is passed as bytes object.\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 elasticapm.instrumentation.packages.dbapi2 import (\n    ConnectionProxy,\n    CursorProxy,\n    DbApi2Instrumentation,\n    extract_signature,\n)\nfrom elasticapm.traces import capture_span\nfrom elasticapm.utils import compat, default_ports\n\n\nclass PGCursorProxy(CursorProxy):\n    provider_name = \"postgresql\"\n\n    def _bake_sql(self, sql):\n        # if this is a Composable object, use its `as_string` method\n        # see http://initd.org/psycopg/docs/sql.html\n        if hasattr(sql, \"as_string\"):\n            return sql.as_string(self.__wrapped__)\n        return sql\n\n    def extract_signature(self, sql):\n        return extract_signature(sql)\n\n    def __enter__(self):\n        return PGCursorProxy(self.__wrapped__.__enter__(), destination_info=self._self_destination_info)\n\n\nclass PGConnectionProxy(ConnectionProxy):\n    cursor_proxy = PGCursorProxy\n\n    def __enter__(self):\n        return PGConnectionProxy(self.__wrapped__.__enter__(), destination_info=self._self_destination_info)\n\n\nclass Psycopg2Instrumentation(DbApi2Instrumentation):\n    name = \"psycopg2\"\n\n    instrument_list = [(\"psycopg2\", \"connect\")]\n\n    def call(self, module, method, wrapped, instance, args, kwargs):\n        signature = \"psycopg2.connect\"\n\n        host = kwargs.get(\"host\")\n        if host:\n            signature += \" \" + compat.text_type(host)\n\n            port = kwargs.get(\"port\")\n            if port:\n                port = str(port)\n                if int(port) != default_ports.get(\"postgresql\"):\n                    host += \":\" + port\n            signature += \" \" + compat.text_type(host)\n        else:\n            # Parse connection string and extract host/port\n            pass\n        destination_info = {\n            \"address\": kwargs.get(\"host\", \"localhost\"),\n            \"port\": int(kwargs.get(\"port\", default_ports.get(\"postgresql\"))),\n            \"service\": {\"name\": \"postgresql\", \"resource\": \"postgresql\", \"type\": \"db\"},\n        }\n        with capture_span(\n            signature,\n            span_type=\"db\",\n            span_subtype=\"postgresql\",\n            span_action=\"connect\",\n            extra={\"destination\": destination_info},\n        ):\n            return PGConnectionProxy(wrapped(*args, **kwargs), destination_info=destination_info)\n\n\nclass Psycopg2ExtensionsInstrumentation(DbApi2Instrumentation):\n    \"\"\"\n    Some extensions do a type check on the Connection/Cursor in C-code, which our\n    proxy fails. For these extensions, we need to ensure that the unwrapped\n    Connection/Cursor is passed.\n    \"\"\"\n\n    name = \"psycopg2\"\n\n    instrument_list = [\n        (\"psycopg2.extensions\", \"register_type\"),\n        # specifically instrument `register_json` as it bypasses `register_type`\n        (\"psycopg2._json\", \"register_json\"),\n        (\"psycopg2.extensions\", \"quote_ident\"),\n        (\"psycopg2.extensions\", \"encrypt_password\"),\n    ]\n\n    def call(self, module, method, wrapped, instance, args, kwargs):\n        if \"conn_or_curs\" in kwargs and hasattr(kwargs[\"conn_or_curs\"], \"__wrapped__\"):\n            kwargs[\"conn_or_curs\"] = kwargs[\"conn_or_curs\"].__wrapped__\n        # register_type takes the connection as second argument\n        elif len(args) == 2 and hasattr(args[1], \"__wrapped__\"):\n            args = (args[0], args[1].__wrapped__)\n        # register_json takes the connection as first argument, and can have\n        # several more arguments\n        elif method == \"register_json\":\n            if args and hasattr(args[0], \"__wrapped__\"):\n                args = (args[0].__wrapped__,) + args[1:]\n\n        elif method == \"encrypt_password\":\n            # connection/cursor is either 3rd argument, or \"scope\" keyword argument\n            if len(args) >= 3 and hasattr(args[2], \"__wrapped__\"):\n                args = args[:2] + (args[2].__wrapped__,) + args[3:]\n            elif \"scope\" in kwargs and hasattr(kwargs[\"scope\"], \"__wrapped__\"):\n                kwargs[\"scope\"] = kwargs[\"scope\"].__wrapped__\n\n        return wrapped(*args, **kwargs)\n", "path": "elasticapm/instrumentation/packages/psycopg2.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.\nfrom __future__ import absolute_import\n\nfrom elasticapm.instrumentation.packages.dbapi2 import (\n    ConnectionProxy,\n    CursorProxy,\n    DbApi2Instrumentation,\n    extract_signature,\n)\nfrom elasticapm.traces import capture_span\nfrom elasticapm.utils import compat, default_ports\n\n\nclass PGCursorProxy(CursorProxy):\n    provider_name = \"postgresql\"\n\n    def _bake_sql(self, sql):\n        from psycopg2 import extensions as psycopg2_extensions\n\n        # if this is a Composable object, use its `as_string` method\n        # see http://initd.org/psycopg/docs/sql.html\n        if hasattr(sql, \"as_string\"):\n            sql = sql.as_string(self.__wrapped__)\n        # if the sql string is already a byte string, we need to decode it using the connection encoding\n        if isinstance(sql, compat.binary_type):\n            sql = sql.decode(psycopg2_extensions.encodings[self.__wrapped__.connection.encoding])\n        return sql\n\n    def extract_signature(self, sql):\n        return extract_signature(sql)\n\n    def __enter__(self):\n        return PGCursorProxy(self.__wrapped__.__enter__(), destination_info=self._self_destination_info)\n\n\nclass PGConnectionProxy(ConnectionProxy):\n    cursor_proxy = PGCursorProxy\n\n    def __enter__(self):\n        return PGConnectionProxy(self.__wrapped__.__enter__(), destination_info=self._self_destination_info)\n\n\nclass Psycopg2Instrumentation(DbApi2Instrumentation):\n    name = \"psycopg2\"\n\n    instrument_list = [(\"psycopg2\", \"connect\")]\n\n    def call(self, module, method, wrapped, instance, args, kwargs):\n        signature = \"psycopg2.connect\"\n\n        host = kwargs.get(\"host\")\n        if host:\n            signature += \" \" + compat.text_type(host)\n\n            port = kwargs.get(\"port\")\n            if port:\n                port = str(port)\n                if int(port) != default_ports.get(\"postgresql\"):\n                    host += \":\" + port\n            signature += \" \" + compat.text_type(host)\n        else:\n            # Parse connection string and extract host/port\n            pass\n        destination_info = {\n            \"address\": kwargs.get(\"host\", \"localhost\"),\n            \"port\": int(kwargs.get(\"port\", default_ports.get(\"postgresql\"))),\n            \"service\": {\"name\": \"postgresql\", \"resource\": \"postgresql\", \"type\": \"db\"},\n        }\n        with capture_span(\n            signature,\n            span_type=\"db\",\n            span_subtype=\"postgresql\",\n            span_action=\"connect\",\n            extra={\"destination\": destination_info},\n        ):\n            return PGConnectionProxy(wrapped(*args, **kwargs), destination_info=destination_info)\n\n\nclass Psycopg2ExtensionsInstrumentation(DbApi2Instrumentation):\n    \"\"\"\n    Some extensions do a type check on the Connection/Cursor in C-code, which our\n    proxy fails. For these extensions, we need to ensure that the unwrapped\n    Connection/Cursor is passed.\n    \"\"\"\n\n    name = \"psycopg2\"\n\n    instrument_list = [\n        (\"psycopg2.extensions\", \"register_type\"),\n        # specifically instrument `register_json` as it bypasses `register_type`\n        (\"psycopg2._json\", \"register_json\"),\n        (\"psycopg2.extensions\", \"quote_ident\"),\n        (\"psycopg2.extensions\", \"encrypt_password\"),\n    ]\n\n    def call(self, module, method, wrapped, instance, args, kwargs):\n        if \"conn_or_curs\" in kwargs and hasattr(kwargs[\"conn_or_curs\"], \"__wrapped__\"):\n            kwargs[\"conn_or_curs\"] = kwargs[\"conn_or_curs\"].__wrapped__\n        # register_type takes the connection as second argument\n        elif len(args) == 2 and hasattr(args[1], \"__wrapped__\"):\n            args = (args[0], args[1].__wrapped__)\n        # register_json takes the connection as first argument, and can have\n        # several more arguments\n        elif method == \"register_json\":\n            if args and hasattr(args[0], \"__wrapped__\"):\n                args = (args[0].__wrapped__,) + args[1:]\n\n        elif method == \"encrypt_password\":\n            # connection/cursor is either 3rd argument, or \"scope\" keyword argument\n            if len(args) >= 3 and hasattr(args[2], \"__wrapped__\"):\n                args = args[:2] + (args[2].__wrapped__,) + args[3:]\n            elif \"scope\" in kwargs and hasattr(kwargs[\"scope\"], \"__wrapped__\"):\n                kwargs[\"scope\"] = kwargs[\"scope\"].__wrapped__\n\n        return wrapped(*args, **kwargs)\n", "path": "elasticapm/instrumentation/packages/psycopg2.py"}]}