diff --git a/README.ipynb b/README.ipynb index feeea70..e56342a 100644 --- a/README.ipynb +++ b/README.ipynb @@ -189,6 +189,7 @@ "outputs": [], "source": [ "# FIXME hmm seems like this doesn't work if there are type annotations on cachew_impl? odd\n", + "# likely this? https://github.com/davidhalter/jedi/issues/2025\n", "doc = getdoc('cachew_impl').split('Usage example:')[-1].lstrip()\n", "dmd(f\"\"\"```python\n", "{doc}\n", diff --git a/README.md b/README.md index df15ea5..a970ecf 100644 --- a/README.md +++ b/README.md @@ -128,7 +128,7 @@ Cachew gives the best of two worlds and makes it both **easy and efficient**. Th - first your objects get [converted](src/cachew/marshall/cachew.py#L29) into a simpler JSON-like representation - after that, they are mapped into byte blobs via [`orjson`](https://github.com/ijl/orjson). -When the function is called, cachew [computes the hash of your function's arguments ](src/cachew/__init__.py#L586) +When the function is called, cachew [computes the hash of your function's arguments ](src/cachew/__init__.py#L580) and compares it against the previously stored hash value. - If they match, it would deserialize and yield whatever is stored in the cache database @@ -145,13 +145,13 @@ and compares it against the previously stored hash value. * primitive: `str`, `int`, `float`, `bool`, `datetime`, `date`, `Exception` - See [tests.test_types](src/cachew/tests/test_cachew.py#L683), [tests.test_primitive](src/cachew/tests/test_cachew.py#L721), [tests.test_dates](src/cachew/tests/test_cachew.py#L633), [tests.test_exceptions](src/cachew/tests/test_cachew.py#L1125) - * [@dataclass and NamedTuple](src/cachew/tests/test_cachew.py#L598) - * [Optional](src/cachew/tests/test_cachew.py#L525) types - * [Union](src/cachew/tests/test_cachew.py#L828) types - * [nested datatypes](src/cachew/tests/test_cachew.py#L441) + See [tests.test_types](src/cachew/tests/test_cachew.py#L682), [tests.test_primitive](src/cachew/tests/test_cachew.py#L720), [tests.test_dates](src/cachew/tests/test_cachew.py#L632), [tests.test_exceptions](src/cachew/tests/test_cachew.py#L1124) + * [@dataclass and NamedTuple](src/cachew/tests/test_cachew.py#L597) + * [Optional](src/cachew/tests/test_cachew.py#L524) types + * [Union](src/cachew/tests/test_cachew.py#L827) types + * [nested datatypes](src/cachew/tests/test_cachew.py#L440) -* detects [datatype schema changes](src/cachew/tests/test_cachew.py#L471) and discards old data automatically +* detects [datatype schema changes](src/cachew/tests/test_cachew.py#L470) and discards old data automatically # Performance @@ -165,12 +165,12 @@ You can find some of my performance tests in [benchmarks/](benchmarks) dir, and # Using -See [docstring](src/cachew/__init__.py#L285) for up-to-date documentation on parameters and return types. +See [docstring](src/cachew/__init__.py#L279) for up-to-date documentation on parameters and return types. You can also use [extensive unit tests](src/cachew/tests/test_cachew.py#L1) as a reference. Some useful (but optional) arguments of `@cachew` decorator: -* `cache_path` can be a directory, or a callable that [returns a path](src/cachew/tests/test_cachew.py#L418) and depends on function's arguments. +* `cache_path` can be a directory, or a callable that [returns a path](src/cachew/tests/test_cachew.py#L417) and depends on function's arguments. By default, `settings.DEFAULT_CACHEW_DIR` is used. @@ -274,7 +274,7 @@ Now you can use `@mcachew` in place of `@cachew`, and be certain things don't br ## Settings -[cachew.settings](src/cachew/__init__.py#L59) exposes some parameters that allow you to control `cachew` behaviour: +[cachew.settings](src/cachew/__init__.py#L55) exposes some parameters that allow you to control `cachew` behaviour: - `ENABLE`: set to `False` if you want to disable caching for without removing the decorators (useful for testing and debugging). You can also use [cachew.extra.disabled_cachew](src/cachew/extra.py#L25) context manager to do it temporarily. - `DEFAULT_CACHEW_DIR`: override to set a different base directory. The default is the "user cache directory" (see [platformdirs docs](https://github.com/tox-dev/platformdirs?tab=readme-ov-file#example-output)). diff --git a/src/cachew/__init__.py b/src/cachew/__init__.py index 30cf311..d4cc6c3 100644 --- a/src/cachew/__init__.py +++ b/src/cachew/__init__.py @@ -39,19 +39,15 @@ def orjson_dumps(*args, **kwargs): # type: ignore[misc] from .backend.common import AbstractBackend from .backend.file import FileBackend from .backend.sqlite import SqliteBackend -from .common import SourceHash +from .common import CachewException, SourceHash, TypeNotSupported from .logging_helper import make_logger from .marshall.cachew import CachewMarshall, build_schema -from .utils import ( - CachewException, - TypeNotSupported, - resolve_type_parameters, -) +from .utils import resolve_type_parameters # in case of changes in the way cachew stores data, this should be changed to discard old caches CACHEW_VERSION: str = importlib.metadata.version(__name__) -PathIsh = Path | str +type PathIsh = Path | str Backend = Literal['sqlite', 'file'] diff --git a/src/cachew/backend/sqlite.py b/src/cachew/backend/sqlite.py index ff99b4a..235b55d 100644 --- a/src/cachew/backend/sqlite.py +++ b/src/cachew/backend/sqlite.py @@ -50,7 +50,6 @@ def set_sqlite_pragma(dbapi_connection, connection_record): # noqa: ARG001 """ @event.listens_for(self.connection, 'begin') - # pylint: disable=unused-variable def do_begin(conn): # NOTE there is also BEGIN CONCURRENT in newer versions of sqlite. could use it later? conn.execute(text('BEGIN DEFERRED')) @@ -178,7 +177,6 @@ def flush_blobs(self, chunk: Sequence[bytes]) -> None: def finalize(self, new_hash: SourceHash) -> None: # delete hash first, so if we are interrupted somewhere, it mismatches next time and everything is recomputed - # pylint: disable=no-value-for-parameter self.connection.execute(self.table_hash.delete()) # checkfirst is necessary since it might not have existed in the first place @@ -189,5 +187,4 @@ def finalize(self, new_hash: SourceHash) -> None: # also seems like sqlalchemy doesn't have any primitives to escape table names.. sigh self.connection.execute(text(f"ALTER TABLE `{self.table_cache_tmp.name}` RENAME TO `{self.table_cache.name}`")) - # pylint: disable=no-value-for-parameter self.connection.execute(self.table_hash.insert().values([{'value': new_hash}])) diff --git a/src/cachew/common.py b/src/cachew/common.py index 6086c32..f2a76c7 100644 --- a/src/cachew/common.py +++ b/src/cachew/common.py @@ -1,2 +1,17 @@ +from dataclasses import dataclass + # TODO better name to represent what it means? -SourceHash = str +type SourceHash = str + + +class CachewException(RuntimeError): + pass + + +@dataclass +class TypeNotSupported(CachewException): + type_: type + reason: str + + def __str__(self) -> str: + return f"{self.type_} isn't supported by cachew: {self.reason}. See https://github.com/karlicoss/cachew#features for the list of supported types." diff --git a/src/cachew/experimental.py b/src/cachew/experimental.py index bfde4c9..74d93b9 100644 --- a/src/cachew/experimental.py +++ b/src/cachew/experimental.py @@ -1,11 +1,12 @@ -from .compat import deprecated +from typing import TYPE_CHECKING +if not TYPE_CHECKING: + from .compat import deprecated -@deprecated("Exceptions are not an experimental feature anymore and enabled by default.") -def enable_exceptions() -> None: - pass + @deprecated("Exceptions are not an experimental feature anymore and enabled by default.") + def enable_exceptions() -> None: + pass - -@deprecated("Exceptions are not an experimental feature anymore and enabled by default.") -def disable_exceptions() -> None: - pass + @deprecated("Exceptions are not an experimental feature anymore and enabled by default.") + def disable_exceptions() -> None: + pass diff --git a/src/cachew/legacy.py b/src/cachew/legacy.py index 2a07338..e3b4970 100644 --- a/src/cachew/legacy.py +++ b/src/cachew/legacy.py @@ -18,7 +18,7 @@ from sqlalchemy import Column from .pytest import parametrize -from .utils import CachewException +from .common import CachewException def get_union_args(cls) -> Optional[tuple[type]]: @@ -300,7 +300,7 @@ class NTBinder(Generic[NT]): >>> binder.from_row(('ann', 25, True, None, None, 'extra')) Traceback (most recent call last): ... - cachew.utils.CachewException: unconsumed items in iterator ['extra'] + cachew.common.CachewException: unconsumed items in iterator ['extra'] """ name: Optional[str] # None means toplevel diff --git a/src/cachew/logging_helper.py b/src/cachew/logging_helper.py index f702db7..85c1b64 100644 --- a/src/cachew/logging_helper.py +++ b/src/cachew/logging_helper.py @@ -4,6 +4,7 @@ import os import warnings from functools import lru_cache +from typing import TYPE_CHECKING def test() -> None: @@ -34,7 +35,7 @@ def test() -> None: M( "\n Also exception logging is kinda lame, doesn't print traceback by default unless you remember to pass exc_info:" ) - l.exception(ex) # type: ignore[possibly-undefined] # pylint: disable=used-before-assignment + l.exception(ex) # type: ignore[possibly-undefined] M( "\n\n With make_logger you get a reasonable logging format, colours (via colorlog library) and other neat things:" @@ -245,6 +246,7 @@ def get_enlighten(): ## legacy/deprecated methods for backwards compatilibity -LazyLogger = make_logger -logger = make_logger +if not TYPE_CHECKING: + LazyLogger = make_logger + logger = make_logger ## diff --git a/src/cachew/marshall/cachew.py b/src/cachew/marshall/cachew.py index 5d6efea..98bdd4c 100644 --- a/src/cachew/marshall/cachew.py +++ b/src/cachew/marshall/cachew.py @@ -21,7 +21,8 @@ ) from zoneinfo import ZoneInfo -from ..utils import TypeNotSupported, is_namedtuple, resolve_type_parameters +from ..common import TypeNotSupported +from ..utils import is_namedtuple, resolve_type_parameters from .common import AbstractMarshall, Json diff --git a/src/cachew/tests/marshall.py b/src/cachew/tests/marshall.py index 02526b0..5009c6d 100644 --- a/src/cachew/tests/marshall.py +++ b/src/cachew/tests/marshall.py @@ -5,10 +5,7 @@ from dataclasses import dataclass from datetime import UTC, datetime from pathlib import Path -from typing import ( - Any, - Literal, -) +from typing import Any, Literal import orjson import pytest diff --git a/src/cachew/tests/test_cachew.py b/src/cachew/tests/test_cachew.py index b985fcd..e768290 100644 --- a/src/cachew/tests/test_cachew.py +++ b/src/cachew/tests/test_cachew.py @@ -410,7 +410,6 @@ def test_return_type_none(tmp_path: Path) -> None: with pytest.raises(CachewException): @cachew(tmp_path) - # pylint: disable=unused-variable def data(): return [] diff --git a/src/cachew/utils.py b/src/cachew/utils.py index eae1633..ced1a6d 100644 --- a/src/cachew/utils.py +++ b/src/cachew/utils.py @@ -1,22 +1,8 @@ from collections.abc import Mapping -from dataclasses import dataclass from types import UnionType from typing import TypeAliasType, TypeVar, get_args, get_origin -class CachewException(RuntimeError): - pass - - -@dataclass -class TypeNotSupported(CachewException): - type_: type - reason: str - - def __str__(self) -> str: - return f"{self.type_} isn't supported by cachew: {self.reason}. See https://github.com/karlicoss/cachew#features for the list of supported types." - - # https://stackoverflow.com/a/2166841/706389 def is_namedtuple(t) -> bool: b = getattr(t, '__bases__', None)