]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
refactor(tests): auto-generate test_connection from async
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>
Mon, 7 Aug 2023 23:16:19 +0000 (00:16 +0100)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Wed, 11 Oct 2023 21:45:38 +0000 (23:45 +0200)
tests/_test_connection.py
tests/test_connection.py
tests/test_connection_async.py
tests/test_dns.py
tests/utils.py
tools/async_to_sync.py
tools/convert_async_to_sync.sh

index 296a7f7f4b95a58246d30fca4da4b8610e68c09f..8ce09029351df0b1eb2237b3ca2042f3e88b5c36 100644 (file)
@@ -9,6 +9,22 @@ import pytest
 import psycopg
 
 
+async def aconn_set(conn, param, value):
+    """Equivalent of 'await conn.set_param(value)'
+
+    Converted to conn_set in sync tests.
+    """
+    await getattr(conn, f"set_{param}")(value)
+
+
+def conn_set(conn, param, value):
+    """Equivalent of 'conn.param = value'.
+
+    Converted from aconn_set in sync tests.
+    """
+    setattr(conn, param, value)
+
+
 @pytest.fixture
 def testctx(svcconn):
     svcconn.execute("create table if not exists testctx (id int primary key)")
index a15a21ec96027bb5a5c457c13b7a533bdb8f70b2..9516281b8934c819d410826010172d16e3acfcc0 100644 (file)
@@ -1,3 +1,6 @@
+# WARNING: this file is auto-generated by 'async_to_sync.py'
+# from the original file 'test_connection_async.py'
+# DO NOT CHANGE! Change the original file instead.
 import sys
 import time
 import pytest
@@ -10,10 +13,10 @@ from psycopg import Notify, pq, errors as e
 from psycopg.rows import tuple_row
 from psycopg.conninfo import conninfo_to_dict, make_conninfo
 
-from .utils import gc_collect
+from .utils import gc_collect, is_async
 from ._test_cursor import my_row_factory
 from ._test_connection import tx_params, tx_params_isolation, tx_values_map
-from ._test_connection import conninfo_params_timeout
+from ._test_connection import conninfo_params_timeout, conn_set
 from ._test_connection import testctx  # noqa: F401  # fixture
 from .test_adapt import make_bin_dumper, make_dumper
 
@@ -93,8 +96,12 @@ def test_cursor_closed(conn):
 
 # TODO: the INERROR started failing in the C implementation in Python 3.12a7
 # compiled with Cython-3.0.0b3, not before.
+
+
 @pytest.mark.xfail(
-    (pq.__impl__ in ("c", "binary") and sys.version_info[:2] == (3, 12)),
+    pq.__impl__ in ("c", "binary")
+    and sys.version_info[:2] == (3, 12)
+    and (not is_async(__name__)),
     reason="Something with Exceptions, C, Python 3.12",
 )
 def test_connection_warn_close(conn_cls, dsn, recwarn):
@@ -173,8 +180,7 @@ def test_context_inerror_rollback_no_clobber(conn_cls, conn, dsn, caplog):
         with conn_cls.connect(dsn) as conn2:
             conn2.execute("select 1")
             conn.execute(
-                "select pg_terminate_backend(%s::int)",
-                [conn2.pgconn.backend_pid],
+                "select pg_terminate_backend(%s::int)", [conn2.pgconn.backend_pid]
             )
             1 / 0
 
@@ -230,14 +236,14 @@ def test_commit(conn):
 
 @pytest.mark.crdb_skip("deferrable")
 def test_commit_error(conn):
-    conn.execute(
-        """
-        drop table if exists selfref;
-        create table selfref (
-            x serial primary key,
-            y int references selfref (x) deferrable initially deferred)
-        """
-    )
+    sql = [
+        "drop table if exists selfref;",
+        "create table selfref (",
+        "x serial primary key,",
+        "y int references selfref (x) deferrable initially deferred)",
+    ]
+
+    conn.execute("".join(sql))
     conn.commit()
 
     conn.execute("insert into selfref (y) values (-1)")
@@ -276,7 +282,8 @@ def test_auto_transaction(conn):
 
     conn.commit()
     assert conn.pgconn.transaction_status == conn.TransactionStatus.IDLE
-    assert cur.execute("select * from foo").fetchone() == (1,)
+    cur.execute("select * from foo")
+    assert cur.fetchone() == (1,)
     assert conn.pgconn.transaction_status == conn.TransactionStatus.INTRANS
 
 
@@ -299,21 +306,34 @@ def test_auto_transaction_fail(conn):
 
     conn.commit()
     assert conn.pgconn.transaction_status == conn.TransactionStatus.IDLE
-    assert cur.execute("select * from foo").fetchone() is None
+    cur.execute("select * from foo")
+    assert cur.fetchone() is None
     assert conn.pgconn.transaction_status == conn.TransactionStatus.INTRANS
 
 
+@pytest.mark.skipif(not is_async(__name__), reason="async test only")
+def test_autocommit_readonly_property(conn):
+    with pytest.raises(AttributeError):
+        conn.autocommit = True
+    assert not conn.autocommit
+
+
 def test_autocommit(conn):
     assert conn.autocommit is False
+
     conn.autocommit = True
     assert conn.autocommit
     cur = conn.cursor()
-    assert cur.execute("select 1").fetchone() == (1,)
+    cur.execute("select 1")
+    assert cur.fetchone() == (1,)
     assert conn.pgconn.transaction_status == conn.TransactionStatus.IDLE
 
     conn.autocommit = ""
-    assert conn.autocommit is False  # type: ignore[comparison-overlap]
+    assert isinstance(conn.autocommit, bool)
+    assert conn.autocommit is False
+
     conn.autocommit = "yeah"
+    assert isinstance(conn.autocommit, bool)
     assert conn.autocommit is True
 
 
@@ -325,7 +345,8 @@ def test_autocommit_connect(conn_cls, dsn):
 
 def test_autocommit_intrans(conn):
     cur = conn.cursor()
-    assert cur.execute("select 1").fetchone() == (1,)
+    cur.execute("select 1")
+    assert cur.fetchone() == (1,)
     assert conn.pgconn.transaction_status == conn.TransactionStatus.INTRANS
     with pytest.raises(psycopg.ProgrammingError):
         conn.autocommit = True
@@ -355,17 +376,17 @@ def test_autocommit_unknown(conn):
     [
         ((), {}, ""),
         (("",), {}, ""),
-        (("host=foo user=bar",), {}, "host=foo user=bar"),
-        (("host=foo",), {"user": "baz"}, "host=foo user=baz"),
+        (("dbname=foo user=bar",), {}, "dbname=foo user=bar"),
+        (("dbname=foo",), {"user": "baz"}, "dbname=foo user=baz"),
         (
-            ("host=foo port=5432",),
-            {"host": "qux", "user": "joe"},
-            "host=qux user=joe port=5432",
+            ("dbname=foo port=5432",),
+            {"dbname": "qux", "user": "joe"},
+            "dbname=qux user=joe port=5432",
         ),
-        (("host=foo",), {"user": None}, "host=foo"),
+        (("dbname=foo",), {"user": None}, "dbname=foo"),
     ],
 )
-def test_connect_args(conn_cls, monkeypatch, pgconn, args, kwargs, want):
+def test_connect_args(conn_cls, monkeypatch, setpgenv, pgconn, args, kwargs, want):
     the_conninfo: str
 
     def fake_connect(conninfo):
@@ -374,6 +395,7 @@ def test_connect_args(conn_cls, monkeypatch, pgconn, args, kwargs, want):
         return pgconn
         yield
 
+    setpgenv({})
     monkeypatch.setattr(psycopg.connection, "connect", fake_connect)
     conn = conn_cls.connect(*args, **kwargs)
     assert conninfo_to_dict(the_conninfo) == conninfo_to_dict(want)
@@ -587,23 +609,32 @@ def test_server_cursor_factory(conn):
 @pytest.mark.parametrize("param", tx_params)
 def test_transaction_param_default(conn, param):
     assert getattr(conn, param.name) is None
-    current, default = conn.execute(
+    cur = conn.execute(
         "select current_setting(%s), current_setting(%s)",
         [f"transaction_{param.guc}", f"default_transaction_{param.guc}"],
-    ).fetchone()
+    )
+    (current, default) = cur.fetchone()
     assert current == default
 
 
+@pytest.mark.skipif(not is_async(__name__), reason="async test only")
+@pytest.mark.parametrize("param", tx_params)
+def test_transaction_param_readonly_property(conn, param):
+    with pytest.raises(AttributeError):
+        setattr(conn, param.name, None)
+
+
 @pytest.mark.parametrize("autocommit", [True, False])
 @pytest.mark.parametrize("param", tx_params_isolation)
 def test_set_transaction_param_implicit(conn, param, autocommit):
     conn.autocommit = autocommit
     for value in param.values:
-        setattr(conn, param.name, value)
-        pgval, default = conn.execute(
+        conn_set(conn, param.name, value)
+        cur = conn.execute(
             "select current_setting(%s), current_setting(%s)",
             [f"transaction_{param.guc}", f"default_transaction_{param.guc}"],
-        ).fetchone()
+        )
+        (pgval, default) = cur.fetchone()
         if autocommit:
             assert pgval == default
         else:
@@ -620,17 +651,15 @@ def test_set_transaction_param_reset(conn, param):
     conn.commit()
 
     for value in param.values:
-        setattr(conn, param.name, value)
-        (pgval,) = conn.execute(
-            "select current_setting(%s)", [f"transaction_{param.guc}"]
-        ).fetchone()
+        conn_set(conn, param.name, value)
+        cur = conn.execute("select current_setting(%s)", [f"transaction_{param.guc}"])
+        (pgval,) = cur.fetchone()
         assert tx_values_map[pgval] == value
         conn.rollback()
 
-        setattr(conn, param.name, None)
-        (pgval,) = conn.execute(
-            "select current_setting(%s)", [f"transaction_{param.guc}"]
-        ).fetchone()
+        conn_set(conn, param.name, None)
+        cur = conn.execute("select current_setting(%s)", [f"transaction_{param.guc}"])
+        (pgval,) = cur.fetchone()
         assert tx_values_map[pgval] == tx_values_map[param.non_default]
         conn.rollback()
 
@@ -640,34 +669,39 @@ def test_set_transaction_param_reset(conn, param):
 def test_set_transaction_param_block(conn, param, autocommit):
     conn.autocommit = autocommit
     for value in param.values:
-        setattr(conn, param.name, value)
+        conn_set(conn, param.name, value)
         with conn.transaction():
-            pgval = conn.execute(
+            cur = conn.execute(
                 "select current_setting(%s)", [f"transaction_{param.guc}"]
-            ).fetchone()[0]
+            )
+            pgval = cur.fetchone()[0]
         assert tx_values_map[pgval] == value
 
 
 @pytest.mark.parametrize("param", tx_params)
 def test_set_transaction_param_not_intrans_implicit(conn, param):
     conn.execute("select 1")
+    value = param.values[0]
     with pytest.raises(psycopg.ProgrammingError):
-        setattr(conn, param.name, param.values[0])
+        conn_set(conn, param.name, value)
 
 
 @pytest.mark.parametrize("param", tx_params)
 def test_set_transaction_param_not_intrans_block(conn, param):
+    value = param.values[0]
     with conn.transaction():
         with pytest.raises(psycopg.ProgrammingError):
-            setattr(conn, param.name, param.values[0])
+            conn_set(conn, param.name, value)
 
 
 @pytest.mark.parametrize("param", tx_params)
 def test_set_transaction_param_not_intrans_external(conn, param):
+    value = param.values[0]
+
     conn.autocommit = True
     conn.execute("begin")
     with pytest.raises(psycopg.ProgrammingError):
-        setattr(conn, param.name, param.values[0])
+        conn_set(conn, param.name, value)
 
 
 @pytest.mark.crdb("skip", reason="transaction isolation")
@@ -677,12 +711,11 @@ def test_set_transaction_param_all(conn):
 
     for param in params:
         value = param.values[0]
-        setattr(conn, param.name, value)
+        conn_set(conn, param.name, value)
 
     for param in params:
-        pgval = conn.execute(
-            "select current_setting(%s)", [f"transaction_{param.guc}"]
-        ).fetchone()[0]
+        cur = conn.execute("select current_setting(%s)", [f"transaction_{param.guc}"])
+        pgval = cur.fetchone()[0]
         assert tx_values_map[pgval] == value
 
 
@@ -702,14 +735,15 @@ def test_set_transaction_param_strange(conn):
 
 
 @pytest.mark.parametrize("dsn, kwargs, exp", conninfo_params_timeout)
-def test_get_connection_params(conn_cls, dsn, kwargs, exp):
+def test_get_connection_params(conn_cls, dsn, kwargs, exp, setpgenv):
+    setpgenv({})
     params = conn_cls._get_connection_params(dsn, **kwargs)
     conninfo = make_conninfo(**params)
     assert conninfo_to_dict(conninfo) == exp[0]
-    assert params.get("connect_timeout") == exp[1]
+    assert params["connect_timeout"] == exp[1]
 
 
-def test_connect_context(conn_cls, dsn):
+def test_connect_context_adapters(conn_cls, dsn):
     ctx = psycopg.adapt.AdaptersMap(psycopg.adapters)
     ctx.register_dumper(str, make_bin_dumper("b"))
     ctx.register_dumper(str, make_dumper("t"))
index 4b1d797ebd1c21e2a4bda7e10ae340173de4e5e3..98edb2d21acd6399ee0af4910c20aa1eec3f2522 100644 (file)
@@ -1,21 +1,21 @@
+import sys
 import time
 import pytest
 import logging
 import weakref
-from typing import List, Any
+from typing import Any, List
 
 import psycopg
-from psycopg import Notify, errors as e
+from psycopg import Notify, pq, errors as e
 from psycopg.rows import tuple_row
 from psycopg.conninfo import conninfo_to_dict, make_conninfo
 
-from .utils import gc_collect
+from .utils import gc_collect, is_async
 from ._test_cursor import my_row_factory
 from ._test_connection import tx_params, tx_params_isolation, tx_values_map
-from ._test_connection import conninfo_params_timeout
+from ._test_connection import conninfo_params_timeout, aconn_set
 from ._test_connection import testctx  # noqa: F401  # fixture
 from .test_adapt import make_bin_dumper, make_dumper
-from .test_conninfo import fake_resolve  # noqa: F401  # fixture
 
 
 async def test_connect(aconn_cls, dsn):
@@ -93,6 +93,14 @@ async def test_cursor_closed(aconn):
         aconn.cursor()
 
 
+# TODO: the INERROR started failing in the C implementation in Python 3.12a7
+# compiled with Cython-3.0.0b3, not before.
+@pytest.mark.xfail(
+    pq.__impl__ in ("c", "binary")
+    and sys.version_info[:2] == (3, 12)
+    and not is_async(__name__),
+    reason="Something with Exceptions, C, Python 3.12",
+)
 async def test_connection_warn_close(aconn_cls, dsn, recwarn):
     conn = await aconn_cls.connect(dsn)
     await conn.close()
@@ -226,14 +234,14 @@ async def test_commit(aconn):
 
 @pytest.mark.crdb_skip("deferrable")
 async def test_commit_error(aconn):
-    await aconn.execute(
-        """
-        drop table if exists selfref;
-        create table selfref (
-            x serial primary key,
-            y int references selfref (x) deferrable initially deferred)
-        """
-    )
+    sql = [
+        "drop table if exists selfref;",
+        "create table selfref (",
+        "x serial primary key,",
+        "y int references selfref (x) deferrable initially deferred)",
+    ]
+
+    await aconn.execute("".join(sql))
     await aconn.commit()
 
     await aconn.execute("insert into selfref (y) values (-1)")
@@ -301,12 +309,15 @@ async def test_auto_transaction_fail(aconn):
     assert aconn.pgconn.transaction_status == aconn.TransactionStatus.INTRANS
 
 
-async def test_autocommit(aconn):
-    assert aconn.autocommit is False
+@pytest.mark.skipif(not is_async(__name__), reason="async test only")
+async def test_autocommit_readonly_property(aconn):
     with pytest.raises(AttributeError):
         aconn.autocommit = True
     assert not aconn.autocommit
 
+
+async def test_autocommit(aconn):
+    assert aconn.autocommit is False
     await aconn.set_autocommit(True)
     assert aconn.autocommit
     cur = aconn.cursor()
@@ -315,8 +326,11 @@ async def test_autocommit(aconn):
     assert aconn.pgconn.transaction_status == aconn.TransactionStatus.IDLE
 
     await aconn.set_autocommit("")
+    assert isinstance(aconn.autocommit, bool)
     assert aconn.autocommit is False
+
     await aconn.set_autocommit("yeah")
+    assert isinstance(aconn.autocommit, bool)
     assert aconn.autocommit is True
 
 
@@ -492,6 +506,7 @@ async def test_notify_handlers(aconn):
     assert n.channel == "foo"
     assert n.payload == "n2"
     assert n.pid == aconn.pgconn.backend_pid
+    assert hash(n)
 
     with pytest.raises(ValueError):
         aconn.remove_notify_handler(cb1)
@@ -603,6 +618,7 @@ async def test_transaction_param_default(aconn, param):
     assert current == default
 
 
+@pytest.mark.skipif(not is_async(__name__), reason="async test only")
 @pytest.mark.parametrize("param", tx_params)
 async def test_transaction_param_readonly_property(aconn, param):
     with pytest.raises(AttributeError):
@@ -614,7 +630,7 @@ async def test_transaction_param_readonly_property(aconn, param):
 async def test_set_transaction_param_implicit(aconn, param, autocommit):
     await aconn.set_autocommit(autocommit)
     for value in param.values:
-        await getattr(aconn, f"set_{param.name}")(value)
+        await aconn_set(aconn, param.name, value)
         cur = await aconn.execute(
             "select current_setting(%s), current_setting(%s)",
             [f"transaction_{param.guc}", f"default_transaction_{param.guc}"],
@@ -636,7 +652,7 @@ async def test_set_transaction_param_reset(aconn, param):
     await aconn.commit()
 
     for value in param.values:
-        await getattr(aconn, f"set_{param.name}")(value)
+        await aconn_set(aconn, param.name, value)
         cur = await aconn.execute(
             "select current_setting(%s)", [f"transaction_{param.guc}"]
         )
@@ -644,7 +660,7 @@ async def test_set_transaction_param_reset(aconn, param):
         assert tx_values_map[pgval] == value
         await aconn.rollback()
 
-        await getattr(aconn, f"set_{param.name}")(None)
+        await aconn_set(aconn, param.name, None)
         cur = await aconn.execute(
             "select current_setting(%s)", [f"transaction_{param.guc}"]
         )
@@ -658,7 +674,7 @@ async def test_set_transaction_param_reset(aconn, param):
 async def test_set_transaction_param_block(aconn, param, autocommit):
     await aconn.set_autocommit(autocommit)
     for value in param.values:
-        await getattr(aconn, f"set_{param.name}")(value)
+        await aconn_set(aconn, param.name, value)
         async with aconn.transaction():
             cur = await aconn.execute(
                 "select current_setting(%s)", [f"transaction_{param.guc}"]
@@ -672,7 +688,7 @@ async def test_set_transaction_param_not_intrans_implicit(aconn, param):
     await aconn.execute("select 1")
     value = param.values[0]
     with pytest.raises(psycopg.ProgrammingError):
-        await getattr(aconn, f"set_{param.name}")(value)
+        await aconn_set(aconn, param.name, value)
 
 
 @pytest.mark.parametrize("param", tx_params)
@@ -680,7 +696,7 @@ async def test_set_transaction_param_not_intrans_block(aconn, param):
     value = param.values[0]
     async with aconn.transaction():
         with pytest.raises(psycopg.ProgrammingError):
-            await getattr(aconn, f"set_{param.name}")(value)
+            await aconn_set(aconn, param.name, value)
 
 
 @pytest.mark.parametrize("param", tx_params)
@@ -689,7 +705,7 @@ async def test_set_transaction_param_not_intrans_external(aconn, param):
     await aconn.set_autocommit(True)
     await aconn.execute("begin")
     with pytest.raises(psycopg.ProgrammingError):
-        await getattr(aconn, f"set_{param.name}")(value)
+        await aconn_set(aconn, param.name, value)
 
 
 @pytest.mark.crdb("skip", reason="transaction isolation")
@@ -699,7 +715,7 @@ async def test_set_transaction_param_all(aconn):
 
     for param in params:
         value = param.values[0]
-        await getattr(aconn, f"set_{param.name}")(value)
+        await aconn_set(aconn, param.name, value)
 
     for param in params:
         cur = await aconn.execute(
@@ -751,33 +767,15 @@ async def test_connect_context_copy(aconn_cls, dsn, aconn):
     aconn.adapters.register_dumper(str, make_bin_dumper("b"))
     aconn.adapters.register_dumper(str, make_dumper("t"))
 
-    aconn2 = await aconn_cls.connect(dsn, context=aconn)
+    conn2 = await aconn_cls.connect(dsn, context=aconn)
 
-    cur = await aconn2.execute("select %s", ["hello"])
+    cur = await conn2.execute("select %s", ["hello"])
     assert (await cur.fetchone())[0] == "hellot"
-    cur = await aconn2.execute("select %b", ["hello"])
+    cur = await conn2.execute("select %b", ["hello"])
     assert (await cur.fetchone())[0] == "hellob"
-    await aconn2.close()
+    await conn2.close()
 
 
 async def test_cancel_closed(aconn):
     await aconn.close()
     aconn.cancel()
-
-
-@pytest.mark.usefixtures("fake_resolve")
-async def test_resolve_hostaddr_conn(aconn_cls, monkeypatch):
-    got = []
-
-    def fake_connect_gen(conninfo, **kwargs):
-        got.append(conninfo)
-        1 / 0
-
-    monkeypatch.setattr(aconn_cls, "_connect_gen", fake_connect_gen)
-
-    with pytest.raises(ZeroDivisionError):
-        await aconn_cls.connect("host=foo.com")
-
-    assert len(got) == 1
-    want = {"host": "foo.com", "hostaddr": "1.1.1.1"}
-    assert conninfo_to_dict(got[0]) == want
index 2eb5569df944309ac2ae0814145365ab11adfa56..b1e8891155f523f3730d1078d2808c3e6a495544 100644 (file)
@@ -3,9 +3,28 @@ import pytest
 import psycopg
 from psycopg.conninfo import conninfo_to_dict
 
-pytestmark = [pytest.mark.dns]
+from .test_conninfo import fake_resolve  # noqa: F401  # fixture
 
 
+@pytest.mark.usefixtures("fake_resolve")
+async def test_resolve_hostaddr_conn(aconn_cls, monkeypatch):
+    got = []
+
+    def fake_connect_gen(conninfo, **kwargs):
+        got.append(conninfo)
+        1 / 0
+
+    monkeypatch.setattr(aconn_cls, "_connect_gen", fake_connect_gen)
+
+    with pytest.raises(ZeroDivisionError):
+        await aconn_cls.connect("host=foo.com")
+
+    assert len(got) == 1
+    want = {"host": "foo.com", "hostaddr": "1.1.1.1"}
+    assert conninfo_to_dict(got[0]) == want
+
+
+@pytest.mark.dns
 @pytest.mark.anyio
 async def test_resolve_hostaddr_async_warning(recwarn):
     import_dnspython()
index f20ca7920ef26e7b2c953129ade48c66c90ae600..64d72fe7faa21a1cd621fe529742eb9fd740b9a6 100644 (file)
@@ -145,6 +145,17 @@ def gc_collect():
         gc.collect()
 
 
+def is_async(obj):
+    """Return true if obj is an async object (class, instance, module name)"""
+    if isinstance(obj, str):
+        # coming from is_async(__name__)
+        return "async" in obj
+
+    if not isinstance(obj, type):
+        obj = type(obj)
+    return "Async" in obj.__name__
+
+
 NO_COUNT_TYPES: Tuple[type, ...] = ()
 
 if sys.version_info[:2] == (3, 10):
index 90ab125aa77935ecb220264658fca6a3b1d39c37..e34c7ae4389e4a7e6c5e42768349cca7b3637e52 100755 (executable)
@@ -33,7 +33,7 @@ def async_to_sync(tree: ast.AST) -> ast.AST:
     tree = BlanksInserter().visit(tree)
     tree = AsyncToSync().visit(tree)
     tree = RenameAsyncToSync().visit(tree)
-    tree = FixSetAutocommit().visit(tree)
+    tree = FixAsyncSetters().visit(tree)
     return tree
 
 
@@ -85,9 +85,12 @@ class RenameAsyncToSync(ast.NodeTransformer):
         "AsyncClientCursor": "ClientCursor",
         "AsyncCursor": "Cursor",
         "AsyncRawCursor": "RawCursor",
+        "AsyncServerCursor": "ServerCursor",
         "aclose": "close",
         "aclosing": "closing",
         "aconn": "conn",
+        "aconn_cls": "conn_cls",
+        "aconn_set": "conn_set",
         "alist": "list",
         "anext": "next",
     }
@@ -136,24 +139,31 @@ class RenameAsyncToSync(ast.NodeTransformer):
         return node
 
 
-class FixSetAutocommit(ast.NodeTransformer):
+class FixAsyncSetters(ast.NodeTransformer):
+    setters_map = {
+        "set_autocommit": "autocommit",
+        "set_read_only": "read_only",
+        "set_isolation_level": "isolation_level",
+        "set_deferrable": "deferrable",
+    }
+
     def visit_Call(self, node: ast.Call) -> ast.AST:
-        new_node = self._fix_autocommit(node)
+        new_node = self._fix_setter(node)
         if new_node:
             return new_node
 
         self.generic_visit(node)
         return node
 
-    def _fix_autocommit(self, node: ast.Call) -> ast.AST | None:
+    def _fix_setter(self, node: ast.Call) -> ast.AST | None:
         if not isinstance(node.func, ast.Attribute):
             return None
-        if node.func.attr != "set_autocommit":
+        if node.func.attr not in self.setters_map:
             return None
         obj = node.func.value
         arg = node.args[0]
         new_node = ast.Assign(
-            targets=[ast.Attribute(value=obj, attr="autocommit")],
+            targets=[ast.Attribute(value=obj, attr=self.setters_map[node.func.attr])],
             value=arg,
         )
         ast.copy_location(new_node, node)
index 93bbc6548f049003c6fcc2eb99b83561bea442b2..e9b9031e829f8196692b469c6d5275c288b4fe65 100755 (executable)
@@ -7,5 +7,7 @@ set -euo pipefail
 dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
 cd "${dir}/.."
 
+python "${dir}/async_to_sync.py" tests/test_connection_async.py > tests/test_connection.py
+black -q tests/test_connection.py
 python "${dir}/async_to_sync.py" tests/test_cursor_async.py > tests/test_cursor.py
 black -q tests/test_cursor.py