Skip to content

Commit 69a3bca

Browse files
committed
improve typing
* use deferred annotations * use collections.abc * check with pyright * verify exported API with pyright
1 parent 0f15cf1 commit 69a3bca

File tree

8 files changed

+154
-154
lines changed

8 files changed

+154
-154
lines changed

src/itsdangerous/_json.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
1+
from __future__ import annotations
2+
13
import json as _json
2-
import typing as _t
4+
import typing as t
35

46

57
class _CompactJSON:
68
"""Wrapper around json module that strips whitespace."""
79

810
@staticmethod
9-
def loads(payload: _t.Union[str, bytes]) -> _t.Any:
11+
def loads(payload: str | bytes) -> t.Any:
1012
return _json.loads(payload)
1113

1214
@staticmethod
13-
def dumps(obj: _t.Any, **kwargs: _t.Any) -> str:
15+
def dumps(obj: t.Any, **kwargs: t.Any) -> str:
1416
kwargs.setdefault("ensure_ascii", False)
1517
kwargs.setdefault("separators", (",", ":"))
1618
return _json.dumps(obj, **kwargs)

src/itsdangerous/encoding.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,31 @@
1+
from __future__ import annotations
2+
13
import base64
24
import string
35
import struct
4-
import typing as _t
6+
import typing as t
57

68
from .exc import BadData
79

8-
_t_str_bytes = _t.Union[str, bytes]
9-
1010

1111
def want_bytes(
12-
s: _t_str_bytes, encoding: str = "utf-8", errors: str = "strict"
12+
s: str | bytes, encoding: str = "utf-8", errors: str = "strict"
1313
) -> bytes:
1414
if isinstance(s, str):
1515
s = s.encode(encoding, errors)
1616

1717
return s
1818

1919

20-
def base64_encode(string: _t_str_bytes) -> bytes:
20+
def base64_encode(string: str | bytes) -> bytes:
2121
"""Base64 encode a string of bytes or text. The resulting bytes are
2222
safe to use in URLs.
2323
"""
2424
string = want_bytes(string)
2525
return base64.urlsafe_b64encode(string).rstrip(b"=")
2626

2727

28-
def base64_decode(string: _t_str_bytes) -> bytes:
28+
def base64_decode(string: str | bytes) -> bytes:
2929
"""Base64 decode a URL-safe string of bytes or text. The result is
3030
bytes.
3131
"""
@@ -43,7 +43,7 @@ def base64_decode(string: _t_str_bytes) -> bytes:
4343

4444
_int64_struct = struct.Struct(">Q")
4545
_int_to_bytes = _int64_struct.pack
46-
_bytes_to_int = _t.cast("_t.Callable[[bytes], _t.Tuple[int]]", _int64_struct.unpack)
46+
_bytes_to_int = t.cast("t.Callable[[bytes], tuple[int]]", _int64_struct.unpack)
4747

4848

4949
def int_to_bytes(num: int) -> bytes:

src/itsdangerous/exc.py

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import typing as _t
2-
from datetime import datetime
1+
from __future__ import annotations
32

4-
_t_opt_any = _t.Optional[_t.Any]
5-
_t_opt_exc = _t.Optional[Exception]
3+
import typing as t
4+
from datetime import datetime
65

76

87
class BadData(Exception):
@@ -23,15 +22,15 @@ def __str__(self) -> str:
2322
class BadSignature(BadData):
2423
"""Raised if a signature does not match."""
2524

26-
def __init__(self, message: str, payload: _t_opt_any = None):
25+
def __init__(self, message: str, payload: t.Any | None = None):
2726
super().__init__(message)
2827

2928
#: The payload that failed the signature test. In some
3029
#: situations you might still want to inspect this, even if
3130
#: you know it was tampered with.
3231
#:
3332
#: .. versionadded:: 0.14
34-
self.payload: _t_opt_any = payload
33+
self.payload: t.Any | None = payload
3534

3635

3736
class BadTimeSignature(BadSignature):
@@ -42,8 +41,8 @@ class BadTimeSignature(BadSignature):
4241
def __init__(
4342
self,
4443
message: str,
45-
payload: _t_opt_any = None,
46-
date_signed: _t.Optional[datetime] = None,
44+
payload: t.Any | None = None,
45+
date_signed: datetime | None = None,
4746
):
4847
super().__init__(message, payload)
4948

@@ -75,19 +74,19 @@ class BadHeader(BadSignature):
7574
def __init__(
7675
self,
7776
message: str,
78-
payload: _t_opt_any = None,
79-
header: _t_opt_any = None,
80-
original_error: _t_opt_exc = None,
77+
payload: t.Any | None = None,
78+
header: t.Any | None = None,
79+
original_error: Exception | None = None,
8180
):
8281
super().__init__(message, payload)
8382

8483
#: If the header is actually available but just malformed it
8584
#: might be stored here.
86-
self.header: _t_opt_any = header
85+
self.header: t.Any | None = header
8786

8887
#: If available, the error that indicates why the payload was
8988
#: not valid. This might be ``None``.
90-
self.original_error: _t_opt_exc = original_error
89+
self.original_error: Exception | None = original_error
9190

9291

9392
class BadPayload(BadData):
@@ -99,9 +98,9 @@ class BadPayload(BadData):
9998
.. versionadded:: 0.15
10099
"""
101100

102-
def __init__(self, message: str, original_error: _t_opt_exc = None):
101+
def __init__(self, message: str, original_error: Exception | None = None):
103102
super().__init__(message)
104103

105104
#: If available, the error that indicates why the payload was
106105
#: not valid. This might be ``None``.
107-
self.original_error: _t_opt_exc = original_error
106+
self.original_error: Exception | None = original_error

src/itsdangerous/serializer.py

Lines changed: 52 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,17 @@
1+
from __future__ import annotations
2+
3+
import collections.abc as cabc
14
import json
2-
import typing as _t
5+
import typing as t
36

47
from .encoding import want_bytes
58
from .exc import BadPayload
69
from .exc import BadSignature
710
from .signer import _make_keys_list
811
from .signer import Signer
912

10-
_t_str_bytes = _t.Union[str, bytes]
11-
_t_opt_str_bytes = _t.Optional[_t_str_bytes]
12-
_t_kwargs = _t.Dict[str, _t.Any]
13-
_t_opt_kwargs = _t.Optional[_t_kwargs]
14-
_t_signer = _t.Type[Signer]
15-
_t_fallbacks = _t.List[_t.Union[_t_kwargs, _t.Tuple[_t_signer, _t_kwargs], _t_signer]]
16-
_t_load_unsafe = _t.Tuple[bool, _t.Any]
17-
_t_secret_key = _t.Union[_t.Iterable[_t_str_bytes], _t_str_bytes]
18-
1913

20-
def is_text_serializer(serializer: _t.Any) -> bool:
14+
def is_text_serializer(serializer: t.Any) -> bool:
2115
"""Checks whether a serializer generates text or binary."""
2216
return isinstance(serializer.dumps({}), str)
2317

@@ -77,31 +71,36 @@ class Serializer:
7771
#: The default serialization module to use to serialize data to a
7872
#: string internally. The default is :mod:`json`, but can be changed
7973
#: to any object that provides ``dumps`` and ``loads`` methods.
80-
default_serializer: _t.Any = json
74+
default_serializer: t.Any = json
8175

8276
#: The default ``Signer`` class to instantiate when signing data.
8377
#: The default is :class:`itsdangerous.signer.Signer`.
84-
default_signer: _t_signer = Signer
78+
default_signer: type[Signer] = Signer
8579

8680
#: The default fallback signers to try when unsigning fails.
87-
default_fallback_signers: _t_fallbacks = []
81+
default_fallback_signers: list[
82+
dict[str, t.Any] | tuple[type[Signer], dict[str, t.Any]] | type[Signer]
83+
] = []
8884

8985
def __init__(
9086
self,
91-
secret_key: _t_secret_key,
92-
salt: _t_opt_str_bytes = b"itsdangerous",
93-
serializer: _t.Any = None,
94-
serializer_kwargs: _t_opt_kwargs = None,
95-
signer: _t.Optional[_t_signer] = None,
96-
signer_kwargs: _t_opt_kwargs = None,
97-
fallback_signers: _t.Optional[_t_fallbacks] = None,
87+
secret_key: str | bytes | cabc.Iterable[str] | cabc.Iterable[bytes],
88+
salt: str | bytes | None = b"itsdangerous",
89+
serializer: t.Any = None,
90+
serializer_kwargs: dict[str, t.Any] | None = None,
91+
signer: type[Signer] | None = None,
92+
signer_kwargs: dict[str, t.Any] | None = None,
93+
fallback_signers: list[
94+
dict[str, t.Any] | tuple[type[Signer], dict[str, t.Any]] | type[Signer]
95+
]
96+
| None = None,
9897
):
9998
#: The list of secret keys to try for verifying signatures, from
10099
#: oldest to newest. The newest (last) key is used for signing.
101100
#:
102101
#: This allows a key rotation system to keep a list of allowed
103102
#: keys and remove expired ones.
104-
self.secret_keys: _t.List[bytes] = _make_keys_list(secret_key)
103+
self.secret_keys: list[bytes] = _make_keys_list(secret_key)
105104

106105
if salt is not None:
107106
salt = want_bytes(salt)
@@ -112,20 +111,22 @@ def __init__(
112111
if serializer is None:
113112
serializer = self.default_serializer
114113

115-
self.serializer: _t.Any = serializer
114+
self.serializer: t.Any = serializer
116115
self.is_text_serializer: bool = is_text_serializer(serializer)
117116

118117
if signer is None:
119118
signer = self.default_signer
120119

121-
self.signer: _t_signer = signer
122-
self.signer_kwargs: _t_kwargs = signer_kwargs or {}
120+
self.signer: type[Signer] = signer
121+
self.signer_kwargs: dict[str, t.Any] = signer_kwargs or {}
123122

124123
if fallback_signers is None:
125-
fallback_signers = list(self.default_fallback_signers or ())
124+
fallback_signers = list(self.default_fallback_signers)
126125

127-
self.fallback_signers: _t_fallbacks = fallback_signers
128-
self.serializer_kwargs: _t_kwargs = serializer_kwargs or {}
126+
self.fallback_signers: list[
127+
dict[str, t.Any] | tuple[type[Signer], dict[str, t.Any]] | type[Signer]
128+
] = fallback_signers
129+
self.serializer_kwargs: dict[str, t.Any] = serializer_kwargs or {}
129130

130131
@property
131132
def secret_key(self) -> bytes:
@@ -134,41 +135,40 @@ def secret_key(self) -> bytes:
134135
"""
135136
return self.secret_keys[-1]
136137

137-
def load_payload(
138-
self, payload: bytes, serializer: _t.Optional[_t.Any] = None
139-
) -> _t.Any:
138+
def load_payload(self, payload: bytes, serializer: t.Any | None = None) -> t.Any:
140139
"""Loads the encoded object. This function raises
141140
:class:`.BadPayload` if the payload is not valid. The
142141
``serializer`` parameter can be used to override the serializer
143142
stored on the class. The encoded ``payload`` should always be
144143
bytes.
145144
"""
146145
if serializer is None:
147-
serializer = self.serializer
146+
use_serializer = self.serializer
148147
is_text = self.is_text_serializer
149148
else:
149+
use_serializer = serializer
150150
is_text = is_text_serializer(serializer)
151151

152152
try:
153153
if is_text:
154-
return serializer.loads(payload.decode("utf-8"))
154+
return use_serializer.loads(payload.decode("utf-8"))
155155

156-
return serializer.loads(payload)
156+
return use_serializer.loads(payload)
157157
except Exception as e:
158158
raise BadPayload(
159159
"Could not load the payload because an exception"
160160
" occurred on unserializing the data.",
161161
original_error=e,
162162
) from e
163163

164-
def dump_payload(self, obj: _t.Any) -> bytes:
164+
def dump_payload(self, obj: t.Any) -> bytes:
165165
"""Dumps the encoded object. The return value is always bytes.
166166
If the internal serializer returns text, the value will be
167167
encoded as UTF-8.
168168
"""
169169
return want_bytes(self.serializer.dumps(obj, **self.serializer_kwargs))
170170

171-
def make_signer(self, salt: _t_opt_str_bytes = None) -> Signer:
171+
def make_signer(self, salt: str | bytes | None = None) -> Signer:
172172
"""Creates a new instance of the signer to be used. The default
173173
implementation uses the :class:`.Signer` base class.
174174
"""
@@ -177,7 +177,7 @@ def make_signer(self, salt: _t_opt_str_bytes = None) -> Signer:
177177

178178
return self.signer(self.secret_keys, salt=salt, **self.signer_kwargs)
179179

180-
def iter_unsigners(self, salt: _t_opt_str_bytes = None) -> _t.Iterator[Signer]:
180+
def iter_unsigners(self, salt: str | bytes | None = None) -> cabc.Iterator[Signer]:
181181
"""Iterates over all signers to be tried for unsigning. Starts
182182
with the configured signer, then constructs each signer
183183
specified in ``fallback_signers``.
@@ -199,7 +199,7 @@ def iter_unsigners(self, salt: _t_opt_str_bytes = None) -> _t.Iterator[Signer]:
199199
for secret_key in self.secret_keys:
200200
yield fallback(secret_key, salt=salt, **kwargs)
201201

202-
def dumps(self, obj: _t.Any, salt: _t_opt_str_bytes = None) -> _t_str_bytes:
202+
def dumps(self, obj: t.Any, salt: str | bytes | None = None) -> str | bytes:
203203
"""Returns a signed string serialized with the internal
204204
serializer. The return value can be either a byte or unicode
205205
string depending on the format of the internal serializer.
@@ -212,17 +212,15 @@ def dumps(self, obj: _t.Any, salt: _t_opt_str_bytes = None) -> _t_str_bytes:
212212

213213
return rv
214214

215-
def dump(
216-
self, obj: _t.Any, f: _t.IO[_t.Any], salt: _t_opt_str_bytes = None
217-
) -> None:
215+
def dump(self, obj: t.Any, f: t.IO[t.Any], salt: str | bytes | None = None) -> None:
218216
"""Like :meth:`dumps` but dumps into a file. The file handle has
219217
to be compatible with what the internal serializer expects.
220218
"""
221219
f.write(self.dumps(obj, salt))
222220

223221
def loads(
224-
self, s: _t_str_bytes, salt: _t_opt_str_bytes = None, **kwargs: _t.Any
225-
) -> _t.Any:
222+
self, s: str | bytes, salt: str | bytes | None = None, **kwargs: t.Any
223+
) -> t.Any:
226224
"""Reverse of :meth:`dumps`. Raises :exc:`.BadSignature` if the
227225
signature validation fails.
228226
"""
@@ -235,15 +233,15 @@ def loads(
235233
except BadSignature as err:
236234
last_exception = err
237235

238-
raise _t.cast(BadSignature, last_exception)
236+
raise t.cast(BadSignature, last_exception)
239237

240-
def load(self, f: _t.IO[_t.Any], salt: _t_opt_str_bytes = None) -> _t.Any:
238+
def load(self, f: t.IO[t.Any], salt: str | bytes | None = None) -> t.Any:
241239
"""Like :meth:`loads` but loads from a file."""
242240
return self.loads(f.read(), salt)
243241

244242
def loads_unsafe(
245-
self, s: _t_str_bytes, salt: _t_opt_str_bytes = None
246-
) -> _t_load_unsafe:
243+
self, s: str | bytes, salt: str | bytes | None = None
244+
) -> tuple[bool, t.Any]:
247245
"""Like :meth:`loads` but without verifying the signature. This
248246
is potentially very dangerous to use depending on how your
249247
serializer works. The return value is ``(signature_valid,
@@ -261,11 +259,11 @@ def loads_unsafe(
261259

262260
def _loads_unsafe_impl(
263261
self,
264-
s: _t_str_bytes,
265-
salt: _t_opt_str_bytes,
266-
load_kwargs: _t_opt_kwargs = None,
267-
load_payload_kwargs: _t_opt_kwargs = None,
268-
) -> _t_load_unsafe:
262+
s: str | bytes,
263+
salt: str | bytes | None,
264+
load_kwargs: dict[str, t.Any] | None = None,
265+
load_payload_kwargs: dict[str, t.Any] | None = None,
266+
) -> tuple[bool, t.Any]:
269267
"""Low level helper function to implement :meth:`loads_unsafe`
270268
in serializer subclasses.
271269
"""
@@ -290,8 +288,8 @@ def _loads_unsafe_impl(
290288
return False, None
291289

292290
def load_unsafe(
293-
self, f: _t.IO[_t.Any], salt: _t_opt_str_bytes = None
294-
) -> _t_load_unsafe:
291+
self, f: t.IO[t.Any], salt: str | bytes | None = None
292+
) -> tuple[bool, t.Any]:
295293
"""Like :meth:`loads_unsafe` but loads from a file.
296294
297295
.. versionadded:: 0.15

0 commit comments

Comments
 (0)