Skip to content

Commit 8bb0f9b

Browse files
committed
Merge branch 'async-to-sync'
2 parents b1c3c19 + 5e99834 commit 8bb0f9b

99 files changed

Lines changed: 9697 additions & 8486 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/lint.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222

2323
- uses: actions/setup-python@v4
2424
with:
25-
python-version: "3.10"
25+
python-version: "3.11"
2626

2727
- name: install packages to tests
2828
run: pip install ./psycopg[dev,test] codespell
@@ -36,6 +36,9 @@ jobs:
3636
- name: Run mypy
3737
run: mypy
3838

39+
- name: Check for sync/async inconsistencies
40+
run: ./tools/async_to_sync.py --all --check
41+
3942
- name: Check spelling
4043
run: codespell
4144

docs/api/connections.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,10 @@ The `!Connection` class
208208
ones: you should call `!await` `~AsyncConnection.set_autocommit`
209209
:samp:`({value})` instead.
210210

211+
.. automethod:: set_autocommit
212+
213+
.. versionadded:: 3.2
214+
211215
The following three properties control the characteristics of new
212216
transactions. See :ref:`transaction-characteristics` for details.
213217

@@ -219,6 +223,10 @@ The `!Connection` class
219223
.. __: https://www.postgresql.org/docs/current/runtime-config-client.html
220224
#GUC-DEFAULT-TRANSACTION-ISOLATION
221225

226+
.. automethod:: set_isolation_level
227+
228+
.. versionadded:: 3.2
229+
222230
.. autoattribute:: read_only
223231

224232
`!None` means use the default set in the default_transaction_read_only__
@@ -227,6 +235,10 @@ The `!Connection` class
227235
.. __: https://www.postgresql.org/docs/current/runtime-config-client.html
228236
#GUC-DEFAULT-TRANSACTION-READ-ONLY
229237

238+
.. automethod:: set_read_only
239+
240+
.. versionadded:: 3.2
241+
230242
.. autoattribute:: deferrable
231243

232244
`!None` means use the default set in the default_transaction_deferrable__
@@ -235,6 +247,10 @@ The `!Connection` class
235247
.. __: https://www.postgresql.org/docs/current/runtime-config-client.html
236248
#GUC-DEFAULT-TRANSACTION-DEFERRABLE
237249

250+
.. automethod:: set_deferrable
251+
252+
.. versionadded:: 3.2
253+
238254

239255
.. rubric:: Checking and configuring the connection state
240256

docs/api/dns.rst

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,14 @@ server before performing a connection.
8989

9090
.. automethod:: psycopg.AsyncConnection._get_connection_params
9191

92-
.. warning::
93-
This is an experimental method.
92+
.. warning::
93+
This is an experimental method.
94+
95+
.. versionchanged:: 3.1
96+
Unlike the sync counterpart, perform non-blocking address
97+
resolution and populate the ``hostaddr`` connection parameter,
98+
unless the user has provided one themselves. See
99+
`resolve_hostaddr_async()` for details.
94100

95101

96102
.. function:: resolve_hostaddr_async(params)

docs/basic/transactions.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,10 @@ In order to set these parameters you can use the connection attributes
328328
can only be changed if there isn't a transaction already active on the
329329
connection.
330330

331+
.. versionadded:: 3.2
332+
Added methods equivalent to setting the properties (such as
333+
`~Connection.set_isolation_level()`) on sync connections too.
334+
331335
.. warning::
332336

333337
Applications running at `~IsolationLevel.REPEATABLE_READ` or

docs/news.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ Psycopg 3.2 (unreleased)
1717
(:ticket:`#332`).
1818
- Add :ref:`raw-query-cursors` to execute queries using placeholders in
1919
PostgreSQL format (`$1`, `$2`...) (:ticket:`#560`).
20+
- Add `~Connection.set_autocommit()` on sync connections, and similar
21+
transaction control methods available on the async connections.
2022
- Add support for libpq functions to close prepared statements and portals
2123
introduced in libpq v17 (:ticket:`#603`).
2224
- Disable receiving more than one result on the same cursor in pipeline mode,

psycopg/psycopg/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,13 @@
1919
from ._column import Column
2020
from .conninfo import ConnectionInfo
2121
from ._pipeline import Pipeline, AsyncPipeline
22-
from .connection import BaseConnection, Connection, Notify
22+
from .connection import Connection
2323
from .transaction import Rollback, Transaction, AsyncTransaction
2424
from .cursor_async import AsyncCursor
2525
from .server_cursor import AsyncServerCursor, ServerCursor
2626
from .client_cursor import AsyncClientCursor, ClientCursor
2727
from .raw_cursor import AsyncRawCursor, RawCursor
28+
from ._connection_base import BaseConnection, Notify
2829
from .connection_async import AsyncConnection
2930

3031
from . import dbapi20

psycopg/psycopg/_acompat.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
"""
2+
Utilities to ease the differences between async and sync code.
3+
4+
These object offer a similar interface between sync and async versions; the
5+
script async_to_sync.py will replace the async names with the sync names
6+
when generating the sync version.
7+
"""
8+
9+
# Copyright (C) 2023 The Psycopg Team
10+
11+
from __future__ import annotations
12+
13+
import queue
14+
import asyncio
15+
import threading
16+
from typing import Any, Callable, Coroutine, TypeVar, TYPE_CHECKING
17+
18+
from typing_extensions import TypeAlias
19+
20+
Worker: TypeAlias = threading.Thread
21+
AWorker: TypeAlias = "asyncio.Task[None]"
22+
T = TypeVar("T")
23+
24+
# Hack required on Python 3.8 because subclassing Queue[T] fails at runtime.
25+
# https://stackoverflow.com/questions/45414066/mypy-how-to-define-a-generic-subclass
26+
if TYPE_CHECKING:
27+
_GQueue: TypeAlias = queue.Queue
28+
_AGQueue: TypeAlias = asyncio.Queue
29+
30+
else:
31+
32+
class FakeGenericMeta(type):
33+
def __getitem__(self, item):
34+
return self
35+
36+
class _GQueue(queue.Queue, metaclass=FakeGenericMeta):
37+
pass
38+
39+
class _AGQueue(asyncio.Queue, metaclass=FakeGenericMeta):
40+
pass
41+
42+
43+
class Queue(_GQueue[T]):
44+
"""
45+
A Queue subclass with an interruptible get() method.
46+
"""
47+
48+
def get(self, block: bool = True, timeout: float | None = None) -> T:
49+
# Always specify a timeout to make the wait interruptible.
50+
if timeout is None:
51+
timeout = 24.0 * 60.0 * 60.0
52+
return super().get(block=block, timeout=timeout)
53+
54+
55+
class AQueue(_AGQueue[T]):
56+
pass
57+
58+
59+
def aspawn(
60+
f: Callable[..., Coroutine[Any, Any, None]],
61+
args: tuple[Any, ...] = (),
62+
name: str | None = None,
63+
) -> asyncio.Task[None]:
64+
"""
65+
Equivalent to asyncio.create_task.
66+
"""
67+
return asyncio.create_task(f(*args), name=name)
68+
69+
70+
def spawn(
71+
f: Callable[..., Any],
72+
args: tuple[Any, ...] = (),
73+
name: str | None = None,
74+
) -> threading.Thread:
75+
"""
76+
Equivalent to creating and running a daemon thread.
77+
"""
78+
t = threading.Thread(target=f, args=args, name=name, daemon=True)
79+
t.start()
80+
return t
81+
82+
83+
async def agather(*tasks: asyncio.Task[Any], timeout: float | None = None) -> None:
84+
"""
85+
Equivalent to asyncio.gather or Thread.join()
86+
"""
87+
wait = asyncio.gather(*tasks)
88+
try:
89+
if timeout is not None:
90+
await asyncio.wait_for(asyncio.shield(wait), timeout=timeout)
91+
else:
92+
await wait
93+
except asyncio.TimeoutError:
94+
pass
95+
else:
96+
return
97+
98+
99+
def gather(*tasks: threading.Thread, timeout: float | None = None) -> None:
100+
"""
101+
Equivalent to asyncio.gather or Thread.join()
102+
"""
103+
for t in tasks:
104+
if not t.is_alive():
105+
continue
106+
t.join(timeout)

psycopg/psycopg/_adapters_map.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from ._typeinfo import TypesRegistry
1616

1717
if TYPE_CHECKING:
18-
from .connection import BaseConnection
18+
from ._connection_base import BaseConnection
1919

2020
RV = TypeVar("RV")
2121

psycopg/psycopg/_column.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from operator import attrgetter
99

1010
if TYPE_CHECKING:
11-
from .cursor import BaseCursor
11+
from ._cursor_base import BaseCursor
1212

1313

1414
class ColumnData(NamedTuple):

0 commit comments

Comments
 (0)