Skip to content

Commit 7cf5785

Browse files
authored
Type annotations (aio-libs#3049)
1 parent 17f5a3d commit 7cf5785

33 files changed

+328
-234
lines changed

CHANGES/3049.feature

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add type hints

MANIFEST.in

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ graft docs
88
graft examples
99
graft tests
1010
recursive-include vendor *
11+
global-include aiohttp *.pyi
1112
global-exclude *.pyc
1213
global-exclude *.pyd
1314
global-exclude *.so

Makefile

+6-4
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,12 @@ flake: .flake
2828
check_changes:
2929
@./tools/check_changes.py
3030

31-
.develop: .install-deps $(shell find aiohttp -type f) .flake check_changes
31+
mypy: .flake
32+
if python -c "import sys; sys.exit(sys.implementation.name!='cpython')"; then \
33+
mypy aiohttp tests; \
34+
fi
35+
36+
.develop: .install-deps $(shell find aiohttp -type f) .flake check_changes mypy
3237
@pip install -e .
3338
@touch .develop
3439

@@ -112,7 +117,4 @@ install:
112117
@pip install -U pip
113118
@pip install -Ur requirements/dev.txt
114119

115-
mypy:
116-
mypy aiohttp tests
117-
118120
.PHONY: all build flake test vtest cov clean doc

aiohttp/__init__.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from . import hdrs # noqa
66
from .client import * # noqa
7+
from .client import ClientSession, ServerFingerprintMismatch # noqa
78
from .cookiejar import * # noqa
89
from .formdata import * # noqa
910
from .helpers import * # noqa
@@ -21,7 +22,7 @@
2122
from .worker import GunicornWebWorker, GunicornUVLoopWebWorker # noqa
2223
workers = ('GunicornWebWorker', 'GunicornUVLoopWebWorker')
2324
except ImportError: # pragma: no cover
24-
workers = ()
25+
workers = () # type: ignore
2526

2627

2728
__all__ = (client.__all__ + # noqa

aiohttp/_helpers.pyi

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from typing import Any
2+
3+
class reify:
4+
def __init__(self, wrapped: Any) -> None: ...
5+
6+
def __get__(self, inst: Any, owner: Any) -> Any: ...
7+
8+
def __set__(self, inst: Any, value: Any) -> None: ...

aiohttp/_http_parser.pyx

+2-2
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ cdef class HttpParser:
308308
return messages, False, b''
309309

310310

311-
cdef class HttpRequestParserC(HttpParser):
311+
cdef class HttpRequestParser(HttpParser):
312312

313313
def __init__(self, protocol, loop, timer=None,
314314
size_t max_line_size=8190, size_t max_headers=32768,
@@ -335,7 +335,7 @@ cdef class HttpRequestParserC(HttpParser):
335335
self._buf.clear()
336336

337337

338-
cdef class HttpResponseParserC(HttpParser):
338+
cdef class HttpResponseParser(HttpParser):
339339

340340
def __init__(self, protocol, loop, timer=None,
341341
size_t max_line_size=8190, size_t max_headers=32768,

aiohttp/client.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,8 @@ def __init__(self, *, connector=None, loop=None, cookies=None,
9292
request_class=ClientRequest, response_class=ClientResponse,
9393
ws_response_class=ClientWebSocketResponse,
9494
version=http.HttpVersion11,
95-
cookie_jar=None, connector_owner=True, raise_for_status=False,
95+
cookie_jar=None, connector_owner=True,
96+
raise_for_status=False,
9697
read_timeout=sentinel, conn_timeout=None,
9798
timeout=sentinel,
9899
auto_decompress=True, trust_env=False,

aiohttp/client_exceptions.py

+7-7
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
try:
88
import ssl
99
except ImportError: # pragma: no cover
10-
ssl = None
10+
ssl = None # type: ignore
1111

1212

1313
__all__ = (
@@ -203,24 +203,24 @@ class ClientSSLError(ClientConnectorError):
203203

204204

205205
if ssl is not None:
206-
certificate_errors = (ssl.CertificateError,)
207-
certificate_errors_bases = (ClientSSLError, ssl.CertificateError,)
206+
cert_errors = (ssl.CertificateError,)
207+
cert_errors_bases = (ClientSSLError, ssl.CertificateError,)
208208

209209
ssl_errors = (ssl.SSLError,)
210210
ssl_error_bases = (ClientSSLError, ssl.SSLError)
211211
else: # pragma: no cover
212-
certificate_errors = tuple()
213-
certificate_errors_bases = (ClientSSLError, ValueError,)
212+
cert_errors = tuple()
213+
cert_errors_bases = (ClientSSLError, ValueError,)
214214

215215
ssl_errors = tuple()
216216
ssl_error_bases = (ClientSSLError,)
217217

218218

219-
class ClientConnectorSSLError(*ssl_error_bases):
219+
class ClientConnectorSSLError(*ssl_error_bases): # type: ignore
220220
"""Response ssl error."""
221221

222222

223-
class ClientConnectorCertificateError(*certificate_errors_bases):
223+
class ClientConnectorCertificateError(*cert_errors_bases): # type: ignore
224224
"""Response certificate error."""
225225

226226
def __init__(self, connection_key, certificate_error):

aiohttp/client_reqrep.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
try:
2828
import ssl
2929
except ImportError: # pragma: no cover
30-
ssl = None
30+
ssl = None # type: ignore
3131

3232
try:
3333
import cchardet as chardet

aiohttp/connector.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
ClientConnectorError, ClientConnectorSSLError,
1919
ClientHttpProxyError,
2020
ClientProxyConnectionError,
21-
ServerFingerprintMismatch, certificate_errors,
21+
ServerFingerprintMismatch, cert_errors,
2222
ssl_errors)
2323
from .client_proto import ResponseHandler
2424
from .client_reqrep import ClientRequest, Fingerprint, _merge_ssl_params
@@ -30,7 +30,7 @@
3030
try:
3131
import ssl
3232
except ImportError: # pragma: no cover
33-
ssl = None
33+
ssl = None # type: ignore
3434

3535

3636
__all__ = ('BaseConnector', 'TCPConnector', 'UnixConnector')
@@ -820,7 +820,7 @@ async def _wrap_create_connection(self, *args,
820820
try:
821821
with CeilTimeout(timeout.sock_connect):
822822
return await self._loop.create_connection(*args, **kwargs)
823-
except certificate_errors as exc:
823+
except cert_errors as exc:
824824
raise ClientConnectorCertificateError(
825825
req.connection_key, exc) from exc
826826
except ssl_errors as exc:

aiohttp/frozenlist.py

+9-10
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,8 @@
44
from .helpers import NO_EXTENSIONS
55

66

7-
if not NO_EXTENSIONS:
8-
try:
9-
from aiohttp._frozenlist import FrozenList
10-
except ImportError: # pragma: no cover
11-
FrozenList = None
12-
13-
147
@total_ordering
15-
class PyFrozenList(MutableSequence):
8+
class FrozenList(MutableSequence):
169

1710
__slots__ = ('_frozen', '_items')
1811

@@ -69,5 +62,11 @@ def __repr__(self):
6962
self._items)
7063

7164

72-
if NO_EXTENSIONS or FrozenList is None:
73-
FrozenList = PyFrozenList
65+
PyFrozenList = FrozenList
66+
67+
try:
68+
from aiohttp._frozenlist import FrozenList as CFrozenList # type: ignore
69+
if not NO_EXTENSIONS:
70+
FrozenList = CFrozenList # type: ignore
71+
except ImportError: # pragma: no cover
72+
pass

aiohttp/helpers.py

+28-23
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@
1414
import time
1515
import weakref
1616
from collections import namedtuple
17-
from collections.abc import Mapping
17+
from collections.abc import Mapping as ABCMapping
1818
from contextlib import suppress
1919
from math import ceil
2020
from pathlib import Path
21+
from typing import Any, Dict, List, Optional, Tuple # noqa
2122
from urllib.parse import quote
2223
from urllib.request import getproxies
2324

@@ -41,8 +42,8 @@
4142
idna_ssl.patch_match_hostname()
4243

4344

44-
sentinel = object()
45-
NO_EXTENSIONS = bool(os.environ.get('AIOHTTP_NO_EXTENSIONS'))
45+
sentinel = object() # type: Any
46+
NO_EXTENSIONS = bool(os.environ.get('AIOHTTP_NO_EXTENSIONS')) # type: bool
4647

4748
# N.B. sys.flags.dev_mode is available on Python 3.7+, use getattr
4849
# for compatibility with older versions
@@ -59,24 +60,26 @@
5960

6061

6162
coroutines = asyncio.coroutines
62-
old_debug = coroutines._DEBUG
63+
old_debug = coroutines._DEBUG # type: ignore
6364

6465
# prevent "coroutine noop was never awaited" warning.
65-
coroutines._DEBUG = False
66+
coroutines._DEBUG = False # type: ignore
6667

6768

6869
@asyncio.coroutine
6970
def noop(*args, **kwargs):
7071
return
7172

7273

73-
coroutines._DEBUG = old_debug
74+
coroutines._DEBUG = old_debug # type: ignore
7475

7576

7677
class BasicAuth(namedtuple('BasicAuth', ['login', 'password', 'encoding'])):
7778
"""Http basic authentication helper."""
7879

79-
def __new__(cls, login, password='', encoding='latin1'):
80+
def __new__(cls, login: str,
81+
password: str='',
82+
encoding: str='latin1') -> 'BasicAuth':
8083
if login is None:
8184
raise ValueError('None is not allowed as login value')
8285

@@ -90,7 +93,7 @@ def __new__(cls, login, password='', encoding='latin1'):
9093
return super().__new__(cls, login, password, encoding)
9194

9295
@classmethod
93-
def decode(cls, auth_header, encoding='latin1'):
96+
def decode(cls, auth_header: str, encoding: str='latin1') -> 'BasicAuth':
9497
"""Create a BasicAuth object from an Authorization HTTP header."""
9598
split = auth_header.strip().split(' ')
9699
if len(split) == 2:
@@ -110,21 +113,22 @@ def decode(cls, auth_header, encoding='latin1'):
110113
return cls(username, password, encoding=encoding)
111114

112115
@classmethod
113-
def from_url(cls, url, *, encoding='latin1'):
116+
def from_url(cls, url: URL,
117+
*, encoding: str='latin1') -> Optional['BasicAuth']:
114118
"""Create BasicAuth from url."""
115119
if not isinstance(url, URL):
116120
raise TypeError("url should be yarl.URL instance")
117121
if url.user is None:
118122
return None
119123
return cls(url.user, url.password or '', encoding=encoding)
120124

121-
def encode(self):
125+
def encode(self) -> str:
122126
"""Encode credentials."""
123127
creds = ('%s:%s' % (self.login, self.password)).encode(self.encoding)
124128
return 'Basic %s' % base64.b64encode(creds).decode(self.encoding)
125129

126130

127-
def strip_auth_from_url(url):
131+
def strip_auth_from_url(url: URL) -> Tuple[URL, Optional[BasicAuth]]:
128132
auth = BasicAuth.from_url(url)
129133
if auth is None:
130134
return url, None
@@ -293,6 +297,9 @@ def content_disposition_header(disptype, quote_fields=True, **params):
293297
return value
294298

295299

300+
KeyMethod = namedtuple('KeyMethod', 'key method')
301+
302+
296303
class AccessLogger(AbstractAccessLogger):
297304
"""Helper object to log access.
298305
@@ -336,9 +343,7 @@ class AccessLogger(AbstractAccessLogger):
336343
LOG_FORMAT = '%a %t "%r" %s %b "%{Referer}i" "%{User-Agent}i"'
337344
FORMAT_RE = re.compile(r'%(\{([A-Za-z0-9\-_]+)\}([ioe])|[atPrsbOD]|Tf?)')
338345
CLEANUP_RE = re.compile(r'(%[^s])')
339-
_FORMAT_CACHE = {}
340-
341-
KeyMethod = namedtuple('KeyMethod', 'key method')
346+
_FORMAT_CACHE = {} # type: Dict[str, Tuple[str, List[KeyMethod]]]
342347

343348
def __init__(self, logger, log_format=LOG_FORMAT):
344349
"""Initialise the logger.
@@ -390,7 +395,7 @@ def compile_format(self, log_format):
390395
m = getattr(AccessLogger, '_format_%s' % atom[2])
391396
m = functools.partial(m, atom[1])
392397

393-
methods.append(self.KeyMethod(format_key, m))
398+
methods.append(KeyMethod(format_key, m))
394399

395400
log_format = self.FORMAT_RE.sub(r'%s', log_format)
396401
log_format = self.CLEANUP_RE.sub(r'%\1', log_format)
@@ -515,7 +520,7 @@ def __set__(self, inst, value):
515520
try:
516521
from ._helpers import reify as reify_c
517522
if not NO_EXTENSIONS:
518-
reify = reify_c
523+
reify = reify_c # type: ignore
519524
except ImportError:
520525
pass
521526

@@ -716,25 +721,25 @@ def _parse_content_type(self, raw):
716721
self._content_type, self._content_dict = cgi.parse_header(raw)
717722

718723
@property
719-
def content_type(self, *, _CONTENT_TYPE=hdrs.CONTENT_TYPE):
724+
def content_type(self):
720725
"""The value of content part for Content-Type HTTP header."""
721-
raw = self._headers.get(_CONTENT_TYPE)
726+
raw = self._headers.get(hdrs.CONTENT_TYPE)
722727
if self._stored_content_type != raw:
723728
self._parse_content_type(raw)
724729
return self._content_type
725730

726731
@property
727-
def charset(self, *, _CONTENT_TYPE=hdrs.CONTENT_TYPE):
732+
def charset(self):
728733
"""The value of charset part for Content-Type HTTP header."""
729-
raw = self._headers.get(_CONTENT_TYPE)
734+
raw = self._headers.get(hdrs.CONTENT_TYPE)
730735
if self._stored_content_type != raw:
731736
self._parse_content_type(raw)
732737
return self._content_dict.get('charset')
733738

734739
@property
735-
def content_length(self, *, _CONTENT_LENGTH=hdrs.CONTENT_LENGTH):
740+
def content_length(self):
736741
"""The value of Content-Length HTTP header."""
737-
content_length = self._headers.get(_CONTENT_LENGTH)
742+
content_length = self._headers.get(hdrs.CONTENT_LENGTH)
738743

739744
if content_length:
740745
return int(content_length)
@@ -750,7 +755,7 @@ def set_exception(fut, exc):
750755
fut.set_exception(exc)
751756

752757

753-
class ChainMapProxy(Mapping):
758+
class ChainMapProxy(ABCMapping):
754759
__slots__ = ('_maps',)
755760

756761
def __init__(self, maps):

0 commit comments

Comments
 (0)