ddp_utils.runtime.cache_storage

import ddp_utils.runtime.cache_storage

Provide persistent namespaced cache storage over RuntimePaths.

CacheStorage supports JSON, text, and byte values, atomic writes, per-key locking, expiration times, and removal of stale entries.

Examples

Use this public operation:

from ddp_utils.runtime.cache_storage import CacheStorage

cache = CacheStorage("example")
class ddp_utils.runtime.cache_storage.CacheStorage(namespace: str, *, create_now: bool = True)

Bases: object

Persistent namespace cache storage.

The storage operates inside RuntimePaths.cache_root and provides a small production-oriented API for storing and retrieving structured or raw cache items.

Main features:

  • namespaced cache root;

  • JSON storage helpers;

  • text and bytes helpers;

  • atomic writes;

  • optional per-key locking;

  • optional TTL-based validity checks;

  • cleanup helpers for stale files.

Cache organization:

.ddp/
    <namespace>/
        cache/
            <group>/
                <key>.json
                <key>.txt
                <key>.bin

Parameters

Name

Type

Description

namespace

str

Nonempty filesystem-safe namespace of the owning component.

create_now

bool

Create the runtime and cache roots immediately when True.

Examples

Create an isolated logical cache namespace:

cache = CacheStorage("reports")

Bind cache operations to one runtime namespace.

Construction also performs the throttled expired-runtime cleanup owned by RuntimePaths. When create_now is true, runtime directories and the cache root are created immediately.

Parameters

Name

Type

Description

namespace

str

Non-empty filesystem-safe cache namespace.

create_now

bool

Create runtime and cache directories during initialization. False defers cache-directory creation.

Raises

Exception

Description

ValueError

namespace is empty or invalid.

OSError

Required runtime directories cannot be created or expired runtime state cannot be processed.

Examples

Describe a cache without eagerly creating its directories:

cache = CacheStorage("reports", create_now=False)
property root: Path

Return the namespace cache root.

Returns

Absolute namespace path ending in cache. Accessing this property does not itself create the directory.

Examples

Inspect the configured cache root:

root = cache.root
group_root(group: str) → Path

Return the root directory for a logical cache group.

Parameters

Name

Type

Description

group

str

Nonempty filesystem-safe group name, such as drivers.

Returns

Type

Description

Path

Group directory below root. The directory and missing parents are created before return.

Raises

Exception

Description

ValueError

group is empty after normalization.

OSError

The group directory cannot be created.

Examples

Create or reuse a logical group:

drivers = cache.group_root("drivers")
path(key: str, *, group: str = 'default', suffix: str = '.json') → Path

Build a cache file path.

Parameters

Name

Type

Description

key

str

Nonempty logical cache key normalized for use as a filename.

group

str

Nonempty logical group whose directory is created as needed.

suffix

str

Filename suffix appended unchanged, normally including its leading dot.

Returns

Type

Description

Path

Path below the selected group. The file itself is not created.

Raises

Exception

Description

ValueError

key or group is empty after normalization.

OSError

The group directory cannot be created.

Examples

Build a path without writing its cache entry:

target = cache.path("latest", group="reports", suffix=".json")
hashed_path(value: str, *, group: str = 'default', suffix: str = '.json', algorithm: str = 'sha256') → Path

Build a cache file path using a hash of the input value.

This is useful for long URLs, query strings, or other values not suitable as a direct file name.

Parameters

Name

Type

Description

value

str

Text encoded as UTF-8 before hashing.

group

str

Nonempty logical group whose directory is created as needed.

suffix

str

Filename suffix appended to the hexadecimal digest.

algorithm

str

Algorithm accepted by hashlib.new().

Returns

Type

Description

Path

Cache path whose filename is the hexadecimal digest plus suffix.

Raises

Exception

Description

ValueError

algorithm is unavailable or group is empty.

OSError

The group directory cannot be created.

Examples

Derive a stable path for a long URL:

target = cache.hashed_path("https://example.com/report?id=42")
exists(key: str, *, group: str = 'default', suffix: str = '.json') → bool

Check whether a cache item exists.

Parameters

Name

Type

Description

key

str

Logical cache key.

group

str

Logical cache group.

suffix

str

Cache filename suffix.

Returns

Type

Description

bool

True when the computed path exists as any filesystem entry; otherwise False.

Examples

Test for a JSON entry:

present = cache.exists("latest", group="reports")
is_fresh(key: str, *, group: str = 'default', suffix: str = '.json', ttl_seconds: float | None = None) → bool

Check whether a cache item is fresh according to TTL.

If ttl_seconds is None, the item is considered fresh if it exists.

Parameters

Name

Type

Description

key

str

Logical cache key.

group

str

Logical cache group.

suffix

str

Cache filename suffix.

ttl_seconds

float | None

Maximum age in seconds. None accepts any existing item; a negative value makes every existing item stale.

Returns

Type

Description

bool

True when the path exists and its age does not exceed the supplied TTL, or when no TTL is supplied.

Raises

Exception

Description

OSError

Metadata for an existing path cannot be read.

Examples

Accept an entry written within the last hour:

fresh = cache.is_fresh("latest", ttl_seconds=3600)
read_text(key: str, *, group: str = 'default', encoding: str = 'utf-8', suffix: str = '.txt', ttl_seconds: float | None = None) → str | None

Read a text cache item.

If the file does not exist or is stale according to TTL, return None.

Parameters

Name

Type

Description

key

str

Logical cache key.

group

str

Logical cache group.

encoding

str

Text codec used to decode the file.

suffix

str

Cache filename suffix.

ttl_seconds

float | None

Maximum accepted age. None disables age checks.

Returns

Type

Description

str | None

Decoded text, or None when the entry is missing, stale, unreadable, or cannot be decoded. Read failures are intentionally soft.

Examples

Read an optional cached report:

report = cache.read_text("latest", group="reports")
write_text(key: str, value: str, *, group: str = 'default', encoding: str = 'utf-8', suffix: str = '.txt', use_lock: bool = False) → Path

Write a text cache item atomically.

Parameters

Name

Type

Description

key

str

Logical cache key.

value

str

Text payload written through a temporary sibling file.

group

str

Logical cache group.

encoding

str

Text codec used for serialization.

suffix

str

Cache filename suffix.

use_lock

bool

Acquire a namespace lock for this group and key before replacing the target when True.

Returns

Type

Description

Path

Final cache path after atomic replacement succeeds.

Raises

Exception

Description

OSError

Directory creation, temporary writing, locking, or atomic replacement fails.

Examples

Atomically store a text report:

target = cache.write_text("latest", "ready", group="reports")
read_bytes(key: str, *, group: str = 'default', suffix: str = '.bin', ttl_seconds: float | None = None) → bytes | None

Read a binary cache item.

Parameters

Name

Type

Description

key

str

Logical cache key.

group

str

Logical cache group.

suffix

str

Cache filename suffix.

ttl_seconds

float | None

Maximum accepted age. None disables age checks.

Returns

Type

Description

bytes | None

File bytes, or None when the entry is missing, stale, or unreadable. Read failures are intentionally soft.

Examples

Read an optional binary artifact:

payload = cache.read_bytes("archive", group="downloads")
write_bytes(key: str, value: bytes, *, group: str = 'default', suffix: str = '.bin', use_lock: bool = False) → Path

Write a binary cache item atomically.

Parameters

Name

Type

Description

key

str

Logical cache key.

value

bytes

Binary payload written through a temporary sibling file.

group

str

Logical cache group.

suffix

str

Cache filename suffix.

use_lock

bool

Acquire a namespace lock for this group and key before replacing the target when True.

Returns

Type

Description

Path

Final cache path after atomic replacement succeeds.

Raises

Exception

Description

OSError

Directory creation, temporary writing, locking, or atomic replacement fails.

Examples

Atomically store a binary artifact:

target = cache.write_bytes("archive", b"payload")
read_json(key: str, *, group: str = 'default', suffix: str = '.json', ttl_seconds: float | None = None) → Any | None

Read a JSON cache item.

Parameters

Name

Type

Description

key

str

Logical cache key.

group

str

Logical cache group.

suffix

str

Cache filename suffix.

ttl_seconds

float | None

Maximum accepted age. None disables age checks.

Returns

Type

Description

Any | None

Decoded JSON value, or None when the entry is missing, stale, unreadable, or invalid JSON. These failures are intentionally soft, so a cached JSON null is indistinguishable from failure.

Examples

Read optional structured state:

state = cache.read_json("state")
write_json(key: str, value: Any, *, group: str = 'default', suffix: str = '.json', ensure_ascii: bool = False, indent: int | None = None, use_lock: bool = False) → Path

Write a JSON cache item atomically.

Parameters

Name

Type

Description

key

str

Logical cache key.

value

Any

JSON-serializable value.

group

str

Logical cache group.

suffix

str

Cache filename suffix.

ensure_ascii

bool

Escape non-ASCII characters when True.

indent

int | None

Optional JSON indentation level. None produces compact output.

use_lock

bool

Acquire a namespace lock for this group and key before replacing the target when True.

Returns

Type

Description

Path

Final cache path after serialization and atomic replacement.

Raises

Exception

Description

TypeError

value contains an unsupported JSON value.

ValueError

JSON serialization rejects a value such as a circular structure.

OSError

Directory creation, locking, writing, or replacement fails.

Examples

Store structured state with readable formatting:

target = cache.write_json("state", {"ready": True}, indent=2)
delete(key: str, *, group: str = 'default', suffix: str = '.json') → bool

Delete a cache item.

Parameters

Name

Type

Description

key

str

Logical cache key.

group

str

Logical cache group.

suffix

str

Cache filename suffix.

Returns

Type

Description

bool

True when an existing path was removed; False when no path existed. The group directory is retained.

Raises

Exception

Description

OSError

The existing path cannot be removed.

Examples

Delete an optional entry:

removed = cache.delete("state")
clear_group(group: str) → None

Remove a whole cache group and recreate it.

Parameters

Name

Type

Description

group

str

Nonempty logical group to reset.

Note

Removal errors are ignored by shutil.rmtree. The method then recreates the group directory, so it is not an atomic clear.

Raises

Exception

Description

ValueError

group is empty after normalization.

OSError

The group directory cannot be recreated.

Examples

Reset one logical group:

cache.clear_group("reports")
cleanup_stale(*, older_than_seconds: float, group: str | None = None) → int

Remove cache files older than the given age.

Parameters

Name

Type

Description

older_than_seconds

float

Inclusive age threshold in seconds. Negative values make every discovered file eligible.

group

str | None

Optional group to scan. None scans every immediate group directory below the cache root.

Returns

Type

Description

int

Number of files successfully removed. Per-file metadata and removal failures are skipped and not counted.

Examples

Remove entries at least one day old:

removed = cache.cleanup_stale(older_than_seconds=86400)
cleanup_empty_dirs() → int

Remove empty directories inside the cache root.

Returns

Type

Description

int

Number of directories successfully removed, deepest first. The cache root itself is retained, and inspection failures are skipped.

Examples

Prune empty group subdirectories:

removed = cache.cleanup_empty_dirs()