Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
7a81a6b
Add Pitch.octaveIsImplicit; use it wherever code tested octave is None
mscuthbert Sep 1, 2026
8decb9e
Pitch.octave is always an int; implicitOctave becomes a synonym
mscuthbert Sep 1, 2026
70d754c
Docs for integer octaves: migration page and User's Guide note
mscuthbert Sep 1, 2026
118483b
Drop a redundant octaveIsImplicit reset in Chord.closedPosition
mscuthbert Sep 1, 2026
e11914f
figuredBass: say 'implicit octave' in the maxPitch error
mscuthbert Sep 1, 2026
ddcf9de
Migrating-to-v11 page: cover every user-facing change since the v10 line
mscuthbert Sep 1, 2026
8258e3a
Fix the maxPitch doctest message; drop a duplicated argument in pitch…
mscuthbert Sep 1, 2026
61912b2
Migration page: cut the 'one floor up' aside
mscuthbert Sep 1, 2026
9ae7eb8
Migration page: open the KeySignature section with 'Similar idea to O…
mscuthbert Sep 1, 2026
1637b50
Migration page: make the one-line summary one line
mscuthbert Sep 1, 2026
9dc36f0
Migration page: describe only what changed in Chord indexing
mscuthbert Sep 1, 2026
5f72c56
Migration page: keep only behavior changes and new features
mscuthbert Sep 1, 2026
59bcc64
Cut the historical defaultOctave comment in Pitch.__init__
mscuthbert Sep 1, 2026
1d4faf5
Migration page: qualify MeasureParser as the MusicXML one
mscuthbert Sep 1, 2026
f38df01
Migration page: identificationToMetadata was on MusicXMLImporter
mscuthbert Sep 1, 2026
958d37e
Migration page: ContainsEnum is not needed, StrEnum or HexEnum instead
mscuthbert Sep 1, 2026
c332e85
intervalNetwork: test octaveIsImplicit once, outside the octave-shift…
mscuthbert Sep 1, 2026
43e022a
Mark implicitOctave deprecated in its docstring; record the property …
mscuthbert Sep 1, 2026
c9dc9a0
octaveIsImplicit setter: inform the client only when the state changes
mscuthbert Sep 1, 2026
c241c19
Migration page: trim the intro's branch aside
mscuthbert Sep 1, 2026
9a9c02f
Migration page: 'what to use instead'
mscuthbert Sep 1, 2026
0459e9a
Mark octave = None as the path to deprecate in v12 and remove in v13
mscuthbert Sep 1, 2026
af5fa3f
octave = None is deprecated, removed in v13; separate the concept fro…
mscuthbert Sep 1, 2026
37ec120
Pitch.octave docstring: lead with an explicit octave
mscuthbert Sep 1, 2026
85e6a97
Pitch.ps docstring: show the implicit octave equals D#4
mscuthbert Sep 1, 2026
8c65af7
Review wording for implicit octaves; record the review-commit rule
mscuthbert Sep 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .agents/skills/writing-docs/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,13 @@ what a reader must act on; drop the before-picture.
* Changed in v11: `stringOutput()` always returns a `str`, never None.
* Changed in v11: emits `\tuplet`; the arguments are now actual, normal.
* New in v11.
* Deprecated in v11: use `.octave`, which is now always an int.
```

A deprecated function or method also gets `@common.deprecated(...)`. A property
gets only the marker, since IDEs read every property while inspecting an object;
leave `# Add real deprecation message here in vX` in its body instead.

A plain bug fix — code now does what it always claimed — gets no marker and no
doctest. It goes in the commit message.

Expand Down
10 changes: 10 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@
`environLocal.printDebug(...)`, or `environLocal.warn(...)` when the user should hear
about it every time. `test/toggleDebug.py` switches debug output on and off.
- New exceptions subclass `exceptions21.Music21Exception`.
- Deprecate a function or method with `@common.deprecated(...)`. Never put it on a
property: IDEs read every property while inspecting an object, so the warning would
fire on people who never used it. For a property, write `* Deprecated in vX: use Y.`
in the docstring and leave `# Add real deprecation message here in vY` in the body.
- Return named things — a small class, a `namedtuple`, a dict — never a positional tuple of more than 2 (maybe 3) elements
whose elements each mean something different and unrelated (x, y, z is okay for instance). Nobody should write `returned[3][0][7]`.
- Don't reuse a variable name once the type of what it holds changes
Expand Down Expand Up @@ -128,6 +132,12 @@
after addressing the problem. (A blind close or close with "not accepted" etc. generally means that the issue/PR
has too many problems to easily solve and has become a burden for the maintainer).
- Do not include a "Tests run" section unless the testing procedure was unusual (like it affects part of the system without standard tests, like the testing system itself.)
- While someone is reviewing a PR or a pushed branch, "do X" is not "commit and push X":
make the change and leave it unstaged. When the list looks finished (or you hear "done!"
or "push it"), offer to commit, or to commit and push. Batch a round's small fixes into
one commit; no micro-commit trains. Prefer new commits to amend + force-push, since the
reviewer may have pulled the branch; if asked to fold a fix into the commit it changes,
amend, force-push with `--force-with-lease` against an explicit SHA, and say so.

# Writing style

Expand Down
1 change: 1 addition & 0 deletions documentation/source/about/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ About `music21`
about
applications
faq
migratingToV11
referenceCorpus
268 changes: 268 additions & 0 deletions documentation/source/about/migratingToV11.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
.. _migratingToV11:

Migrating to music21 v11
========================

*(This is an in-progress guide that was automatically generated by Myke's AI Agent.)*

What changed between the v10 line and v11, and what to use instead. The big, code-breaking items come first, then smaller
improvements by area, then one table of everything removed or deprecated.


Python 3.12 or newer
--------------------

Python 3.11 support is gone; 3.12, 3.13, and 3.14 are tested. The payoff is
real generics: ``Stream`` and its iterators use the PEP 695 bracket form, so a
type checker can follow ``stream.Stream[note.Note]()`` all the way to
``for n in s: n.pitch``.


Octaves are always integers
---------------------------

**In one line:** ``octave`` is always an ``int``, and ``octaveIsImplicit`` says whether you gave one.

Before v11, ``pitch.Pitch('F#').octave`` was ``None``: a lovely idea (an
F-sharp in *any* octave) that crashed the moment someone wrote
``p.octave + 1``. The pitch knew all along which octave it would use for
MIDI or a staff. It kept that in ``implicitOctave``, a property that nobody
remembered. Now ``octave`` does that job itself:

>>> from music21 import *
>>> anyFSharp = pitch.Pitch('F#')
>>> anyFSharp.octave
4
>>> anyFSharp.octaveIsImplicit
True

Nothing else about an octave-less pitch changes. It still prints without a
number, transposes without one, and is not equal to an explicit F#4:

>>> anyFSharp
<music21.pitch.Pitch F#>
>>> anyFSharp.transpose('P8')
<music21.pitch.Pitch F#>
>>> anyFSharp == pitch.Pitch('F#4')
False

Give it an octave and the flag flips:

>>> anyFSharp.octave = 5
>>> anyFSharp.octaveIsImplicit
False
>>> anyFSharp
<music21.pitch.Pitch F#5>

Notes follow along: ``note.Note('B-').octave`` is ``4``, and the flag lives
on the note's pitch, ``n.pitch.octaveIsImplicit``.

.. list-table::
:header-rows: 1
:widths: 50 50

* - Before v11
- v11
* - ``if p.octave is None:``
- ``if p.octaveIsImplicit:``
* - ``p.octave = None``
- ``p.octaveIsImplicit = True`` (the old spelling is deprecated; removed in v13)
* - ``p.implicitOctave``
- ``p.octave``
* - ``if p.octave is None: p.octave = p.implicitOctave``
- ``p.octaveIsImplicit = False``
* - ``octave: int | None``
- ``octave: int``

``p.octave + 1`` and ``f'{p.name}{p.octave}'`` no longer crash. The few
methods that need a pitch's *own* octave, such as
:meth:`~music21.pitch.Pitch.transposeAboveTarget`, still raise for an
implicit one; set ``.octave`` first. ``implicitOctave`` stays as a synonym
so nothing breaks today, but it is deprecated and starts warning in v12.


KeySignature.sharps is always an int
------------------------------------

Similar idea to Octave. ``KeySignature.sharps`` (and ``Key.sharps``) is an
``int`` you can add to and subtract from. A non-traditional signature is a
flag plus a list of pitches. ``sharps=None`` still works but warns:

>>> unusual = key.KeySignature()
>>> unusual.isNonTraditional = True
>>> unusual.alteredPitches = ['E-', 'G#4']
>>> unusual
<music21.key.KeySignature of pitches: [E-, G#4]>


Chord indexing gets simpler
---------------------------

``c[1]``, ``c['G4']`` and ``c[somePitch]`` still return the chord's
:class:`~music21.note.Note`; everything fancier is gone. Attribute paths
such as ``c['2.tie']`` and ``c['D-4.style.color']`` no longer work, a Note is
no longer accepted as a key (use its pitch), and the per-note getters and
setters (``getTie``, ``setColor``, ``getNotehead``, and their siblings) are
deprecated. Index, then use the Note:

>>> c = chord.Chord('C4 E4 G4')
>>> c['E4'].tie = tie.Tie('start')
>>> c[1].tie
<music21.tie.Tie start>


VoiceLeadingQuartet needs its four notes
----------------------------------------

``voiceLeading.VoiceLeadingQuartet(v1n1, v1n2, v2n1, v2n2)`` requires all
four notes up front, the first Music21Object ever to insist on arguments. In
return they are always Notes, never ``None``. ``analyticKey`` is now
``key``, ``vIntervals`` and ``hIntervals`` are tuples, and two dogmatic names
got truer ones: ``opensIncorrectly()`` is ``not modalOpening()`` and
``closesIncorrectly()`` is ``not clausulaVera()``.


Roman numerals
--------------

* ``romanNumeralFromChord`` keeps the sharp on major-quality chords built on
the raised sixth and seventh degrees in minor: ``#VI``, not ``VI#63``.
``correctRNAlterationForMinor`` gained a ``chordHasMajorThird`` keyword for
the same reason.
* Figures containing ``x``, ``y`` or ``z`` raise. ``RomanNumeral('IIIx')``
used to be a III13 chord by accident.
* ``FigureTuple`` and ``PitchFigureTuple`` are typed NamedTuples, and
``FigureTuple.fromPitchAndReference()`` replaces ``figureTupleSolo``.


Durations and sorting
---------------------

* Two Durations with ``expressionIsInferred`` True are equal when their
quarterLengths match; type, dots and tuplets are free to be re-expressed.
* ``sorting.SortTuple`` is a modern NamedTuple. ``priority`` and
``classSortOrder`` may be floats, and ``modify()`` with a bad field name
raises ``ValueError``.
* tinyNotation's undocumented ``0`` duration (a whole bar plus a fermata) is
deprecated.


Streams
-------

* New ``Stream.isAtEnd(el)`` says whether ``storeAtEnd`` put ``el`` there.
* ``Stream.hasElement(el)`` is removed: write ``el in s``.
``hasElementOfClass`` is deprecated: write ``if s.getElementsByClass(X):``.


Pitches
-------

``pitch.simplifyMultipleEnharmonics`` takes ``criterion`` and ``keyContext``
as keyword-only arguments.


File formats
------------

* **ABC**: ``w:`` lyric lines are imported, with hyphenation and ``*`` skips.
``abcToStreamOpus`` always returns an Opus.
* **Humdrum**: grace notes keep their written duration instead of becoming
eighths; duration parsing lives in a new ``hdStringToDuration``; a token
with no duration warns when a quarter is assumed; ``GlobalReference`` is no
longer a Music21Object.
* **LilyPond**: output targets current LilyPond (``\tuplet``, modern barline
names, ``\markuplist``), and every ``stringOutput()`` returns a ``str``.
* **MIDI**: ``midiEventsToInstrument`` is removed; use
``midiEventToInstrument``.
* **Vexflow**: the ``music21.vexflow`` module is gone. It had not worked in
a decade; music21j is the way to draw in a browser.
* **configure**: choosing MuseScore as the MusicXML reader also sets
``musescoreDirectPNGPath``.


figuredBass, features, tree
---------------------------

* figuredBass: ``hiddenFifth`` and ``hiddenOctave`` are now ``hiddenFifths``
and ``hiddenOctaves``, matching ``parallelFifths``. ``FiguredBassScale``'s
first argument is ``scaleTonic``, not ``scaleValue``. Rests inside a
possibility are typed sentinels rather than the string ``'RT'``.
* features: ``Feature.vector`` starts as ``[]`` rather than ``None``, and
``FeatureExtractor.dimensions`` defaults to 1.
* tree: ``ElementTimespan`` and ``PitchedTimespan`` take their arguments in
the same order as ``Timespan``; ``offset``, ``endTime`` and ``element`` are
required.


For developers
--------------

* Type annotations across nearly the whole library, with ``t.cast()`` for
narrowing and ``@property`` decorators throughout.
* ``common.enums.ContainsEnum`` is not needed: Python 3.12's ``StrEnum``
suffices, or ``HexEnum`` for hex values such as MIDI. The alias leaves in
v12. ``common.defaultlist`` is deprecated.
* The test runners import modules the normal way, so a module's tests no
longer need ``from music21.key import KeySignature`` to compare types.
* AI agents get their own instructions, skills and shared memory in
``AGENTS.md`` and ``.agents/``.


Removed and deprecated
----------------------

.. list-table:: Removed in v11
:header-rows: 1
:widths: 50 50

* - Gone
- Use instead
* - Python 3.11
- Python 3.12 or newer
* - ``music21.vexflow``
- music21j
* - ``midi.translate.midiEventsToInstrument``
- ``midiEventToInstrument``
* - ``Stream.hasElement(el)``
- ``el in s``
* - ``musicxml.xmlToM21.MusicXMLImporter.identificationToMetadata``
- ``addIdentificationToMetadata``
* - a string for ``musicxml.xmlToM21.MeasureParser.getStaffNumber``
- an ``int``
* - ``VoiceLeadingQuartet.unison`` / ``.fifth`` / ``.octave``
- ``interval.Interval('P1')`` and friends
* - ``c['2.tie']`` attribute paths into a Chord
- ``c[2].tie``
* - a Note as a Chord index, ``c[someNote]``
- ``c[someNote.pitch]``

.. list-table:: Deprecated in v11 (removed in v12 unless noted)
:header-rows: 1
:widths: 50 50

* - Deprecated
- Use instead
* - ``Stream.hasElementOfClass(X)``
- ``if s.getElementsByClass(X):``
* - ``Chord.getTie``, ``setTie``, ``getColor``, ``setColor``,
``getNotehead``, ``setNotehead``, ``getNoteheadFill``,
``setNoteheadFill``, ``getStemDirection``, ``setStemDirection``
- ``c[i].tie``, ``c[i].style.color``, ``c[i].notehead``,
``c[i].noteheadFill``, ``c[i].stemDirection``
* - ``roman.figureTupleSolo``
- ``FigureTuple.fromPitchAndReference()``
* - ``VoiceLeadingQuartet.opensIncorrectly()`` / ``closesIncorrectly()``
- ``not modalOpening()`` / ``not clausulaVera()``
* - ``KeySignature(sharps=None)``
- ``ks.isNonTraditional = True``
* - ``variant.addVariant(replacementDuration=...)``
- ``replacementQuarterLength=...``
* - ``common.defaultlist``
- a ``list`` or a ``dict``
* - ``common.enums.ContainsEnum``
- not needed: ``enum.StrEnum`` suffices, or ``HexEnum`` for hex values such as MIDI
* - tinyNotation ``0`` duration
- the duration plus an ``expressions.Fermata``
* - ``Pitch.implicitOctave``
- ``Pitch.octave`` (warns from v12, removed later)
Loading
Loading