Add typing to RomanText#1937
Merged
Merged
Conversation
mscuthbert
added a commit
that referenced
this pull request
Jun 21, 2026
These two folders are now typed in branch typing_small_folders (its own PR) to keep #1937 reviewable. Reverting them here to master's state so there is no overlap between the two PRs. AI-assisted (Claude)
This was referenced Jun 21, 2026
alexandermorgan
pushed a commit
to alexandermorgan/music21
that referenced
this pull request
Jun 21, 2026
Split out of cuthbertLab#1937 to keep that PR reviewable. Adds parameter/return type annotations to: - ipython21/ (__init__, ipExtension, converters) - languageExcerpts/ (instrumentLookup, naturalLanguageObjects) Whole-package mypy + ruff clean; doctests pass. AI-assisted (Claude)
Type-annotate the romanText package (clercqTemperley, rtObjects, translate, tsvConverter, writeRoman). Also: - Fix a CTSong keyword bug where `year=` was assigning the keyword *name* rather than its value; add a CTSong title/year test. - Annotate features.Feature.__init__ / _getVectors (missed by the small-folders typing pass). - Note in AGENTS.md that PR CI tests the branch merged with the latest master, so merge master before opening/updating a PR. AI-assisted (Claude)
Bare `assert` in a test gives a poor traceback on failure. Replace them with real unittest assertions: - testReA: precede each Optional-narrowing `assert g is not None` with `self.assertIsNotNone(g)` (the meaningful check); the bare assert stays after it -- guaranteed to succeed -- purely so mypy narrows the re.Match | None. - testRepeats: `assert s.duration.quarterLength == 10` -> assertEqual. - writeRoman test: `assert ....endswith(...)` -> assertTrue. AI-assisted (Claude)
Collapse the redundant `self.assertIsNotNone(g)` + `assert g is not None` pairs into a single `assert g is not None, '<regex> did not match'`, which narrows re.Match | None for mypy and gives a useful failure message. AI-assisted (Claude)
…sentinels' None These |None types were never really None -- the call sites already cast them away. Tighten them and remove the casts: - _copySingleMeasure / _copyMultipleMeasures now require kCurrent: key.Key (a Key is always supplied, defaulting to C at instantiation) and return a non-Optional Key. The dead "no past key definitions" guards are removed. - Both now raise RomanTextTranslateException if the copy *source* measure(s) are not found, instead of returning None / an empty list and failing obscurely downstream. Added tests for both (forward-reference copy targets). - currentMeasureToken and lastMeasureToken are initialised to a measure-0 RTMeasure sentinel (always overwritten before use), so they are typed as a plain RTMeasure and the four t.casts are gone. previousChordInMeasure stays Optional on purpose: it is reset to None at the start of every measure and the `is None` checks are the first-chord-in-measure detection. AI-assisted (Claude)
…asures translateSingleMeasure cast self.previousRn to GeneralNote when adjusting the last chord's duration, but previousRn is None until the first chord atom is seen. A piece that begins with an empty or key-only measure therefore crashed with "AttributeError: 'NoneType' object has no attribute 'quarterLength'" (the cast was masking it). Guard on `self.previousRn is not None` instead -- if no chord was added there is no last chord to resize -- which also removes the cast. Added a regression test. AI-assisted (Claude)
…test Assert the empty first measure has no RomanNumeral and the second measure parses as a I chord, so the test guards correct parsing of a piece that begins without chord atoms, not merely that it does not crash. AI-assisted (Claude)
…None previousRn was typed note.GeneralNote|None but only ever holds a RomanNumeral (a chord) or a Rest (from an N.C. atom), or None before the first chord. Narrow the annotation accordingly. No behavior change -- it still starts as None, so the existing is-None guards are unaffected. AI-assisted (Claude)
…gap bug) Note next to the initial previousRn assignment that it should start as a note.Rest (dropping None) so a piece beginning before its first chord -- e.g. one starting with N.C. -- gets leading rests rather than a gap. AI-assisted (Claude)
Cross-reference the previousRn TODO from the measure-start back-fill: it does not fire at the start of a piece because previousRn is None there. AI-assisted (Claude)
key, stream, rtObjects, and roman are already imported at module level; the new tests re-imported them, tripping pylint's redefined-outer-name / reimported warnings. Use the module-level names (keep only the local converter import, which is not module-level). AI-assisted (Claude)
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.
Originally this PR was going to add type annotations across a lot of directories, but they've been split out:
figuredBass/ (all 10 modules)-- split into Add typing to figuredBass #1940languageExcerpts/-- split into Add type annotations to ipython21 and languageExcerpts #1939ipython21/-- split into Add type annotations to ipython21 and languageExcerpts #1939scale/ (-- Add typing to scale modules #1950__init__, intervalNetwork, scala)search/ (base, lyrics, segment, serial)-- Typing Search module #1952So, only remaining is
romanText/(clercqTemperley, rtObjects, translate, tsvConverter, writeRoman)Two latent bugs surfaced while typing and fixed:
AI-assisted (Claude)