Skip to content

Commit d42432b

Browse files
authored
Add initial typings (#1127)
* Added typings to miscellaneous files * Added unit test to check codebase with mypy * Updated release workflow and build to account for annotations * Updated manifest to include stub files
1 parent 1d4e568 commit d42432b

16 files changed

+512
-60
lines changed

.flake8

+3-1
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/release.yml

+1-1
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'

.gitignore

+2
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

+1-1
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

asyncpg/__init__.py

+6-1
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

+10-3
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/_version.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,8 @@
1010
# supported platforms, publish the packages on PyPI, merge the PR
1111
# to the target branch, create a Git tag pointing to the commit.
1212

13-
__version__ = '0.30.0.dev0'
13+
from __future__ import annotations
14+
15+
import typing
16+
17+
__version__: typing.Final = '0.30.0.dev0'

asyncpg/compat.py

+18-6
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,25 @@
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
import pathlib
910
import platform
1011
import typing
1112
import sys
1213

14+
if typing.TYPE_CHECKING:
15+
import asyncio
1316

14-
SYSTEM = platform.uname().system
17+
SYSTEM: typing.Final = platform.uname().system
1518

1619

17-
if SYSTEM == 'Windows':
20+
if sys.platform == 'win32':
1821
import ctypes.wintypes
1922

20-
CSIDL_APPDATA = 0x001a
23+
CSIDL_APPDATA: typing.Final = 0x001a
2124

22-
def get_pg_home_directory() -> typing.Optional[pathlib.Path]:
25+
def get_pg_home_directory() -> pathlib.Path | None:
2326
# We cannot simply use expanduser() as that returns the user's
2427
# home directory, whereas Postgres stores its config in
2528
# %AppData% on Windows.
@@ -31,14 +34,14 @@ def get_pg_home_directory() -> typing.Optional[pathlib.Path]:
3134
return pathlib.Path(buf.value) / 'postgresql'
3235

3336
else:
34-
def get_pg_home_directory() -> typing.Optional[pathlib.Path]:
37+
def get_pg_home_directory() -> pathlib.Path | None:
3538
try:
3639
return pathlib.Path.home()
3740
except (RuntimeError, KeyError):
3841
return None
3942

4043

41-
async def wait_closed(stream):
44+
async def wait_closed(stream: asyncio.StreamWriter) -> None:
4245
# Not all asyncio versions have StreamWriter.wait_closed().
4346
if hasattr(stream, 'wait_closed'):
4447
try:
@@ -59,3 +62,12 @@ async def wait_closed(stream):
5962
from ._asyncio_compat import timeout_ctx as timeout # noqa: F401
6063
else:
6164
from asyncio import timeout as timeout # noqa: F401
65+
66+
if sys.version_info < (3, 9):
67+
from typing import ( # noqa: F401
68+
Awaitable as Awaitable,
69+
)
70+
else:
71+
from collections.abc import ( # noqa: F401
72+
Awaitable as Awaitable,
73+
)

asyncpg/introspection.py

+14-8
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,14 @@
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

8-
_TYPEINFO_13 = '''\
9+
import typing
10+
11+
if typing.TYPE_CHECKING:
12+
from . import protocol
13+
14+
_TYPEINFO_13: typing.Final = '''\
915
(
1016
SELECT
1117
t.oid AS oid,
@@ -124,7 +130,7 @@
124130
'''.format(typeinfo=_TYPEINFO_13)
125131

126132

127-
_TYPEINFO = '''\
133+
_TYPEINFO: typing.Final = '''\
128134
(
129135
SELECT
130136
t.oid AS oid,
@@ -248,7 +254,7 @@
248254
'''.format(typeinfo=_TYPEINFO)
249255

250256

251-
TYPE_BY_NAME = '''\
257+
TYPE_BY_NAME: typing.Final = '''\
252258
SELECT
253259
t.oid,
254260
t.typelem AS elemtype,
@@ -277,16 +283,16 @@
277283
SCALAR_TYPE_KINDS = (b'b', b'd', b'e')
278284

279285

280-
def is_scalar_type(typeinfo) -> bool:
286+
def is_scalar_type(typeinfo: protocol.Record) -> bool:
281287
return (
282288
typeinfo['kind'] in SCALAR_TYPE_KINDS and
283289
not typeinfo['elemtype']
284290
)
285291

286292

287-
def is_domain_type(typeinfo) -> bool:
288-
return typeinfo['kind'] == b'd'
293+
def is_domain_type(typeinfo: protocol.Record) -> bool:
294+
return typeinfo['kind'] == b'd' # type: ignore[no-any-return]
289295

290296

291-
def is_composite_type(typeinfo) -> bool:
292-
return typeinfo['kind'] == b'c'
297+
def is_composite_type(typeinfo: protocol.Record) -> bool:
298+
return typeinfo['kind'] == b'c' # type: ignore[no-any-return]

asyncpg/protocol/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,6 @@
66

77
# flake8: NOQA
88

9+
from __future__ import annotations
10+
911
from .protocol import Protocol, Record, NO_TIMEOUT, BUILTIN_TYPE_NAME_MAP

0 commit comments

Comments
 (0)