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
5 changes: 5 additions & 0 deletions doc/serialization.org
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
#+begin_quote
Historical note: this document describes the structural SQLite serializer retired from the installed package in 2026.
Its frozen source is preserved in [[file:../misc/legacy/README.md][misc/legacy]], while current Cachew marshals each item into a single serialized payload.
#+end_quote

Cachew works kinda like =functools.lru_cache=, but it also works in-between program runs.
For that, it needs to somehow persist the objects on the disk (unlike =lru_cache= which just keeps references to the objects already in process memory).

Expand Down
16 changes: 16 additions & 0 deletions misc/legacy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Legacy structural SQLite serializer

This directory preserves Cachew's former `NTBinder` implementation for design reference.
It flattened annotated Python values into schema-derived SQLite columns.
Cachew replaced it with single-column serialized payloads in 2023 and retired it from the installed package, tests, and active benchmark matrix in 2026.

This is frozen historical source, not a supported backend or public API.
Its imports and embedded tests reflect its former `cachew.legacy` location and may no longer run.
Direct imports such as `from cachew.legacy import NTBinder` are no longer supported.

The old representation contributed two ideas that remain worth exploring independently: recursive structural schema fingerprints and schema-ordered positional records.
Those experiments belong in the current marshaller rather than reviving this implementation.

See the [historical comparison](../../doc/benchmarks/20230912-comparison-with-legacy.org) and the committed [baseline benchmark](../../.benchmarks/Linux-CPython-3.14-64bit/0007_baseline.json) for recorded results.

Cachew still recognizes the old SQLite table name when refreshing caches, so archiving this Python source does not remove on-disk cleanup compatibility.
File renamed without changes.
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ enable_error_code = [
[tool.ty.src]
exclude = [
"doc/test_serialization.py",
"src/cachew/legacy.py",
"misc/legacy/",
]


Expand Down Expand Up @@ -233,7 +233,7 @@ lint.ignore = [


extend-exclude = [
"src/cachew/legacy.py", # TODO dunno, remove it for good?
"misc/legacy/", # frozen historical source, intentionally outside active checks
]

[tool.ruff.format]
Expand Down
20 changes: 0 additions & 20 deletions src/cachew/pytest.py

This file was deleted.

19 changes: 0 additions & 19 deletions src/cachew/tests/benchmarks/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
type Impl = Literal[
'cachew',
'cattrs',
'legacy',
'pickle',
'msgspec-json',
'msgspec-msgpack',
Expand Down Expand Up @@ -179,18 +178,7 @@ def make_marshaller_impl(
# union=Type,
# converter=converter,
# )

return unstruct_func, lambda x: struct_func(x, Type), orjson.dumps, orjson.loads
elif impl == 'legacy':
from ...legacy import NTBinder

# NOTE: legacy binder emits a tuple which can be inserted directly into
# the database. So blob/sqlite stages are not directly comparable to the
# JSON-like implementations, where you first marshal and then encode.
# That also gives legacy a bit of an advantage for custom types, since
# those would otherwise normally be handled by SQLAlchemy instead.
binder = NTBinder.make(Type)
return binder.to_row, binder.from_row, orjson.dumps, orjson.loads
elif impl == 'pickle':
# Keep the protocol explicit so cross-version benchmark results are
# comparable even if pickle defaults change in the future.
Expand All @@ -216,13 +204,6 @@ def _validate_sample_equal(_impl: Impl, result: list[Any], expected: list[Any])


def _validate_datetimes(impl: Impl, result: list[Any], expected: list[Any]) -> None:
if impl == 'legacy':
# Legacy relies on SQLAlchemy's datetime adapter in the real sqlite path.
# In these raw blob/json pipeline stages, orjson turns top-level datetime
# payloads into strings, and NTBinder.from_row() does not convert them
# back. Keep legacy in the benchmark for throughput numbers, but skip the
# stronger round-trip assertion for this benchmark-only transport path.
return
assert _sample(result) == _sample(expected)
if not _is_msgspec_impl(impl):
# msgspec reconstructs fixed-offset tzinfo from RFC3339 rather than the
Expand Down