aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrobot-piglet <robot-piglet@yandex-team.com>2024-05-07 08:48:39 +0300
committerrobot-piglet <robot-piglet@yandex-team.com>2024-05-07 08:58:26 +0300
commit492446437a68ea9f463a02485d5e87fd21586047 (patch)
tree38fc646b572738d5e602ea4b0d0f36aa663e6bd9
parente81a331668b8aeb6178e2ab028aefe6cdaa9ba26 (diff)
downloadydb-492446437a68ea9f463a02485d5e87fd21586047.tar.gz
Intermediate changes
-rw-r--r--contrib/python/itsdangerous/py3/.dist-info/METADATA87
-rw-r--r--contrib/python/itsdangerous/py3/LICENSE.txt (renamed from contrib/python/itsdangerous/py3/LICENSE.rst)0
-rw-r--r--contrib/python/itsdangerous/py3/README.md40
-rw-r--r--contrib/python/itsdangerous/py3/README.rst68
-rw-r--r--contrib/python/itsdangerous/py3/itsdangerous/__init__.py21
-rw-r--r--contrib/python/itsdangerous/py3/itsdangerous/_json.py8
-rw-r--r--contrib/python/itsdangerous/py3/itsdangerous/encoding.py14
-rw-r--r--contrib/python/itsdangerous/py3/itsdangerous/exc.py29
-rw-r--r--contrib/python/itsdangerous/py3/itsdangerous/serializer.py215
-rw-r--r--contrib/python/itsdangerous/py3/itsdangerous/signer.py63
-rw-r--r--contrib/python/itsdangerous/py3/itsdangerous/timed.py84
-rw-r--r--contrib/python/itsdangerous/py3/itsdangerous/url_safe.py23
-rw-r--r--contrib/python/itsdangerous/py3/tests/test_itsdangerous/test_serializer.py9
-rw-r--r--contrib/python/itsdangerous/py3/ya.make2
14 files changed, 367 insertions, 296 deletions
diff --git a/contrib/python/itsdangerous/py3/.dist-info/METADATA b/contrib/python/itsdangerous/py3/.dist-info/METADATA
index 1d935ed3de..ddf5464849 100644
--- a/contrib/python/itsdangerous/py3/.dist-info/METADATA
+++ b/contrib/python/itsdangerous/py3/.dist-info/METADATA
@@ -1,32 +1,23 @@
Metadata-Version: 2.1
Name: itsdangerous
-Version: 2.1.2
+Version: 2.2.0
Summary: Safely pass data to untrusted environments and back.
-Home-page: https://palletsprojects.com/p/itsdangerous/
-Author: Armin Ronacher
-Author-email: armin.ronacher@active-4.com
-Maintainer: Pallets
-Maintainer-email: contact@palletsprojects.com
-License: BSD-3-Clause
-Project-URL: Donate, https://palletsprojects.com/donate
-Project-URL: Documentation, https://itsdangerous.palletsprojects.com/
-Project-URL: Changes, https://itsdangerous.palletsprojects.com/changes/
-Project-URL: Source Code, https://github.com/pallets/itsdangerous/
-Project-URL: Issue Tracker, https://github.com/pallets/itsdangerous/issues/
-Project-URL: Twitter, https://twitter.com/PalletsTeam
-Project-URL: Chat, https://discord.gg/pallets
-Platform: UNKNOWN
+Maintainer-email: Pallets <contact@palletsprojects.com>
+Requires-Python: >=3.8
+Description-Content-Type: text/markdown
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
-Requires-Python: >=3.7
-Description-Content-Type: text/x-rst
-License-File: LICENSE.rst
+Classifier: Typing :: Typed
+Project-URL: Changes, https://itsdangerous.palletsprojects.com/changes/
+Project-URL: Chat, https://discord.gg/pallets
+Project-URL: Documentation, https://itsdangerous.palletsprojects.com/
+Project-URL: Donate, https://palletsprojects.com/donate
+Project-URL: Source, https://github.com/pallets/itsdangerous/
-ItsDangerous
-============
+# ItsDangerous
... so better sign this
@@ -39,59 +30,31 @@ needed. A timestamp can be added and verified automatically while
loading a token.
-Installing
-----------
-
-Install and update using `pip`_:
-
-.. code-block:: text
-
- pip install -U itsdangerous
-
-.. _pip: https://pip.pypa.io/en/stable/getting-started/
-
-
-A Simple Example
-----------------
+## A Simple Example
Here's how you could generate a token for transmitting a user's id and
name between web requests.
-.. code-block:: python
-
- from itsdangerous import URLSafeSerializer
- auth_s = URLSafeSerializer("secret key", "auth")
- token = auth_s.dumps({"id": 5, "name": "itsdangerous"})
+```python
+from itsdangerous import URLSafeSerializer
+auth_s = URLSafeSerializer("secret key", "auth")
+token = auth_s.dumps({"id": 5, "name": "itsdangerous"})
- print(token)
- # eyJpZCI6NSwibmFtZSI6Iml0c2Rhbmdlcm91cyJ9.6YP6T0BaO67XP--9UzTrmurXSmg
+print(token)
+# eyJpZCI6NSwibmFtZSI6Iml0c2Rhbmdlcm91cyJ9.6YP6T0BaO67XP--9UzTrmurXSmg
- data = auth_s.loads(token)
- print(data["name"])
- # itsdangerous
+data = auth_s.loads(token)
+print(data["name"])
+# itsdangerous
+```
-Donate
-------
+## Donate
The Pallets organization develops and supports ItsDangerous and other
popular packages. In order to grow the community of contributors and
users, and allow the maintainers to devote more time to the projects,
-`please donate today`_.
-
-.. _please donate today: https://palletsprojects.com/donate
-
-
-Links
------
-
-- Documentation: https://itsdangerous.palletsprojects.com/
-- Changes: https://itsdangerous.palletsprojects.com/changes/
-- PyPI Releases: https://pypi.org/project/ItsDangerous/
-- Source Code: https://github.com/pallets/itsdangerous/
-- Issue Tracker: https://github.com/pallets/itsdangerous/issues/
-- Website: https://palletsprojects.com/p/itsdangerous/
-- Twitter: https://twitter.com/PalletsTeam
-- Chat: https://discord.gg/pallets
+[please donate today][].
+[please donate today]: https://palletsprojects.com/donate
diff --git a/contrib/python/itsdangerous/py3/LICENSE.rst b/contrib/python/itsdangerous/py3/LICENSE.txt
index 7b190ca671..7b190ca671 100644
--- a/contrib/python/itsdangerous/py3/LICENSE.rst
+++ b/contrib/python/itsdangerous/py3/LICENSE.txt
diff --git a/contrib/python/itsdangerous/py3/README.md b/contrib/python/itsdangerous/py3/README.md
new file mode 100644
index 0000000000..59b9a5b9b7
--- /dev/null
+++ b/contrib/python/itsdangerous/py3/README.md
@@ -0,0 +1,40 @@
+# ItsDangerous
+
+... so better sign this
+
+Various helpers to pass data to untrusted environments and to get it
+back safe and sound. Data is cryptographically signed to ensure that a
+token has not been tampered with.
+
+It's possible to customize how data is serialized. Data is compressed as
+needed. A timestamp can be added and verified automatically while
+loading a token.
+
+
+## A Simple Example
+
+Here's how you could generate a token for transmitting a user's id and
+name between web requests.
+
+```python
+from itsdangerous import URLSafeSerializer
+auth_s = URLSafeSerializer("secret key", "auth")
+token = auth_s.dumps({"id": 5, "name": "itsdangerous"})
+
+print(token)
+# eyJpZCI6NSwibmFtZSI6Iml0c2Rhbmdlcm91cyJ9.6YP6T0BaO67XP--9UzTrmurXSmg
+
+data = auth_s.loads(token)
+print(data["name"])
+# itsdangerous
+```
+
+
+## Donate
+
+The Pallets organization develops and supports ItsDangerous and other
+popular packages. In order to grow the community of contributors and
+users, and allow the maintainers to devote more time to the projects,
+[please donate today][].
+
+[please donate today]: https://palletsprojects.com/donate
diff --git a/contrib/python/itsdangerous/py3/README.rst b/contrib/python/itsdangerous/py3/README.rst
deleted file mode 100644
index f3dca9e307..0000000000
--- a/contrib/python/itsdangerous/py3/README.rst
+++ /dev/null
@@ -1,68 +0,0 @@
-ItsDangerous
-============
-
-... so better sign this
-
-Various helpers to pass data to untrusted environments and to get it
-back safe and sound. Data is cryptographically signed to ensure that a
-token has not been tampered with.
-
-It's possible to customize how data is serialized. Data is compressed as
-needed. A timestamp can be added and verified automatically while
-loading a token.
-
-
-Installing
-----------
-
-Install and update using `pip`_:
-
-.. code-block:: text
-
- pip install -U itsdangerous
-
-.. _pip: https://pip.pypa.io/en/stable/getting-started/
-
-
-A Simple Example
-----------------
-
-Here's how you could generate a token for transmitting a user's id and
-name between web requests.
-
-.. code-block:: python
-
- from itsdangerous import URLSafeSerializer
- auth_s = URLSafeSerializer("secret key", "auth")
- token = auth_s.dumps({"id": 5, "name": "itsdangerous"})
-
- print(token)
- # eyJpZCI6NSwibmFtZSI6Iml0c2Rhbmdlcm91cyJ9.6YP6T0BaO67XP--9UzTrmurXSmg
-
- data = auth_s.loads(token)
- print(data["name"])
- # itsdangerous
-
-
-Donate
-------
-
-The Pallets organization develops and supports ItsDangerous and other
-popular packages. In order to grow the community of contributors and
-users, and allow the maintainers to devote more time to the projects,
-`please donate today`_.
-
-.. _please donate today: https://palletsprojects.com/donate
-
-
-Links
------
-
-- Documentation: https://itsdangerous.palletsprojects.com/
-- Changes: https://itsdangerous.palletsprojects.com/changes/
-- PyPI Releases: https://pypi.org/project/ItsDangerous/
-- Source Code: https://github.com/pallets/itsdangerous/
-- Issue Tracker: https://github.com/pallets/itsdangerous/issues/
-- Website: https://palletsprojects.com/p/itsdangerous/
-- Twitter: https://twitter.com/PalletsTeam
-- Chat: https://discord.gg/pallets
diff --git a/contrib/python/itsdangerous/py3/itsdangerous/__init__.py b/contrib/python/itsdangerous/py3/itsdangerous/__init__.py
index fdb2dfd00a..ea55256ebd 100644
--- a/contrib/python/itsdangerous/py3/itsdangerous/__init__.py
+++ b/contrib/python/itsdangerous/py3/itsdangerous/__init__.py
@@ -1,3 +1,7 @@
+from __future__ import annotations
+
+import typing as t
+
from .encoding import base64_decode as base64_decode
from .encoding import base64_encode as base64_encode
from .encoding import want_bytes as want_bytes
@@ -16,4 +20,19 @@ from .timed import TimestampSigner as TimestampSigner
from .url_safe import URLSafeSerializer as URLSafeSerializer
from .url_safe import URLSafeTimedSerializer as URLSafeTimedSerializer
-__version__ = "2.1.2"
+
+def __getattr__(name: str) -> t.Any:
+ if name == "__version__":
+ import importlib.metadata
+ import warnings
+
+ warnings.warn(
+ "The '__version__' attribute is deprecated and will be removed in"
+ " ItsDangerous 2.3. Use feature detection or"
+ " 'importlib.metadata.version(\"itsdangerous\")' instead.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ return importlib.metadata.version("itsdangerous")
+
+ raise AttributeError(name)
diff --git a/contrib/python/itsdangerous/py3/itsdangerous/_json.py b/contrib/python/itsdangerous/py3/itsdangerous/_json.py
index c70d37a958..fc23feaaff 100644
--- a/contrib/python/itsdangerous/py3/itsdangerous/_json.py
+++ b/contrib/python/itsdangerous/py3/itsdangerous/_json.py
@@ -1,16 +1,18 @@
+from __future__ import annotations
+
import json as _json
-import typing as _t
+import typing as t
class _CompactJSON:
"""Wrapper around json module that strips whitespace."""
@staticmethod
- def loads(payload: _t.Union[str, bytes]) -> _t.Any:
+ def loads(payload: str | bytes) -> t.Any:
return _json.loads(payload)
@staticmethod
- def dumps(obj: _t.Any, **kwargs: _t.Any) -> str:
+ def dumps(obj: t.Any, **kwargs: t.Any) -> str:
kwargs.setdefault("ensure_ascii", False)
kwargs.setdefault("separators", (",", ":"))
return _json.dumps(obj, **kwargs)
diff --git a/contrib/python/itsdangerous/py3/itsdangerous/encoding.py b/contrib/python/itsdangerous/py3/itsdangerous/encoding.py
index edb04d1a63..f5ca80f905 100644
--- a/contrib/python/itsdangerous/py3/itsdangerous/encoding.py
+++ b/contrib/python/itsdangerous/py3/itsdangerous/encoding.py
@@ -1,15 +1,15 @@
+from __future__ import annotations
+
import base64
import string
import struct
-import typing as _t
+import typing as t
from .exc import BadData
-_t_str_bytes = _t.Union[str, bytes]
-
def want_bytes(
- s: _t_str_bytes, encoding: str = "utf-8", errors: str = "strict"
+ s: str | bytes, encoding: str = "utf-8", errors: str = "strict"
) -> bytes:
if isinstance(s, str):
s = s.encode(encoding, errors)
@@ -17,7 +17,7 @@ def want_bytes(
return s
-def base64_encode(string: _t_str_bytes) -> bytes:
+def base64_encode(string: str | bytes) -> bytes:
"""Base64 encode a string of bytes or text. The resulting bytes are
safe to use in URLs.
"""
@@ -25,7 +25,7 @@ def base64_encode(string: _t_str_bytes) -> bytes:
return base64.urlsafe_b64encode(string).rstrip(b"=")
-def base64_decode(string: _t_str_bytes) -> bytes:
+def base64_decode(string: str | bytes) -> bytes:
"""Base64 decode a URL-safe string of bytes or text. The result is
bytes.
"""
@@ -43,7 +43,7 @@ _base64_alphabet = f"{string.ascii_letters}{string.digits}-_=".encode("ascii")
_int64_struct = struct.Struct(">Q")
_int_to_bytes = _int64_struct.pack
-_bytes_to_int = _t.cast("_t.Callable[[bytes], _t.Tuple[int]]", _int64_struct.unpack)
+_bytes_to_int = t.cast("t.Callable[[bytes], tuple[int]]", _int64_struct.unpack)
def int_to_bytes(num: int) -> bytes:
diff --git a/contrib/python/itsdangerous/py3/itsdangerous/exc.py b/contrib/python/itsdangerous/py3/itsdangerous/exc.py
index c38a6af520..a75adcd527 100644
--- a/contrib/python/itsdangerous/py3/itsdangerous/exc.py
+++ b/contrib/python/itsdangerous/py3/itsdangerous/exc.py
@@ -1,8 +1,7 @@
-import typing as _t
-from datetime import datetime
+from __future__ import annotations
-_t_opt_any = _t.Optional[_t.Any]
-_t_opt_exc = _t.Optional[Exception]
+import typing as t
+from datetime import datetime
class BadData(Exception):
@@ -23,7 +22,7 @@ class BadData(Exception):
class BadSignature(BadData):
"""Raised if a signature does not match."""
- def __init__(self, message: str, payload: _t_opt_any = None):
+ def __init__(self, message: str, payload: t.Any | None = None):
super().__init__(message)
#: The payload that failed the signature test. In some
@@ -31,7 +30,7 @@ class BadSignature(BadData):
#: you know it was tampered with.
#:
#: .. versionadded:: 0.14
- self.payload: _t_opt_any = payload
+ self.payload: t.Any | None = payload
class BadTimeSignature(BadSignature):
@@ -42,8 +41,8 @@ class BadTimeSignature(BadSignature):
def __init__(
self,
message: str,
- payload: _t_opt_any = None,
- date_signed: _t.Optional[datetime] = None,
+ payload: t.Any | None = None,
+ date_signed: datetime | None = None,
):
super().__init__(message, payload)
@@ -75,19 +74,19 @@ class BadHeader(BadSignature):
def __init__(
self,
message: str,
- payload: _t_opt_any = None,
- header: _t_opt_any = None,
- original_error: _t_opt_exc = None,
+ payload: t.Any | None = None,
+ header: t.Any | None = None,
+ original_error: Exception | None = None,
):
super().__init__(message, payload)
#: If the header is actually available but just malformed it
#: might be stored here.
- self.header: _t_opt_any = header
+ self.header: t.Any | None = header
#: If available, the error that indicates why the payload was
#: not valid. This might be ``None``.
- self.original_error: _t_opt_exc = original_error
+ self.original_error: Exception | None = original_error
class BadPayload(BadData):
@@ -99,9 +98,9 @@ class BadPayload(BadData):
.. versionadded:: 0.15
"""
- def __init__(self, message: str, original_error: _t_opt_exc = None):
+ def __init__(self, message: str, original_error: Exception | None = None):
super().__init__(message)
#: If available, the error that indicates why the payload was
#: not valid. This might be ``None``.
- self.original_error: _t_opt_exc = original_error
+ self.original_error: Exception | None = original_error
diff --git a/contrib/python/itsdangerous/py3/itsdangerous/serializer.py b/contrib/python/itsdangerous/py3/itsdangerous/serializer.py
index 9f4a84a172..5ddf3871d8 100644
--- a/contrib/python/itsdangerous/py3/itsdangerous/serializer.py
+++ b/contrib/python/itsdangerous/py3/itsdangerous/serializer.py
@@ -1,5 +1,8 @@
+from __future__ import annotations
+
+import collections.abc as cabc
import json
-import typing as _t
+import typing as t
from .encoding import want_bytes
from .exc import BadPayload
@@ -7,22 +10,36 @@ from .exc import BadSignature
from .signer import _make_keys_list
from .signer import Signer
-_t_str_bytes = _t.Union[str, bytes]
-_t_opt_str_bytes = _t.Optional[_t_str_bytes]
-_t_kwargs = _t.Dict[str, _t.Any]
-_t_opt_kwargs = _t.Optional[_t_kwargs]
-_t_signer = _t.Type[Signer]
-_t_fallbacks = _t.List[_t.Union[_t_kwargs, _t.Tuple[_t_signer, _t_kwargs], _t_signer]]
-_t_load_unsafe = _t.Tuple[bool, _t.Any]
-_t_secret_key = _t.Union[_t.Iterable[_t_str_bytes], _t_str_bytes]
+if t.TYPE_CHECKING:
+ import typing_extensions as te
+
+ # This should be either be str or bytes. To avoid having to specify the
+ # bound type, it falls back to a union if structural matching fails.
+ _TSerialized = te.TypeVar(
+ "_TSerialized", bound=t.Union[str, bytes], default=t.Union[str, bytes]
+ )
+else:
+ # Still available at runtime on Python < 3.13, but without the default.
+ _TSerialized = t.TypeVar("_TSerialized", bound=t.Union[str, bytes])
+
+class _PDataSerializer(t.Protocol[_TSerialized]):
+ def loads(self, payload: _TSerialized, /) -> t.Any: ...
+ # A signature with additional arguments is not handled correctly by type
+ # checkers right now, so an overload is used below for serializers that
+ # don't match this strict protocol.
+ def dumps(self, obj: t.Any, /) -> _TSerialized: ...
-def is_text_serializer(serializer: _t.Any) -> bool:
+
+# Use TypeIs once it's available in typing_extensions or 3.13.
+def is_text_serializer(
+ serializer: _PDataSerializer[t.Any],
+) -> te.TypeGuard[_PDataSerializer[str]]:
"""Checks whether a serializer generates text or binary."""
return isinstance(serializer.dumps({}), str)
-class Serializer:
+class Serializer(t.Generic[_TSerialized]):
"""A serializer wraps a :class:`~itsdangerous.signer.Signer` to
enable serializing and securely signing data other than bytes. It
can unsign to verify that the data hasn't been changed.
@@ -77,31 +94,120 @@ class Serializer:
#: The default serialization module to use to serialize data to a
#: string internally. The default is :mod:`json`, but can be changed
#: to any object that provides ``dumps`` and ``loads`` methods.
- default_serializer: _t.Any = json
+ default_serializer: _PDataSerializer[t.Any] = json
#: The default ``Signer`` class to instantiate when signing data.
#: The default is :class:`itsdangerous.signer.Signer`.
- default_signer: _t_signer = Signer
+ default_signer: type[Signer] = Signer
#: The default fallback signers to try when unsigning fails.
- default_fallback_signers: _t_fallbacks = []
+ default_fallback_signers: list[
+ dict[str, t.Any] | tuple[type[Signer], dict[str, t.Any]] | type[Signer]
+ ] = []
+
+ # Serializer[str] if no data serializer is provided, or if it returns str.
+ @t.overload
+ def __init__(
+ self: Serializer[str],
+ secret_key: str | bytes | cabc.Iterable[str] | cabc.Iterable[bytes],
+ salt: str | bytes | None = b"itsdangerous",
+ serializer: None | _PDataSerializer[str] = None,
+ serializer_kwargs: dict[str, t.Any] | None = None,
+ signer: type[Signer] | None = None,
+ signer_kwargs: dict[str, t.Any] | None = None,
+ fallback_signers: list[
+ dict[str, t.Any] | tuple[type[Signer], dict[str, t.Any]] | type[Signer]
+ ]
+ | None = None,
+ ): ...
+
+ # Serializer[bytes] with a bytes data serializer positional argument.
+ @t.overload
+ def __init__(
+ self: Serializer[bytes],
+ secret_key: str | bytes | cabc.Iterable[str] | cabc.Iterable[bytes],
+ salt: str | bytes | None,
+ serializer: _PDataSerializer[bytes],
+ serializer_kwargs: dict[str, t.Any] | None = None,
+ signer: type[Signer] | None = None,
+ signer_kwargs: dict[str, t.Any] | None = None,
+ fallback_signers: list[
+ dict[str, t.Any] | tuple[type[Signer], dict[str, t.Any]] | type[Signer]
+ ]
+ | None = None,
+ ): ...
+
+ # Serializer[bytes] with a bytes data serializer keyword argument.
+ @t.overload
+ def __init__(
+ self: Serializer[bytes],
+ secret_key: str | bytes | cabc.Iterable[str] | cabc.Iterable[bytes],
+ salt: str | bytes | None = b"itsdangerous",
+ *,
+ serializer: _PDataSerializer[bytes],
+ serializer_kwargs: dict[str, t.Any] | None = None,
+ signer: type[Signer] | None = None,
+ signer_kwargs: dict[str, t.Any] | None = None,
+ fallback_signers: list[
+ dict[str, t.Any] | tuple[type[Signer], dict[str, t.Any]] | type[Signer]
+ ]
+ | None = None,
+ ): ...
+
+ # Fall back with a positional argument. If the strict signature of
+ # _PDataSerializer doesn't match, fall back to a union, requiring the user
+ # to specify the type.
+ @t.overload
+ def __init__(
+ self,
+ secret_key: str | bytes | cabc.Iterable[str] | cabc.Iterable[bytes],
+ salt: str | bytes | None,
+ serializer: t.Any,
+ serializer_kwargs: dict[str, t.Any] | None = None,
+ signer: type[Signer] | None = None,
+ signer_kwargs: dict[str, t.Any] | None = None,
+ fallback_signers: list[
+ dict[str, t.Any] | tuple[type[Signer], dict[str, t.Any]] | type[Signer]
+ ]
+ | None = None,
+ ): ...
+
+ # Fall back with a keyword argument.
+ @t.overload
+ def __init__(
+ self,
+ secret_key: str | bytes | cabc.Iterable[str] | cabc.Iterable[bytes],
+ salt: str | bytes | None = b"itsdangerous",
+ *,
+ serializer: t.Any,
+ serializer_kwargs: dict[str, t.Any] | None = None,
+ signer: type[Signer] | None = None,
+ signer_kwargs: dict[str, t.Any] | None = None,
+ fallback_signers: list[
+ dict[str, t.Any] | tuple[type[Signer], dict[str, t.Any]] | type[Signer]
+ ]
+ | None = None,
+ ): ...
def __init__(
self,
- secret_key: _t_secret_key,
- salt: _t_opt_str_bytes = b"itsdangerous",
- serializer: _t.Any = None,
- serializer_kwargs: _t_opt_kwargs = None,
- signer: _t.Optional[_t_signer] = None,
- signer_kwargs: _t_opt_kwargs = None,
- fallback_signers: _t.Optional[_t_fallbacks] = None,
+ secret_key: str | bytes | cabc.Iterable[str] | cabc.Iterable[bytes],
+ salt: str | bytes | None = b"itsdangerous",
+ serializer: t.Any | None = None,
+ serializer_kwargs: dict[str, t.Any] | None = None,
+ signer: type[Signer] | None = None,
+ signer_kwargs: dict[str, t.Any] | None = None,
+ fallback_signers: list[
+ dict[str, t.Any] | tuple[type[Signer], dict[str, t.Any]] | type[Signer]
+ ]
+ | None = None,
):
#: The list of secret keys to try for verifying signatures, from
#: oldest to newest. The newest (last) key is used for signing.
#:
#: This allows a key rotation system to keep a list of allowed
#: keys and remove expired ones.
- self.secret_keys: _t.List[bytes] = _make_keys_list(secret_key)
+ self.secret_keys: list[bytes] = _make_keys_list(secret_key)
if salt is not None:
salt = want_bytes(salt)
@@ -112,20 +218,22 @@ class Serializer:
if serializer is None:
serializer = self.default_serializer
- self.serializer: _t.Any = serializer
+ self.serializer: _PDataSerializer[_TSerialized] = serializer
self.is_text_serializer: bool = is_text_serializer(serializer)
if signer is None:
signer = self.default_signer
- self.signer: _t_signer = signer
- self.signer_kwargs: _t_kwargs = signer_kwargs or {}
+ self.signer: type[Signer] = signer
+ self.signer_kwargs: dict[str, t.Any] = signer_kwargs or {}
if fallback_signers is None:
- fallback_signers = list(self.default_fallback_signers or ())
+ fallback_signers = list(self.default_fallback_signers)
- self.fallback_signers: _t_fallbacks = fallback_signers
- self.serializer_kwargs: _t_kwargs = serializer_kwargs or {}
+ self.fallback_signers: list[
+ dict[str, t.Any] | tuple[type[Signer], dict[str, t.Any]] | type[Signer]
+ ] = fallback_signers
+ self.serializer_kwargs: dict[str, t.Any] = serializer_kwargs or {}
@property
def secret_key(self) -> bytes:
@@ -135,8 +243,8 @@ class Serializer:
return self.secret_keys[-1]
def load_payload(
- self, payload: bytes, serializer: _t.Optional[_t.Any] = None
- ) -> _t.Any:
+ self, payload: bytes, serializer: _PDataSerializer[t.Any] | None = None
+ ) -> t.Any:
"""Loads the encoded object. This function raises
:class:`.BadPayload` if the payload is not valid. The
``serializer`` parameter can be used to override the serializer
@@ -144,16 +252,17 @@ class Serializer:
bytes.
"""
if serializer is None:
- serializer = self.serializer
+ use_serializer = self.serializer
is_text = self.is_text_serializer
else:
+ use_serializer = serializer
is_text = is_text_serializer(serializer)
try:
if is_text:
- return serializer.loads(payload.decode("utf-8"))
+ return use_serializer.loads(payload.decode("utf-8")) # type: ignore[arg-type]
- return serializer.loads(payload)
+ return use_serializer.loads(payload) # type: ignore[arg-type]
except Exception as e:
raise BadPayload(
"Could not load the payload because an exception"
@@ -161,14 +270,14 @@ class Serializer:
original_error=e,
) from e
- def dump_payload(self, obj: _t.Any) -> bytes:
+ def dump_payload(self, obj: t.Any) -> bytes:
"""Dumps the encoded object. The return value is always bytes.
If the internal serializer returns text, the value will be
encoded as UTF-8.
"""
return want_bytes(self.serializer.dumps(obj, **self.serializer_kwargs))
- def make_signer(self, salt: _t_opt_str_bytes = None) -> Signer:
+ def make_signer(self, salt: str | bytes | None = None) -> Signer:
"""Creates a new instance of the signer to be used. The default
implementation uses the :class:`.Signer` base class.
"""
@@ -177,7 +286,7 @@ class Serializer:
return self.signer(self.secret_keys, salt=salt, **self.signer_kwargs)
- def iter_unsigners(self, salt: _t_opt_str_bytes = None) -> _t.Iterator[Signer]:
+ def iter_unsigners(self, salt: str | bytes | None = None) -> cabc.Iterator[Signer]:
"""Iterates over all signers to be tried for unsigning. Starts
with the configured signer, then constructs each signer
specified in ``fallback_signers``.
@@ -199,7 +308,7 @@ class Serializer:
for secret_key in self.secret_keys:
yield fallback(secret_key, salt=salt, **kwargs)
- def dumps(self, obj: _t.Any, salt: _t_opt_str_bytes = None) -> _t_str_bytes:
+ def dumps(self, obj: t.Any, salt: str | bytes | None = None) -> _TSerialized:
"""Returns a signed string serialized with the internal
serializer. The return value can be either a byte or unicode
string depending on the format of the internal serializer.
@@ -208,19 +317,19 @@ class Serializer:
rv = self.make_signer(salt).sign(payload)
if self.is_text_serializer:
- return rv.decode("utf-8")
+ return rv.decode("utf-8") # type: ignore[return-value]
- return rv
+ return rv # type: ignore[return-value]
- def dump(self, obj: _t.Any, f: _t.IO, salt: _t_opt_str_bytes = None) -> None:
+ def dump(self, obj: t.Any, f: t.IO[t.Any], salt: str | bytes | None = None) -> None:
"""Like :meth:`dumps` but dumps into a file. The file handle has
to be compatible with what the internal serializer expects.
"""
f.write(self.dumps(obj, salt))
def loads(
- self, s: _t_str_bytes, salt: _t_opt_str_bytes = None, **kwargs: _t.Any
- ) -> _t.Any:
+ self, s: str | bytes, salt: str | bytes | None = None, **kwargs: t.Any
+ ) -> t.Any:
"""Reverse of :meth:`dumps`. Raises :exc:`.BadSignature` if the
signature validation fails.
"""
@@ -233,15 +342,15 @@ class Serializer:
except BadSignature as err:
last_exception = err
- raise _t.cast(BadSignature, last_exception)
+ raise t.cast(BadSignature, last_exception)
- def load(self, f: _t.IO, salt: _t_opt_str_bytes = None) -> _t.Any:
+ def load(self, f: t.IO[t.Any], salt: str | bytes | None = None) -> t.Any:
"""Like :meth:`loads` but loads from a file."""
return self.loads(f.read(), salt)
def loads_unsafe(
- self, s: _t_str_bytes, salt: _t_opt_str_bytes = None
- ) -> _t_load_unsafe:
+ self, s: str | bytes, salt: str | bytes | None = None
+ ) -> tuple[bool, t.Any]:
"""Like :meth:`loads` but without verifying the signature. This
is potentially very dangerous to use depending on how your
serializer works. The return value is ``(signature_valid,
@@ -259,11 +368,11 @@ class Serializer:
def _loads_unsafe_impl(
self,
- s: _t_str_bytes,
- salt: _t_opt_str_bytes,
- load_kwargs: _t_opt_kwargs = None,
- load_payload_kwargs: _t_opt_kwargs = None,
- ) -> _t_load_unsafe:
+ s: str | bytes,
+ salt: str | bytes | None,
+ load_kwargs: dict[str, t.Any] | None = None,
+ load_payload_kwargs: dict[str, t.Any] | None = None,
+ ) -> tuple[bool, t.Any]:
"""Low level helper function to implement :meth:`loads_unsafe`
in serializer subclasses.
"""
@@ -287,7 +396,9 @@ class Serializer:
except BadPayload:
return False, None
- def load_unsafe(self, f: _t.IO, salt: _t_opt_str_bytes = None) -> _t_load_unsafe:
+ def load_unsafe(
+ self, f: t.IO[t.Any], salt: str | bytes | None = None
+ ) -> tuple[bool, t.Any]:
"""Like :meth:`loads_unsafe` but loads from a file.
.. versionadded:: 0.15
diff --git a/contrib/python/itsdangerous/py3/itsdangerous/signer.py b/contrib/python/itsdangerous/py3/itsdangerous/signer.py
index aa12005e9a..e324dc03da 100644
--- a/contrib/python/itsdangerous/py3/itsdangerous/signer.py
+++ b/contrib/python/itsdangerous/py3/itsdangerous/signer.py
@@ -1,6 +1,9 @@
+from __future__ import annotations
+
+import collections.abc as cabc
import hashlib
import hmac
-import typing as _t
+import typing as t
from .encoding import _base64_alphabet
from .encoding import base64_decode
@@ -8,10 +11,6 @@ from .encoding import base64_encode
from .encoding import want_bytes
from .exc import BadSignature
-_t_str_bytes = _t.Union[str, bytes]
-_t_opt_str_bytes = _t.Optional[_t_str_bytes]
-_t_secret_key = _t.Union[_t.Iterable[_t_str_bytes], _t_str_bytes]
-
class SigningAlgorithm:
"""Subclasses must implement :meth:`get_signature` to provide
@@ -38,30 +37,40 @@ class NoneAlgorithm(SigningAlgorithm):
return b""
+def _lazy_sha1(string: bytes = b"") -> t.Any:
+ """Don't access ``hashlib.sha1`` until runtime. FIPS builds may not include
+ SHA-1, in which case the import and use as a default would fail before the
+ developer can configure something else.
+ """
+ return hashlib.sha1(string)
+
+
class HMACAlgorithm(SigningAlgorithm):
"""Provides signature generation using HMACs."""
#: The digest method to use with the MAC algorithm. This defaults to
#: SHA1, but can be changed to any other function in the hashlib
#: module.
- default_digest_method: _t.Any = staticmethod(hashlib.sha1)
+ default_digest_method: t.Any = staticmethod(_lazy_sha1)
- def __init__(self, digest_method: _t.Any = None):
+ def __init__(self, digest_method: t.Any = None):
if digest_method is None:
digest_method = self.default_digest_method
- self.digest_method: _t.Any = digest_method
+ self.digest_method: t.Any = digest_method
def get_signature(self, key: bytes, value: bytes) -> bytes:
mac = hmac.new(key, msg=value, digestmod=self.digest_method)
return mac.digest()
-def _make_keys_list(secret_key: _t_secret_key) -> _t.List[bytes]:
+def _make_keys_list(
+ secret_key: str | bytes | cabc.Iterable[str] | cabc.Iterable[bytes],
+) -> list[bytes]:
if isinstance(secret_key, (str, bytes)):
return [want_bytes(secret_key)]
- return [want_bytes(s) for s in secret_key]
+ return [want_bytes(s) for s in secret_key] # pyright: ignore
class Signer:
@@ -108,7 +117,7 @@ class Signer:
#: doesn't apply when used intermediately in HMAC.
#:
#: .. versionadded:: 0.14
- default_digest_method: _t.Any = staticmethod(hashlib.sha1)
+ default_digest_method: t.Any = staticmethod(_lazy_sha1)
#: The default scheme to use to derive the signing key from the
#: secret key and salt. The default is ``django-concat``. Possible
@@ -119,19 +128,19 @@ class Signer:
def __init__(
self,
- secret_key: _t_secret_key,
- salt: _t_opt_str_bytes = b"itsdangerous.Signer",
- sep: _t_str_bytes = b".",
- key_derivation: _t.Optional[str] = None,
- digest_method: _t.Optional[_t.Any] = None,
- algorithm: _t.Optional[SigningAlgorithm] = None,
+ secret_key: str | bytes | cabc.Iterable[str] | cabc.Iterable[bytes],
+ salt: str | bytes | None = b"itsdangerous.Signer",
+ sep: str | bytes = b".",
+ key_derivation: str | None = None,
+ digest_method: t.Any | None = None,
+ algorithm: SigningAlgorithm | None = None,
):
#: The list of secret keys to try for verifying signatures, from
#: oldest to newest. The newest (last) key is used for signing.
#:
#: This allows a key rotation system to keep a list of allowed
#: keys and remove expired ones.
- self.secret_keys: _t.List[bytes] = _make_keys_list(secret_key)
+ self.secret_keys: list[bytes] = _make_keys_list(secret_key)
self.sep: bytes = want_bytes(sep)
if self.sep in _base64_alphabet:
@@ -156,7 +165,7 @@ class Signer:
if digest_method is None:
digest_method = self.default_digest_method
- self.digest_method: _t.Any = digest_method
+ self.digest_method: t.Any = digest_method
if algorithm is None:
algorithm = HMACAlgorithm(self.digest_method)
@@ -170,7 +179,7 @@ class Signer:
"""
return self.secret_keys[-1]
- def derive_key(self, secret_key: _t_opt_str_bytes = None) -> bytes:
+ def derive_key(self, secret_key: str | bytes | None = None) -> bytes:
"""This method is called to derive the key. The default key
derivation choices can be overridden here. Key derivation is not
intended to be used as a security method to make a complex key
@@ -189,9 +198,9 @@ class Signer:
secret_key = want_bytes(secret_key)
if self.key_derivation == "concat":
- return _t.cast(bytes, self.digest_method(self.salt + secret_key).digest())
+ return t.cast(bytes, self.digest_method(self.salt + secret_key).digest())
elif self.key_derivation == "django-concat":
- return _t.cast(
+ return t.cast(
bytes, self.digest_method(self.salt + b"signer" + secret_key).digest()
)
elif self.key_derivation == "hmac":
@@ -203,19 +212,19 @@ class Signer:
else:
raise TypeError("Unknown key derivation method")
- def get_signature(self, value: _t_str_bytes) -> bytes:
+ def get_signature(self, value: str | bytes) -> bytes:
"""Returns the signature for the given value."""
value = want_bytes(value)
key = self.derive_key()
sig = self.algorithm.get_signature(key, value)
return base64_encode(sig)
- def sign(self, value: _t_str_bytes) -> bytes:
+ def sign(self, value: str | bytes) -> bytes:
"""Signs the given string."""
value = want_bytes(value)
return value + self.sep + self.get_signature(value)
- def verify_signature(self, value: _t_str_bytes, sig: _t_str_bytes) -> bool:
+ def verify_signature(self, value: str | bytes, sig: str | bytes) -> bool:
"""Verifies the signature for the given value."""
try:
sig = base64_decode(sig)
@@ -232,7 +241,7 @@ class Signer:
return False
- def unsign(self, signed_value: _t_str_bytes) -> bytes:
+ def unsign(self, signed_value: str | bytes) -> bytes:
"""Unsigns the given string."""
signed_value = want_bytes(signed_value)
@@ -246,7 +255,7 @@ class Signer:
raise BadSignature(f"Signature {sig!r} does not match", payload=value)
- def validate(self, signed_value: _t_str_bytes) -> bool:
+ def validate(self, signed_value: str | bytes) -> bool:
"""Only validates the given signed value. Returns ``True`` if
the signature exists and is valid.
"""
diff --git a/contrib/python/itsdangerous/py3/itsdangerous/timed.py b/contrib/python/itsdangerous/py3/itsdangerous/timed.py
index cad8da341c..73843755d9 100644
--- a/contrib/python/itsdangerous/py3/itsdangerous/timed.py
+++ b/contrib/python/itsdangerous/py3/itsdangerous/timed.py
@@ -1,6 +1,8 @@
+from __future__ import annotations
+
+import collections.abc as cabc
import time
-import typing
-import typing as _t
+import typing as t
from datetime import datetime
from datetime import timezone
@@ -12,16 +14,10 @@ from .encoding import want_bytes
from .exc import BadSignature
from .exc import BadTimeSignature
from .exc import SignatureExpired
+from .serializer import _TSerialized
from .serializer import Serializer
from .signer import Signer
-_t_str_bytes = _t.Union[str, bytes]
-_t_opt_str_bytes = _t.Optional[_t_str_bytes]
-_t_opt_int = _t.Optional[int]
-
-if _t.TYPE_CHECKING:
- import typing_extensions as _te
-
class TimestampSigner(Signer):
"""Works like the regular :class:`.Signer` but also records the time
@@ -46,7 +42,7 @@ class TimestampSigner(Signer):
"""
return datetime.fromtimestamp(ts, tz=timezone.utc)
- def sign(self, value: _t_str_bytes) -> bytes:
+ def sign(self, value: str | bytes) -> bytes:
"""Signs the given string and also attaches time information."""
value = want_bytes(value)
timestamp = base64_encode(int_to_bytes(self.get_timestamp()))
@@ -57,30 +53,28 @@ class TimestampSigner(Signer):
# Ignore overlapping signatures check, return_timestamp is the only
# parameter that affects the return type.
- @typing.overload
- def unsign( # type: ignore
+ @t.overload
+ def unsign( # type: ignore[overload-overlap]
self,
- signed_value: _t_str_bytes,
- max_age: _t_opt_int = None,
- return_timestamp: "_te.Literal[False]" = False,
- ) -> bytes:
- ...
+ signed_value: str | bytes,
+ max_age: int | None = None,
+ return_timestamp: t.Literal[False] = False,
+ ) -> bytes: ...
- @typing.overload
+ @t.overload
def unsign(
self,
- signed_value: _t_str_bytes,
- max_age: _t_opt_int = None,
- return_timestamp: "_te.Literal[True]" = True,
- ) -> _t.Tuple[bytes, datetime]:
- ...
+ signed_value: str | bytes,
+ max_age: int | None = None,
+ return_timestamp: t.Literal[True] = True,
+ ) -> tuple[bytes, datetime]: ...
def unsign(
self,
- signed_value: _t_str_bytes,
- max_age: _t_opt_int = None,
+ signed_value: str | bytes,
+ max_age: int | None = None,
return_timestamp: bool = False,
- ) -> _t.Union[_t.Tuple[bytes, datetime], bytes]:
+ ) -> tuple[bytes, datetime] | bytes:
"""Works like the regular :meth:`.Signer.unsign` but can also
validate the time. See the base docstring of the class for
the general behavior. If ``return_timestamp`` is ``True`` the
@@ -112,8 +106,8 @@ class TimestampSigner(Signer):
raise BadTimeSignature("timestamp missing", payload=result)
value, ts_bytes = result.rsplit(sep, 1)
- ts_int: _t_opt_int = None
- ts_dt: _t.Optional[datetime] = None
+ ts_int: int | None = None
+ ts_dt: datetime | None = None
try:
ts_int = bytes_to_int(base64_decode(ts_bytes))
@@ -163,7 +157,7 @@ class TimestampSigner(Signer):
return value
- def validate(self, signed_value: _t_str_bytes, max_age: _t_opt_int = None) -> bool:
+ def validate(self, signed_value: str | bytes, max_age: int | None = None) -> bool:
"""Only validates the given signed value. Returns ``True`` if
the signature exists and is valid."""
try:
@@ -173,28 +167,28 @@ class TimestampSigner(Signer):
return False
-class TimedSerializer(Serializer):
+class TimedSerializer(Serializer[_TSerialized]):
"""Uses :class:`TimestampSigner` instead of the default
:class:`.Signer`.
"""
- default_signer: _t.Type[TimestampSigner] = TimestampSigner
+ default_signer: type[TimestampSigner] = TimestampSigner
def iter_unsigners(
- self, salt: _t_opt_str_bytes = None
- ) -> _t.Iterator[TimestampSigner]:
- return _t.cast("_t.Iterator[TimestampSigner]", super().iter_unsigners(salt))
+ self, salt: str | bytes | None = None
+ ) -> cabc.Iterator[TimestampSigner]:
+ return t.cast("cabc.Iterator[TimestampSigner]", super().iter_unsigners(salt))
# TODO: Signature is incompatible because parameters were added
# before salt.
- def loads( # type: ignore
+ def loads( # type: ignore[override]
self,
- s: _t_str_bytes,
- max_age: _t_opt_int = None,
+ s: str | bytes,
+ max_age: int | None = None,
return_timestamp: bool = False,
- salt: _t_opt_str_bytes = None,
- ) -> _t.Any:
+ salt: str | bytes | None = None,
+ ) -> t.Any:
"""Reverse of :meth:`dumps`, raises :exc:`.BadSignature` if the
signature validation fails. If a ``max_age`` is provided it will
ensure the signature is not older than that time in seconds. In
@@ -223,12 +217,12 @@ class TimedSerializer(Serializer):
except BadSignature as err:
last_exception = err
- raise _t.cast(BadSignature, last_exception)
+ raise t.cast(BadSignature, last_exception)
- def loads_unsafe( # type: ignore
+ def loads_unsafe( # type: ignore[override]
self,
- s: _t_str_bytes,
- max_age: _t_opt_int = None,
- salt: _t_opt_str_bytes = None,
- ) -> _t.Tuple[bool, _t.Any]:
+ s: str | bytes,
+ max_age: int | None = None,
+ salt: str | bytes | None = None,
+ ) -> tuple[bool, t.Any]:
return self._loads_unsafe_impl(s, salt, load_kwargs={"max_age": max_age})
diff --git a/contrib/python/itsdangerous/py3/itsdangerous/url_safe.py b/contrib/python/itsdangerous/py3/itsdangerous/url_safe.py
index d5a9b0c266..56a0793315 100644
--- a/contrib/python/itsdangerous/py3/itsdangerous/url_safe.py
+++ b/contrib/python/itsdangerous/py3/itsdangerous/url_safe.py
@@ -1,29 +1,32 @@
-import typing as _t
+from __future__ import annotations
+
+import typing as t
import zlib
from ._json import _CompactJSON
from .encoding import base64_decode
from .encoding import base64_encode
from .exc import BadPayload
+from .serializer import _PDataSerializer
from .serializer import Serializer
from .timed import TimedSerializer
-class URLSafeSerializerMixin(Serializer):
+class URLSafeSerializerMixin(Serializer[str]):
"""Mixed in with a regular serializer it will attempt to zlib
compress the string to make it shorter if necessary. It will also
base64 encode the string so that it can safely be placed in a URL.
"""
- default_serializer = _CompactJSON
+ default_serializer: _PDataSerializer[str] = _CompactJSON
def load_payload(
self,
payload: bytes,
- *args: _t.Any,
- serializer: _t.Optional[_t.Any] = None,
- **kwargs: _t.Any,
- ) -> _t.Any:
+ *args: t.Any,
+ serializer: t.Any | None = None,
+ **kwargs: t.Any,
+ ) -> t.Any:
decompress = False
if payload.startswith(b"."):
@@ -49,7 +52,7 @@ class URLSafeSerializerMixin(Serializer):
return super().load_payload(json, *args, **kwargs)
- def dump_payload(self, obj: _t.Any) -> bytes:
+ def dump_payload(self, obj: t.Any) -> bytes:
json = super().dump_payload(obj)
is_compressed = False
compressed = zlib.compress(json)
@@ -66,14 +69,14 @@ class URLSafeSerializerMixin(Serializer):
return base64d
-class URLSafeSerializer(URLSafeSerializerMixin, Serializer):
+class URLSafeSerializer(URLSafeSerializerMixin, Serializer[str]):
"""Works like :class:`.Serializer` but dumps and loads into a URL
safe string consisting of the upper and lowercase character of the
alphabet as well as ``'_'``, ``'-'`` and ``'.'``.
"""
-class URLSafeTimedSerializer(URLSafeSerializerMixin, TimedSerializer):
+class URLSafeTimedSerializer(URLSafeSerializerMixin, TimedSerializer[str]):
"""Works like :class:`.TimedSerializer` but dumps and loads into a
URL safe string consisting of the upper and lowercase character of
the alphabet as well as ``'_'``, ``'-'`` and ``'.'``.
diff --git a/contrib/python/itsdangerous/py3/tests/test_itsdangerous/test_serializer.py b/contrib/python/itsdangerous/py3/tests/test_itsdangerous/test_serializer.py
index cdc419191c..2c511cd202 100644
--- a/contrib/python/itsdangerous/py3/tests/test_itsdangerous/test_serializer.py
+++ b/contrib/python/itsdangerous/py3/tests/test_itsdangerous/test_serializer.py
@@ -14,17 +14,16 @@ import pytest
from itsdangerous.exc import BadPayload
from itsdangerous.exc import BadSignature
from itsdangerous.serializer import Serializer
+from itsdangerous.signer import _lazy_sha1
from itsdangerous.signer import Signer
@overload
-def coerce_str(ref: str, s: str) -> str:
- ...
+def coerce_str(ref: str, s: str) -> str: ...
@overload
-def coerce_str(ref: bytes, s: str) -> bytes:
- ...
+def coerce_str(ref: bytes, s: str) -> bytes: ...
def coerce_str(ref: Union[str, bytes], s: str) -> Union[str, bytes]:
@@ -179,7 +178,7 @@ class TestSerializer:
)
unsigners = serializer.iter_unsigners()
- assert next(unsigners).digest_method == hashlib.sha1
+ assert next(unsigners).digest_method == _lazy_sha1
for signer in unsigners:
assert signer.digest_method == hashlib.sha256
diff --git a/contrib/python/itsdangerous/py3/ya.make b/contrib/python/itsdangerous/py3/ya.make
index e74a40e020..f1b983d06e 100644
--- a/contrib/python/itsdangerous/py3/ya.make
+++ b/contrib/python/itsdangerous/py3/ya.make
@@ -2,7 +2,7 @@
PY3_LIBRARY()
-VERSION(2.1.2)
+VERSION(2.2.0)
LICENSE(BSD-3-Clause)