All keys, fields, and values are strings; empty strings are valid and None denotes absence. No disk, network, wall time, or threading is needed.
Prototype: implement RecordStore() with set(key, field, value) -> None, get(key, field) -> str | None, and scan(key, prefix="") -> list[tuple[str, str]]. Setting replaces an existing field. Scan includes fields starting with the case-sensitive prefix and sorts by field name. Missing records produce None or []. Returned lists are fresh snapshots; records and instances are independent.
Final: implement TemporalDatabase():
- Every public method takes a nonnegative integer
ts. Valid calls, including reads and unsuccessful conditional/deletion calls, advance the accepted time. Equal timestamps are allowed; a later call at the same timestamp wins. A smaller timestamp raisesValueErrorwithout changing history or accepted time. Booleans and noninteger arguments are outside this exercise's input domain. set(ts, key, field, value, ttl=None) -> Nonewrites a version.ttl=Nonemeans no expiration; otherwisettlis a positive integer. A write attsis visible on[ts, ts + ttl). Replacing a field replaces its lifetime too. A nonpositive TTL raisesValueErrorbefore any time or state change.get(ts, key, field) -> str | Nonereads atts.delete(ts, key, field) -> boolappends a deletion marker only when the field is currently visible. Missing or expired fields returnFalse.compare_and_set(ts, key, field, expected, value, ttl=None) -> boolwrites the new value and lifetime only if the current visible value equals the stringexpected. Missing/expired fields returnFalse. TTL validation occurs even if comparison would fail.scan(ts, key, prefix="")returns a fresh list of visible(field, value)tuples, filtered by case-sensitive prefix and sorted by field name.get_at(ts, key, field, at) -> str | Nonereads history at integerat, where0 <= at <= ts. InvalidatraisesValueErrorwithout advancing accepted time. Reads before creation, at/after expiration, or after a deletion returnNone. An expired newer version never reveals an older version. History reflects operations already processed; later same-timestamp writes become visible to later calls.
Rejected operations preserve history. All validation errors preserve accepted time as well. Separate instances share nothing. Run python main.py --stage prototype, then python main.py --stage final (the default). Edit exercise.py; leave main.py unchanged.
Complete the starter code, then run the checks.
Results and errors will appear here.