Skip to content

v0.5.0

Choose a tag to compare

@jeandet jeandet released this 10 May 18:56
· 1 commit to main since this release

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_utils dependency dropped
  • event.rating field added (Alembic migration 9a655086fb51)

API / DSL

  • New filtering DSL: from tscat.filtering import event then event.author == "Alice", combinable with & | ~
  • get_catalogue(uuid=...) / get_catalogue(name=...) / get_catalogue(predicate=...) helper
  • existing_tags() returns the union of all tags used across events and catalogues
  • Predicate.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.add in Session._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() returns False after restore() again
  • Bulk import on large catalogues no longer hits the SQLite bind-variable limit

Full Changelog: v0.4.4...v0.5.0