Repository navigation
Conversation
tilia_core.timemap.plan turns time signatures for some measures, from a source or an edit, into a plan of basic writes with their old and new values, and applies a plan to a document in memory. plan_time_signatures matches each setting's bar with find_rows (a number alone means every time through; a movement narrows it) or by downbeat id. A bar the table doesn't have goes to unmatched, one found in more than one movement with none given to ambiguous, and nothing is written for either. Each measure's own taps share its time signature: the numerator divided by the taps, exactly, or a grouping's parts where there is one per tap. The unit goes on the beat of the unit already starting in the measure, else on the downbeat, not assumed. Every measure set records the source; when every measure would end with the same one, the timeline's measure_source changes and the set measures' marks lose their own. A measure whose governing unit, assumed flag and source are already right gets no write, so planning again after applying gives an empty plan. after="carry" is the only mode: "restore" raises ValueError until TimeLineAnnotator/project-management#194's part B is implemented. apply_plan checks every old value first, against the document as the changes before it leave it, and raises PlanOutOfDate naming the first that differs, changing nothing. It then applies the changes in order and brings the measure table's rows (sources, and the beat units of measures whose beats changed) in line. It opens no file. TimeSignature.parse reads what format_time_signature writes. Part of TimeLineAnnotator/project-management#217 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…view fixes A plan's correctness depends on more than the values it writes: the taps of each measure, the unit carried into it, and the timeline's source. A plan now records what it was made from (Plan.basis: the measure table as computed, the sources in the marks, and the timeline's), and apply_plan refuses it with PlanOutOfDate once that has changed, after checking every change's old value. Adding a beat to a planned measure, changing the unit carried into one, or changing the timeline's source after planning used to apply without error and write wrong values. A plan made by hand has no basis and is checked by its old values alone. PlanOutOfDate takes a message and the change, which is None when the basis differs. The timeline's measure_source changes only when every measure of the table is set and records the same source, as FR-071 says; it used to change whenever every measure would end with the same source, set or not. TimeSignature takes whole numbers and fractions only, at most 32 characters each, as a unit: a float or a string gave units nobody can read. A unit too long to read back raises ValueError when planning. Smaller fixes: - the timeline's source is read in one place, as stored or else as its table has it, so the plan, its old values and the table agree; - apply_plan checks changes to the timeline's source against each other whatever their component id; - the table's beat_units list the units the measure table reads, through _rows' own helpers; - bars are matched against the rows with their number or label only. Part of TimeLineAnnotator/project-management#217 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🤖 Claude
Adds
tilia_core.timemap.plan. Time signatures for some measures, from a source (a score, an import) or a person's edit, become a plan of basic writes, each with its old and new value. A caller can preview the plan, apply it in memory and undo it. Nothing here opens a file: writing the document is the.tlawriter's job.What changed
New
packages/tilia-core/src/tilia_core/timemap/plan.py:TimeSignature(parts,denominator):parsereads whatformat_time_signaturewrites (6/8,2+2+3/8,(4/3)/4,(1/2+1)/8), and raisesValueErrorfor anything else or for a denominator outside 1–128.BarRef(number,label,pass_,movement) andChange(timeline_id,component_id,key,old,new).Plan(changes,unmatched,ambiguous, andbasis, what it was made from).plan_time_signatures(document, timeline_id, settings, *, source, after):find_rows: a number alone means every time through, and amovementnarrows the matches. A downbeat's id names its measure. A bar the table doesn't have goes tounmatched, and a bar found in more than one movement with none given goes toambiguous; nothing is written for either. Of two settings for one measure, the later wins. In a folded table, a pass counts the measures in written order (MeasureRow.folded).3,2or3/2. With a grouping that has one part per tap, the parts are the units (2+2+3), written as one unit when they are all equal. A unit too long to read back raisesValueError.score,import, or for an editedited, unless the measure istapped. When every measure of the table is set and records the same source, the timeline'smeasure_sourcechanges instead, and the marks lose their own. A measure the settings don't name is never written.assumedflag and source are already right gets no write. The governing unit is followed through the table, so bars 1–120 at 3/4, one tap each, get one unit, on bar 1. Planning again after applying gives an empty plan.afteris keyword-only, with no default. Only"carry"is implemented: nothing more is written, so the measures after a set run take its unit."restore"raisesValueErrornaming TimeLineAnnotator/project-management#194.apply_plan(document, plan):basisis checked: the measure table as computed, the sources in the marks, and the timeline's own source. If either differs, it raisesPlanOutOfDateand changes nothing.PlanOutOfDate.changenames the change, or isNonewhen the basis differs. So a plan is also refused after a beat is added to a planned measure, after the unit carried into one changes, or after the timeline's source changes.source, and thebeat_unitsof the measures whose beats changed, as the measure table reads them. A row'sbeat_unit(the unit in force) isn't updated.New
packages/tilia-core/tests/timemap/test_timemap_plan.py, 111 tests. They cover:2+2+3;after;apply_plan: values changed after planning, a planned measure changed, order, a reverse plan restoring the components, and no file being opened (openmonkeypatched to fail).__init__.pydoesn't export the new names yet; the package's public surface is a later task.What this PR doesn't change. In a timeline whose first measure has no unit (TiLiA and the migration always put one there), the first unit a plan writes changes how the measures before it read. They stay assumed, and they take that unit's values, by the rule for measures before the first unit. The plan writes nothing to them. Whether it should is TimeLineAnnotator/project-management#322.
Tests run
uv run --package tilia-core pytest packages/tilia-core/tests/timemap/test_timemap_plan.py -q: 111 passed.uv run --package tilia-core pytest packages/tilia-core -q: 905 passed, 21 skipped, 8 xfailed. Before this PR: 794 passed, 21 skipped, 8 xfailed.python scripts/check_packages.py: all package checks passed.Guarantees: what removing their code does
For each guarantee, its code in
plan.pywas neutralised,test_timemap_plan.py(111 tests) was run, and the code was restored:oldnot copiedafter="restore"is refused, naming TimeLineAnnotator/project-management#194sourceis refusedparse: a fraction in the numerator needs bracketsparse: the denominator is digits only (+8,1_0)open()added toapply_planEvery guarantee in this PR is caught by at least one test.
Part of TimeLineAnnotator/project-management#217
🤖 Generated with Claude Code