We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Exception when using `quote_ident` in psycopg2
**Describe the bug**:
If you make use of the function `psycopg2.extensions.quote_ident` [docs](http://initd.org/psycopg/docs/extensions.html#psycopg2.extensions.quote_ident), a `TypeError` exception is thrown. This is because the cursor object, when under instrumentation from ES-APM, is an instance of `PGCursorProxy`, not the actual cursor, and `quote_ident` does not allow this because the type is checked in the C code [link](https://github.com/psycopg/psycopg2/blob/2_7_6_1/psycopg/psycopgmodule.c#L181). With the error message saying `TypeError: argument 2 must be a connection or a cursor`. Inspecting the cur object at a debug breakpoint we can see it is the proxy object:

```
>>> cur
<PGCursorProxy at 0x7fd7f70f9a88 for NamedTupleCursor at 0x7fd7f70f0148>
>>> type(cur)
<class 'elasticapm.instrumentation.packages.psycopg2.PGCursorProxy'>
>>> type(cur.__wrapped__)
<class 'psycopg2.extras.NamedTupleCursor'>
```

**To Reproduce**

```python
from psycopg2.extensions import quote_ident
....
....
with psycopg2.connect(DSN) as conn:
    with conn.cursor() as curs:
        ident = quote_ident("column_name", cur)
        curs.execute(f"SELECT {column_name} FROM data.table;")
        data = curs.fetchall()
```
passing the underlying wrapped cursor works:
```python
from psycopg2.extensions import quote_ident
....
....
with psycopg2.connect(DSN) as conn:
    with conn.cursor() as curs:
        ident = quote_ident("column_name", cur.__wrapped__)
        curs.execute(f"SELECT {column_name} FROM data.table;")
        data = curs.fetchall()
```

**Environment (please complete the following information)**
- OS: Linux
- Python version: 3.6.4
- Agent version: 5.2.2


**Additional context**
Looks like the same problem was encountered here https://github.com/DataDog/dd-trace-py/issues/474, and was fixed by also patching quote_ident to pass the `__wrapped__` object. Testing this out with a basic in-module proxy function worked, but obviously a patch right at the top level from the apm module would sort it out.

```python
def quote_ident(string, cursor):
    return psycopg2.extensions.quote_ident(string, cursor.__wrapped__)
```



--- 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/register.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 
32 from elasticapm.utils.module_import import import_string
33 
34 _cls_register = {
35     "elasticapm.instrumentation.packages.botocore.BotocoreInstrumentation",
36     "elasticapm.instrumentation.packages.jinja2.Jinja2Instrumentation",
37     "elasticapm.instrumentation.packages.psycopg2.Psycopg2Instrumentation",
38     "elasticapm.instrumentation.packages.psycopg2.Psycopg2RegisterTypeInstrumentation",
39     "elasticapm.instrumentation.packages.mysql.MySQLInstrumentation",
40     "elasticapm.instrumentation.packages.pylibmc.PyLibMcInstrumentation",
41     "elasticapm.instrumentation.packages.pymongo.PyMongoInstrumentation",
42     "elasticapm.instrumentation.packages.pymongo.PyMongoBulkInstrumentation",
43     "elasticapm.instrumentation.packages.pymongo.PyMongoCursorInstrumentation",
44     "elasticapm.instrumentation.packages.python_memcached.PythonMemcachedInstrumentation",
45     "elasticapm.instrumentation.packages.redis.RedisInstrumentation",
46     "elasticapm.instrumentation.packages.redis.RedisPipelineInstrumentation",
47     "elasticapm.instrumentation.packages.requests.RequestsInstrumentation",
48     "elasticapm.instrumentation.packages.sqlite.SQLiteInstrumentation",
49     "elasticapm.instrumentation.packages.urllib3.Urllib3Instrumentation",
50     "elasticapm.instrumentation.packages.elasticsearch.ElasticsearchConnectionInstrumentation",
51     "elasticapm.instrumentation.packages.elasticsearch.ElasticsearchInstrumentation",
52     "elasticapm.instrumentation.packages.cassandra.CassandraInstrumentation",
53     "elasticapm.instrumentation.packages.pymssql.PyMSSQLInstrumentation",
54     "elasticapm.instrumentation.packages.pyodbc.PyODBCInstrumentation",
55     "elasticapm.instrumentation.packages.django.template.DjangoTemplateInstrumentation",
56     "elasticapm.instrumentation.packages.django.template.DjangoTemplateSourceInstrumentation",
57     "elasticapm.instrumentation.packages.urllib.UrllibInstrumentation",
58 }
59 
60 
61 def register(cls):
62     _cls_register.add(cls)
63 
64 
65 _instrumentation_singletons = {}
66 
67 
68 def get_instrumentation_objects():
69     for cls_str in _cls_register:
70         if cls_str not in _instrumentation_singletons:
71             cls = import_string(cls_str)
72             _instrumentation_singletons[cls_str] = cls()
73 
74         obj = _instrumentation_singletons[cls_str]
75         yield obj
76
```

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 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__())
56 
57 
58 class PGConnectionProxy(ConnectionProxy):
59     cursor_proxy = PGCursorProxy
60 
61     def __enter__(self):
62         return PGConnectionProxy(self.__wrapped__.__enter__())
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 += " " + str(host)
76 
77             port = kwargs.get("port")
78             if port:
79                 port = str(port)
80                 if int(port) != default_ports.get("postgresql"):
81                     signature += ":" + port
82         else:
83             # Parse connection string and extract host/port
84             pass
85 
86         with capture_span(signature, span_type="db", span_subtype="postgresql", span_action="connect"):
87             return PGConnectionProxy(wrapped(*args, **kwargs))
88 
89 
90 class Psycopg2RegisterTypeInstrumentation(DbApi2Instrumentation):
91     name = "psycopg2-register-type"
92 
93     instrument_list = [
94         ("psycopg2.extensions", "register_type"),
95         # specifically instrument `register_json` as it bypasses `register_type`
96         ("psycopg2._json", "register_json"),
97     ]
98 
99     def call(self, module, method, wrapped, instance, args, kwargs):
100         if "conn_or_curs" in kwargs and hasattr(kwargs["conn_or_curs"], "__wrapped__"):
101             kwargs["conn_or_curs"] = kwargs["conn_or_curs"].__wrapped__
102         # register_type takes the connection as second argument
103         elif len(args) == 2 and hasattr(args[1], "__wrapped__"):
104             args = (args[0], args[1].__wrapped__)
105         # register_json takes the connection as first argument, and can have
106         # several more arguments
107         elif method == "register_json":
108             if args and hasattr(args[0], "__wrapped__"):
109                 args = (args[0].__wrapped__,) + args[1:]
110 
111         return wrapped(*args, **kwargs)
112
```
--- 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
@@ -87,13 +87,21 @@
             return PGConnectionProxy(wrapped(*args, **kwargs))
 
 
-class Psycopg2RegisterTypeInstrumentation(DbApi2Instrumentation):
-    name = "psycopg2-register-type"
+class Psycopg2ExtensionsInstrumentation(DbApi2Instrumentation):
+    """
+    Some extensions do a type check on the Connection/Cursor in C-code, which our
+    proxy fails. For these extensions, we need to ensure that the unwrapped
+    Connection/Cursor is passed.
+    """
+
+    name = "psycopg2"
 
     instrument_list = [
         ("psycopg2.extensions", "register_type"),
         # specifically instrument `register_json` as it bypasses `register_type`
         ("psycopg2._json", "register_json"),
+        ("psycopg2.extensions", "quote_ident"),
+        ("psycopg2.extensions", "encrypt_password"),
     ]
 
     def call(self, module, method, wrapped, instance, args, kwargs):
@@ -108,4 +116,11 @@
             if args and hasattr(args[0], "__wrapped__"):
                 args = (args[0].__wrapped__,) + args[1:]
 
+        elif method == "encrypt_password":
+            # connection/cursor is either 3rd argument, or "scope" keyword argument
+            if len(args) >= 3 and hasattr(args[2], "__wrapped__"):
+                args = args[:2] + (args[2].__wrapped__,) + args[3:]
+            elif "scope" in kwargs and hasattr(kwargs["scope"], "__wrapped__"):
+                kwargs["scope"] = kwargs["scope"].__wrapped__
+
         return wrapped(*args, **kwargs)
diff --git a/elasticapm/instrumentation/register.py b/elasticapm/instrumentation/register.py
--- a/elasticapm/instrumentation/register.py
+++ b/elasticapm/instrumentation/register.py
@@ -35,7 +35,7 @@
     "elasticapm.instrumentation.packages.botocore.BotocoreInstrumentation",
     "elasticapm.instrumentation.packages.jinja2.Jinja2Instrumentation",
     "elasticapm.instrumentation.packages.psycopg2.Psycopg2Instrumentation",
-    "elasticapm.instrumentation.packages.psycopg2.Psycopg2RegisterTypeInstrumentation",
+    "elasticapm.instrumentation.packages.psycopg2.Psycopg2ExtensionsInstrumentation",
     "elasticapm.instrumentation.packages.mysql.MySQLInstrumentation",
     "elasticapm.instrumentation.packages.pylibmc.PyLibMcInstrumentation",
     "elasticapm.instrumentation.packages.pymongo.PyMongoInstrumentation",

{"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@@ -87,13 +87,21 @@\n             return PGConnectionProxy(wrapped(*args, **kwargs))\n \n \n-class Psycopg2RegisterTypeInstrumentation(DbApi2Instrumentation):\n-    name = \"psycopg2-register-type\"\n+class 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@@ -108,4 +116,11 @@\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)\ndiff --git a/elasticapm/instrumentation/register.py b/elasticapm/instrumentation/register.py\n--- a/elasticapm/instrumentation/register.py\n+++ b/elasticapm/instrumentation/register.py\n@@ -35,7 +35,7 @@\n     \"elasticapm.instrumentation.packages.botocore.BotocoreInstrumentation\",\n     \"elasticapm.instrumentation.packages.jinja2.Jinja2Instrumentation\",\n     \"elasticapm.instrumentation.packages.psycopg2.Psycopg2Instrumentation\",\n-    \"elasticapm.instrumentation.packages.psycopg2.Psycopg2RegisterTypeInstrumentation\",\n+    \"elasticapm.instrumentation.packages.psycopg2.Psycopg2ExtensionsInstrumentation\",\n     \"elasticapm.instrumentation.packages.mysql.MySQLInstrumentation\",\n     \"elasticapm.instrumentation.packages.pylibmc.PyLibMcInstrumentation\",\n     \"elasticapm.instrumentation.packages.pymongo.PyMongoInstrumentation\",\n", "issue": "Exception when using `quote_ident` in psycopg2\n**Describe the bug**:\r\nIf you make use of the function `psycopg2.extensions.quote_ident` [docs](http://initd.org/psycopg/docs/extensions.html#psycopg2.extensions.quote_ident), a `TypeError` exception is thrown. This is because the cursor object, when under instrumentation from ES-APM, is an instance of `PGCursorProxy`, not the actual cursor, and `quote_ident` does not allow this because the type is checked in the C code [link](https://github.com/psycopg/psycopg2/blob/2_7_6_1/psycopg/psycopgmodule.c#L181). With the error message saying `TypeError: argument 2 must be a connection or a cursor`. Inspecting the cur object at a debug breakpoint we can see it is the proxy object:\r\n\r\n```\r\n>>> cur\r\n<PGCursorProxy at 0x7fd7f70f9a88 for NamedTupleCursor at 0x7fd7f70f0148>\r\n>>> type(cur)\r\n<class 'elasticapm.instrumentation.packages.psycopg2.PGCursorProxy'>\r\n>>> type(cur.__wrapped__)\r\n<class 'psycopg2.extras.NamedTupleCursor'>\r\n```\r\n\r\n**To Reproduce**\r\n\r\n```python\r\nfrom psycopg2.extensions import quote_ident\r\n....\r\n....\r\nwith psycopg2.connect(DSN) as conn:\r\n    with conn.cursor() as curs:\r\n        ident = quote_ident(\"column_name\", cur)\r\n        curs.execute(f\"SELECT {column_name} FROM data.table;\")\r\n        data = curs.fetchall()\r\n```\r\npassing the underlying wrapped cursor works:\r\n```python\r\nfrom psycopg2.extensions import quote_ident\r\n....\r\n....\r\nwith psycopg2.connect(DSN) as conn:\r\n    with conn.cursor() as curs:\r\n        ident = quote_ident(\"column_name\", cur.__wrapped__)\r\n        curs.execute(f\"SELECT {column_name} FROM data.table;\")\r\n        data = curs.fetchall()\r\n```\r\n\r\n**Environment (please complete the following information)**\r\n- OS: Linux\r\n- Python version: 3.6.4\r\n- Agent version: 5.2.2\r\n\r\n\r\n**Additional context**\r\nLooks like the same problem was encountered here https://github.com/DataDog/dd-trace-py/issues/474, and was fixed by also patching quote_ident to pass the `__wrapped__` object. Testing this out with a basic in-module proxy function worked, but obviously a patch right at the top level from the apm module would sort it out.\r\n\r\n```python\r\ndef quote_ident(string, cursor):\r\n    return psycopg2.extensions.quote_ident(string, cursor.__wrapped__)\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\n\nfrom elasticapm.utils.module_import import import_string\n\n_cls_register = {\n    \"elasticapm.instrumentation.packages.botocore.BotocoreInstrumentation\",\n    \"elasticapm.instrumentation.packages.jinja2.Jinja2Instrumentation\",\n    \"elasticapm.instrumentation.packages.psycopg2.Psycopg2Instrumentation\",\n    \"elasticapm.instrumentation.packages.psycopg2.Psycopg2RegisterTypeInstrumentation\",\n    \"elasticapm.instrumentation.packages.mysql.MySQLInstrumentation\",\n    \"elasticapm.instrumentation.packages.pylibmc.PyLibMcInstrumentation\",\n    \"elasticapm.instrumentation.packages.pymongo.PyMongoInstrumentation\",\n    \"elasticapm.instrumentation.packages.pymongo.PyMongoBulkInstrumentation\",\n    \"elasticapm.instrumentation.packages.pymongo.PyMongoCursorInstrumentation\",\n    \"elasticapm.instrumentation.packages.python_memcached.PythonMemcachedInstrumentation\",\n    \"elasticapm.instrumentation.packages.redis.RedisInstrumentation\",\n    \"elasticapm.instrumentation.packages.redis.RedisPipelineInstrumentation\",\n    \"elasticapm.instrumentation.packages.requests.RequestsInstrumentation\",\n    \"elasticapm.instrumentation.packages.sqlite.SQLiteInstrumentation\",\n    \"elasticapm.instrumentation.packages.urllib3.Urllib3Instrumentation\",\n    \"elasticapm.instrumentation.packages.elasticsearch.ElasticsearchConnectionInstrumentation\",\n    \"elasticapm.instrumentation.packages.elasticsearch.ElasticsearchInstrumentation\",\n    \"elasticapm.instrumentation.packages.cassandra.CassandraInstrumentation\",\n    \"elasticapm.instrumentation.packages.pymssql.PyMSSQLInstrumentation\",\n    \"elasticapm.instrumentation.packages.pyodbc.PyODBCInstrumentation\",\n    \"elasticapm.instrumentation.packages.django.template.DjangoTemplateInstrumentation\",\n    \"elasticapm.instrumentation.packages.django.template.DjangoTemplateSourceInstrumentation\",\n    \"elasticapm.instrumentation.packages.urllib.UrllibInstrumentation\",\n}\n\n\ndef register(cls):\n    _cls_register.add(cls)\n\n\n_instrumentation_singletons = {}\n\n\ndef get_instrumentation_objects():\n    for cls_str in _cls_register:\n        if cls_str not in _instrumentation_singletons:\n            cls = import_string(cls_str)\n            _instrumentation_singletons[cls_str] = cls()\n\n        obj = _instrumentation_singletons[cls_str]\n        yield obj\n", "path": "elasticapm/instrumentation/register.py"}, {"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 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__())\n\n\nclass PGConnectionProxy(ConnectionProxy):\n    cursor_proxy = PGCursorProxy\n\n    def __enter__(self):\n        return PGConnectionProxy(self.__wrapped__.__enter__())\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 += \" \" + str(host)\n\n            port = kwargs.get(\"port\")\n            if port:\n                port = str(port)\n                if int(port) != default_ports.get(\"postgresql\"):\n                    signature += \":\" + port\n        else:\n            # Parse connection string and extract host/port\n            pass\n\n        with capture_span(signature, span_type=\"db\", span_subtype=\"postgresql\", span_action=\"connect\"):\n            return PGConnectionProxy(wrapped(*args, **kwargs))\n\n\nclass Psycopg2RegisterTypeInstrumentation(DbApi2Instrumentation):\n    name = \"psycopg2-register-type\"\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    ]\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        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.\n\n\nfrom elasticapm.utils.module_import import import_string\n\n_cls_register = {\n    \"elasticapm.instrumentation.packages.botocore.BotocoreInstrumentation\",\n    \"elasticapm.instrumentation.packages.jinja2.Jinja2Instrumentation\",\n    \"elasticapm.instrumentation.packages.psycopg2.Psycopg2Instrumentation\",\n    \"elasticapm.instrumentation.packages.psycopg2.Psycopg2ExtensionsInstrumentation\",\n    \"elasticapm.instrumentation.packages.mysql.MySQLInstrumentation\",\n    \"elasticapm.instrumentation.packages.pylibmc.PyLibMcInstrumentation\",\n    \"elasticapm.instrumentation.packages.pymongo.PyMongoInstrumentation\",\n    \"elasticapm.instrumentation.packages.pymongo.PyMongoBulkInstrumentation\",\n    \"elasticapm.instrumentation.packages.pymongo.PyMongoCursorInstrumentation\",\n    \"elasticapm.instrumentation.packages.python_memcached.PythonMemcachedInstrumentation\",\n    \"elasticapm.instrumentation.packages.redis.RedisInstrumentation\",\n    \"elasticapm.instrumentation.packages.redis.RedisPipelineInstrumentation\",\n    \"elasticapm.instrumentation.packages.requests.RequestsInstrumentation\",\n    \"elasticapm.instrumentation.packages.sqlite.SQLiteInstrumentation\",\n    \"elasticapm.instrumentation.packages.urllib3.Urllib3Instrumentation\",\n    \"elasticapm.instrumentation.packages.elasticsearch.ElasticsearchConnectionInstrumentation\",\n    \"elasticapm.instrumentation.packages.elasticsearch.ElasticsearchInstrumentation\",\n    \"elasticapm.instrumentation.packages.cassandra.CassandraInstrumentation\",\n    \"elasticapm.instrumentation.packages.pymssql.PyMSSQLInstrumentation\",\n    \"elasticapm.instrumentation.packages.pyodbc.PyODBCInstrumentation\",\n    \"elasticapm.instrumentation.packages.django.template.DjangoTemplateInstrumentation\",\n    \"elasticapm.instrumentation.packages.django.template.DjangoTemplateSourceInstrumentation\",\n    \"elasticapm.instrumentation.packages.urllib.UrllibInstrumentation\",\n}\n\n\ndef register(cls):\n    _cls_register.add(cls)\n\n\n_instrumentation_singletons = {}\n\n\ndef get_instrumentation_objects():\n    for cls_str in _cls_register:\n        if cls_str not in _instrumentation_singletons:\n            cls = import_string(cls_str)\n            _instrumentation_singletons[cls_str] = cls()\n\n        obj = _instrumentation_singletons[cls_str]\n        yield obj\n", "path": "elasticapm/instrumentation/register.py"}, {"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 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__())\n\n\nclass PGConnectionProxy(ConnectionProxy):\n    cursor_proxy = PGCursorProxy\n\n    def __enter__(self):\n        return PGConnectionProxy(self.__wrapped__.__enter__())\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 += \" \" + str(host)\n\n            port = kwargs.get(\"port\")\n            if port:\n                port = str(port)\n                if int(port) != default_ports.get(\"postgresql\"):\n                    signature += \":\" + port\n        else:\n            # Parse connection string and extract host/port\n            pass\n\n        with capture_span(signature, span_type=\"db\", span_subtype=\"postgresql\", span_action=\"connect\"):\n            return PGConnectionProxy(wrapped(*args, **kwargs))\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"}]}