timeseries-cache
A persistent SQLite cache for time-series API data. It fetches only missing UTC date ranges and reuses saved rows across runs. The package itself uses only the Python standard library.
Install
Requires Python 3.10 or newer.
uv add timeseries-cache
Use the package
Declare a Schema with one timezone-aware datetime dimension, then create a
Cache and call load(selection, start, end, fetcher). The fetcher receives
(start, end, refresh) and returns rows with the declared dimensions and a
value. load returns rows, a status (live, cache, stale, or
unavailable), and any missing ranges. Pass refresh=True to fetch a range
again. Cache files are stored in the directory passed to Cache.
Here is a runnable example using generated hourly observations. Replace the fetcher's body with your API call and return rows in the same format.
from datetime import datetime, timedelta, timezone
from timeseries_cache import Cache, Schema
schema = Schema({"timestamp": datetime, "sensor": str}, "timestamp")
cache = Cache("data/cache", schema)
selection = {"sensor": "A"}
def fetch(start, end, refresh):
timestamp = start
while timestamp < end:
yield {"timestamp": timestamp, **selection, "value": 12.5}
timestamp += timedelta(hours=1)
start = datetime(2024, 1, 1, tzinfo=timezone.utc)
end = start + timedelta(days=1)
first = cache.load(selection, start, end, fetch)
second = cache.load(selection, start, end, fetch)
print(first.status, second.status, len(second.rows))
# With an empty cache: live cache 24
The file data/cache/timeseries_cache.sqlite persists across Python sessions.
Requesting a larger interval fetches only the uncovered parts. read() returns
stored rows without fetching; missing() returns uncovered intervals.
| Result status | Meaning |
|---|---|
live |
The requested fetches succeeded. |
cache |
No fetch was needed. |
stale |
Refreshing failed or was unavailable, but stored coverage exists. |
unavailable |
Some interval remains uncovered. |
Always inspect result.error and result.missing_ranges when a load is not
successful. A failed fetch can still return previously cached rows.
Cache contract
- One
selectionfixes every non-time dimension. For multiple series, callloadfor each selection. - A fetcher must return the complete response for
[start, end), including all pages/chunks, or raise. An empty iterable means the interval was checked successfully. Missing observations alone cannot reveal a failed download. - Every row contains exactly the schema dimensions and
value. Timestamps must be timezone-aware. Missing values useNone; NaN and infinity are rejected. - Include inputs that affect results in
options, such as provider URL, aggregation or model version. Use separate directories for separate accounts. The cache cannot identify which API a callback uses. JSON dictionaries must have string keys at every nesting level. refresh=Truereloads the whole interval.max_age=timedelta(hours=1)reloads only missing or expired intervals. Either option passesTrueto the fetcher when fetching, allowing it to bypass upstream caches.- If fetching cannot proceed, covered data returns
stale; uncovered intervals returnunavailable.missing_rangesdescribes absent coverage, independent of age. Successful zero-row responses still count as coverage.
By default, SQLite stores observations in cache_rows and fetched intervals in
cache_ranges. Setting Schema.partition_dimension creates separate pairs of
tables for each selected partition value.
Development
uv sync --extra test
uv run --extra test python -m pytest -q
uv run ruff check .
uv run ruff format --check .
Tests use temporary databases and mocked fetchers. No credentials or live API requests are needed.
Build the wheel and source archive with:
uv build --no-sources
License
Released under the MIT License.
The full license text is included in the LICENSE file.
Metadata
Release files for timeseries-cache 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| timeseries_cache-0.1.1.tar.gz | 11.6 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| timeseries_cache-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 22.2 kB
Release files / timeseries_cache-0.1.1.tar.gz
| Download URL | timeseries_cache-0.1.1.tar.gz |
|---|---|
| Size | 11.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9a1bc0661bef6257228a67e641dbf3353713284b4d2834c8212e1e6ff180108c
|
|
BLAKE2b-256 checksum How to use checksums |
e088483467bfe4c19f94f4d183d19f70e7523be09afd7b7779d67dc9f27e1ca6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.2 {"installer":{"name":"uv","version":"0.12.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Pop!_OS","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / timeseries_cache-0.1.1-py3-none-any.whl
| Download URL | timeseries_cache-0.1.1-py3-none-any.whl |
|---|---|
| Size | 10.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b95343affd8465bdc3b715d45be488389695c61a15a634a1a85e1b6e7d6aba99
|
|
BLAKE2b-256 checksum How to use checksums |
c7dfa7aa19f53eb9d13b3b1ee75d1ef0fe175e9f728e17424bc52badc64f8e3e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.2 {"installer":{"name":"uv","version":"0.12.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Pop!_OS","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for timeseries-cache 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.