feat: version-aware .tla loading with migrations#550
Conversation
- open_tla now warns and asks for confirmation before opening a file written by a newer TiLiA version, since data that version understands may be silently dropped on save. - Add tilia/file/migration.py: an ordered list of dict->dict migrations applied to the raw file data on load. A migration runs only when file_version < target <= app_version, so a migration written for an unreleased version stays dormant until that version ships. - Move the inline display_position->ordinal shim out of deserialize_timelines into the registry (target 0.1.1) and relocate its test, giving version upgrades a single source of truth. - Register the upcoming 0.7 timeline-kind rename (MARKER_TIMELINE -> Marker) as a dormant migration (target 0.7.0).
|
is this meant to merge into dev instead of main? |
Yes. I wonder if we are doing the main vs. dev thing in a unconventional way, Claude always seems to get it wrong. |
I suppose there is also main vs stable... |
| def _parse_version(version: str) -> tuple[int, ...]: | ||
| """Parse a version string into a comparable tuple of ints. | ||
|
|
||
| Reads the leading integer of each dot-separated segment, so suffixes | ||
| are ignored (``"0.7.0rc1"`` -> ``(0, 7, 0)``). | ||
| """ | ||
| parts: list[int] = [] | ||
| for segment in version.split("."): | ||
| digits = "" | ||
| for char in segment: | ||
| if not char.isdigit(): | ||
| break | ||
| digits += char | ||
| if not digits: | ||
| break | ||
| parts.append(int(digits)) | ||
| return tuple(parts) or (0,) |
There was a problem hiding this comment.
stdlib's platform._comparable_version?
| "This file was created with TiLiA {} but you are running {}.\n" | ||
| "It may not load correctly, and saving could discard data that this " | ||
| "version doesn't understand.\n\n" | ||
| "Open it anyway?" |
There was a problem hiding this comment.
this message seems a bit tame. something about "Errors may occur and information may be lost" would probably be more accurate. do we need to add info on where a newer version can be downloaded from?
There was a problem hiding this comment.
related to this: what happens when loading a newer version with a tl type that is not known to the older one? pretty sure nothing loads at all because of it, but do verify!
|
|
||
|
|
||
| def is_from_newer_version(data: dict, app_version: str = VERSION) -> bool: | ||
| return _parse_version(data.get("version", "0.0.0")) > _parse_version(app_version) |
There was a problem hiding this comment.
should there be a default? or should it error when not found?
(rather this actually depends on whether or not we validate or migrate first...)
| ) | ||
| return False, None, None | ||
|
|
||
| valid, reason = validate_tla_data(data) |
There was a problem hiding this comment.
are we sure we want to validate before migrating?
| "0.1.0", | ||
| {"0": make_timeline("HIERARCHY_TIMELINE", display_position=0)}, | ||
| ) | ||
| del data["timelines"]["0"]["ordinal"] |
There was a problem hiding this comment.
add a comment on the del or make a function that adds for >0.1?
Summary
Adds version handling when opening
.tlafiles. Until nowopen_tlastored thefile
versionbut never read it: opening a file written by a newer TiLiA silentlypartial-loaded and could drop unknown data on save, and older-format upgrades were
done with ad-hoc shims scattered inside
deserialize_timelines.This PR:
(
open_tla→Get.FROM_USER_YES_OR_NO). Declining aborts the load.tilia/file/migration.py: an ordered list of small, composabledict -> dictmigrations applied to the raw file data on load.display_position -> ordinalshim out ofdeserialize_timelinesinto the registry (target0.1.1), giving versionupgrades a single source of truth.
(
MARKER_TIMELINE -> Marker) as a dormant migration (target0.7.0)Follow-up
When syncing onto the
v0.7.0branch, delete the inline_get_timeline_class_stringshim there — the dormant
0.7.0migration replaces it.