Replace log dict toggles with logdiff kwarg and kanta.transaction.diff logger
Per-transaction logdiff=False skips building and printing the diff body, logging only the header. Globally, configure_logging(diff=False) disables the kanta.transaction.diff child logger, which now carries all diff lines, so applications can route or silence diffs separately from headers.
This commit is contained in:
+37
-57
@@ -1,15 +1,5 @@
|
||||
"""Kanta feature demo.
|
||||
|
||||
Run from the project root: python demo/main.py
|
||||
|
||||
Demonstrates bootstrap, colored transaction diffs, logfmt value formatting,
|
||||
logging toggles, rollback, migrations, and a custom clock.
|
||||
The database is recreated with fixed timestamps on every run; everything else
|
||||
lives in this file.
|
||||
"""
|
||||
|
||||
#!/usr/bin/env -S uv run
|
||||
import asyncio
|
||||
import logging
|
||||
import sys
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from pathlib import Path
|
||||
@@ -38,34 +28,24 @@ kanta_v0 = Kanta(filename, Data())
|
||||
@kanta_v0.bootstrap
|
||||
def bootstrap(data: Data) -> None:
|
||||
"""Create the initial admin user."""
|
||||
data.users["u1"] = {"name": "Alice", "role": "admin"}
|
||||
data.users["userid001"] = {"name": "Alice", "role": "admin"}
|
||||
|
||||
|
||||
# Redefinition to simulate new version with counter renamed to total
|
||||
# Redefinition to simulate new version
|
||||
class Data(msgspec.Struct):
|
||||
users: dict[str, dict] = {}
|
||||
total: int = 0
|
||||
total: int = 0 # Replaces old counter field
|
||||
lang: str = "en" # New field
|
||||
|
||||
|
||||
def migrate_v1(d: dict) -> None:
|
||||
"""Rename counter to total"""
|
||||
d["total"] = d.pop("counter")
|
||||
d["total"] = d["counter"]
|
||||
|
||||
|
||||
kanta_v1 = Kanta(filename, Data(), migrations=sys.modules[__name__])
|
||||
|
||||
|
||||
_now = datetime(2027, 1, 1, tzinfo=UTC)
|
||||
|
||||
|
||||
@kanta_v0.clock
|
||||
@kanta_v1.clock
|
||||
def fake_clock() -> datetime:
|
||||
global _now
|
||||
_now += timedelta(hours=1)
|
||||
return _now
|
||||
|
||||
|
||||
@kanta_v0.logfmt
|
||||
@kanta_v1.logfmt
|
||||
def resolve_user(
|
||||
@@ -82,61 +62,61 @@ def resolve_user(
|
||||
return None
|
||||
|
||||
|
||||
def section(title: str) -> None:
|
||||
print(f"\n# {title}", flush=True)
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
filename.unlink(missing_ok=True)
|
||||
|
||||
section("Database creation with v0 schema and basic access")
|
||||
print("# Database creation with v0 schema and basic ops, pretty logs", flush=True)
|
||||
# Open and close automatically; you can also `await kanta.open()` instead
|
||||
async with kanta_v0 as kanta:
|
||||
with kanta.transaction(action="create", user="u2") as data:
|
||||
data.users["u2"] = {"name": "Bob", "role": "user"}
|
||||
with kanta.transaction(action="create", user="userid001") as data:
|
||||
data.users["userid002"] = {"name": "Bob", "role": "user"}
|
||||
|
||||
with kanta.transaction(action="update", user="u1") as data:
|
||||
data.users["u2"]["role"] = "editor"
|
||||
with kanta.transaction(action="update", user="userid001") as data:
|
||||
data.users["userid002"]["role"] = "editor"
|
||||
data.counter = 1
|
||||
|
||||
with kanta.transaction(action="delete", user="u1") as data:
|
||||
del data.users["u2"]
|
||||
with kanta.transaction(action="delete", user="userid002") as data:
|
||||
del data.users["userid001"]
|
||||
|
||||
# Display-only extra string, appended after the action.
|
||||
with kanta.transaction(action="export", user="u1", extra=filename.name) as data:
|
||||
with kanta.transaction(
|
||||
action="export", user="userid002", extra="extra info"
|
||||
) as data:
|
||||
data.counter = 2
|
||||
|
||||
# Compact logging, diff only: a system fix stamped by the clock, but the
|
||||
# modification time (m) is not updated.
|
||||
with kanta.transaction(
|
||||
action="repair", mtime=False, log={"header": False, "diff": True}
|
||||
) as data:
|
||||
data.users["u3"] = {"name": "Carol", "role": "user"}
|
||||
|
||||
# A failing transaction rolls back and logs a warning.
|
||||
try:
|
||||
with kanta.transaction(action="reset", user="u1") as data:
|
||||
with kanta.transaction(action="reset") as data:
|
||||
data.counter = 99
|
||||
raise ValueError("simulated failure")
|
||||
except ValueError:
|
||||
pass
|
||||
print(f"# Reading does not need transaction: {data.counter=}", flush=True)
|
||||
|
||||
# Compact logging, header only.
|
||||
with kanta.transaction(
|
||||
action="import", user="u1", log={"header": True, "diff": False}
|
||||
) as data:
|
||||
with kanta.transaction(action="import", logdiff=False) as data:
|
||||
data.counter = 3
|
||||
|
||||
section("A later version of our application with new data model and migrations")
|
||||
print(
|
||||
"\n# A later version of our application with new data model and migrations",
|
||||
flush=True,
|
||||
)
|
||||
async with kanta_v1 as kanta:
|
||||
with kanta.transaction(action="update", user="u1", extra=filename.name) as data:
|
||||
with kanta.transaction(
|
||||
action="update", user="userid002", extra=filename.name
|
||||
) as data:
|
||||
data.total = 4
|
||||
|
||||
with kanta.transaction(action="create", user="u3", extra="Dave (u4)") as data:
|
||||
data.users["u4"] = {"name": "Dave", "role": "user"}
|
||||
|
||||
# Fake clock for deterministic timestamps
|
||||
_now = datetime(2027, 1, 1, tzinfo=UTC)
|
||||
|
||||
|
||||
@kanta_v0.clock
|
||||
@kanta_v1.clock
|
||||
def fake_clock() -> datetime:
|
||||
global _now
|
||||
_now += timedelta(hours=1)
|
||||
return _now
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
configure_logging()
|
||||
logging.getLogger("kanta").setLevel(logging.DEBUG) # show migration diffs
|
||||
asyncio.run(main())
|
||||
|
||||
Reference in New Issue
Block a user