From bcb787f991e96743a8bb239be50b6655a65e47e2 Mon Sep 17 00:00:00 2001 From: Aleksei Maslakov Date: Wed, 20 Sep 2023 23:39:05 +0300 Subject: [PATCH] Setup mypy config and pre-commit hook --- .github/workflows/pre-commit.yml | 5 ++++ .gitignore | 2 ++ .pre-commit-config.yaml | 9 ++++++ cashews/backends/memory.py | 4 +-- cashews/backends/redis/backend.py | 24 ++++++++-------- cashews/decorators/__init__.py | 46 ++++++++++++++++++++++--------- cashews/decorators/locked.py | 2 +- cashews/picklers.py | 2 +- cashews/ttl.py | 4 +-- cashews/utils/__init__.py | 6 ++++ cashews/wrapper/__init__.py | 8 ++++-- mypy.ini | 12 ++++++++ setup.cfg | 3 ++ 13 files changed, 94 insertions(+), 33 deletions(-) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 7aebbbb..588c26a 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -12,4 +12,9 @@ jobs: steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v4 + + - name: install extras for mypy + run: | + pip install -e ".[redis,diskcache,speedup,dill,lint]" + - uses: pre-commit/action@v3.0.0 diff --git a/.gitignore b/.gitignore index 3c125d2..847242d 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ build/ *.egg-info .tox .hypothesis +.pytest_cache +.mypy_cache diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 508b678..251d2ec 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -35,3 +35,12 @@ repos: rev: 6.0.0 hooks: - id: flake8 + + - repo: local + hooks: + - id: mypy + name: mypy + entry: python3 -m mypy + exclude: (^tests/|^perf/|^examples/|setup.py) + language: system + types: [python] diff --git a/cashews/backends/memory.py b/cashews/backends/memory.py index 516c374..912f47c 100644 --- a/cashews/backends/memory.py +++ b/cashews/backends/memory.py @@ -100,7 +100,7 @@ async def scan(self, pattern: str, batch_size: int = 100) -> AsyncIterator[Key]: yield key async def incr(self, key: Key, value: int = 1, expire: float | None = None) -> int: - value += int(await self._get(key, 0)) # type: ignore + value += int(await self._get(key, 0)) _expire = None if value != 1 else expire self._set(key=key, value=value, expire=_expire) return value @@ -158,7 +158,7 @@ async def ping(self, message: bytes | None = None) -> bytes: return message async def get_bits(self, key: Key, *indexes: int, size: int = 1) -> tuple[int, ...]: - array: Bitarray = await self._get(key, default=Bitarray("0")) # type: ignore + array: Bitarray = await self._get(key, default=Bitarray("0")) return tuple(array.get(index, size) for index in indexes) async def incr_bits(self, key: Key, *indexes: int, size: int = 1, by: int = 1) -> tuple[int, ...]: diff --git a/cashews/backends/redis/backend.py b/cashews/backends/redis/backend.py index 98dc571..2bad858 100644 --- a/cashews/backends/redis/backend.py +++ b/cashews/backends/redis/backend.py @@ -154,7 +154,7 @@ async def is_locked( async def unlock(self, key: Key, value: Value) -> bool: if "UNLOCK" not in self._sha: self._sha["UNLOCK"] = await self._client.script_load(_UNLOCK.replace("\n", " ")) - return await self._client.evalsha(self._sha["UNLOCK"], 1, key, value) # type: ignore + return await self._client.evalsha(self._sha["UNLOCK"], 1, key, value) async def delete(self, key: Key) -> bool: try: @@ -241,7 +241,7 @@ async def incr(self, key: Key, value: int = 1, expire: float | None = None) -> i self._sha["INCR_EXPIRE"] = await self._client.script_load(_INCR_EXPIRE.replace("\n", " ")) expire = expire or 0 expire = int(expire * 1000) - return await self._client.evalsha(self._sha["INCR_EXPIRE"], 1, key, value, expire) # type: ignore + return await self._client.evalsha(self._sha["INCR_EXPIRE"], 1, key, value, expire) async def get_bits(self, key: Key, *indexes: int, size: int = 1) -> tuple[int, ...]: """ @@ -249,14 +249,16 @@ async def get_bits(self, key: Key, *indexes: int, size: int = 1) -> tuple[int, . """ bitops = self._client.bitfield(key) for index in indexes: - bitops.get(fmt=f"u{size}", offset=f"#{index}") - return tuple(await bitops.execute() or []) + bitops.get(fmt=f"u{size}", offset=f"#{index}") # type: ignore[attr-defined] + return tuple(await bitops.execute() or []) # type: ignore[attr-defined] async def incr_bits(self, key: Key, *indexes: int, size: int = 1, by: int = 1) -> tuple[int, ...]: bitops = self._client.bitfield(key) for index in indexes: - bitops.incrby(fmt=f"u{size}", offset=f"#{index}", increment=by, overflow="SAT") - return tuple(await bitops.execute()) + bitops.incrby( # type: ignore[attr-defined] + fmt=f"u{size}", offset=f"#{index}", increment=by, overflow="SAT" + ) + return tuple(await bitops.execute()) # type: ignore[attr-defined] async def ping(self, message: bytes | None = None) -> bytes: await self._client.ping() @@ -277,13 +279,11 @@ async def slice_incr( expire = int(expire * 1000) if "INCR_SLICE" not in self._sha: self._sha["INCR_SLICE"] = await self._client.script_load(_INCR_SLICE.replace("\n", " ")) - return await self._client.evalsha( # type: ignore[misc] - self._sha["INCR_SLICE"], 1, key, start, end, maxvalue, expire # type: ignore[arg-type] - ) + return await self._client.evalsha(self._sha["INCR_SLICE"], 1, key, start, end, maxvalue, expire) async def set_add(self, key: Key, *values: str, expire: float | None = None): if expire is None: - return await self._client.sadd(key, *values) # type: ignore[misc] + return await self._client.sadd(key, *values) expire = int(expire * 1000) async with self._pipeline as pipe: await pipe.sadd(key, *values) @@ -291,10 +291,10 @@ async def set_add(self, key: Key, *values: str, expire: float | None = None): await pipe.execute() async def set_remove(self, key: Key, *values: str): - await self._client.srem(key, *values) # type: ignore[misc] + await self._client.srem(key, *values) async def set_pop(self, key: Key, count: int = 100) -> Iterable[str]: - return [value.decode() for value in await self._client.spop(key, count)] # type: ignore[misc] + return [value.decode() for value in await self._client.spop(key, count)] # type: ignore[union-attr] async def get_keys_count(self) -> int: return await self._client.dbsize() diff --git a/cashews/decorators/__init__.py b/cashews/decorators/__init__.py index 81a0190..e6107eb 100644 --- a/cashews/decorators/__init__.py +++ b/cashews/decorators/__init__.py @@ -1,14 +1,34 @@ -from cashews.cache_condition import NOT_NONE # noqa +from cashews.cache_condition import NOT_NONE -from .bloom import bloom, dual_bloom # noqa -from .cache.defaults import CacheDetect, context_cache_detect # noqa -from .cache.early import early # noqa -from .cache.fail import failover, fast_condition # noqa -from .cache.hit import hit # noqa -from .cache.iterator import iterator # noqa -from .cache.simple import cache # noqa -from .cache.soft import soft # noqa -from .circuit_breaker import circuit_breaker # noqa -from .locked import locked, thunder_protection # noqa -from .rate import rate_limit # noqa -from .rate_slide import slice_rate_limit # noqa +from .bloom import bloom, dual_bloom +from .cache.defaults import CacheDetect, context_cache_detect +from .cache.early import early +from .cache.fail import failover, fast_condition +from .cache.hit import hit +from .cache.iterator import iterator +from .cache.simple import cache +from .cache.soft import soft +from .circuit_breaker import circuit_breaker +from .locked import locked, thunder_protection +from .rate import rate_limit +from .rate_slide import slice_rate_limit + +__all__ = [ + "NOT_NONE", + "bloom", + "dual_bloom", + "CacheDetect", + "context_cache_detect", + "early", + "failover", + "fast_condition", + "hit", + "iterator", + "cache", + "soft", + "circuit_breaker", + "locked", + "thunder_protection", + "rate_limit", + "slice_rate_limit", +] diff --git a/cashews/decorators/locked.py b/cashews/decorators/locked.py index 1ebc4d0..7d29f13 100644 --- a/cashews/decorators/locked.py +++ b/cashews/decorators/locked.py @@ -77,7 +77,7 @@ async def _wrap(*args, **kwargs): yield chunk return - return _wrap # type: ignore[return-value] + return _wrap def thunder_protection(key: KeyOrTemplate | None = None) -> Callable[[DecoratedFunc], DecoratedFunc]: diff --git a/cashews/picklers.py b/cashews/picklers.py index f125a56..43d3ce7 100644 --- a/cashews/picklers.py +++ b/cashews/picklers.py @@ -8,7 +8,7 @@ from sqlalchemy.ext import serializer as sqlalchemy_pickle except ImportError: _SQLALC_PICKLE = False - sqlalchemy_pickle = pickle # type: ignore[misc] + sqlalchemy_pickle = pickle # type: ignore[misc,unused-ignore] _DILL_PICKLE = True try: diff --git a/cashews/ttl.py b/cashews/ttl.py index 87bb320..38eddfa 100644 --- a/cashews/ttl.py +++ b/cashews/ttl.py @@ -10,9 +10,9 @@ def ttl_to_seconds(ttl: TTL, *args, with_callable: bool = False, result=None, ** return None _type = type(ttl) # isinstance is slow if _type == str: - return _ttl_from_str(ttl) # type: ignore[union-attr, arg-type] + return _ttl_from_str(ttl) # type: ignore[arg-type] if _type == int: - return ttl # type: ignore[union-attr, return-value] + return ttl # type: ignore[return-value] if _type == timedelta: return ttl.total_seconds() # type: ignore[union-attr] diff --git a/cashews/utils/__init__.py b/cashews/utils/__init__.py index 5a70ae8..f554e42 100644 --- a/cashews/utils/__init__.py +++ b/cashews/utils/__init__.py @@ -4,3 +4,9 @@ from ._bitarray import Bitarray # type: ignore[assignment] from .object_size import get_obj_size from .split_hash import get_indexes + +__all__ = [ + "Bitarray", + "get_obj_size", + "get_indexes", +] diff --git a/cashews/wrapper/__init__.py b/cashews/wrapper/__init__.py index 2b10cfa..fa7563e 100644 --- a/cashews/wrapper/__init__.py +++ b/cashews/wrapper/__init__.py @@ -1,13 +1,17 @@ from cashews.backends.interface import _BackendInterface from cashews.decorators import context_cache_detect -from .backend_settings import register_backend # noqa +from .backend_settings import register_backend from .decorators import DecoratorsWrapper from .disable_control import ControlWrapper from .tags import CommandsTagsWrapper from .transaction import TransactionMode, TransactionWrapper -__all__ = ["Cache", "register_backend"] +__all__ = [ + "Cache", + "TransactionMode", + "register_backend", +] class Cache( diff --git a/mypy.ini b/mypy.ini index 770e603..151b473 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1,6 +1,15 @@ [mypy] python_version = 3.7 +# warn_return_any = True +warn_redundant_casts = True +warn_unused_configs = True +warn_unused_ignores = True +# disallow_any_generics = True +# check_untyped_defs = True +no_implicit_reexport = True +no_implicit_optional = False + [mypy-dill.*] ignore_missing_imports = True @@ -10,5 +19,8 @@ ignore_missing_imports = True [mypy-prometheus_client.*] ignore_missing_imports = True +[mypy-starlette.*] +ignore_missing_imports = True + [mypy-sqlalchemy.*] ignore_missing_imports = True diff --git a/setup.cfg b/setup.cfg index f6256a9..7b81b3c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -51,6 +51,9 @@ speedup = hiredis dill = dill +lint = + mypy >= 1.5.0 + types-redis tests = pytest pytest-asyncio