Skip to content

Commit 5159e8e

Browse files
committed
Fix mypy 1.16 issues
Improve typing for music21 based on issues uncovered in mypy 1.16. Fix error in `Interval.noteEnd = None` -- setting noteStart, noteEnd, pitchStart, or pitchEnd to None raised exceptions
1 parent 3ad399f commit 5159e8e

File tree

11 files changed

+66
-38
lines changed

11 files changed

+66
-38
lines changed

music21/analysis/discrete.py

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ def getSolutionsUsed(self):
153153
post.append(solution)
154154
return post
155155

156-
def solutionLegend(self, compress=False):
156+
def solutionLegend(self, compress: bool = False) -> list[list[str|list[tuple[int|str, str|None]]]]:
157157
'''
158158
A list of pairs showing all discrete results and the assigned color.
159159
Data should be organized to be passed to
@@ -162,7 +162,7 @@ def solutionLegend(self, compress=False):
162162
If `compress` is True, the legend will only show values for solutions
163163
that have been encountered.
164164
'''
165-
pass
165+
return []
166166

167167
def solutionUnitString(self):
168168
'''
@@ -211,7 +211,7 @@ class KeyWeightKeyAnalysis(DiscreteAnalysis):
211211
# favor eb minor
212212
# C- major cannot be determined if no enharmonics are present
213213
# C# major can be determined w/o enharmonics
214-
keysValidMajor = (
214+
keysValidMajor: tuple[str, ...] = (
215215
'C', 'C#', 'C-',
216216
'D-', 'D',
217217
'E-', 'E',
@@ -221,7 +221,7 @@ class KeyWeightKeyAnalysis(DiscreteAnalysis):
221221
'B-', 'B',
222222
)
223223

224-
keysValidMinor = (
224+
keysValidMinor: tuple[str, ...] = (
225225
'C', 'C#',
226226
'D', 'D#',
227227
'E-', 'E',
@@ -442,7 +442,7 @@ def _getDifference(self, keyResults, pcDistribution, weightType) -> None|list[fl
442442
solution[i] = float(top[i] / ((bottomRight[i] * bottomLeft[i]) ** 0.5))
443443
return solution
444444

445-
def solutionLegend(self, compress=False):
445+
def solutionLegend(self, compress: bool = False) -> list[list[str|list[tuple[int|str, str|None]]]]:
446446
'''
447447
Returns a list of lists of possible results for the creation of a legend.
448448
@@ -478,16 +478,18 @@ def solutionLegend(self, compress=False):
478478
keySortOrderFiltered = _keySortOrder
479479

480480
data = []
481-
valid = None
481+
valid: tuple[str, ...] = ()
482482

483+
yLabel: str
483484
for yLabel in ['Major', 'Minor']:
484485
if yLabel == 'Major':
485486
valid = self.keysValidMajor
486487
elif yLabel == 'Minor':
487488
valid = self.keysValidMinor
488489

489-
row: list[t.Any] = [yLabel]
490-
pairs = []
490+
row: list[str|list[tuple[int|str, str|None]]] = [yLabel]
491+
pairs: list[tuple[int|str, str|None]] = []
492+
color: str|None
491493
for keyPitch in [pitch.Pitch(p) for p in keySortOrderFiltered]:
492494
try:
493495
color = self.solutionToColor([keyPitch, yLabel])
@@ -499,6 +501,7 @@ def solutionLegend(self, compress=False):
499501
mask = True
500502
if keyPitch.name not in valid:
501503
mask = True
504+
502505
if mask:
503506
# set as white to maintain spacing
504507
color = '#ffffff'
@@ -1076,7 +1079,7 @@ def getPitchSpan(self, subStream) -> tuple[pitch.Pitch, pitch.Pitch]|None:
10761079

10771080
return minPitchObj, maxPitchObj
10781081

1079-
def solutionLegend(self, compress=False):
1082+
def solutionLegend(self, compress: bool = False) -> list[list[str|list[tuple[int|str, str|None]]]]:
10801083
'''
10811084
Return legend data.
10821085
@@ -1119,7 +1122,7 @@ def solutionLegend(self, compress=False):
11191122

11201123
data = []
11211124

1122-
colors = {} # a filtered dictionary
1125+
colors: dict[int, str] = {} # a filtered dictionary
11231126
for i in range(len(self._pitchSpanColors.keys())):
11241127
if compress:
11251128
if self._pitchSpanColors[i] not in colorsUsed:
@@ -1135,8 +1138,8 @@ def solutionLegend(self, compress=False):
11351138

11361139
# split keys into two groups for two rows (optional)
11371140
for keyGroup in [keysTopRow, keysBottomRow]:
1138-
row: list[t.Any] = [''] # empty row label
1139-
pairs = []
1141+
row: list[str|list[tuple[int|str, str|None]]] = [''] # empty row label
1142+
pairs: list[tuple[int|str, str|None]] = []
11401143
for i in keyGroup:
11411144
color = colors[i] # get form colors
11421145
pairs.append((i, color))
@@ -1151,7 +1154,7 @@ def solutionUnitString(self):
11511154
'''
11521155
return 'Half-Steps'
11531156

1154-
def solutionToColor(self, solution):
1157+
def solutionToColor(self, solution: int|None) -> str:
11551158
'''
11561159
11571160
>>> p = analysis.discrete.Ambitus()

music21/bar.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ def times(self) -> int|None:
335335
return self._times
336336

337337
@times.setter
338-
def times(self, value: int):
338+
def times(self, value: int|None) -> None:
339339
if value is None:
340340
self._times = None
341341
else:

music21/chord/__init__.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3230,6 +3230,8 @@ def isItalianAugmentedSixth(self, *, restrictDoublings=False, permitAnyInversion
32303230
root = self.root()
32313231
third = self.third
32323232
fifth = self.fifth
3233+
if not fifth:
3234+
return False
32333235

32343236
# only the tonic (that is, fifth) can be doubled
32353237
for p in self.pitches:
@@ -4955,7 +4957,10 @@ def _isSeventhWithPerfectFifthsAboveRootAndThird(c: Chord) -> bool:
49554957
hypothetical_fifth = c.root().transpose('P5')
49564958
if hypothetical_fifth.name not in c.pitchNames:
49574959
return False
4958-
hypothetical_seventh = c.third.transpose('P5')
4960+
third = c.third
4961+
if not third:
4962+
return False
4963+
hypothetical_seventh = third.transpose('P5')
49594964
if hypothetical_seventh.name not in c.pitchNames:
49604965
return False
49614966
return True
@@ -5731,7 +5736,7 @@ def pitches(self) -> tuple[pitch.Pitch, ...]:
57315736
return pitches
57325737

57335738
@pitches.setter
5734-
def pitches(self, value: Sequence[str|pitch.Pitch|int]):
5739+
def pitches(self, value: Iterable[pitch.Pitch]) -> None:
57355740
self._notes = []
57365741
self.clearCache()
57375742
# TODO: individual ties are not being retained here

music21/derivation.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,8 @@ def __init__(self, client: base.Music21Object|None = None):
153153
self._client: weakref.ReferenceType|None = None
154154
self._clientId: int|None = None # store python-id to optimize w/o unwrapping
155155
self._method: str|None = None
156-
# origin should be stored as a weak ref -- the place where the client was derived from.
157-
self._origin = None
156+
# origin could be stored as a weak ref -- the place where the client was derived from.
157+
self._origin: base.Music21Object|None = None
158158
self._originId: int|None = None # store id to optimize w/o unwrapping
159159

160160
# set client; can handle None

music21/interval.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3573,7 +3573,7 @@ def reverse(self):
35733573
chromatic=self.chromatic.reverse())
35743574

35753575
@property
3576-
def pitchStart(self):
3576+
def pitchStart(self) -> pitch.Pitch|None:
35773577
'''
35783578
Get the start pitch or set it a new value.
35793579
Setting this will adjust the value of the end pitch (`pitchEnd`).
@@ -3618,12 +3618,17 @@ def pitchStart(self):
36183618
return self._pitchStart
36193619

36203620
@pitchStart.setter
3621-
def pitchStart(self, p: pitch.Pitch):
3621+
def pitchStart(self, p: pitch.Pitch|None) -> None:
36223622
'''
36233623
Assuming that this interval is defined,
36243624
we can set a new start Pitch (_pitchStart) and
36253625
automatically set the end Pitch (_pitchEnd).
36263626
'''
3627+
if p is None:
3628+
self._pitchStart = None
3629+
self._pitchEnd = None
3630+
return
3631+
36273632
# this is based on the procedure found in transposePitch() and
36283633
# transposeNote() but offers a more object-oriented approach
36293634
pitch2 = self.transposePitch(p)
@@ -3632,7 +3637,7 @@ def pitchStart(self, p: pitch.Pitch):
36323637
self._pitchEnd = pitch2
36333638

36343639
@property
3635-
def pitchEnd(self):
3640+
def pitchEnd(self) -> pitch.Pitch|None:
36363641
'''
36373642
Set the
36383643
end pitch to a new value; this will adjust
@@ -3665,11 +3670,16 @@ def pitchEnd(self):
36653670
return self._pitchEnd
36663671

36673672
@pitchEnd.setter
3668-
def pitchEnd(self, p: music21.pitch.Pitch):
3673+
def pitchEnd(self, p: music21.pitch.Pitch|None) -> None:
36693674
'''
36703675
Assuming that this interval is defined, we can
36713676
set a new end note (_pitchEnd) and automatically have the start pitch (_pitchStart).
36723677
'''
3678+
if p is None:
3679+
self._pitchStart = None
3680+
self._pitchEnd = None
3681+
return
3682+
36733683
# this is based on the procedure found in transposePitch() but offers
36743684
# a more object-oriented approach
36753685
pitch1 = self.transposePitch(p, reverse=True)
@@ -3689,9 +3699,11 @@ def noteStart(self) -> music21.note.Note|None:
36893699
elif p:
36903700
from music21 import note
36913701
return note.Note(pitch=p)
3702+
else:
3703+
return None
36923704

36933705
@noteStart.setter
3694-
def noteStart(self, n: music21.note.Note|None):
3706+
def noteStart(self, n: music21.note.Note|None) -> None:
36953707
if n:
36963708
self.pitchStart = n.pitch
36973709
else:
@@ -3709,9 +3721,11 @@ def noteEnd(self) -> music21.note.Note|None:
37093721
elif p:
37103722
from music21 import note
37113723
return note.Note(pitch=p)
3724+
else:
3725+
return None
37123726

37133727
@noteEnd.setter
3714-
def noteEnd(self, n: music21.note.Note|None):
3728+
def noteEnd(self, n: music21.note.Note|None) -> None:
37153729
if n:
37163730
self.pitchEnd = n.pitch
37173731
else:

music21/musicxml/partStaffExporter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ def processSubsequentPartStaff(self,
389389
DIVIDER_COMMENT = '========================= Measure [NNN] =========================='
390390
PLACEHOLDER = '[NNN]'
391391

392-
def makeDivider(inner_sourceNumber: int|str) -> Element:
392+
def makeDivider(inner_sourceNumber: int|str) -> Element[t.Any]:
393393
return Comment(DIVIDER_COMMENT.replace(PLACEHOLDER, str(inner_sourceNumber)))
394394

395395
sourceMeasures = iter(source.findall('measure'))

music21/musicxml/xmlToM21.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1779,7 +1779,7 @@ def parseMeasures(self):
17791779
self.removeFinaleIncorrectEndingForwardRest()
17801780
part.coreElementsChanged()
17811781

1782-
def removeFinaleIncorrectEndingForwardRest(self):
1782+
def removeFinaleIncorrectEndingForwardRest(self) -> None:
17831783
'''
17841784
If Finale generated the file AND it ended with an incomplete
17851785
measure (like 4/4 beginning with a quarter pickup and ending
@@ -1793,18 +1793,18 @@ def removeFinaleIncorrectEndingForwardRest(self):
17931793
17941794
* New in v7.
17951795
'''
1796-
if self.lastMeasureParser is None: # pragma: no cover
1797-
return # should not happen
17981796
lmp = self.lastMeasureParser
1797+
if lmp is None: # pragma: no cover
1798+
return # should not happen
17991799
self.lastMeasureParser = None # clean memory
18001800

18011801
if lmp.lastForwardTagCreatedByFinale is None:
18021802
return
1803-
if lmp.useVoices is True:
1803+
if lmp.useVoices:
18041804
return
18051805
endingForwardRest: note.Rest|None = lmp.lastForwardTagCreatedByFinale
18061806
# important that we find that the last GeneralNote is this Forward tag
1807-
if lmp.stream[note.GeneralNote].last() is endingForwardRest:
1807+
if lmp.stream[note.GeneralNote].last() is endingForwardRest and endingForwardRest is not None:
18081808
lmp.stream.remove(endingForwardRest, recurse=True)
18091809

18101810
def separateOutPartStaves(self) -> list[stream.PartStaff]:
@@ -2379,7 +2379,7 @@ def __init__(self,
23792379
self.staffReference: StaffReferenceType = {}
23802380
self.activeTuplets: list[duration.Tuplet|None] = self.parent.activeTuplets
23812381

2382-
self.useVoices = False
2382+
self.useVoices: bool = False
23832383
self.voicesById: dict[str|int, stream.Voice] = {}
23842384
self.voiceIndices: set[str|int] = set()
23852385
self.staves = 1
@@ -2598,7 +2598,7 @@ def parse(self):
25982598
meth = getattr(self, methName)
25992599
meth(mxObj)
26002600

2601-
if self.useVoices is True:
2601+
if self.useVoices:
26022602
for v in self.stream.iter().voices:
26032603
if v: # do not bother with empty voices
26042604
# the musicDataMethods use insertCore, thus the voices need to run

music21/note.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -881,7 +881,7 @@ def pitches(self) -> tuple[Pitch, ...]:
881881
return ()
882882

883883
@pitches.setter
884-
def pitches(self, _value: Iterable[Pitch]):
884+
def pitches(self, _value: Iterable[Pitch]) -> None:
885885
pass
886886

887887

@@ -1709,7 +1709,7 @@ def pitches(self) -> tuple[Pitch, ...]:
17091709
return (self.pitch,)
17101710

17111711
@pitches.setter
1712-
def pitches(self, value: Sequence[Pitch]):
1712+
def pitches(self, value: Iterable[Pitch]) -> None:
17131713
if common.isListLike(value) and value:
17141714
self.pitch = value[0]
17151715
else:

music21/percussion.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ def pitches(self) -> tuple[pitch.Pitch, ...]:
155155
return pitches
156156

157157
@pitches.setter
158-
def pitches(self, value: t.Sequence[str|pitch.Pitch|int]):
158+
def pitches(self, value: t.Iterable[pitch.Pitch]) -> None:
159159
self._notes = []
160160
# TODO: individual ties are not being retained here
161161
for p in value:

music21/roman.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1209,9 +1209,15 @@ def romanNumeralFromChord(
12091209
rnString = aug6NoKeyObjectSubs[rnString]
12101210
nationalityStart = rnString[:2] # nb: Ger = Ge
12111211
if nationalityStart in ('It', 'Ge'):
1212-
keyObj = _getKeyFromCache(chordObj.fifth.name.lower())
1212+
fifth = chordObj.fifth
1213+
if t.TYPE_CHECKING:
1214+
assert fifth is not None
1215+
keyObj = _getKeyFromCache(fifth.name.lower())
12131216
elif nationalityStart in ('Fr', 'Sw'):
1214-
keyObj = _getKeyFromCache(chordObj.seventh.name.lower())
1217+
seventh = chordObj.seventh
1218+
if t.TYPE_CHECKING:
1219+
assert seventh is not None
1220+
keyObj = _getKeyFromCache(seventh.name.lower())
12151221

12161222
if (
12171223
preferSecondaryDominants

0 commit comments

Comments
 (0)