From 5837f41a0bdfe297ec5e64e9bc2ec9ba268b854c Mon Sep 17 00:00:00 2001 From: hexbold <37342551+hexbold@users.noreply.github.com> Date: Mon, 26 Jan 2026 17:54:07 +0100 Subject: [PATCH 1/4] Fix MusicXML export losing measure boundaries with chords Chords were not incrementing the bar duration counter, causing measure boundaries to be lost in MusicXML export. A 3-bar piece with chords would incorrectly export as 2 bars. Two functions affected: - new_chordbase(): explicit chord notation - copy_prev_chord(): duration shorthand after chords (4 4) Added self.increase_bar_dura(duration) to both functions to match the behavior of regular notes. Fixes #171 --- ly/musicxml/ly2xml_mediator.py | 2 + tests/test_xml.py | 4 + tests/test_xml_files/chord_duration.ly | 15 ++ tests/test_xml_files/chord_duration.xml | 254 ++++++++++++++++++++++++ 4 files changed, 275 insertions(+) create mode 100644 tests/test_xml_files/chord_duration.ly create mode 100644 tests/test_xml_files/chord_duration.xml diff --git a/ly/musicxml/ly2xml_mediator.py b/ly/musicxml/ly2xml_mediator.py index e67004e0..5b18068f 100644 --- a/ly/musicxml/ly2xml_mediator.py +++ b/ly/musicxml/ly2xml_mediator.py @@ -564,6 +564,7 @@ def new_chordbase(self, note, duration, rel=False): self.current_note.set_duration(duration) self.current_lynote = note self.check_current_note(rel) + self.increase_bar_dura(duration) def new_chordnote(self, note, rel): chord_note = self.create_barnote_from_note(note) @@ -600,6 +601,7 @@ def copy_prev_chord(self, duration): cn.set_tie('stop') self.bar.add(cn) self.tied = False + self.increase_bar_dura(duration) def clear_chord(self): self.q_chord = self.current_chord diff --git a/tests/test_xml.py b/tests/test_xml.py index a3eb5df6..e5ca26bd 100644 --- a/tests/test_xml.py +++ b/tests/test_xml.py @@ -76,6 +76,10 @@ def test_no_barcheck(): compare_output('no_barcheck') +def test_chord_duration(): + compare_output('chord_duration') + + def ly_to_xml(filename): """Read Lilypond file and return XML string.""" writer = ly.musicxml.writer() diff --git a/tests/test_xml_files/chord_duration.ly b/tests/test_xml_files/chord_duration.ly new file mode 100644 index 00000000..df2dea39 --- /dev/null +++ b/tests/test_xml_files/chord_duration.ly @@ -0,0 +1,15 @@ +\version "2.24.0" + +% Test chord duration counting for bar boundaries (issue #171) +% Both explicit chords and duration shorthand must count toward measure duration + +\score { + << + % Explicit chord notation: 3 measures + \new Staff { c''1 | 1 | e''1 } + + % Duration shorthand after chord: 3 measures + \new Staff { \time 3/4 4 4 4 | 4 4 8 r8 | r4 a' g' } + >> + \layout { } +} diff --git a/tests/test_xml_files/chord_duration.xml b/tests/test_xml_files/chord_duration.xml new file mode 100644 index 00000000..0992e3e0 --- /dev/null +++ b/tests/test_xml_files/chord_duration.xml @@ -0,0 +1,254 @@ + + + + + + python-ly 0.9.9 + 2026-01-26 + + + + + + + + + + + + + + 2 + + + G + 2 + + + + + C + 5 + + 8 + 1 + whole + + + 8 + + + + + + D + 5 + + 8 + 1 + whole + + + + + + + F + 5 + + 8 + 1 + whole + + + + + A + 5 + + 8 + 1 + whole + + + + E + 5 + + 8 + 1 + whole + + + + + + + 2 + + + G + 2 + + + + + E + 4 + + 2 + 1 + quarter + + + + + F + 4 + + 2 + 1 + quarter + + + + E + 4 + + 2 + 1 + quarter + + + + + F + 4 + + 2 + 1 + quarter + + + + E + 4 + + 2 + 1 + quarter + + + + + F + 4 + + 2 + 1 + quarter + + + 6 + + + + + + E + 4 + + 2 + 1 + quarter + + + + + F + 4 + + 2 + 1 + quarter + + + + E + 4 + + 2 + 1 + quarter + + + + + F + 4 + + 2 + 1 + quarter + + + + E + 4 + + 1 + 1 + eighth + + + + + F + 4 + + 1 + 1 + eighth + + + + 1 + 1 + eighth + + + + + + 2 + 1 + quarter + + + + A + 4 + + 2 + 1 + quarter + + + + G + 4 + + 2 + 1 + quarter + + + + From cfccab333097170d46d7ef2a08a2a862dab1e2ee Mon Sep 17 00:00:00 2001 From: Federico Bruni Date: Fri, 3 Apr 2026 09:24:14 +0200 Subject: [PATCH 2/4] 0.9.10 release --- CHANGELOG.md | 16 +++++++++++++++- ly/pkginfo.py | 2 +- tests/test_xml_files/break.xml | 2 +- tests/test_xml_files/breathe.xml | 2 +- tests/test_xml_files/chord_duration.xml | 2 +- tests/test_xml_files/church_modes.xml | 2 +- tests/test_xml_files/dynamics.xml | 2 +- tests/test_xml_files/full_bar_rest.xml | 2 +- tests/test_xml_files/glissando.xml | 2 +- tests/test_xml_files/mark.xml | 2 +- tests/test_xml_files/markup.xml | 2 +- tests/test_xml_files/merge_voice.xml | 2 +- tests/test_xml_files/merge_voice_slurs.xml | 2 +- tests/test_xml_files/no_barcheck.xml | 2 +- tests/test_xml_files/partial.xml | 2 +- tests/test_xml_files/stem.xml | 2 +- tests/test_xml_files/tie.xml | 2 +- tests/test_xml_files/tuplet.xml | 2 +- tests/test_xml_files/variable.xml | 2 +- 19 files changed, 33 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37140171..6acaee46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,20 @@ defined at the bottom of this file. All notable changes to the python-ly project are documented in this file. +## [0.9.10] - 2026-04-04 + +### Fixed + +- Fix MusicXML export losing measure boundaries with chords (#183) +- Add more robust logic to find the last Bar in a ScorePart (#180) +- ly2xml, check that list is not empty before working on it (#184). This fixes an error when exporting a LilyPond file to MusicXML: frescobaldi/frescobaldi#1712 +- Fix tests errors (#189) + +### Changed + +- License changed from GPL-2.0-or-later to GPL-3.0-or-later (#187) + + ## [0.9.9] - 2025-01-04 ### Changed @@ -255,7 +269,7 @@ All notable changes to the python-ly project are documented in this file. Initial release. - +[0.9.10]: https://github.com/frescobaldi/python-ly/compare/v0.9.9...v0.9.10 [0.9.9]: https://github.com/frescobaldi/python-ly/compare/v0.9.8...v0.9.9 [0.9.8]: https://github.com/frescobaldi/python-ly/compare/v0.9.7...v0.9.8 [0.9.7]: https://github.com/frescobaldi/python-ly/compare/v0.9.6...v0.9.7 diff --git a/ly/pkginfo.py b/ly/pkginfo.py index c0000296..93a34887 100644 --- a/ly/pkginfo.py +++ b/ly/pkginfo.py @@ -1,3 +1,3 @@ """Meta-information about the LY package.""" -version = "0.9.9" +version = "0.9.10" diff --git a/tests/test_xml_files/break.xml b/tests/test_xml_files/break.xml index c3200ac5..9cc0005a 100644 --- a/tests/test_xml_files/break.xml +++ b/tests/test_xml_files/break.xml @@ -4,7 +4,7 @@ - python-ly 0.9.9 + python-ly 0.9.10 2024-08-10 diff --git a/tests/test_xml_files/breathe.xml b/tests/test_xml_files/breathe.xml index 1a6f21bf..eda87244 100644 --- a/tests/test_xml_files/breathe.xml +++ b/tests/test_xml_files/breathe.xml @@ -4,7 +4,7 @@ - python-ly 0.9.9 + python-ly 0.9.10 2024-08-10 diff --git a/tests/test_xml_files/chord_duration.xml b/tests/test_xml_files/chord_duration.xml index 0992e3e0..d47b1fc0 100644 --- a/tests/test_xml_files/chord_duration.xml +++ b/tests/test_xml_files/chord_duration.xml @@ -4,7 +4,7 @@ - python-ly 0.9.9 + python-ly 0.9.10 2026-01-26 diff --git a/tests/test_xml_files/church_modes.xml b/tests/test_xml_files/church_modes.xml index b0617f8d..64c72986 100644 --- a/tests/test_xml_files/church_modes.xml +++ b/tests/test_xml_files/church_modes.xml @@ -4,7 +4,7 @@ - python-ly 0.9.9 + python-ly 0.9.10 2024-08-10 diff --git a/tests/test_xml_files/dynamics.xml b/tests/test_xml_files/dynamics.xml index a3cef187..787181bd 100644 --- a/tests/test_xml_files/dynamics.xml +++ b/tests/test_xml_files/dynamics.xml @@ -4,7 +4,7 @@ - python-ly 0.9.9 + python-ly 0.9.10 2024-08-10 diff --git a/tests/test_xml_files/full_bar_rest.xml b/tests/test_xml_files/full_bar_rest.xml index 567c604a..9e933e2a 100644 --- a/tests/test_xml_files/full_bar_rest.xml +++ b/tests/test_xml_files/full_bar_rest.xml @@ -4,7 +4,7 @@ - python-ly 0.9.9 + python-ly 0.9.10 2024-08-10 diff --git a/tests/test_xml_files/glissando.xml b/tests/test_xml_files/glissando.xml index d750a5fa..50c0b6d9 100644 --- a/tests/test_xml_files/glissando.xml +++ b/tests/test_xml_files/glissando.xml @@ -4,7 +4,7 @@ - python-ly 0.9.9 + python-ly 0.9.10 2024-08-10 diff --git a/tests/test_xml_files/mark.xml b/tests/test_xml_files/mark.xml index 66005814..3eafc72a 100644 --- a/tests/test_xml_files/mark.xml +++ b/tests/test_xml_files/mark.xml @@ -4,7 +4,7 @@ - python-ly 0.9.9 + python-ly 0.9.10 2024-08-10 diff --git a/tests/test_xml_files/markup.xml b/tests/test_xml_files/markup.xml index c5d6226a..24db966c 100644 --- a/tests/test_xml_files/markup.xml +++ b/tests/test_xml_files/markup.xml @@ -4,7 +4,7 @@ - python-ly 0.9.9 + python-ly 0.9.10 2017-06-30 diff --git a/tests/test_xml_files/merge_voice.xml b/tests/test_xml_files/merge_voice.xml index c9fe85ec..efe372eb 100644 --- a/tests/test_xml_files/merge_voice.xml +++ b/tests/test_xml_files/merge_voice.xml @@ -5,7 +5,7 @@ Somebody to love - python-ly 0.9.9 + python-ly 0.9.10 2024-08-10 diff --git a/tests/test_xml_files/merge_voice_slurs.xml b/tests/test_xml_files/merge_voice_slurs.xml index d3bb9055..04aa94ee 100644 --- a/tests/test_xml_files/merge_voice_slurs.xml +++ b/tests/test_xml_files/merge_voice_slurs.xml @@ -5,7 +5,7 @@ merge voices with slurs - python-ly 0.9.9 + python-ly 0.9.10 2024-08-10 diff --git a/tests/test_xml_files/no_barcheck.xml b/tests/test_xml_files/no_barcheck.xml index eea590c9..fe0c041d 100644 --- a/tests/test_xml_files/no_barcheck.xml +++ b/tests/test_xml_files/no_barcheck.xml @@ -4,7 +4,7 @@ - python-ly 0.9.9 + python-ly 0.9.10 2024-08-10 diff --git a/tests/test_xml_files/partial.xml b/tests/test_xml_files/partial.xml index 7eaccc43..cf1ef54e 100644 --- a/tests/test_xml_files/partial.xml +++ b/tests/test_xml_files/partial.xml @@ -4,7 +4,7 @@ - python-ly 0.9.9 + python-ly 0.9.10 2017-05-28 diff --git a/tests/test_xml_files/stem.xml b/tests/test_xml_files/stem.xml index 61b819b3..aac72505 100644 --- a/tests/test_xml_files/stem.xml +++ b/tests/test_xml_files/stem.xml @@ -4,7 +4,7 @@ - python-ly 0.9.9 + python-ly 0.9.10 2024-08-10 diff --git a/tests/test_xml_files/tie.xml b/tests/test_xml_files/tie.xml index d146e1d8..dd89ccf0 100644 --- a/tests/test_xml_files/tie.xml +++ b/tests/test_xml_files/tie.xml @@ -4,7 +4,7 @@ - python-ly 0.9.9 + python-ly 0.9.10 2024-08-10 diff --git a/tests/test_xml_files/tuplet.xml b/tests/test_xml_files/tuplet.xml index dbf46a6c..b236c4c8 100644 --- a/tests/test_xml_files/tuplet.xml +++ b/tests/test_xml_files/tuplet.xml @@ -4,7 +4,7 @@ - python-ly 0.9.9 + python-ly 0.9.10 2024-08-10 diff --git a/tests/test_xml_files/variable.xml b/tests/test_xml_files/variable.xml index 0adabf5a..c51f81ae 100644 --- a/tests/test_xml_files/variable.xml +++ b/tests/test_xml_files/variable.xml @@ -4,7 +4,7 @@ - python-ly 0.9.9 + python-ly 0.9.10 2024-08-10 From d2bd73a5051d6895c3e39f98367a6259e9e9a2f3 Mon Sep 17 00:00:00 2001 From: Federico Bruni Date: Fri, 3 Apr 2026 09:47:42 +0200 Subject: [PATCH 3/4] Add a justfile to bump version using hatch --- justfile | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 justfile diff --git a/justfile b/justfile new file mode 100644 index 00000000..9a6aa92c --- /dev/null +++ b/justfile @@ -0,0 +1,8 @@ +# Bump version: just bump [major|minor|patch] (default: patch) +bump part="patch": + #!/usr/bin/env bash + old=$(hatch version) + hatch version {{part}} + new=$(hatch version) + sed -i "s/python-ly $old/python-ly $new/g" tests/test_xml_files/*.xml + echo "Bumped $old -> $new" From eacf328b3c41b7a4b64a3b32fffe39792bbb9fa7 Mon Sep 17 00:00:00 2001 From: Knute Snortum Date: Tue, 9 Jun 2026 13:06:52 -0700 Subject: [PATCH 4/4] Update LilyPond reserved words for version 2.26 --- ly/words.py | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/ly/words.py b/ly/words.py index 8d13497a..639efbed 100644 --- a/ly/words.py +++ b/ly/words.py @@ -71,6 +71,7 @@ 'addInstrumentDefinition', 'addlyrics', 'addQuote', + 'addToTagGroup', # since 2.26 'after', 'afterGrace', #'afterGraceFraction', # this is a parser variable @@ -86,10 +87,12 @@ 'ambitusAfter', #'AncientRemoveEmptyStaffContext', 'appendToTag', # since 2.16 + 'appendToTagMarkup', # since 2.26 'applyContext', 'applyMusic', 'applyOutput', 'appoggiatura', + 'approximatePitch', # since 2.26 'arabicStringNumbers', # since 2.20 'arpeggio', 'arpeggioArrowDown', @@ -101,6 +104,8 @@ 'ascendens', 'assertBeamQuant', 'assertBeamSlope', + 'atLeft', # since 2.26 + 'atRight', # since 2.26 'auctum', 'aug', 'augmentum', @@ -134,6 +139,8 @@ 'bookOutputSuffix', 'bp', # since 2.24 'break', + 'breakAlignInsert', # since 2.26 + 'breakDynamicSpan', # since 2.26 'breathe', 'breve', 'cadenzaOff', @@ -147,6 +154,7 @@ 'chordRepeats', # since 2.16 #'chordRootNamer', 'chords', + 'chordSlur', # since 2.26 'clef', 'cm', 'codaMark', # since 2.24 @@ -155,6 +163,7 @@ 'compressEmptyMeasures', 'compressMMRests', # since 2.23 'context', + 'contextPropertyCheck', # since 2.26 'cr', 'cresc', 'crescHairpin', @@ -212,6 +221,7 @@ 'easyHeadsOff', 'easyHeadsOn', 'EnableGregorianDivisiones', # 2.24 + 'enablePerStaffTiming', # since 2.26 'enablePolymeter', 'endcr', 'endcresc', @@ -220,6 +230,7 @@ 'endincipit', 'endSkipNCs', 'endSpanners', + 'englishChords', # since 2.26 'episemFinis', 'episemInitium', 'eventChords', # since 2.23 @@ -239,6 +250,8 @@ 'finger', # since 2.23 'fingeringOrientations', 'fixed', # since 2.23 + 'flagStyleDefault', # since 2.26 + 'flagStyleStacked', # since 2.26 'flexa', 'footnote', # since 2.23 'fp', @@ -250,6 +263,7 @@ 'germanChords', 'glide', # since 2.24 'glissando', + 'glissandoMap', # since 2.20 'grace', 'graceSettings', 'grobdescriptions', # since 2.23 @@ -276,6 +290,7 @@ 'inclinatum', 'includePageLayoutFile', 'indent', + 'initialContextFrom', # since 2.26 'inStaffSegno', # since 2.23 'instrumentSwitch', 'instrumentTransposition', @@ -327,6 +342,7 @@ 'modalInversion', # since 2.23 'modalTranspose', # since 2.23 'mp', + 'musicLength', # since 2.26 'musicMap', 'n', # since 2.22 'neumeDemoLayout', @@ -334,6 +350,7 @@ 'newSpacingSection', 'noBeam', 'noBreak', + 'nonArpeggiato', # since 2.26 'noPageBreak', 'noPageTurn', 'normalsize', @@ -384,6 +401,8 @@ 'pointAndClickOff', 'pointAndClickOn', 'pointAndClickTypes', + 'polymetric', # since 2.26 + 'popContextProperty', # since 2.26 'pp', 'ppp', 'pppp', @@ -398,7 +417,9 @@ 'propertyTweak', # since 2.23 'propertyUnset', # since 2.23 'pt', + 'pushContextProperty', # since 2.26 'pushToTag', # since 2.16 + 'pushToTagMarkup', # since 2.26 'quilisma', 'quoteDuring', 'raiseNote', # since 2.23 @@ -408,10 +429,14 @@ 'RemoveEmptyRhythmicStaffContext', 'RemoveEmptyStaffContext', 'RemoveEmptyStaves', # since 2.20 + 'removeFromTagGroup', # since 2.26 'removeWithTag', 'repeat', 'repeatTie', + 'replaceWithTagMarkup', # since 2.26 'resetRelativeOctave', + 'resetTagGroup', # since 2.26 + 'resetTagGroups', # since 2.26 'responsum', 'rest', 'retrograde', # since 2.23 @@ -449,6 +474,7 @@ 'skipNCs', 'skipTypesetting', 'slashedGrace', # since 2.20 + 'slashSeparator', # since 2.20 'slurDashed', 'slurDashPattern', 'slurDotted', @@ -474,6 +500,7 @@ 'startGroup', 'startMeasureCount', 'startMeasureSpanner', + 'startOptionalMaterial', # since 2.26 'startSlashedGraceMusic', 'startStaff', 'startTextSpan', @@ -487,6 +514,7 @@ 'stopGroup', 'stopMeasureCount', 'stopMeasureSpanner', + 'stopOptionalMaterial', # since 2.26 'stopSlashedGraceMusic', 'stopStaff', 'stopStaffHighlight', @@ -497,12 +525,14 @@ 'strokeFingerOrientations', 'stropha', 'styledNoteHeads', # since 2.23 + 'submeasureBarsOn', # since 2.26 'sustainOff', 'sustainOn', 'tabChordRepeats', 'tabChordRepetition', 'tabFullNotation', 'tag', + 'tagGroupRef', # since 2.26 'teeny', 'tempo', 'tempoWholesPerMinute', @@ -523,10 +553,12 @@ 'tieSolid', 'tieUp', 'time', + 'timeAbbrev', # since 2.26 'times', 'timing', 'tiny', 'tocItem', # since ? + 'tocItemWithDotsMarkup', # since 2.14 'transpose', 'transposedCueDuring', 'transposition', @@ -558,11 +590,13 @@ 'voiceTwoStyle', 'void', # since 2.23 'volta', # since 2.24 + 'vowelTransition', # since 2.26 'vshape', # since 2.23 'walkerHeads', 'walkerHeadsMinor', 'whiteTriangleMarkup', 'withMusicProperty', + 'withRelativeDir', # since 2.26 'xNote', 'xNotesOff', 'xNotesOn', @@ -581,6 +615,7 @@ ornaments = ( + 'bachschleifer', # since 2.26 'downmordent', 'downprall', 'haydnturn', # since 2.22 @@ -615,15 +650,24 @@ 'downbow', 'flageolet', 'halfopen', + 'heel', # since 2.26 + 'heelcircle', # since 2.26 'lheel', + 'lheeltoe', # since 2.26 'ltoe', + 'ltoeheel', # since 2.26 'open', 'rheel', + 'rheeltoe', # since 2.26 'rtoe', + 'rtoeheel', # since 2.26 'snappizzicato', 'stopped', 'thumb', + 'toe', # since 2.26 'upbow', + 'varheel', # since 2.26 + 'vartoe', # since 2.26 ) @@ -679,7 +723,10 @@ ), # one argument ( + 'abs-hspace', # since 2.26 + 'abs-vspace', # since 2.26 'accidental', + 'annotate-moving', # since 2.26 'backslashed-digit', 'bold', 'box', @@ -735,6 +782,7 @@ 'normal-size-sub', 'normal-size-super', 'normal-text', + 'normal-weight', # since 2.26 'number', 'oval', # since 2.18 'overlay', # since 2.20 @@ -752,6 +800,7 @@ 'sans', 'score', 'score-lines', + 'serif', # since 2.26 'simple', 'slashed-digit', 'small', @@ -798,6 +847,7 @@ 'hcenter-in', 'if', 'lower', + 'keep-with-tag', # since 2.26 'magnify', 'note', 'on-the-fly', @@ -807,6 +857,7 @@ 'pad-x', 'page-link', 'path', # added in LP 2.13.31 + 'qr-code', # since 2.26 'raise', 'replace', 'rest-by-number', @@ -827,6 +878,7 @@ ), # three arguments ( + 'append-to-tag', # since 2.26 'arrow-head', 'beam', 'draw-circle', @@ -834,9 +886,11 @@ 'epsfile', 'filled-box', 'general-align', + 'image', # since 2.26 'note-by-number', 'pad-to-box', 'page-ref', + 'push-to-tag', # since 2.26 'with-dimension', 'with-dimension-from', 'with-dimensions',