Skip to content

Commit 5308a1f

Browse files
authored
Merge branch 'master' into fetchmany
2 parents 7a54353 + 3aa9894 commit 5308a1f

18 files changed

+347
-197
lines changed

.github/workflows/release.yml

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ jobs:
3737
mkdir -p dist/
3838
echo "${VERSION}" > dist/VERSION
3939
40-
- uses: actions/upload-artifact@v3
40+
- uses: actions/upload-artifact@v4
4141
with:
4242
name: dist
4343
path: dist/
@@ -56,7 +56,7 @@ jobs:
5656
submodules: true
5757

5858
- name: Set up Python
59-
uses: actions/setup-python@v4
59+
uses: actions/setup-python@v5
6060
with:
6161
python-version: "3.x"
6262

@@ -65,7 +65,7 @@ jobs:
6565
pip install -U setuptools wheel pip
6666
python setup.py sdist
6767
68-
- uses: actions/upload-artifact@v3
68+
- uses: actions/upload-artifact@v4
6969
with:
7070
name: dist
7171
path: dist/*.tar.*
@@ -77,10 +77,10 @@ jobs:
7777
include: ${{ steps.set-matrix.outputs.include }}
7878
steps:
7979
- uses: actions/checkout@v4
80-
- uses: actions/setup-python@v4
80+
- uses: actions/setup-python@v5
8181
with:
8282
python-version: "3.x"
83-
- run: pip install cibuildwheel==2.16.2
83+
- run: pip install cibuildwheel==2.21.3
8484
- id: set-matrix
8585
run: |
8686
MATRIX_INCLUDE=$(
@@ -119,13 +119,13 @@ jobs:
119119
if: runner.os == 'Linux'
120120
uses: docker/setup-qemu-action@v2
121121

122-
- uses: pypa/cibuildwheel@fff9ec32ed25a9c576750c91e06b410ed0c15db7 # v2.16.2
122+
- uses: pypa/cibuildwheel@7940a4c0e76eb2030e473a5f864f291f63ee879b # v2.21.3
123123
with:
124124
only: ${{ matrix.only }}
125125
env:
126126
CIBW_BUILD_VERBOSITY: 1
127127

128-
- uses: actions/upload-artifact@v3
128+
- uses: actions/upload-artifact@v4
129129
with:
130130
name: dist
131131
path: wheelhouse/*.whl
@@ -145,7 +145,7 @@ jobs:
145145
submodules: true
146146

147147
- name: Set up Python
148-
uses: actions/setup-python@v4
148+
uses: actions/setup-python@v5
149149
with:
150150
python-version: "3.x"
151151

@@ -186,7 +186,7 @@ jobs:
186186
fetch-depth: 5
187187
submodules: false
188188

189-
- uses: actions/download-artifact@v3
189+
- uses: actions/download-artifact@v4
190190
with:
191191
name: dist
192192
path: dist/

.github/workflows/tests.yml

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,13 @@ jobs:
1717
# job.
1818
strategy:
1919
matrix:
20-
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
20+
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
2121
os: [ubuntu-latest, macos-latest, windows-latest]
2222
loop: [asyncio, uvloop]
2323
exclude:
2424
# uvloop does not support windows
2525
- loop: uvloop
2626
os: windows-latest
27-
# No 3.12 release yet
28-
- loop: uvloop
29-
python-version: "3.12"
3027

3128
runs-on: ${{ matrix.os }}
3229

@@ -60,7 +57,7 @@ jobs:
6057
postgresql-version: 16
6158

6259
- name: Set up Python ${{ matrix.python-version }}
63-
uses: actions/setup-python@v4
60+
uses: actions/setup-python@v5
6461
if: steps.release.outputs.version == 0
6562
with:
6663
python-version: ${{ matrix.python-version }}
@@ -121,7 +118,7 @@ jobs:
121118
>> "${GITHUB_ENV}"
122119
123120
- name: Set up Python ${{ matrix.python-version }}
124-
uses: actions/setup-python@v4
121+
uses: actions/setup-python@v5
125122
if: steps.release.outputs.version == 0
126123
with:
127124
python-version: "3.x"

asyncpg/compat.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from __future__ import annotations
88

9+
import enum
910
import pathlib
1011
import platform
1112
import typing
@@ -78,3 +79,10 @@ def markcoroutinefunction(c): # type: ignore
7879
from collections.abc import ( # noqa: F401
7980
Awaitable as Awaitable,
8081
)
82+
83+
if sys.version_info < (3, 11):
84+
class StrEnum(str, enum.Enum):
85+
__str__ = str.__str__
86+
__repr__ = enum.Enum.__repr__
87+
else:
88+
from enum import StrEnum as StrEnum # noqa: F401

asyncpg/connect_utils.py

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ def parse(cls, sslmode):
4545
return getattr(cls, sslmode.replace('-', '_'))
4646

4747

48+
class SSLNegotiation(compat.StrEnum):
49+
postgres = "postgres"
50+
direct = "direct"
51+
52+
4853
_ConnectionParameters = collections.namedtuple(
4954
'ConnectionParameters',
5055
[
@@ -53,7 +58,7 @@ def parse(cls, sslmode):
5358
'database',
5459
'ssl',
5560
'sslmode',
56-
'direct_tls',
61+
'ssl_negotiation',
5762
'server_settings',
5863
'target_session_attrs',
5964
'krbsrvname',
@@ -269,6 +274,7 @@ def _parse_connect_dsn_and_args(*, dsn, host, port, user,
269274
auth_hosts = None
270275
sslcert = sslkey = sslrootcert = sslcrl = sslpassword = None
271276
ssl_min_protocol_version = ssl_max_protocol_version = None
277+
sslnegotiation = None
272278

273279
if dsn:
274280
parsed = urllib.parse.urlparse(dsn)
@@ -362,6 +368,9 @@ def _parse_connect_dsn_and_args(*, dsn, host, port, user,
362368
if 'sslrootcert' in query:
363369
sslrootcert = query.pop('sslrootcert')
364370

371+
if 'sslnegotiation' in query:
372+
sslnegotiation = query.pop('sslnegotiation')
373+
365374
if 'sslcrl' in query:
366375
sslcrl = query.pop('sslcrl')
367376

@@ -503,13 +512,36 @@ def _parse_connect_dsn_and_args(*, dsn, host, port, user,
503512
if ssl is None and have_tcp_addrs:
504513
ssl = 'prefer'
505514

515+
if direct_tls is not None:
516+
sslneg = (
517+
SSLNegotiation.direct if direct_tls else SSLNegotiation.postgres
518+
)
519+
else:
520+
if sslnegotiation is None:
521+
sslnegotiation = os.environ.get("PGSSLNEGOTIATION")
522+
523+
if sslnegotiation is not None:
524+
try:
525+
sslneg = SSLNegotiation(sslnegotiation)
526+
except ValueError:
527+
modes = ', '.join(
528+
m.name.replace('_', '-')
529+
for m in SSLNegotiation
530+
)
531+
raise exceptions.ClientConfigurationError(
532+
f'`sslnegotiation` parameter must be one of: {modes}'
533+
) from None
534+
else:
535+
sslneg = SSLNegotiation.postgres
536+
506537
if isinstance(ssl, (str, SSLMode)):
507538
try:
508539
sslmode = SSLMode.parse(ssl)
509540
except AttributeError:
510541
modes = ', '.join(m.name.replace('_', '-') for m in SSLMode)
511542
raise exceptions.ClientConfigurationError(
512-
'`sslmode` parameter must be one of: {}'.format(modes))
543+
'`sslmode` parameter must be one of: {}'.format(modes)
544+
) from None
513545

514546
# docs at https://www.postgresql.org/docs/10/static/libpq-connect.html
515547
if sslmode < SSLMode.allow:
@@ -676,7 +708,7 @@ def _parse_connect_dsn_and_args(*, dsn, host, port, user,
676708

677709
params = _ConnectionParameters(
678710
user=user, password=password, database=database, ssl=ssl,
679-
sslmode=sslmode, direct_tls=direct_tls,
711+
sslmode=sslmode, ssl_negotiation=sslneg,
680712
server_settings=server_settings,
681713
target_session_attrs=target_session_attrs,
682714
krbsrvname=krbsrvname, gsslib=gsslib)
@@ -882,9 +914,9 @@ async def __connect_addr(
882914
# UNIX socket
883915
connector = loop.create_unix_connection(proto_factory, addr)
884916

885-
elif params.ssl and params.direct_tls:
886-
# if ssl and direct_tls are given, skip STARTTLS and perform direct
887-
# SSL connection
917+
elif params.ssl and params.ssl_negotiation is SSLNegotiation.direct:
918+
# if ssl and ssl_negotiation is `direct`, skip STARTTLS and perform
919+
# direct SSL connection
888920
connector = loop.create_connection(
889921
proto_factory, *addr, ssl=params.ssl
890922
)

asyncpg/connection.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2049,7 +2049,7 @@ async def connect(dsn=None, *,
20492049
max_cacheable_statement_size=1024 * 15,
20502050
command_timeout=None,
20512051
ssl=None,
2052-
direct_tls=False,
2052+
direct_tls=None,
20532053
connection_class=Connection,
20542054
record_class=protocol.Record,
20552055
server_settings=None,

asyncpg/prepared_stmt.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,8 @@ async def explain(self, *args, analyze=False):
147147
# will discard any output that a SELECT would return, other
148148
# side effects of the statement will happen as usual. If you
149149
# wish to use EXPLAIN ANALYZE on an INSERT, UPDATE, DELETE,
150-
# CREATE TABLE AS, or EXECUTE statement without letting the
151-
# command affect your data, use this approach:
150+
# MERGE, CREATE TABLE AS, or EXECUTE statement without letting
151+
# the command affect your data, use this approach:
152152
# BEGIN;
153153
# EXPLAIN ANALYZE ...;
154154
# ROLLBACK;

asyncpg/protocol/prepared_stmt.pyx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ cdef class PreparedStatementState:
142142
# that the user tried to parametrize a statement that does
143143
# not support parameters.
144144
hint += (r' Note that parameters are supported only in'
145-
r' SELECT, INSERT, UPDATE, DELETE, and VALUES'
145+
r' SELECT, INSERT, UPDATE, DELETE, MERGE and VALUES'
146146
r' statements, and will *not* work in statements '
147147
r' like CREATE VIEW or DECLARE CURSOR.')
148148

asyncpg/utils.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,11 @@ async def _mogrify(conn, query, args):
4242

4343
# Finally, replace $n references with text values.
4444
return re.sub(
45-
r'\$(\d+)\b', lambda m: textified[int(m.group(1)) - 1], query)
45+
r"\$(\d+)\b",
46+
lambda m: (
47+
textified[int(m.group(1)) - 1]
48+
if textified[int(m.group(1)) - 1] is not None
49+
else "NULL"
50+
),
51+
query,
52+
)

pyproject.toml

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ classifiers = [
2828
"Topic :: Database :: Front-Ends",
2929
]
3030
dependencies = [
31-
'async_timeout>=4.0.3; python_version < "3.12.0"',
31+
'async_timeout>=4.0.3; python_version < "3.11.0"',
3232
]
3333

3434
[project.urls]
@@ -42,7 +42,7 @@ gssauth = [
4242
test = [
4343
'flake8~=6.1',
4444
'flake8-pyi~=24.1.0',
45-
'uvloop>=0.15.3; platform_system != "Windows" and python_version < "3.12.0"',
45+
'uvloop>=0.15.3; platform_system != "Windows" and python_version < "3.14.0"',
4646
'gssapi; platform_system == "Linux"',
4747
'k5test; platform_system == "Linux"',
4848
'sspilib; platform_system == "Windows"',
@@ -58,7 +58,6 @@ docs = [
5858
requires = [
5959
"setuptools>=60",
6060
"wheel",
61-
6261
"Cython(>=0.29.24,<4.0.0)"
6362
]
6463
build-backend = "setuptools.build_meta"
@@ -113,6 +112,15 @@ exclude_lines = [
113112
show_missing = true
114113

115114
[tool.mypy]
115+
exclude = [
116+
"^.eggs",
117+
"^.github",
118+
"^.vscode",
119+
"^build",
120+
"^dist",
121+
"^docs",
122+
"^tests",
123+
]
116124
incremental = true
117125
strict = true
118126
implicit_reexport = true

tests/certs/ca.cert.pem

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,35 @@
11
-----BEGIN CERTIFICATE-----
2-
MIIGFjCCA/6gAwIBAgIIDAM+rFY5KqgwDQYJKoZIhvcNAQELBQAwgaExCzAJBgNV
2+
MIIGJjCCBA6gAwIBAgIICJCUmtkcj2MwDQYJKoZIhvcNAQELBQAwgaExCzAJBgNV
33
BAYTAkNBMRAwDgYDVQQIDAdPbnRhcmlvMRAwDgYDVQQHDAdUb3JvbnRvMRgwFgYD
44
VQQKDA9NYWdpY1N0YWNrIEluYy4xFjAUBgNVBAsMDWFzeW5jcGcgdGVzdHMxHTAb
55
BgNVBAMMFGFzeW5jcGcgdGVzdCByb290IGNhMR0wGwYJKoZIhvcNAQkBFg5oZWxs
6-
b0BtYWdpYy5pbzAeFw0yMTA5MTMxNjA2MDFaFw00MDExMTMxNjA2MDFaMIGhMQsw
6+
b0BtYWdpYy5pbzAeFw0yNDEwMTYxNzIzNTZaFw00MzEyMTcxNzIzNTZaMIGhMQsw
77
CQYDVQQGEwJDQTEQMA4GA1UECAwHT250YXJpbzEQMA4GA1UEBwwHVG9yb250bzEY
88
MBYGA1UECgwPTWFnaWNTdGFjayBJbmMuMRYwFAYDVQQLDA1hc3luY3BnIHRlc3Rz
99
MR0wGwYDVQQDDBRhc3luY3BnIHRlc3Qgcm9vdCBjYTEdMBsGCSqGSIb3DQEJARYO
10-
aGVsbG9AbWFnaWMuaW8wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDK
11-
mu24288Os23VtRf8kp57sj7+s+PSD/8+KiZiJ4sy5KrUUVijVQgfCpxPzpWWtQ/7
12-
JbjQMt+kZqJwKqdzXAY8osnljpYYvbNWnc0GZY09F6z95GqVgX/81Fe8W3Jz6I9w
13-
S2CXVneKGtux+6fztKbrA2b1kn69b3xClEHRLFZl9hKG8ck2H+gI5AEDgQmhTIXa
14-
pl85bPuh54uKiUGnedPk07biCw3ZE5GTGWzEq5qMqFEfb19/L1vOvgx/Q4aqmjJw
15-
lONB9DzMftetdKaR5SS+vH0QUhiWXwy7j1TjYtJP4M6fLinwguMYG8Qbg7NkL4QC
16-
9T7zR5CZPJ0Q/Npiwv7qdMzyL7QklZ9y3YeA5wceyc2/zh0INN5bf4J1mDZjhYH9
17-
CIgVHSj6z44rWq9L+OzYT0EMDhZO0OeakTWgqXNICfeEXZ5hy3QVCUvKrgmnqs0f
18-
imdH6dZQIGQIQ8Vcg/psk2hEP1hRWROn/cgCdadcEqbMdbtOUuMcnr0K6B/bVbXx
19-
jAV4eVcCcS3w3wIG4Ki2aIXnXrHyEJmZJb03Ko7VXP0NTGuGfPYQj2ox4a4wViOG
20-
pxxbnGGAFqV+BIVlhUMfL9PlatqsI6kUzJIsJUiyk6oPb3KeNQ5+MtS0S1DV0jA5
21-
wxDQZyEFiUsl6GLYSm4RajxoHdLR7Xqj3D7EWKGt/wIDAQABo1AwTjAMBgNVHRME
22-
BTADAQH/MB0GA1UdDgQWBBRvLFXv6sI+ePP5aegYUWoVHAfRzTAfBgNVHSMEGDAW
23-
gBRvLFXv6sI+ePP5aegYUWoVHAfRzTANBgkqhkiG9w0BAQsFAAOCAgEAK+QAtzhk
24-
ih8Tng9cOheswrbWf9pclMyfl38+NsJxsZnpa2SlBp3qJl0fymyNLLBfyeRUFr++
25-
x1cRAEwVv6R6Iepj252+U+Cmz48xIthF29JxoC+x2P2YDGyqVBm4uuw54EIF0r0H
26-
AvjTPSNa54gA3+KiK64ypFdlHZrwx3W9b5tUsfycpj2Jrn2HgTbWQD2gaYeIIdq6
27-
DNmPCJg6NQE9jlvNmVqlBavjc7MJqqd+0+XtCIWhaoqeu/T6g2Epth25cuqPKc0E
28-
rltKiXNiZHcDfFnu7B6kw2LVA6EQdf5GO9JtAaiwhRugp1dJ5rdQqdaYpJngZtvd
29-
8+PSdDZrXow0a1jW2w+3lM5XW3qtzIKJz4Q8CXL540s+SeRjLRwY02OZCvG4fC8c
30-
D57MIFKoReYy5LgBHdPGmx8Kexo7vk2ib9taQCSd6fh0Ol070pNiOnLP9lE9iEqq
31-
EvU1A+0dtPHbfyXqw9tdY18nxXbooypQZSqfxPSq3Bpv8KTsr9SSG+DV2LcJRfvi
32-
OfVTPeIWW8C8SkbEXaTCUVgaNeYqvFsfsvkTmfhO8GHglDgnsveXHfnAwlC2Uxdq
33-
T64oKToV7N1L2RA0JR9gJ4RQwPfyaFOHOPjd+3t4DFVl54GNbNfvELHRReoyJPse
34-
SZeL4h6T3L17FWzugHMjxFi4f1/nPNk7d5Y=
10+
aGVsbG9AbWFnaWMuaW8wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCP
11+
+oCl0qrReSlWj+yvfGz68UQqm6joL9VgeA0Tvc8S23Ia3S73wcTTdGhIQwMOaIuW
12+
y+m3J3js2wtpF0fmULYHr1ED7vQ+QOWarTyv/cGxSCyOYo4KVPHBfT6lYQTJk5NW
13+
Oc2wr5ff/9nhdO61sGxZa2GVBjmbLOJ9IBKTvRcmNgLmPo60wMHtF4L5/PuwVPuu
14+
+zRoETfEh12avtY7Y2G+0i4ZRm4uBmw7hmByWzWCwqrV619BaFHaJUf2bEh5eCbz
15+
1nhF7WHVjBfnSJOgDxmZbKZZPmNzTVm8UxN22g9Ao6cZSxjbFAdpIhlQhAT6sjlW
16+
hvI6b58A3AJKi7zo+a7lnbPIeckduSkgbil3LZ4KxWgx6fPCBLqGH1XN6I8MQnX/
17+
e1ewiFXwuZMb+FgoKxaQBseuPVaA3ViYefysjvLjP7U9eRzv6qRimOmH5efaplbD
18+
zGhRUKA8GgmN/B+S3ofqDhpp3zz7gFxjkE1f4/XNACqXt79iGaH+EscV4znxlsZj
19+
gUQYAcExpAmKrJg5kmxagHcgu0pVKlyUvSba/kKQ/aYDgdddgPutH+UHs5pssc69
20+
YBpEXQTG9CMeRh6ZUgcrR0foJLM5g2k53xpG1oTHiJcCKARFZPRpDoZ6NjCIuFKY
21+
6+HMcpFRVDsDnUXmFah9bUhsSQbc6MHHX/iTbpMGNwIDAQABo2AwXjAPBgNVHRMB
22+
Af8EBTADAQH/MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUhGQbAW97KXQs68Z3efEj
23+
55zsc4UwHwYDVR0jBBgwFoAUhGQbAW97KXQs68Z3efEj55zsc4UwDQYJKoZIhvcN
24+
AQELBQADggIBADsy7jhBmwGbOZPox0XvB2XzWjOPl3uI3Ys3uGaAXVbGVnP3nDtU
25+
waGg7Fhf/ibQVAOkWLfm9FCJEO6bEojF4CjCa//iMqXgnPJaWeYceb8+CzuF5Ukg
26+
n/kfbj04dVvOnPa8KYkMOWQ6zsBgKuNaA5jOKWYwoHFgQNjKRiVikyOp6zF3aPu0
27+
wW7M7FOVHn0ZhMRBcJG8dGbQ8vaeu8z4i04tlvpQaFgtY66ECeUwhTIrvVuqtQOl
28+
jR//w70TUTIH3JzzYmyCubOCjdqcNRYPRRiA/L+mdzrE7honSTQfo0iupT/5bJcu
29+
GRjLHL/aRvYrq8ogqQKIYW0EbVuFzHfb+kPV61Bf5APbA26GU/14XkA4KwzJnDMR
30+
d2wr0RivSceXtY2ZakYP6+2cqjuhk6Y0tl0FBuyQXqAbe1L7X2VctLJMi5UgksVB
31+
q5rdHSJ3fbHRoCUpj4/rSafqJNHlAf2MEE/q8l0D8JhYoN69RhvyFQJLFEU4c74b
32+
XHdFt6bfyxm4+ZzUdj/TXadPAUO1YfQCn9Tf7QOoR68acSvQxEDbChZlJYkdAE+C
33+
zxNcoHVc6XIpk7NIr09qTQ5viz736fV6EI6OIoUaqrz9u+NZ3sPPD2Gf+rOinVFQ
34+
R2Q5kxHYo8Kt1DK0fFcUe1cOZk3df7seQWw1OdJngp5S7gEWBiWg8zr7
3535
-----END CERTIFICATE-----

0 commit comments

Comments
 (0)