Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ ignore-paths=
# Files or directories matching the regular expression patterns are skipped.
# The regex matches against base names, not paths. The default value ignores
# Emacs file locks
ignore-patterns=^\.#
ignore-patterns=^\.#,
.*\.pyi$

# List of module names for which member attributes should not be checked
# (useful for modules/projects where namespaces are manipulated during runtime
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

diskcache-community forked from DiskCache at upstream commit ebfa37c (3 March 2024), two commits after the 5.6.3 release. Each entry names the pull request in this repository. Imported upstream pull requests are referenced by number, and their commits keep their original authors in the git history.

## Unreleased

### Packaging

- The package ships type information: `py.typed` and `.pyi` stubs covering the API, including `SafeDisk`, `SafeDisk.with_types`, `Disk.with_options`, `configure`, `default_disk`, `ReadOnlyError`, `read_only`, `store_time`, `iterkeys(tag=...)` and the `disk` arguments of `Deque`, `Index` and `FanoutCache.deque` and `index`, so mypy and pyright check code that uses the fork without a separate stubs package. `mypy.stubtest` reports no differences from the runtime (PR #29).
- `requires-python` is `>=3.9`, so the fork installs on Python 3.9; the test suite passes there unchanged (PR #29).

## 5.8.0 (2026-10-18)

The package is now imported as `diskcache_community`, so it installs next to the upstream `diskcache` package instead of replacing it. The `cache.db` schema, the file layout, the serialization modes, the memoize key format and the persisted settings are unchanged, so a 5.6.3 process opens a directory written by 5.8.0 and the reverse. `SafeDisk` stays opt-in and gains an allowlist for your own classes, a process-wide default disk selects it for caches that libraries create, and caches can treat unreadable entries as missing.
Expand Down
58 changes: 58 additions & 0 deletions diskcache_community/__init__.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from diskcache_community.core import DEFAULT_SETTINGS as DEFAULT_SETTINGS
from diskcache_community.core import ENOVAL as ENOVAL
from diskcache_community.core import EVICTION_POLICY as EVICTION_POLICY
from diskcache_community.core import UNKNOWN as UNKNOWN
from diskcache_community.core import Cache as Cache
from diskcache_community.core import Disk as Disk
from diskcache_community.core import EmptyDirWarning as EmptyDirWarning
from diskcache_community.core import JSONDisk as JSONDisk
from diskcache_community.core import ReadOnlyError as ReadOnlyError
from diskcache_community.core import SafeDisk as SafeDisk
from diskcache_community.core import Timeout as Timeout
from diskcache_community.core import UnknownFileWarning as UnknownFileWarning
from diskcache_community.core import configure as configure
from diskcache_community.core import default_disk as default_disk
from diskcache_community.fanout import FanoutCache as FanoutCache
from diskcache_community.persistent import Deque as Deque
from diskcache_community.persistent import Index as Index
from diskcache_community.recipes import Averager as Averager
from diskcache_community.recipes import BoundedSemaphore as BoundedSemaphore
from diskcache_community.recipes import Lock as Lock
from diskcache_community.recipes import RLock as RLock
from diskcache_community.recipes import barrier as barrier
from diskcache_community.recipes import memoize_stampede as memoize_stampede
from diskcache_community.recipes import throttle as throttle

__all__ = [
'Averager',
'BoundedSemaphore',
'Cache',
'DEFAULT_SETTINGS',
'Deque',
'Disk',
'ENOVAL',
'EVICTION_POLICY',
'EmptyDirWarning',
'FanoutCache',
'Index',
'JSONDisk',
'Lock',
'RLock',
'ReadOnlyError',
'SafeDisk',
'Timeout',
'UNKNOWN',
'UnknownFileWarning',
'barrier',
'configure',
'default_disk',
'memoize_stampede',
'throttle',
]

__title__: str
__version__: str
__build__: int
__author__: str
__license__: str
__copyright__: str
227 changes: 227 additions & 0 deletions diskcache_community/_types.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
import warnings
from typing import (
Any,
Callable,
Container,
ContextManager,
Iterator,
Literal,
Protocol,
overload,
)

from _typeshed import Incomplete
from typing_extensions import ParamSpec, Self, TypeAlias, TypedDict, TypeVar

from diskcache_community import Disk

_T = TypeVar('_T')
_IntOrStrT = TypeVar('_IntOrStrT', bound=int | str)
_T_co = TypeVar('_T_co', covariant=True)
_P = ParamSpec('_P')

DbName: TypeAlias = Literal['cache.db']
ModeNone: TypeAlias = Literal[0]
ModeRaw: TypeAlias = Literal[1]
ModeBinary: TypeAlias = Literal[2]
ModeText: TypeAlias = Literal[3]
ModePickle: TypeAlias = Literal[4]
ModeLiteral: TypeAlias = (
ModeNone | ModeRaw | ModeBinary | ModeText | ModePickle
)
EnovalType: TypeAlias = Literal['ENOVAL']
UnknownType: TypeAlias = Literal['UNKNOWN']

ExpireTime: TypeAlias = float | None
Tag: TypeAlias = str | None
Ignore: TypeAlias = Container[int | str]

ServerSide: TypeAlias = Literal['back', 'front']
KeyValuePair: TypeAlias = tuple[_IntOrStrT, Any]
NullPair: TypeAlias = tuple[None, None]
NullablePair: TypeAlias = KeyValuePair[_IntOrStrT] | NullPair

EvictionPolicyKey: TypeAlias = Literal[
'none',
'least-recently-stored',
'least-recently-used',
'least-frequently-used',
]
EvictionPolicyItemKey: TypeAlias = Literal['init', 'get', 'cull']
EvictionPolicyItem: TypeAlias = dict[EvictionPolicyItemKey, str | None]

class Memoized(Protocol[_P, _T_co]):
def __cache_key__(
self, *args: _P.args, **kwargs: _P.kwargs
) -> tuple[Any, ...]: ...
def __call__(self, *args: _P.args, **kwargs: _P.kwargs) -> _T_co: ...

class Settings(TypedDict, total=False):
statistics: int
tag_index: int
eviction_policy: str
size_limit: int
cull_limit: int
sqlite_auto_vacuum: int
sqlite_cache_size: int
sqlite_journal_mode: str
sqlite_mmap_size: int
sqlite_synchronous: int
disk_min_file_size: int
disk_pickle_protocol: int

class DefaultSettings(TypedDict, total=True):
statistics: int
tag_index: int
eviction_policy: str
size_limit: int
cull_limit: int
sqlite_auto_vacuum: int
sqlite_cache_size: int
sqlite_journal_mode: str
sqlite_mmap_size: int
sqlite_synchronous: int
disk_min_file_size: int
disk_pickle_protocol: int

class Metadata(TypedDict, total=False):
count: int
size: int
hits: int
misses: int

class DefaultMetadata(TypedDict, total=True):
count: int
size: int
hits: int
misses: int

class InitSettings(TypedDict, total=False):
# settings
statistics: int
tag_index: int
eviction_policy: str
size_limit: int
cull_limit: int
sqlite_auto_vacuum: int
sqlite_cache_size: int
sqlite_journal_mode: str
sqlite_mmap_size: int
sqlite_synchronous: int
disk_min_file_size: int
disk_pickle_protocol: int
# metadata
count: int
size: int
hits: int
misses: int

class BaseCache(Protocol):
### Settings
@property
def statistics(self) -> int: ...
@property
def tag_index(self) -> int: ...
@property
def eviction_policy(self) -> str: ...
@property
def size_limit(self) -> int: ...
@property
def cull_limit(self) -> int: ...
@property
def sqlite_auto_vacuum(self) -> int: ...
@property
def sqlite_cache_size(self) -> int: ...
@property
def sqlite_journal_mode(self) -> str: ...
@property
def sqlite_mmap_size(self) -> int: ...
@property
def sqlite_synchronous(self) -> int: ...
@property
def disk_min_file_size(self) -> int: ...
@property
def disk_pickle_protocol(self) -> int: ...
###
@property
def directory(self) -> str: ...
@property
def timeout(self) -> int: ...
@property
def disk(self) -> Disk: ...
def transact(self, retry: bool = ...) -> ContextManager[None]: ...
def set( # noqa: PLR0913
self,
key: Any,
value: Any,
expire: ExpireTime = ...,
read: bool = ...,
tag: Tag = ...,
retry: bool = ...,
) -> Literal[True]: ...
def __setitem__(self, key: Any, value: Any) -> None: ...
def touch(
self, key: Any, expire: ExpireTime = ..., retry: bool = ...
) -> bool: ...
def add( # noqa: PLR0913
self,
key: Any,
value: Any,
expire: ExpireTime = ...,
read: bool = ...,
tag: Tag = ...,
retry: bool = ...,
) -> bool: ...
def incr(
self, key: Any, delta: int = ..., default: int = ..., retry: bool = ...
) -> int: ...
def decr(
self, key: Any, delta: int = ..., default: int = ..., retry: bool = ...
) -> int: ...
def get( # noqa: PLR0913
self,
key: Any,
default: Any = ...,
read: bool = ...,
expire_time: bool = ...,
tag: bool = ...,
retry: bool = ...,
store_time: bool = ...,
) -> Incomplete: ...
def __getitem__(self, key: Any) -> Any: ...
def __contains__(self, key: Any) -> bool: ...
def pop(
self,
key: Any,
default: Any = ...,
expire_time: bool = ...,
tag: bool = ...,
retry: bool = ...,
) -> Incomplete: ...
def delete(self, key: Any, retry: bool = ...) -> bool: ...
def create_tag_index(self) -> None: ...
def drop_tag_index(self) -> None: ...
def check(
self, fix: bool = ..., retry: bool = ...
) -> list[warnings.WarningMessage]: ...
def evict(self, tag: str, retry: bool = ...) -> int: ...
def cull(self, retry: bool = ...) -> int: ...
def clear(self, retry: bool = ...) -> int: ...
def __iter__(self) -> Iterator[Any]: ...
def __reversed__(self) -> Iterator[Any]: ...
def stats(
self, enable: bool = ..., reset: bool = ...
) -> tuple[Any, Any]: ...
def volume(self) -> int: ...
def close(self) -> None: ...
def __enter__(self) -> Self: ...
def __exit__(self, *exception: object) -> None: ...
def __len__(self) -> int: ...
def memoize(
self,
name: str | None = ...,
typed: bool = ...,
expire: ExpireTime = ...,
tag: Tag = ...,
ignore: Ignore = ...,
) -> Callable[[Callable[_P, _T]], Memoized[_P, _T]]: ...
Loading
Loading