Repository navigation
v0.5.0
Highlights
This release modernizes the internals and migrates the on-disk schema. Existing databases are auto-migrated on first open and a timestamped .sqlite.backup is created next to them.
Storage / migrations
- Predicates serialized as JSON instead of pickle (Alembic migration
4a1d43bb9936) - Tags & products stored as JSON arrays instead of comma-separated strings (Alembic migration
df61d8f30e7c); the comma-restriction on tag/product values is removed sqlalchemy_utilsdependency droppedevent.ratingfield added (Alembic migration9a655086fb51)
API / DSL
- New filtering DSL:
from tscat.filtering import eventthenevent.author == "Alice", combinable with&|~ get_catalogue(uuid=...)/get_catalogue(name=...)/get_catalogue(predicate=...)helperexisting_tags()returns the union of all tags used across events and cataloguesPredicate.to_dict()/Predicate.from_dict()for explicit JSON serialization
Internals
- Migrated to SQLAlchemy 2.0 (
DeclarativeBase,Mapped[T],mapped_column,select()) - Build system migrated to
uv+hatchling - Python ≥ 3.10 (3.10 — 3.14 tested in CI)
Performance
- ~14 % faster event creation, 1.6× faster catalogue reads, 2.1× faster tag filtering on a 10K-event benchmark
- Raw-SQL fast path for bulk event loads (~2× speedup)
Fixes
- Eager
session.addinSession._track— fixes silent relationship-skip warning on import (#) - Mutable default arguments removed
- Identity-map cleared after
rollback() - Import dedup for catalogues batched by UUID set
- Set-based
remove_events_from_catalogue(was O(n·m)) is_removed()returnsFalseafterrestore()again- Bulk import on large catalogues no longer hits the SQLite bind-variable limit
Full Changelog: v0.4.4...v0.5.0