Skip to content

Commit e504875

Browse files
authored
Merge branch 'master' into will.miller/issue-729
2 parents 50bb192 + 3ee19ba commit e504875

Some content is hidden

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

52 files changed

+1586
-396
lines changed

.flake8

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
[flake8]
2+
select = C90,E,F,W,Y0
23
ignore = E402,E731,W503,W504,E252
3-
exclude = .git,__pycache__,build,dist,.eggs,.github,.local,.venv,.tox
4+
exclude = .git,__pycache__,build,dist,.eggs,.github,.local,.venv*,.tox
5+
per-file-ignores = *.pyi: F401,F403,F405,F811,E127,E128,E203,E266,E301,E302,E305,E501,E701,E704,E741,B303,W503,W504

.github/workflows/install-krb5.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/bin/bash
2+
3+
set -Eexuo pipefail
4+
5+
if [ "$RUNNER_OS" == "Linux" ]; then
6+
# Assume Ubuntu since this is the only Linux used in CI.
7+
sudo apt-get update
8+
sudo apt-get install -y --no-install-recommends \
9+
libkrb5-dev krb5-user krb5-kdc krb5-admin-server
10+
fi

.github/workflows/release.yml

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
github_token: ${{ secrets.RELEASE_BOT_GITHUB_TOKEN }}
2323
version_file: asyncpg/_version.py
2424
version_line_pattern: |
25-
__version__\s*=\s*(?:['"])([[:PEP440:]])(?:['"])
25+
__version__(?:\s*:\s*typing\.Final)?\s*=\s*(?:['"])([[:PEP440:]])(?:['"])
2626
2727
- name: Stop if not approved
2828
if: steps.checkver.outputs.approved != 'true'
@@ -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: 11 additions & 7 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

@@ -53,15 +50,21 @@ jobs:
5350
version_line_pattern: |
5451
__version__\s*=\s*(?:['"])([[:PEP440:]])(?:['"])
5552
53+
- name: Setup PostgreSQL
54+
if: steps.release.outputs.version == 0 && matrix.os == 'macos-latest'
55+
run: |
56+
brew install postgresql
57+
5658
- name: Set up Python ${{ matrix.python-version }}
57-
uses: actions/setup-python@v4
59+
uses: actions/setup-python@v5
5860
if: steps.release.outputs.version == 0
5961
with:
6062
python-version: ${{ matrix.python-version }}
6163

6264
- name: Install Python Deps
6365
if: steps.release.outputs.version == 0
6466
run: |
67+
.github/workflows/install-krb5.sh
6568
python -m pip install -U pip setuptools wheel
6669
python -m pip install -e .[test]
6770
@@ -79,7 +82,7 @@ jobs:
7982
test-postgres:
8083
strategy:
8184
matrix:
82-
postgres-version: ["9.5", "9.6", "10", "11", "12", "13", "14", "15", "16"]
85+
postgres-version: ["9.5", "9.6", "10", "11", "12", "13", "14", "15", "16", "17"]
8386

8487
runs-on: ubuntu-latest
8588

@@ -114,14 +117,15 @@ jobs:
114117
>> "${GITHUB_ENV}"
115118
116119
- name: Set up Python ${{ matrix.python-version }}
117-
uses: actions/setup-python@v4
120+
uses: actions/setup-python@v5
118121
if: steps.release.outputs.version == 0
119122
with:
120123
python-version: "3.x"
121124

122125
- name: Install Python Deps
123126
if: steps.release.outputs.version == 0
124127
run: |
128+
.github/workflows/install-krb5.sh
125129
python -m pip install -U pip setuptools wheel
126130
python -m pip install -e .[test]
127131

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,5 @@ docs/_build
3434
/.eggs
3535
/.vscode
3636
/.mypy_cache
37+
/.venv*
38+
/.tox

MANIFEST.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
recursive-include docs *.py *.rst Makefile *.css
22
recursive-include examples *.py
33
recursive-include tests *.py *.pem
4-
recursive-include asyncpg *.pyx *.pxd *.pxi *.py *.c *.h
4+
recursive-include asyncpg *.pyx *.pxd *.pxi *.py *.pyi *.c *.h
55
include LICENSE README.rst Makefile performance.png .flake8

README.rst

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ framework. You can read more about asyncpg in an introductory
1414
`blog post <http://magic.io/blog/asyncpg-1m-rows-from-postgres-to-python/>`_.
1515

1616
asyncpg requires Python 3.8 or later and is supported for PostgreSQL
17-
versions 9.5 to 16. Older PostgreSQL versions or other databases implementing
18-
the PostgreSQL protocol *may* work, but are not being actively tested.
17+
versions 9.5 to 17. Other PostgreSQL versions or other databases
18+
implementing the PostgreSQL protocol *may* work, but are not being
19+
actively tested.
1920

2021

2122
Documentation
@@ -58,11 +59,18 @@ This enables asyncpg to have easy-to-use support for:
5859
Installation
5960
------------
6061

61-
asyncpg is available on PyPI and has no dependencies.
62-
Use pip to install::
62+
asyncpg is available on PyPI. When not using GSSAPI/SSPI authentication it
63+
has no dependencies. Use pip to install::
6364

6465
$ pip install asyncpg
6566

67+
If you need GSSAPI/SSPI authentication, use::
68+
69+
$ pip install 'asyncpg[gssauth]'
70+
71+
For more details, please `see the documentation
72+
<https://magicstack.github.io/asyncpg/current/installation.html>`_.
73+
6674

6775
Basic Usage
6876
-----------
@@ -81,8 +89,7 @@ Basic Usage
8189
)
8290
await conn.close()
8391
84-
loop = asyncio.get_event_loop()
85-
loop.run_until_complete(run())
92+
asyncio.run(run())
8693
8794
8895
License

asyncpg/__init__.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
# This module is part of asyncpg and is released under
55
# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0
66

7+
from __future__ import annotations
78

89
from .connection import connect, Connection # NOQA
910
from .exceptions import * # NOQA
@@ -14,6 +15,10 @@
1415

1516
from ._version import __version__ # NOQA
1617

18+
from . import exceptions
1719

18-
__all__ = ('connect', 'create_pool', 'Pool', 'Record', 'Connection')
20+
21+
__all__: tuple[str, ...] = (
22+
'connect', 'create_pool', 'Pool', 'Record', 'Connection'
23+
)
1924
__all__ += exceptions.__all__ # NOQA

asyncpg/_asyncio_compat.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,25 @@
44
#
55
# SPDX-License-Identifier: PSF-2.0
66

7+
from __future__ import annotations
78

89
import asyncio
910
import functools
1011
import sys
12+
import typing
13+
14+
if typing.TYPE_CHECKING:
15+
from . import compat
1116

1217
if sys.version_info < (3, 11):
1318
from async_timeout import timeout as timeout_ctx
1419
else:
1520
from asyncio import timeout as timeout_ctx
1621

22+
_T = typing.TypeVar('_T')
23+
1724

18-
async def wait_for(fut, timeout):
25+
async def wait_for(fut: compat.Awaitable[_T], timeout: float | None) -> _T:
1926
"""Wait for the single Future or coroutine to complete, with timeout.
2027
2128
Coroutine will be wrapped in Task.
@@ -65,7 +72,7 @@ async def wait_for(fut, timeout):
6572
return await fut
6673

6774

68-
async def _cancel_and_wait(fut):
75+
async def _cancel_and_wait(fut: asyncio.Future[_T]) -> None:
6976
"""Cancel the *fut* future or task and wait until it completes."""
7077

7178
loop = asyncio.get_running_loop()
@@ -82,6 +89,6 @@ async def _cancel_and_wait(fut):
8289
fut.remove_done_callback(cb)
8390

8491

85-
def _release_waiter(waiter, *args):
92+
def _release_waiter(waiter: asyncio.Future[typing.Any], *args: object) -> None:
8693
if not waiter.done():
8794
waiter.set_result(None)

asyncpg/_testbase/__init__.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -118,10 +118,22 @@ def setUp(self):
118118
self.__unhandled_exceptions = []
119119

120120
def tearDown(self):
121-
if self.__unhandled_exceptions:
121+
excs = []
122+
for exc in self.__unhandled_exceptions:
123+
if isinstance(exc, ConnectionResetError):
124+
texc = traceback.TracebackException.from_exception(
125+
exc, lookup_lines=False)
126+
if texc.stack[-1].name == "_call_connection_lost":
127+
# On Windows calling socket.shutdown may raise
128+
# ConnectionResetError, which happens in the
129+
# finally block of _call_connection_lost.
130+
continue
131+
excs.append(exc)
132+
133+
if excs:
122134
formatted = []
123135

124-
for i, context in enumerate(self.__unhandled_exceptions):
136+
for i, context in enumerate(excs):
125137
formatted.append(self._format_loop_exception(context, i + 1))
126138

127139
self.fail(
@@ -263,6 +275,7 @@ def create_pool(dsn=None, *,
263275
max_size=10,
264276
max_queries=50000,
265277
max_inactive_connection_lifetime=60.0,
278+
connect=None,
266279
setup=None,
267280
init=None,
268281
loop=None,
@@ -272,12 +285,18 @@ def create_pool(dsn=None, *,
272285
**connect_kwargs):
273286
return pool_class(
274287
dsn,
275-
min_size=min_size, max_size=max_size,
276-
max_queries=max_queries, loop=loop, setup=setup, init=init,
288+
min_size=min_size,
289+
max_size=max_size,
290+
max_queries=max_queries,
291+
loop=loop,
292+
connect=connect,
293+
setup=setup,
294+
init=init,
277295
max_inactive_connection_lifetime=max_inactive_connection_lifetime,
278296
connection_class=connection_class,
279297
record_class=record_class,
280-
**connect_kwargs)
298+
**connect_kwargs,
299+
)
281300

282301

283302
class ClusterTestCase(TestCase):

0 commit comments

Comments
 (0)