diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index 22acb09a64..97fe104cfd 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -35,6 +35,9 @@
+
+
+
diff --git a/documentation/nbvalNotebook.py b/documentation/nbvalNotebook.py
index 1a58800feb..4c6dfc13c0 100644
--- a/documentation/nbvalNotebook.py
+++ b/documentation/nbvalNotebook.py
@@ -9,9 +9,9 @@
import sys
import subprocess
# noinspection PyPackageRequirements
-import pytest # @UnusedImport # pylint: disable=unused-import,import-error
+import pytest # pylint: disable=unused-import,import-error
# noinspection PyPackageRequirements
-import nbval # @UnusedImport # pylint: disable=unused-import,import-error
+import nbval # pylint: disable=unused-import,import-error
from music21 import environment
from music21 import common
diff --git a/documentation/source/conf.py b/documentation/source/conf.py
index 5456fec905..884e2ed9d9 100644
--- a/documentation/source/conf.py
+++ b/documentation/source/conf.py
@@ -48,7 +48,7 @@
project = 'music21'
# pylint: disable=redefined-builtin
# noinspection PyShadowingBuiltins
-copyright = '2006-2021, Michael Scott Cuthbert and cuthbertLab' # @ReservedAssignment
+copyright = '2006-2021, Michael Scott Cuthbert and cuthbertLab'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
diff --git a/music21/alpha/analysis/fixer.py b/music21/alpha/analysis/fixer.py
index 4f10fde366..6c508b53a2 100644
--- a/music21/alpha/analysis/fixer.py
+++ b/music21/alpha/analysis/fixer.py
@@ -63,7 +63,7 @@ def fix(self):
for (midiRef, omrRef, op) in self.changes:
if self.checkIfNoteInstance(midiRef, omrRef) is False:
continue
- # if the are the same, don't bother to try changing it
+ # if they are the same, don't bother to try changing it
# 3 is the number of noChange Ops
if isinstance(op, aligner.ChangeOps) and op == aligner.ChangeOps.NoChange:
continue
@@ -246,7 +246,7 @@ def fix(self):
# if they're not notes, don't bother with rest
if self.checkIfNoteInstance(midiRef, omrRef) is False:
continue
- # if the are the same, don't bother to try changing it
+ # if they are the same, don't bother to try changing it
# 3 is the number of noChange Ops
if isinstance(op, aligner.ChangeOps) and op == aligner.ChangeOps.NoChange:
continue
@@ -394,7 +394,7 @@ def addOrnament(self,
def fix(self: _T, *, show=False, inPlace=True) -> Optional[_T]:
'''
- Corrects missed ornaments in omr stream according to mid stream
+ Corrects missed ornaments in omrStream according to midiStream
:param show: Whether to show results
:param inPlace: Whether to make changes to own omr stream or
return a new OrnamentFixer with changes
diff --git a/music21/analysis/reduction.py b/music21/analysis/reduction.py
index 309217e93e..9cc7add387 100644
--- a/music21/analysis/reduction.py
+++ b/music21/analysis/reduction.py
@@ -22,7 +22,6 @@
from music21 import exceptions21
from music21 import chord
-from music21 import clef
from music21 import common
from music21 import expressions
from music21 import instrument
@@ -952,42 +951,42 @@ def testExtractionB(self):
self.assertEqual(len(match), 3)
# post.show()
- def testExtractionC(self):
- from music21 import analysis
- from music21 import corpus
- # http://solomonsmusic.net/schenker.htm
- # shows extracting an Ursatz line
-
- # BACH pre;ide !, WTC
-
- src = corpus.parse('bwv846')
- import warnings
- with warnings.catch_warnings(): # catch deprecation warning
- warnings.simplefilter('ignore', category=exceptions21.Music21DeprecationWarning)
- chords = src.flattenParts().makeChords(minimumWindowSize=4,
- makeRests=False)
- for c in chords.flatten().notes:
- c.quarterLength = 4
- for m in chords.getElementsByClass('Measure'):
- m.clef = clef.bestClef(m, recurse=True)
-
- chords.measure(1).notes[0].addLyric('::/p:e/o:5/nf:no/ta:3/g:Ursatz')
- chords.measure(1).notes[0].addLyric('::/p:c/o:4/nf:no/tb:I')
-
- chords.measure(24).notes[0].addLyric('::/p:d/o:5/nf:no/ta:2')
- chords.measure(24).notes[0].addLyric('::/p:g/o:3/nf:no/tb:V')
-
- chords.measure(30).notes[0].addLyric('::/p:f/o:4/tb:7')
-
- chords.measure(34).notes[0].addLyric('::/p:c/o:5/nf:no/v:1/ta:1')
- chords.measure(34).notes[0].addLyric('::/p:g/o:4/nf:no/v:2')
- chords.measure(34).notes[0].addLyric('::/p:c/o:4/nf:no/v:1/tb:I')
-
- sr = analysis.reduction.ScoreReduction()
- sr.chordReduction = chords
- # sr.score = src
- unused_post = sr.reduce()
- # unused_post.show()
+ # def testExtractionC(self):
+ # from music21 import analysis
+ # from music21 import corpus
+ # # http://solomonsmusic.net/schenker.htm
+ # # shows extracting an Ursatz line
+ #
+ # # BACH pre;ide !, WTC
+ #
+ # src = corpus.parse('bwv846')
+ # import warnings
+ # with warnings.catch_warnings(): # catch deprecation warning
+ # warnings.simplefilter('ignore', category=exceptions21.Music21DeprecationWarning)
+ # chords = src.flattenParts().makeChords(minimumWindowSize=4,
+ # makeRests=False)
+ # for c in chords.flatten().notes:
+ # c.quarterLength = 4
+ # for m in chords.getElementsByClass('Measure'):
+ # m.clef = clef.bestClef(m, recurse=True)
+ #
+ # chords.measure(1).notes[0].addLyric('::/p:e/o:5/nf:no/ta:3/g:Ursatz')
+ # chords.measure(1).notes[0].addLyric('::/p:c/o:4/nf:no/tb:I')
+ #
+ # chords.measure(24).notes[0].addLyric('::/p:d/o:5/nf:no/ta:2')
+ # chords.measure(24).notes[0].addLyric('::/p:g/o:3/nf:no/tb:V')
+ #
+ # chords.measure(30).notes[0].addLyric('::/p:f/o:4/tb:7')
+ #
+ # chords.measure(34).notes[0].addLyric('::/p:c/o:5/nf:no/v:1/ta:1')
+ # chords.measure(34).notes[0].addLyric('::/p:g/o:4/nf:no/v:2')
+ # chords.measure(34).notes[0].addLyric('::/p:c/o:4/nf:no/v:1/tb:I')
+ #
+ # sr = analysis.reduction.ScoreReduction()
+ # sr.chordReduction = chords
+ # # sr.score = src
+ # unused_post = sr.reduce()
+ # # unused_post.show()
def testExtractionD(self):
diff --git a/music21/audioSearch/recording.py b/music21/audioSearch/recording.py
index 9071510900..9a8613ff70 100644
--- a/music21/audioSearch/recording.py
+++ b/music21/audioSearch/recording.py
@@ -55,7 +55,7 @@ def samplesFromRecording(seconds=10.0, storeFile=True,
Returns a list of samples.
'''
# noinspection PyPackageRequirements
- import pyaudio # @UnresolvedImport # pylint: disable=import-error
+ import pyaudio # pylint: disable=import-error
recordFormatDefault = pyaudio.paInt16
if recordFormat is None:
diff --git a/music21/bar.py b/music21/bar.py
index e2513d4c07..2d52df348d 100644
--- a/music21/bar.py
+++ b/music21/bar.py
@@ -129,7 +129,7 @@ class Barline(base.Music21Object):
classSortOrder = -5
def __init__(self,
- type=None, # @ReservedAssignment # pylint: disable=redefined-builtin
+ type=None, # pylint: disable=redefined-builtin
location=None):
super().__init__()
diff --git a/music21/base.py b/music21/base.py
index e1d1ce4d48..e5a44c0916 100644
--- a/music21/base.py
+++ b/music21/base.py
@@ -837,7 +837,6 @@ def getOffsetBySite(
site: 'music21.stream.Stream',
*,
returnSpecial=False,
- stringReturns=False,
) -> Union[float, fractions.Fraction, str]:
'''
If this class has been registered in a container such as a Stream,
@@ -913,10 +912,6 @@ def getOffsetBySite(
Changed in v7. -- stringReturns renamed to returnSpecial. Returns an OffsetSpecial Enum.
'''
- if stringReturns and not returnSpecial: # pragma: no cover
- returnSpecial = stringReturns
- environLocal.warn('stringReturns is deprecated: use returnSpecial instead')
-
if site is None:
return self._naiveOffset
@@ -4019,48 +4014,8 @@ def __getattr__(self, name: str) -> Any:
raise AttributeError(f'Could not get attribute {name!r} in an object-less element')
return object.__getattribute__(storedObj, name)
- def isTwin(self, other: 'ElementWrapper') -> bool:
- '''
- DEPRECATED: Just run::
-
- (wrapper1.obj == wrapper2.obj)
-
- A weaker form of equality. a.isTwin(b) is true if
- a and b store either the same object OR objects that are equal.
- In other words, it is essentially the same object in a different context
-
- >>> import copy
- >>> import music21
-
- >>> aE = music21.ElementWrapper(obj='hello')
-
- >>> bE = copy.copy(aE)
- >>> aE is bE
- False
- >>> aE == bE
- True
- >>> aE.isTwin(bE)
- True
-
- >>> bE.offset = 14.0
- >>> bE.priority = -4
- >>> aE == bE
- False
- >>> aE.isTwin(bE)
- True
- '''
- if not hasattr(other, 'obj'):
- return False
-
- if self.obj is other.obj or self.obj == other.obj:
- return True
- else:
- return False
-
# -----------------------------------------------------------------------------
-
-
class TestMock(Music21Object):
pass
diff --git a/music21/braille/lookup.py b/music21/braille/lookup.py
index 784efbf00d..1f3210a75b 100644
--- a/music21/braille/lookup.py
+++ b/music21/braille/lookup.py
@@ -24,7 +24,7 @@
New International Manual of Braille Music Notation (by Bettye Krolick), which we will cite as
"Krolick" or "krolick".
'''
-
+from typing import Dict, Union
import itertools
_DOC_IGNORE_MODULE_OR_PACKAGE = True
@@ -280,48 +280,49 @@ def makePitchNameToNotes():
'decresc.': _B[345] + _B[145] + _B[15] + _B[14] + _B[1235] + _B[3],
'decr.': _B[345] + _B[145] + _B[15] + _B[14] + _B[1235] + _B[3]}
-alphabet = {'a': _B[1],
- 'b': _B[12],
- 'c': _B[14],
- 'd': _B[145],
- 'e': _B[15],
- 'f': _B[124],
- 'g': _B[1245],
- 'h': _B[125],
- 'i': _B[24],
- 'j': _B[245],
- 'k': _B[13],
- 'l': _B[123],
- 'm': _B[134],
- 'n': _B[1345],
- 'o': _B[135],
- 'p': _B[1234],
- 'q': _B[12345],
- 'r': _B[1235],
- 's': _B[234],
- 't': _B[2345],
- 'u': _B[136],
- 'v': _B[1236],
- 'w': _B[2456],
- 'x': _B[1346],
- 'y': _B[13456],
- 'z': _B[1356],
- ' ': _B[0],
- '!': _B[235],
- "'": _B[3],
- ',': _B[2],
- '-': _B[356],
- '.': _B[256],
- '/': _B[34],
- ':': _B[25],
- '?': _B[236],
- '(': _B[2356],
- ')': _B[2356],
- '^': _B[4], # substitute for accent mark
- '[': _B[6] + _B[2356],
- ']': _B[2356] + _B[3],
- '*': _B[35] + _B[35],
- }
+alphabet: Dict[Union[str, int], str] = {
+ 'a': _B[1],
+ 'b': _B[12],
+ 'c': _B[14],
+ 'd': _B[145],
+ 'e': _B[15],
+ 'f': _B[124],
+ 'g': _B[1245],
+ 'h': _B[125],
+ 'i': _B[24],
+ 'j': _B[245],
+ 'k': _B[13],
+ 'l': _B[123],
+ 'm': _B[134],
+ 'n': _B[1345],
+ 'o': _B[135],
+ 'p': _B[1234],
+ 'q': _B[12345],
+ 'r': _B[1235],
+ 's': _B[234],
+ 't': _B[2345],
+ 'u': _B[136],
+ 'v': _B[1236],
+ 'w': _B[2456],
+ 'x': _B[1346],
+ 'y': _B[13456],
+ 'z': _B[1356],
+ ' ': _B[0],
+ '!': _B[235],
+ "'": _B[3],
+ ',': _B[2],
+ '-': _B[356],
+ '.': _B[256],
+ '/': _B[34],
+ ':': _B[25],
+ '?': _B[236],
+ '(': _B[2356],
+ ')': _B[2356],
+ '^': _B[4], # substitute for accent mark
+ '[': _B[6] + _B[2356],
+ ']': _B[2356] + _B[3],
+ '*': _B[35] + _B[35],
+}
alphabet.update(numbersUpper)
chordSymbols = {
diff --git a/music21/braille/noteGrouping.py b/music21/braille/noteGrouping.py
index f4bdb37ccb..d8a4d498e1 100644
--- a/music21/braille/noteGrouping.py
+++ b/music21/braille/noteGrouping.py
@@ -271,7 +271,7 @@ def transcribeNoteGrouping(brailleElementGrouping, showLeadingOctave=True):
'''
transcribe a group of notes, possibly excluding certain attributes.
- To be DEPRECATED -- called only be BrailleGrandSegment now.
+ To be DEPRECATED -- called only by BrailleGrandSegment now.
'''
ngt = NoteGroupingTranscriber()
ngt.showLeadingOctave = showLeadingOctave
diff --git a/music21/chord/__init__.py b/music21/chord/__init__.py
index b5dabfbbc5..c929ee69d3 100644
--- a/music21/chord/__init__.py
+++ b/music21/chord/__init__.py
@@ -4469,7 +4469,8 @@ def transpose(self, value, *, inPlace=False):
# PUBLIC PROPERTIES #
- @property
+ # see https://github.com/python/mypy/issues/1362
+ @property # type: ignore
@cacheMethod
def chordTablesAddress(self):
'''
@@ -4503,7 +4504,7 @@ def chordTablesAddress(self):
return tables.ChordTableAddress(0, 0, 0, 0)
- @property
+ @property # type: ignore
@cacheMethod
def commonName(self):
'''
@@ -4797,7 +4798,7 @@ def duration(self, durationObj):
# need to permit Duration object assignment here
raise ChordException(f'this must be a Duration object, not {durationObj}')
- @property
+ @property # type: ignore
@cacheMethod
def fifth(self) -> Optional[pitch.Pitch]:
'''
@@ -5110,7 +5111,7 @@ def notes(self, newNotes):
self._notes.clear()
self.add(newNotes, runSort=False)
- @property
+ @property # type: ignore
@cacheMethod
def normalOrder(self):
'''
@@ -5504,7 +5505,7 @@ def primeFormString(self) -> str:
return Chord.formatVectorString(self.primeForm)
- @property
+ @property # type: ignore
@cacheMethod
def quality(self):
'''
@@ -5690,7 +5691,7 @@ def scaleDegrees(self):
degrees.append(tupleKey)
return degrees
- @property
+ @property # type: ignore
@cacheMethod
def seventh(self):
'''
@@ -5717,7 +5718,7 @@ def seventh(self):
except ChordException:
return None
- @property
+ @property # type: ignore
@cacheMethod
def third(self) -> Optional[pitch.Pitch]:
'''
diff --git a/music21/chord/tables.py b/music21/chord/tables.py
index 3d6f50e799..d4047d609f 100644
--- a/music21/chord/tables.py
+++ b/music21/chord/tables.py
@@ -53,6 +53,7 @@ class ChordTablesException(exceptions21.Music21Exception):
# is symmetrical under inversion.
t1 = ((0,), (0,0,0,0,0,0), (1,1,1,1,11,11,11,11), 0) # 1-1
monad = (None, t1)
+del t1
t1 = ((0,1), (1,0,0,0,0,0), (1,1,0,0,9,9,8,8), 0) # 2-1
t2 = ((0,2), (0,1,0,0,0,0), (1,1,1,1,9,9,9,9), 0) # 2-2
@@ -61,6 +62,7 @@ class ChordTablesException(exceptions21.Music21Exception):
t5 = ((0,5), (0,0,0,0,1,0), (1,1,0,0,9,9,8,8), 0) # 2-5
t6 = ((0,6), (0,0,0,0,0,1), (2,2,2,2,10,10,10), 0) # 2-6
diad = (None, t1, t2, t3, t4, t5, t6)
+del t1, t2, t3, t4, t5, t6
t1 = ((0,1,2), (2,1,0,0,0,0), (1,1,0,0,7,7,4,4), 0) # 3-1
t2 = ((0,1,3), (1,1,1,0,0,0), (1,0,0,0,5,6,5,5), 0) # 3-2
@@ -75,6 +77,7 @@ class ChordTablesException(exceptions21.Music21Exception):
t11 = ((0,3,7), (0,0,1,1,1,0), (1,0,0,0,5,6,5,5), 0) # 3-11
t12 = ((0,4,8), (0,0,0,3,0,0), (3,3,3,3,9,9,9,9), 0) # 3-12
trichord = (None, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12)
+del t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12
t1 = ((0,1,2,3), (3,2,1,0,0,0), (1,1,0,0,5,5,1,1), 0) # 4-1
t2 = ((0,1,2,4), (2,2,1,1,0,0), (1,0,0,0,3,4,1,1), 0) # 4-2
@@ -110,7 +113,9 @@ class ChordTablesException(exceptions21.Music21Exception):
t10, t11, t12, t13, t14, t15, t16, t17, t18, t19,
t20, t21, t22, t23, t24, t25, t26, t27, t28, t29
)
-
+del t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12
+del t13, t14, t15, t16, t17, t18, t19, t20, t21, t22
+del t23, t24, t25, t26, t27, t28, t29
t1 = ((0,1,2,3,4), (4,3,2,1,0,0), (1,1,0,0,3,3,0,0), 0) # 5-1
t2 = ((0,1,2,3,5), (3,3,2,1,1,0), (1,0,0,0,1,2,1,1), 0) # 5-2
@@ -156,6 +161,10 @@ class ChordTablesException(exceptions21.Music21Exception):
t20, t21, t22, t23, t24, t25, t26, t27, t28, t29,
t30, t31, t32, t33, t34, t35, t36, t37, t38
)
+del t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12
+del t13, t14, t15, t16, t17, t18, t19, t20, t21, t22
+del t23, t24, t25, t26, t27, t28, t29
+del t30, t31, t32, t33, t34, t35, t36, t37, t38
t1 = ((0,1,2,3,4,5), (5,4,3,2,1,0), (1,1,0,0,1,1,0,0), 0) # 6-1 A
t2 = ((0,1,2,3,4,6), (4,4,3,2,1,1), (1,0,0,0,0,1,0,0), 0) # 6-2
@@ -215,6 +224,10 @@ class ChordTablesException(exceptions21.Music21Exception):
t40, t41, t42, t43, t44, t45, t46, t47, t48, t49,
t50,
)
+del t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12
+del t13, t14, t15, t16, t17, t18, t19, t20, t21, t22
+del t23, t24, t25, t26, t27, t28, t29
+del t30, t31, t32, t33, t34, t35, t36, t37, t38
del t39, t40, t41, t42, t43, t44, t45, t46, t47, t48, t49, t50
t1 = ((0,1,2,3,4,5, 6), (6,5,4,3,2,1), (1,1,0,0,0,0,0,0), 0) # 7-1
@@ -261,6 +274,9 @@ class ChordTablesException(exceptions21.Music21Exception):
t20, t21, t22, t23, t24, t25, t26, t27, t28, t29,
t30, t31, t32, t33, t34, t35, t36, t37, t38
)
+del t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12
+del t13, t14, t15, t16, t17, t18, t19, t20, t21, t22
+del t23, t24, t25, t26, t27, t28, t29
del t30, t31, t32, t33, t34, t35, t36, t37, t38
t1 = ((0,1,2,3,4,5,6, 7), (7,6,5,4,4,2), (1,1,0,0,0,0,0,0), 0 ) # 8-1
@@ -297,6 +313,7 @@ class ChordTablesException(exceptions21.Music21Exception):
t10, t11, t12, t13, t14, t15, t16, t17, t18, t19,
t20, t21, t22, t23, t24, t25, t26, t27, t28, t29
)
+del t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12
del t13, t14, t15, t16, t17, t18, t19
del t20, t21, t22, t23, t24, t25, t26, t27, t28, t29
@@ -313,7 +330,7 @@ class ChordTablesException(exceptions21.Music21Exception):
t11 = ((0,1,2,3,5,6,7,9,10), (6,6,7,7,7,3), (1,0,0,0,0,0,0,0), 0) # 9-11
t12 = ((0,1,2,4,5,6,8,9,10), (6,6,6,9,6,3), (3,3,3,3,0,0,0,0), 0) # 9-12
nonachord = (None, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12)
-del t7, t8, t9, t10, t11, t12
+del t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12
t1 = ((0,1,2,3,4,5,6,7,8, 9), (9,8,8,8,8,4), (1,1,0,0,0,0,0,0), 0) # 10-1
t2 = ((0,1,2,3,4,5,6,7,8,10), (8,9,8,8,8,4), (1,1,1,1,0,0,0,0), 0) # 10-2
@@ -322,10 +339,11 @@ class ChordTablesException(exceptions21.Music21Exception):
t5 = ((0,1,2,3,4,5,7,8,9,10), (8,8,8,8,9,4), (1,1,0,0,0,0,0,0), 0) # 10-5
t6 = ((0,1,2,3,4,6,7,8,9,10), (8,8,8,8,8,5), (2,2,2,2,0,0,0,0), 0) # 10-6
decachord = (None, t1, t2, t3, t4, t5, t6)
-del t2, t3, t4, t5, t6
+del t1, t2, t3, t4, t5, t6
t1 = ((0,1,2,3,4,5,6,7,8,9,10), (10,10,10,10,10,5), (1,1,1,1,0,0,0,0), 0) # 11-1
undecachord = (None, t1)
+del t1
t1 = ((0,1,2,3,4,5,6,7,8,9,10,11), (12,12,12,12,12,6), (12,12,12,12,0,0,0,0), 0) # 12-1
dodecachord = (None, t1)
diff --git a/music21/clef.py b/music21/clef.py
index fe03fa3e2f..86348819ab 100644
--- a/music21/clef.py
+++ b/music21/clef.py
@@ -820,7 +820,7 @@ def clefFromString(clefString, octaveShift=0) -> Clef:
else:
lineNum = False
elif len(xnStr) > 2:
- from music21 import clef as myself # @UnresolvedImport
+ from music21 import clef as myself
xnLower = xnStr.lower()
for x in dir(myself):
if 'Clef' not in x:
diff --git a/music21/common/classTools.py b/music21/common/classTools.py
index 9e62bc8284..bfb75db50a 100644
--- a/music21/common/classTools.py
+++ b/music21/common/classTools.py
@@ -137,7 +137,7 @@ def classToClassStr(classObj: Type) -> str:
def getClassSet(instance, classNameTuple=None):
'''
- Return the classSet for an instance (whether a Music21Object or something else.
+ Return the classSet for an instance (whether a Music21Object or something else).
See base.Music21Object.classSet for more details.
>>> p = pitch.Pitch()
diff --git a/music21/common/fileTools.py b/music21/common/fileTools.py
index 26687eeb26..c9f9d66554 100644
--- a/music21/common/fileTools.py
+++ b/music21/common/fileTools.py
@@ -78,7 +78,7 @@ def readFileEncodingSafe(filePath, firstGuess='utf-8') -> str:
Slow, but will read a file of unknown encoding as safely as possible using
the chardet package.
- Let's try to load this file as ascii -- it has a copyright symbol at the top
+ Let's try to load this file as ascii -- it has a copyright symbol at the top,
so it won't load in Python3:
>>> import os
@@ -96,7 +96,7 @@ def readFileEncodingSafe(filePath, firstGuess='utf-8') -> str:
>>> data[0:30]
'# -*- coding: utf-8 -*-\n# ----'
- Well, that's nothing, since the first guess here is utf-8 and it's right. So let's
+ Well, that's nothing, since the first guess here is utf-8, and it's right. So let's
give a worse first guess:
>>> data = common.readFileEncodingSafe(c, firstGuess='SHIFT_JIS') # old Japanese standard
diff --git a/music21/common/formats.py b/music21/common/formats.py
index a486dfa949..70c2786204 100644
--- a/music21/common/formats.py
+++ b/music21/common/formats.py
@@ -307,7 +307,7 @@ def findFormatExtURL(url):
>>> urlA = 'http://somesite.com/?l=cc/schubert/piano/d0576&file=d0576-06.krn&f=xml'
>>> urlB = 'http://somesite.com/cgi-bin/ksdata?l=cc/schubert/d0576&file=d0576-06.krn&f=kern'
>>> urlC = 'http://somesite.com/cgi-bin/ksdata?l=cc/bach/cello&file=bwv1007-01.krn&f=xml'
- >>> urlF = 'http://junk'
+ >>> urlF = 'https://junk'
>>> urlM = 'http://somesite.com/files/mid001.mid'
>>> common.findFormatExtURL(urlA)
diff --git a/music21/common/numberTools.py b/music21/common/numberTools.py
index fbdc6b04ad..727be23a0d 100644
--- a/music21/common/numberTools.py
+++ b/music21/common/numberTools.py
@@ -6,10 +6,12 @@
# Authors: Michael Scott Cuthbert
# Christopher Ariza
#
-# Copyright: Copyright © 2009-2015 Michael Scott Cuthbert and the music21 Project
+# Copyright: Copyright © 2009-2022 Michael Scott Cuthbert and the music21 Project
# License: BSD, see license.txt
# ------------------------------------------------------------------------------
+from functools import lru_cache
import math
+import numbers
import random
import unittest
from typing import List, no_type_check, Tuple, Union, Sequence, Iterable
@@ -29,7 +31,6 @@
'opFrac', 'mixedNumeral',
'roundToHalfInteger',
- 'almostEquals',
'addFloatPrecision', 'strTrimFloat',
'nearestMultiple',
@@ -146,15 +147,15 @@ def numToIntOrFloat(value: Union[int, float]) -> Union[int, float]:
DENOM_LIMIT = defaults.limitOffsetDenominator
-
-def _preFracLimitDenominator(n, d):
+@lru_cache(1024)
+def _preFracLimitDenominator(n: int, d: int) -> Tuple[int, int]:
# noinspection PyShadowingNames
'''
Used in opFrac
Copied from fractions.limit_denominator. Their method
- requires creating three new Fraction instances to get one back. this doesn't create any
- call before Fraction...
+ requires creating three new Fraction instances to get one back.
+ This doesn't create any call before Fraction...
DENOM_LIMIT is hardcoded to defaults.limitOffsetDenominator for speed...
@@ -270,7 +271,8 @@ def opFrac(num: Union[OffsetQLIn, None]) -> Union[OffsetQL, None]:
'''
# This is a performance critical operation, tuned to go as fast as possible.
# hence redundancy -- first we check for type (no inheritance) and then we
- # repeat exact same test with inheritance. Note that the later examples are more verbose
+ # repeat exact same test with inheritance.
+ # Note that the later examples are more verbose
t = type(num)
if t is float:
# quick test of power of whether denominator is a power
@@ -318,7 +320,8 @@ def opFrac(num: Union[OffsetQLIn, None]) -> Union[OffsetQL, None]:
raise TypeError(f'Cannot convert num: {num}')
-def mixedNumeral(expr, limitDenominator=defaults.limitOffsetDenominator):
+def mixedNumeral(expr: numbers.Real,
+ limitDenominator=defaults.limitOffsetDenominator):
'''
Returns a string representing a mixedNumeral form of a number
@@ -367,8 +370,7 @@ def mixedNumeral(expr, limitDenominator=defaults.limitOffsetDenominator):
quotient = 0.0
remainderFrac = remainderFrac - 1
else:
- # noinspection PyTypeChecker
- quotient = int(expr) # int seems completely supported for Fractions
+ quotient = int(float(expr))
remainderFrac = expr - quotient
if quotient < 0:
remainderFrac *= -1
@@ -431,27 +433,6 @@ def roundToHalfInteger(num: Union[float, int]) -> Union[float, int]:
floatVal = 1
return intVal + floatVal
-@deprecated('v.7', 'v.8', 'just call math.isclose(x, y, abs_tol=1e-7)')
-def almostEquals(x, y=0.0, grain=1e-7) -> bool:
- # noinspection PyShadowingNames
- '''
- almostEquals(x, y) -- returns True if x and y are
- within grain (default 0.0000001) of each other
-
- Allows comparisons between floats that are normally inconsistent.
-
- DEPRECATED in v.7 -- just call `isclose` with `abs_tol`:
-
- >>> from math import isclose
- >>> isclose(1.000000001, 1, abs_tol=1e-7)
- True
- >>> isclose(1.001, 1, abs_tol=1e-7)
- False
- >>> isclose(1.001, 1, abs_tol=0.1)
- True
- '''
- return isclose(x, y, abs_tol=grain)
-
def addFloatPrecision(x, grain=1e-2) -> Union[float, Fraction]:
'''
@@ -525,7 +506,8 @@ def nearestMultiple(n: float, unit: float) -> Tuple[float, float, float]:
>>> print(common.nearestMultiple(0.20, 0.25))
(0.25, 0.05..., -0.05...)
- Note that this one also has an error of 0.1 but it's a positive error off of 0.5
+ Note that this one also has an error of 0.1, but it's a positive error off of 0.5
+
>>> print(common.nearestMultiple(0.4, 0.25))
(0.5, 0.1..., -0.1...)
@@ -556,12 +538,12 @@ def nearestMultiple(n: float, unit: float) -> Tuple[float, float, float]:
>>> common.nearestMultiple(-0.5, 0.125)
Traceback (most recent call last):
- ValueError: n (-0.5) is less than zero. Thus cannot find nearest
+ ValueError: n (-0.5) is less than zero. Thus cannot find the nearest
multiple for a value less than the unit, 0.125
'''
if n < 0:
raise ValueError(f'n ({n}) is less than zero. '
- + 'Thus cannot find nearest multiple for a value '
+ + 'Thus cannot find the nearest multiple for a value '
+ f'less than the unit, {unit}')
mult = math.floor(n / unit) # can start with the floor
@@ -582,6 +564,9 @@ def nearestMultiple(n: float, unit: float) -> Tuple[float, float, float]:
return matchHigh, round(matchHigh - n, 7), round(n - matchHigh, 7)
+_DOT_LOOKUP = (1.0, 1.5, 1.75, 1.875, 1.9375,
+ 1.96875, 1.984375, 1.9921875, 1.99609375)
+
def dotMultiplier(dots: int) -> float:
'''
dotMultiplier(dots) returns how long to multiply the note
@@ -601,6 +586,9 @@ def dotMultiplier(dots: int) -> float:
>>> common.dotMultiplier(0)
1.0
'''
+ if dots < 9:
+ return _DOT_LOOKUP[dots]
+
return ((2 ** (dots + 1.0)) - 1.0) / (2 ** dots)
@@ -629,10 +617,7 @@ def decimalToTuplet(decNum: float) -> Tuple[int, int]:
>>> common.decimalToTuplet(-.02)
Traceback (most recent call last):
ZeroDivisionError: number must be greater than zero
-
- TODO: replace with fractions...
'''
-
def findSimpleFraction(inner_working):
'Utility function.'
for index in range(1, 1000):
@@ -854,7 +839,7 @@ def lcm(filterList: Iterable[int]) -> int:
'''
def _lcm(a, b):
- '''find lowest common multiple of a, b'''
+ '''find the least common multiple of a, b'''
# // forces integer style division (no remainder)
return abs(a * b) // euclidGCD(a, b)
diff --git a/music21/common/objects.py b/music21/common/objects.py
index 772436eda7..1da1f26c98 100644
--- a/music21/common/objects.py
+++ b/music21/common/objects.py
@@ -15,7 +15,6 @@
'RelativeCounter',
'SlottedObjectMixin',
'EqualSlottedObjectMixin',
- 'Iterator',
'Timer',
]
@@ -23,7 +22,6 @@
import time
from typing import Tuple
import weakref
-from music21.common.decorators import deprecated
class RelativeCounter(collections.Counter):
@@ -248,7 +246,7 @@ class EqualSlottedObjectMixin(SlottedObjectMixin):
Slots are the only things compared, so do not mix with a __dict__ based object.
- Ignores differences in .id
+ The equal comparison ignores differences in .id
'''
def __eq__(self, other):
if type(self) is not type(other):
@@ -268,34 +266,6 @@ def __ne__(self, other):
# ------------------------------------------------------------------------------
-class Iterator(collections.abc.Iterator): # pragma: no cover
- '''
- A simple Iterator object used to handle iteration of Streams and other
- list-like objects.
-
- Deprecated in v7 -- not needed since Python 2.6 became music21 minimum!
- '''
- # TODO: remove in v.8
- def __init__(self, data):
- self.data = data
- self.index = 0
-
- @deprecated('2021 Jan v7', '2022 Jan',
- 'common.Iterator is deprecated. use `iter(X)` instead.')
- def __iter__(self):
- self.index = 0
- return self
-
- def __next__(self):
- if self.index >= len(self.data):
- raise StopIteration
- post = self.data[self.index]
- self.index += 1
- return post
-
-# ------------------------------------------------------------------------------
-
-
class Timer:
'''
An object for timing. Call it to get the current time since starting.
diff --git a/music21/common/parallel.py b/music21/common/parallel.py
index 6a3503b429..c91c4171e2 100644
--- a/music21/common/parallel.py
+++ b/music21/common/parallel.py
@@ -55,7 +55,7 @@ def runParallel(iterable, parallelFunction, *,
... c = corpus.parse(fn) # this is the slow call that is good to parallelize
... return len(c.recurse().notes)
>>> #_DOCS_SHOW outputs = common.runParallel(files, countNotes)
- >>> outputs = common.runNonParallel(files, countNotes) #_DOCS_HIDE cant pickle doctest funcs.
+ >>> outputs = common.runNonParallel(files, countNotes) #_DOCS_HIDE cannot pickle doctest funcs.
>>> outputs
[165, 50, 131]
diff --git a/music21/common/pathTools.py b/music21/common/pathTools.py
index 7ccbb944e9..4b6a223b81 100644
--- a/music21/common/pathTools.py
+++ b/music21/common/pathTools.py
@@ -117,7 +117,7 @@ def getCorpusContentDirs() -> List[str]:
def getRootFilePath() -> pathlib.Path:
'''
- Return the root directory for music21 -- outside of the music21 namespace
+ Return the root directory for music21 -- outside the music21 namespace
which has directories such as "dist", "documentation", "music21"
>>> fp = common.getRootFilePath()
@@ -147,9 +147,9 @@ def relativepath(path: str, start: Optional[str] = None) -> str:
def cleanpath(path: Union[str, pathlib.Path], *, returnPathlib=None) -> Union[str, pathlib.Path]:
'''
Normalizes the path by expanding ~user on Unix, ${var} environmental vars
- (is this a good idea?), expanding %name% on Windows, normalizing path names (Windows
- turns backslashes to forward slashes, and finally if that file is not an absolute path,
- turns it from a relative path to an absolute path.
+ (is this a good idea?), expanding %name% on Windows, normalizing path names
+ (Windows turns backslashes to forward slashes), and finally if that file
+ is not an absolute path, turns it from a relative path to an absolute path.
v5 -- returnPathlib -- None (default) does not convert. False, returns a string,
True, returns a pathlib.Path.
diff --git a/music21/common/stringTools.py b/music21/common/stringTools.py
index e0e0b106e9..5815de2b0a 100644
--- a/music21/common/stringTools.py
+++ b/music21/common/stringTools.py
@@ -81,7 +81,7 @@ def getNumFromStr(usrStr: str, numbers: str = '0123456789') -> Tuple[str, str]:
found.append(char)
else:
remain.append(char)
- # returns numbers, and then characters
+ # returns numbers and then characters
return ''.join(found), ''.join(remain)
diff --git a/music21/common/types.py b/music21/common/types.py
index 8ffcb566fe..dd75a597c5 100644
--- a/music21/common/types.py
+++ b/music21/common/types.py
@@ -9,10 +9,16 @@
# License: BSD, see license.txt
# ------------------------------------------------------------------------------
from fractions import Fraction
-from typing import Union
+from typing import Union, TypeVar, TYPE_CHECKING
from music21.common.enums import OffsetSpecial
+if TYPE_CHECKING:
+ import music21
+
OffsetQL = Union[float, Fraction]
OffsetQLSpecial = Union[float, Fraction, OffsetSpecial]
OffsetQLIn = Union[int, float, Fraction]
+
+StreamType = TypeVar('StreamType', bound='music21.stream.Stream')
+M21ObjType = TypeVar('M21ObjType', bound='music21.base.Music21Object')
diff --git a/music21/converter/__init__.py b/music21/converter/__init__.py
index 7633e9c4a5..d8905c96bd 100644
--- a/music21/converter/__init__.py
+++ b/music21/converter/__init__.py
@@ -22,7 +22,7 @@
is supported, a :class:`~music21.stream.Score` will be returned.
-This is the most general public interface for all formats. Programmers
+This is the most general, public interface for all formats. Programmers
adding their own formats to the system should provide an interface here to
their own parsers (such as humdrum, musicxml, etc.)
@@ -95,7 +95,7 @@ class ConverterFileException(exceptions21.Music21Exception):
class ArchiveManager:
r'''
Before opening a file path, this class can check if this is an
- archived file collection, such as a .zip or or .mxl file. This will return the
+ archived file collection, such as a .zip or .mxl file. This will return the
data from the archive.
>>> fnCorpus = corpus.getWork('bwv66.6', fileExtensions=('.xml',))
@@ -413,7 +413,7 @@ def unregisterSubconverter(removeSubconverter):
>>> mxlConverter in c.subconvertersList()
False
- if there is no such subConverter registered and it is not a default subconverter,
+ If there is no such subConverter registered, and it is not a default subconverter,
then a converter.ConverterException is raised:
>>> class ConverterSonix(converter.subConverters.SubConverter):
@@ -480,7 +480,7 @@ def _getDownloadFp(self, directory, ext, url):
# pylint: disable=redefined-builtin
# noinspection PyShadowingBuiltins
def parseFileNoPickle(self, fp, number=None,
- format=None, forceSource=False, **keywords): # @ReservedAssignment
+ format=None, forceSource=False, **keywords):
'''
Given a file path, parse and store a music21 Stream.
@@ -584,7 +584,7 @@ def parseFile(self, fp, number=None,
self.stream.fileFormat = useFormat
def parseData(self, dataStr, number=None,
- format=None, forceSource=False, **keywords): # @ReservedAssignment
+ format=None, forceSource=False, **keywords):
'''
Given raw data, determine format and parse into a music21 Stream.
'''
@@ -635,29 +635,36 @@ def parseData(self, dataStr, number=None,
self.subConverter.keywords = keywords
self.subConverter.parseData(dataStr, number=number)
- def parseURL(self, url, *, format=None, number=None,
- forceSource=False, **keywords): # @ReservedAssignment
- '''Given a url, download and parse the file
+ def parseURL(self,
+ url,
+ *,
+ format=None,
+ number=None,
+ forceSource=False,
+ **keywords):
+ '''
+ Given a url, download and parse the file
into a music21 Stream stored in the `stream`
property of the converter object.
Note that this checks the user Environment
`autoDownload` setting before downloading.
- Use `forceSource=True` to download every time rather than read from a cached file.
+ Use `forceSource=True` to download every time rather than
+ re-reading from a cached file.
- >>> jeanieLightBrownURL = ('https://github.com/cuthbertLab/music21/raw/master' +
- ... '/music21/corpus/leadSheet/fosterBrownHair.mxl')
+ >>> joplinURL = ('https://github.com/cuthbertLab/music21/raw/master' +
+ ... '/music21/corpus/joplin/maple_leaf_rag.mxl')
>>> c = converter.Converter()
- >>> #_DOCS_SHOW c.parseURL(jeanieLightBrownURL)
- >>> #_DOCS_SHOW jeanieStream = c.stream
+ >>> #_DOCS_SHOW c.parseURL(joplinURL)
+ >>> #_DOCS_SHOW joplinStream = c.stream
Changed in v.7 -- made keyword-only and added `forceSource` option.
'''
autoDownload = environLocal['autoDownload']
if autoDownload in ('deny', 'ask'):
- message = 'Automatic downloading of URLs is presently set to {!r};'
- message += ' configure your Environment "autoDownload" setting to '
+ message = 'Automatic downloading of URLs is presently set to {!r}; '
+ message += 'configure your Environment "autoDownload" setting to '
message += '"allow" to permit automatic downloading: '
message += "environment.set('autoDownload', 'allow')"
message = message.format(autoDownload)
@@ -700,6 +707,7 @@ def parseURL(self, url, *, format=None, number=None,
self.stream.filePath = fp # These are attributes defined outside of
self.stream.fileNumber = number # __init__ and will be moved to
self.stream.fileFormat = useFormat # Metadata in v8.
+ # TODO: v8 -- move to Metadata
# -----------------------------------------------------------------------#
# Subconverters
@@ -1028,7 +1036,7 @@ def stream(self):
# pylint: disable=redefined-builtin
# noinspection PyShadowingBuiltins
-def parseFile(fp, number=None, format=None, forceSource=False, **keywords): # @ReservedAssignment
+def parseFile(fp, number=None, format=None, forceSource=False, **keywords):
'''
Given a file path, attempt to parse the file into a Stream.
'''
@@ -1039,7 +1047,7 @@ def parseFile(fp, number=None, format=None, forceSource=False, **keywords): # @
# pylint: disable=redefined-builtin
# noinspection PyShadowingBuiltins
-def parseData(dataStr, number=None, format=None, **keywords): # @ReservedAssignment
+def parseData(dataStr, number=None, format=None, **keywords):
'''
Given musical data represented within a Python string, attempt to parse the
data into a Stream.
@@ -1051,7 +1059,7 @@ def parseData(dataStr, number=None, format=None, **keywords): # @ReservedAssign
# pylint: disable=redefined-builtin
# noinspection PyShadowingBuiltins
def parseURL(url, *, format=None, number=None,
- forceSource=False, **keywords): # @ReservedAssignment
+ forceSource=False, **keywords):
'''
Given a URL, attempt to download and parse the file into a Stream. Note:
URL downloading will not happen automatically unless the user has set their
@@ -1097,7 +1105,7 @@ def parse(value: Union[bundles.MetadataEntry, bytes, str, pathlib.Path],
URL:
- >>> #_DOCS_SHOW s = converter.parse('http://midirepository.org/file220/file.mid')
+ >>> #_DOCS_SHOW s = converter.parse('https://midirepository.org/file220/file.mid')
Data is preceded by an identifier such as "tinynotation:"
@@ -1178,7 +1186,7 @@ def parse(value: Union[bundles.MetadataEntry, bytes, str, pathlib.Path],
forceSource=forceSource, **keywords)
elif isinstance(value, pathlib.Path):
raise FileNotFoundError(f'Cannot find file in {str(value)}')
- elif (isinstance(value, str) and common.findFormatFile(value) is not None):
+ elif isinstance(value, str) and common.findFormatFile(value) is not None:
# assume mistyped file path
raise FileNotFoundError(f'Cannot find file in {str(value)}')
else:
@@ -1967,7 +1975,7 @@ def testParseURL(self):
# This file should have been written, above
destFp = Converter()._getDownloadFp(e.getRootTempDir(), '.krn', url)
- # Hack garbage into it so that we can test whether or not forceSource works
+ # Hack garbage into it so that we can test whether forceSource works
with open(destFp, 'a', encoding='utf-8') as fp:
fp.write('all sorts of garbage that Humdrum cannot parse')
diff --git a/music21/converter/qmConverter.py b/music21/converter/qmConverter.py
index 92bb1350ad..db74532220 100644
--- a/music21/converter/qmConverter.py
+++ b/music21/converter/qmConverter.py
@@ -41,8 +41,8 @@ def parseData(self, strData, number=None):
>>> from music21.converter.qmConverter import QMConverter
>>> qmc = QMConverter()
>>> qmc.parseData('C D E G C')
- >>> s = qmc.stream
- >>> s.show('text')
+ >>> q_stream = qmc.stream
+ >>> q_stream.show('text')
{0.0}
{0.0}
{0.0}
diff --git a/music21/converter/subConverters.py b/music21/converter/subConverters.py
index 3b1746e2a8..5f82cd7903 100644
--- a/music21/converter/subConverters.py
+++ b/music21/converter/subConverters.py
@@ -16,7 +16,8 @@
parseData method that sets self.stream.
'''
# ------------------------------------------------------------------------------
-# Converters are associated classes; they are not subclasses, but most define a parseData() method,
+# Converters are associated classes; they are not subclasses,
+# but most define a parseData() method,
# a parseFile() method, and a .stream attribute or property.
import base64
import io
@@ -142,7 +143,7 @@ def launch(self, filePath, fmt=None,
'''
Opens the appropriate viewer for the file generated by .write()
- app is the path to an application to launch. Specify it and/or a launchKey
+ app is the path to an application to launch. Specify it and/or a launchKey.
launchKey is the specific key in .music21rc (such as graphicsPath), etc.
to search for the application. If it's not specified then there might be
a default one for the converter in self.launchKey. If it can't find it
@@ -359,7 +360,7 @@ def show(self, obj, fmt, app=None, subformats=None, **keywords): # pragma: no c
helperSubConverter = helperConverter.subConverter
# noinspection PyPackageRequirements
- from IPython.display import Image, display, HTML # @UnresolvedImport
+ from IPython.display import Image, display, HTML
if helperFormat in ('musicxml', 'xml', 'lilypond', 'lily'):
# hack to make musescore excerpts -- fix with a converter class in MusicXML
@@ -895,7 +896,7 @@ def parseFile(self, fp: Union[str, pathlib.Path], number=None):
archData = arch.getData()
c.xmlText = archData
c.parseXMLText()
- else: # its a file path or a raw musicxml string
+ else: # it is a file path or a raw musicxml string
c.readFile(fp)
# movement titles can be stored in more than one place in musicxml
@@ -1193,7 +1194,7 @@ def parseFile(self, fp, number=None):
otherwise, a :class:`~music21.stream.Score` is returned.
If `number` is provided, and this ABC file defines multiple works
- with a X: tag, just the specified work will be returned.
+ with an X: tag, just the specified work will be returned.
'''
# environLocal.printDebug(['ConverterABC.parseFile: got number', number])
from music21 import abcFormat
@@ -1205,7 +1206,7 @@ def parseFile(self, fp, number=None):
af.close()
# only create opus if multiple ref numbers
- # are defined; if a number is given an opus will no be created
+ # are defined; if a number is given an opus will not be created
if abcHandler.definesReferenceNumbers():
# this creates a Score or Opus object, depending on if a number
# is given
@@ -1484,7 +1485,7 @@ def testImportMei3(self):
'''
When the file uses UTF-16 encoding rather than UTF-8 (which happens if
it was exported from
- the "sibmei" plug-in for Sibelius.
+ the "sibmei" plug-in for Sibelius).
'''
from unittest import mock # pylint: disable=no-name-in-module
with mock.patch('music21.mei.MeiToM21Converter') as mockConv:
@@ -1581,7 +1582,7 @@ def testWriteMusicXMLMakeNotation(self):
s = s.splitAtDurations(recurse=True)[0]
out2 = s.write(makeNotation=False)
round_trip_back = converter.parse(out2)
- # 4/4 will not be assumed; quarter note will still be split out from 5.0QL
+ # 4/4 will not be assumed; quarter note will still be split out from 5.0QL,
# but it will remain in measure 1
# and there will be no rests in measure 2
self.assertEqual(
diff --git a/music21/corpus/__init__.py b/music21/corpus/__init__.py
index f850ea5254..5c874e5cec 100644
--- a/music21/corpus/__init__.py
+++ b/music21/corpus/__init__.py
@@ -289,7 +289,7 @@ def parse(workName,
number=None,
fileExtensions=None,
forceSource=False,
- format=None # @ReservedAssignment
+ format=None
):
'''
The most important method call for corpus.
@@ -332,7 +332,7 @@ def parse(workName,
number=number,
fileExtensions=fileExtensions,
forceSource=forceSource,
- format=format # @ReservedAssignment
+ format=format
)
diff --git a/music21/corpus/chorales.py b/music21/corpus/chorales.py
index becd06b4fd..ea0fc41a77 100644
--- a/music21/corpus/chorales.py
+++ b/music21/corpus/chorales.py
@@ -939,12 +939,15 @@ def prepareList(self):
class Iterator:
# noinspection SpellCheckingInspection
'''
- This is a class for iterating over many Bach Chorales. It is designed to make it easier to use
- one of music21's most accessible datasets. It will parse each chorale in the selected
+ This is a class for iterating over many Bach Chorales. It is designed
+ to make it easier to use
+ one of music21's most accessible datasets. It will parse each chorale
+ in the selected
range in a lazy fashion so that a list of chorales need not be parsed up front. To select a
range of chorales, first select a .numberingSystem
('riemenschneider', 'bwv', 'kalmus', 'budapest',
- 'baerenreiter', or 'title'). Then, set .currentNumber to the lowest number in the range and
+ 'baerenreiter', or 'title'). Then, set .currentNumber to the lowest
+ number in the range and
.highestNumber to the highest in the range. This can either be done by catalogue number
(iterationType = 'number') or by index (iterationType = 'index').
diff --git a/music21/corpus/manager.py b/music21/corpus/manager.py
index 778ea33267..fb6131fc77 100644
--- a/music21/corpus/manager.py
+++ b/music21/corpus/manager.py
@@ -169,7 +169,7 @@ def parse(workName,
number=None,
fileExtensions=None,
forceSource=False,
- format=None # @ReservedAssignment
+ format=None
):
filePath = getWork(workName=workName,
movementNumber=movementNumber,
diff --git a/music21/duration.py b/music21/duration.py
index d7fd04a1b6..b6a5d45600 100644
--- a/music21/duration.py
+++ b/music21/duration.py
@@ -46,15 +46,17 @@
2
'''
+from collections import namedtuple
import contextlib
import copy
import fractions
+from functools import lru_cache
import io
-import unittest
from math import inf, isnan
-from typing import Union, Tuple, Dict, List, Optional, Iterable, Literal
+from typing import (Union, Tuple, Dict, List, Optional,
+ Iterable, Literal, Type, Callable)
+import unittest
-from collections import namedtuple
from music21 import prebase
@@ -478,8 +480,8 @@ def quarterLengthToTuplet(
# try multiples of the tuplet division, from 1 to max - 1
for m in range(1, i):
for numberOfDots in POSSIBLE_DOTS_IN_TUPLETS:
- tupletMultiplier = fractions.Fraction(common.dotMultiplier(numberOfDots))
- qLenCandidate = qLenBase * m * tupletMultiplier
+ tupletMultiplier = common.dotMultiplier(numberOfDots)
+ qLenCandidate = opFrac(qLenBase * m * tupletMultiplier)
if qLenCandidate == qLen:
tupletDuration = durationTupleFromTypeDots(typeKey, numberOfDots)
newTuplet = Tuplet(numberNotesActual=i,
@@ -488,8 +490,8 @@ def quarterLengthToTuplet(
durationNormal=tupletDuration,)
post.append(newTuplet)
break
- # not looking for these matches will add tuple alternative
- # representations; this could be useful
+ # not looking for these matches will add tuple alternative
+ # representations; this could be useful
if len(post) >= maxToReturn:
break
if len(post) >= maxToReturn:
@@ -813,48 +815,37 @@ def convertTypeToNumber(dType: str) -> float:
# -----------------------------------------------------------------------------------
-DurationTuple = namedtuple('DurationTuple', 'type dots quarterLength')
-
-
-def _augmentOrDiminishTuple(self, amountToScale):
- return durationTupleFromQuarterLength(self.quarterLength * amountToScale)
-
-
-DurationTuple.augmentOrDiminish = _augmentOrDiminishTuple # type: ignore[attr-defined]
+class DurationTuple(namedtuple('DurationTuple', 'type dots quarterLength')):
+ def augmentOrDiminish(self, amountToScale):
+ return durationTupleFromQuarterLength(self.quarterLength * amountToScale)
+ @property
+ def ordinal(self):
+ '''
+ Converts type to an ordinal number where maxima = 1 and 1024th = 14;
+ whole = 4 and quarter = 6. Based on duration.ordinalTypeFromNum
-del _augmentOrDiminishTuple
-
-
-def _durationTupleOrdinal(self):
- '''
- Converts type to an ordinal number where maxima = 1 and 1024th = 14;
- whole = 4 and quarter = 6. Based on duration.ordinalTypeFromNum
-
- >>> a = duration.DurationTuple('whole', 0, 4.0)
- >>> a.ordinal
- 4
-
- >>> b = duration.DurationTuple('maxima', 0, 32.0)
- >>> b.ordinal
- 1
-
- >>> c = duration.DurationTuple('1024th', 0, 1/256)
- >>> c.ordinal
- 14
- '''
- ordinalFound = None
- for i in range(len(ordinalTypeFromNum)):
- if self.type == ordinalTypeFromNum[i]:
- ordinalFound = i
- break
- if ordinalFound is None:
- raise DurationException(
- f'Could not determine durationNumber from {ordinalFound}')
- return ordinalFound
+ >>> a = duration.DurationTuple('whole', 0, 4.0)
+ >>> a.ordinal
+ 4
+ >>> b = duration.DurationTuple('maxima', 0, 32.0)
+ >>> b.ordinal
+ 1
-DurationTuple.ordinal = property(_durationTupleOrdinal) # type: ignore[attr-defined]
+ >>> c = duration.DurationTuple('1024th', 0, 1/256)
+ >>> c.ordinal
+ 14
+ '''
+ ordinalFound = None
+ for i in range(len(ordinalTypeFromNum)):
+ if self.type == ordinalTypeFromNum[i]:
+ ordinalFound = i
+ break
+ if ordinalFound is None:
+ raise DurationException(
+ f'Could not determine durationNumber from {ordinalFound}')
+ return ordinalFound
_durationTupleCacheTypeDots: Dict[Tuple[str, int], DurationTuple] = {}
@@ -891,6 +882,7 @@ def durationTupleFromQuarterLength(ql=1.0) -> DurationTuple:
return DurationTuple('inexpressible', 0, ql)
+@lru_cache(1024)
def durationTupleFromTypeDots(durType='quarter', dots=0):
'''
Returns a DurationTuple (which knows its quarterLength) for
@@ -2183,19 +2175,6 @@ def consolidate(self):
# some notations will not properly unlink, and raise an error
self.components = [dur]
- @common.deprecated('v7', 'v8', 'Was intended for testing only')
- def fill(self, quarterLengthList=('quarter', 'half', 'quarter')): # pragma: no cover
- '''
- Utility method for testing; a quick way to fill components. This will
- remove any existing values.
-
- Deprecated in v7.
- '''
- self.components = []
- for x in quarterLengthList:
- self.addDurationTuple(Duration(x))
- self.informClient()
-
def getGraceDuration(
self,
appoggiatura=False
@@ -2245,7 +2224,7 @@ def getGraceDuration(
if c_type == 'zero':
c_type = 'eighth'
newComponents.append(DurationTuple(c.type, c.dots, 0.0))
- gd.components = newComponents # set new components
+ gd.components = tuple(newComponents) # set new components
gd.linked = False
gd.type = new_type
gd.quarterLength = 0.0
@@ -2448,7 +2427,7 @@ def _updateQuarterLength(self):
# PUBLIC PROPERTIES #
@property
- def components(self):
+ def components(self) -> Tuple[DurationTuple, ...]:
'''
Returns or sets a tuple of the component DurationTuples of this
Duration object
@@ -2988,7 +2967,7 @@ def type(self, value: str):
if self.linked is True:
nt = durationTupleFromTypeDots(value, self.dots)
- self.components = [nt]
+ self.components = (nt,)
self._quarterLengthNeedsUpdating = True
self.expressionIsInferred = False
self.informClient()
@@ -3094,7 +3073,7 @@ def makeTime(self):
return self._makeTime
@makeTime.setter
- def makeTime(self, expr):
+ def makeTime(self, expr: Literal[True, False, None]):
if expr not in (True, False, None):
raise ValueError('expr must be True, False, or None')
self._makeTime = bool(expr)
@@ -3844,7 +3823,9 @@ def testExceptions(self):
# -------------------------------------------------------------------------------
# define presented order in documentation
-_DOC_ORDER = [Duration, Tuplet, convertQuarterLengthToType, TupletFixer]
+_DOC_ORDER: List[Union[Type, Callable]] = [
+ Duration, Tuplet, GraceDuration, convertQuarterLengthToType, TupletFixer,
+]
if __name__ == '__main__':
diff --git a/music21/features/base.py b/music21/features/base.py
index c8f5397c7e..9b9f604d82 100644
--- a/music21/features/base.py
+++ b/music21/features/base.py
@@ -538,7 +538,8 @@ class DataInstance:
multiple commonly-used stream representations once, providing rapid processing.
'''
# pylint: disable=redefined-builtin
- def __init__(self, streamOrPath=None, id=None): # @ReservedAssignment
+ # noinspection PyShadowingBuiltins
+ def __init__(self, streamOrPath=None, id=None):
if isinstance(streamOrPath, stream.Stream):
self.stream = streamOrPath
self.streamPath = None
@@ -896,7 +897,8 @@ def addMultipleData(self, dataList, classValues, ids=None):
self.addData(d, cv, thisId)
# pylint: disable=redefined-builtin
- def addData(self, dataOrStreamOrPath, classValue=None, id=None): # @ReservedAssignment
+ # noinspection PyShadowingBuiltins
+ def addData(self, dataOrStreamOrPath, classValue=None, id=None):
'''
Add a Stream, DataInstance, MetadataEntry, or path (Posix or str)
to a corpus or local file to this data set.
@@ -1069,7 +1071,7 @@ def getString(self, outputFmt='tab'):
return outputFormat.getString()
# pylint: disable=redefined-builtin
- def write(self, fp=None, format=None, includeClassLabel=True): # @ReservedAssignment
+ def write(self, fp=None, format=None, includeClassLabel=True):
'''
Set the output format object.
'''
@@ -1644,7 +1646,7 @@ def x_testRegionClassificationJSymbolicB(self): # pragma: no cover
# def xtestOrangeBayesA(self): # pragma: no cover
# '''Using an already created test file with a BayesLearner.
# '''
-# import orange # @UnresolvedImport # pylint: disable=import-error
+# import orange # pylint: disable=import-error
# data = orange.ExampleTable(
# '~/music21Ext/mlDataSets/bachMonteverdi-a/bachMonteverdi-a.tab')
# classifier = orange.BayesLearner(data)
@@ -1656,7 +1658,7 @@ def x_testRegionClassificationJSymbolicB(self): # pragma: no cover
# def xtestClassifiersA(self): # pragma: no cover
# '''Using an already created test file with a BayesLearner.
# '''
-# import orange, orngTree # @UnresolvedImport # pylint: disable=import-error
+# import orange, orngTree # pylint: disable=import-error
# data1 = orange.ExampleTable(
# '~/music21Ext/mlDataSets/chinaMitteleuropa-b/chinaMitteleuropa-b1.tab')
#
@@ -1696,7 +1698,7 @@ def x_testRegionClassificationJSymbolicB(self): # pragma: no cover
# def xtestClassifiersB(self): # pragma: no cover
# '''Using an already created test file with a BayesLearner.
# '''
-# import orange, orngTree # @UnresolvedImport # pylint: disable=import-error
+# import orange, orngTree # pylint: disable=import-error
# data1 = orange.ExampleTable(
# '~/music21Ext/mlDataSets/chinaMitteleuropa-b/chinaMitteleuropa-b1.tab')
#
@@ -1742,7 +1744,7 @@ def x_testRegionClassificationJSymbolicB(self): # pragma: no cover
# This test shows how to compare four classifiers; replace the file path
# with a path to the .tab data file.
# '''
-# import orange, orngTree # @UnresolvedImport # pylint: disable=import-error
+# import orange, orngTree # pylint: disable=import-error
# data = orange.ExampleTable(
# '~/music21Ext/mlDataSets/bachMonteverdi-a/bachMonteverdi-a.tab')
#
@@ -1774,7 +1776,7 @@ def x_testRegionClassificationJSymbolicB(self): # pragma: no cover
#
#
# def xtestOrangeClassifierTreeLearner(self): # pragma: no cover
-# import orange, orngTree # @UnresolvedImport # pylint: disable=import-error
+# import orange, orngTree # pylint: disable=import-error
# data = orange.ExampleTable(
# '~/music21Ext/mlDataSets/bachMonteverdi-a/bachMonteverdi-a.tab')
#
diff --git a/music21/graph/findPlot.py b/music21/graph/findPlot.py
index 9288b37265..82052f6d91 100644
--- a/music21/graph/findPlot.py
+++ b/music21/graph/findPlot.py
@@ -15,7 +15,7 @@
import collections
import types
import unittest
-
+from typing import List, Tuple
from music21.graph import axis
from music21.graph import plot
@@ -32,14 +32,15 @@
# all formats need to be here, and first for each row must match a graphType.
-FORMAT_SYNONYMS = [('horizontalbar', 'bar', 'horizontal', 'pianoroll', 'piano'),
- ('histogram', 'histo', 'count'),
- ('scatter', 'point'),
- ('scatterweighted', 'weightedscatter', 'weighted'),
- ('3dbars', '3d'),
- ('colorgrid', 'grid', 'window', 'windowed'),
- ('horizontalbarweighted', 'barweighted', 'weightedbar')
- ] # type: List[Tuple[str]]
+FORMAT_SYNONYMS: List[Tuple[str, ...]] = [
+ ('horizontalbar', 'bar', 'horizontal', 'pianoroll', 'piano'),
+ ('histogram', 'histo', 'count'),
+ ('scatter', 'point'),
+ ('scatterweighted', 'weightedscatter', 'weighted'),
+ ('3dbars', '3d'),
+ ('colorgrid', 'grid', 'window', 'windowed'),
+ ('horizontalbarweighted', 'barweighted', 'weightedbar')
+]
# define co format strings
FORMATS = [syn[0] for syn in FORMAT_SYNONYMS]
diff --git a/music21/lily/lilyObjects.py b/music21/lily/lilyObjects.py
index 6a2695dbc7..dbbef51483 100644
--- a/music21/lily/lilyObjects.py
+++ b/music21/lily/lilyObjects.py
@@ -1284,7 +1284,7 @@ class LyPrefixCompositeMusic(LyObject):
| re_rhythmed_music
'''
# pylint: disable=redefined-builtin
- def __init__(self, type=None, genericPrefixMusicScm=None, # @ReservedAssignment
+ def __init__(self, type=None, genericPrefixMusicScm=None,
simpleString=None, optionalId=None, optionalContextMod=None,
music=None, fraction=None, repeatedMusic=None,
pitchAlsoInChords1=None, pitchAlsoInChords2=None,
diff --git a/music21/lily/translate.py b/music21/lily/translate.py
index 02d68275a0..cc2c75e489 100644
--- a/music21/lily/translate.py
+++ b/music21/lily/translate.py
@@ -851,7 +851,7 @@ def lyPrefixCompositeMusicFromStream(
self,
streamIn,
contextType=None,
- type=None, # @ReservedAssignment
+ type=None,
beforeMatter=None
):
# noinspection PyShadowingNames
@@ -2421,7 +2421,7 @@ def writeLyFile(self, ext='', fp=None):
return self.tempName
# noinspection PyShadowingBuiltins
- def runThroughLily(self, format=None, # @ReservedAssignment
+ def runThroughLily(self, format=None,
backend=None, fileName=None, skipWriting=False):
r'''
creates a .ly file from self.topLevelObject via .writeLyFile
diff --git a/music21/metadata/caching.py b/music21/metadata/caching.py
index 269bc7ce09..e32114596e 100644
--- a/music21/metadata/caching.py
+++ b/music21/metadata/caching.py
@@ -388,7 +388,7 @@ def process_serial(jobs):
# -----------------------------------------------------------------------------
-class WorkerProcess(multiprocessing.Process): # @UndefinedVariable pylint: disable=inherit-non-class
+class WorkerProcess(multiprocessing.Process): # pylint: disable=inherit-non-class
'''
A worker process for use by the multi-threaded metadata-caching job
processor.
diff --git a/music21/meter/base.py b/music21/meter/base.py
index 3b4eff8d32..0b29e1fd22 100644
--- a/music21/meter/base.py
+++ b/music21/meter/base.py
@@ -561,18 +561,6 @@ def load(self, value: str, divisions=None):
except MeterException:
environLocal.printDebug(['cannot set default accents for:', self])
- @common.deprecated('v7', 'v8', 'call .ratioString or .load()')
- def loadRatio(self, numerator, denominator, divisions=None): # pragma: no cover
- '''
- Change the numerator and denominator, like ratioString, but with
- optional divisions and without resetting other parameters.
-
- DEPRECATED in v7. -- call .ratioString or .load with
- value = f'{numerator}/{denominator}'
- '''
- value = f'{numerator}/{denominator}'
- self.load(value, divisions)
-
@property
def ratioString(self):
'''
diff --git a/music21/meter/tools.py b/music21/meter/tools.py
index 2ea79b17e7..49135e3042 100644
--- a/music21/meter/tools.py
+++ b/music21/meter/tools.py
@@ -6,7 +6,7 @@
# Authors: Christopher Ariza
# Michael Scott Cuthbert
#
-# Copyright: Copyright © 2009-2012, 2015, 2021 Michael Scott Cuthbert
+# Copyright: Copyright © 2009-2022 Michael Scott Cuthbert
# and the music21 Project
# License: BSD, see license.txt
# -----------------------------------------------------------------------------
@@ -219,7 +219,7 @@ def fractionToSlashMixed(fList: NumDenomTuple) -> Tuple[Tuple[str, int], ...]:
def fractionSum(numDenomTuple: NumDenomTuple) -> NumDenom:
'''
Given a tuple of tuples of numerator and denominator,
- find the sum; does NOT reduce to lowest terms.
+ find the sum; does NOT reduce to its lowest terms.
>>> from music21.meter.tools import fractionSum
>>> fractionSum(((3, 8), (5, 8), (1, 8)))
@@ -234,8 +234,8 @@ def fractionSum(numDenomTuple: NumDenomTuple) -> NumDenom:
(0, 1)
This method might seem like an easy place to optimize and simplify
- by just doing a fractions.Fraction() sum (I tried!), but not reducing to lowest
- terms is a feature of this method. 3/8 + 3/8 = 6/8, not 3/4:
+ by just doing a fractions.Fraction() sum (I tried!), but not reducing to
+ its lowest terms is a feature of this method. 3/8 + 3/8 = 6/8, not 3/4:
>>> fractionSum(((3, 8), (3, 8)))
(6, 8)
@@ -610,7 +610,7 @@ def divisionOptionsAlgo(n, d) -> MeterOptions:
# add src representation
opts.append((f'{n}/{d}',))
# additive multiples with the same denominators
- # add to opts in-place
+ # add to "opts" in-place
opts.extend(divisionOptionsAdditiveMultiples(n, d))
# additive multiples with smaller denominators
# only doing this for numerators of 1 for now
diff --git a/music21/midi/__init__.py b/music21/midi/__init__.py
index a3ace91c06..046964a0c6 100644
--- a/music21/midi/__init__.py
+++ b/music21/midi/__init__.py
@@ -463,10 +463,9 @@ class MidiEvent(prebase.ProtoM21Object):
'''
# pylint: disable=redefined-builtin
-
def __init__(self,
track: Optional['music21.midi.MidiTrack'] = None,
- type=None, # @ReservedAssignment
+ type=None,
time: int = 0,
channel: Optional[int] = None):
self.track: Optional['music21.midi.MidiTrack'] = track # a MidiTrack object
diff --git a/music21/midi/translate.py b/music21/midi/translate.py
index 8eff8658dd..3041a42fc6 100644
--- a/music21/midi/translate.py
+++ b/music21/midi/translate.py
@@ -16,7 +16,7 @@
import unittest
import math
import copy
-from typing import Optional, List, Tuple, Dict, Union, Any
+from typing import Optional, List, Tuple, Dict, Union, Any, TypeVar, Type
import warnings
from music21 import chord
@@ -40,6 +40,8 @@
PERCUSSION_MAPPER = PercussionMapper()
+NotRestType = TypeVar('NotRestType', bound=note.NotRest)
+
# ------------------------------------------------------------------------------
class TranslateException(exceptions21.Music21Exception):
pass
@@ -208,7 +210,7 @@ def getStartEvents(mt=None, channel=1, instrumentObj=None):
# additional allocation of instruments may happen elsewhere
# this may lead to two program changes happening at time zero
- # however, this assures that the program change happens before the
+ # however, this assures that the program change happens before
# the clearing of the pitch bend data
if instrumentObj is not None and instrumentObj.midiProgram is not None:
sub = instrumentToMidiEvents(instrumentObj, includeDeltaTime=True,
@@ -273,15 +275,15 @@ def music21ObjectToMidiFile(
# ------------------------------------------------------------------------------
# Notes
-def _constructOrUpdateNotRest(
+def _constructOrUpdateNotRestSubclass(
eOn: 'music21.midi.MidiEvent',
tOn: int,
tOff: int,
ticksPerQuarter: int,
*,
- inputM21: Optional[note.NotRest] = None,
- preferredClass: type = note.Note,
-) -> note.NotRest:
+ inputM21: Optional[NotRestType] = None,
+ preferredClass: Type[NotRestType] = note.Note,
+) -> NotRestType:
'''
Construct (or edit the duration of) a NotRest subclass, usually
a note.Note (or a chord.Chord if provided to `preferredClass`).
@@ -321,8 +323,8 @@ def _constructOrUpdateNotRest(
def midiEventsToNote(
eventList,
ticksPerQuarter=None,
- inputM21: Optional[note.NotRest] = None,
-) -> Optional[note.NotRest]:
+ inputM21: Optional[NotRestType] = None,
+) -> Optional[NotRestType]:
# noinspection PyShadowingNames
'''
Convert from a list of midi.DeltaTime and midi.MidiEvent objects to a music21 Note.
@@ -425,8 +427,14 @@ def midiEventsToNote(
else:
raise TranslateException(f'cannot handle MIDI event list in the form: {eventList!r}')
- nr = _constructOrUpdateNotRest(
- eOn, tOn, tOff, ticksPerQuarter, inputM21=inputM21, preferredClass=note.Note)
+ nr = _constructOrUpdateNotRestSubclass(
+ eOn,
+ tOn,
+ tOff,
+ ticksPerQuarter,
+ inputM21=inputM21,
+ preferredClass=note.Note
+ )
if isinstance(nr, note.Note):
nr.pitch.midi = eOn.pitch
@@ -622,7 +630,7 @@ def midiEventsToChord(
firstOn: Optional['music21.midi.MidiEvent'] = None
any_channel_10 = False
# this is a format provided by the Stream conversion of
- # midi events; it pre groups events for a chord together in nested pairs
+ # midi events; it pre-groups events for a chord together in nested pairs
# of abs start time and the event object
if isinstance(eventList, list) and eventList and isinstance(eventList[0], tuple):
# pairs of pairs
@@ -668,8 +676,14 @@ def midiEventsToChord(
preferredClass = percussion.PercussionChord
else:
preferredClass = chord.Chord
- c: chord.ChordBase = _constructOrUpdateNotRest(
- firstOn, tOn, tOff, ticksPerQuarter, inputM21=inputM21, preferredClass=preferredClass)
+ c: chord.ChordBase = _constructOrUpdateNotRestSubclass(
+ firstOn,
+ tOn,
+ tOff,
+ ticksPerQuarter,
+ inputM21=inputM21,
+ preferredClass=preferredClass
+ )
if isinstance(c, percussion.PercussionChord):
# Construct note.Unpitched objects
@@ -848,12 +862,12 @@ def midiEventsToInstrument(eventList):
The percussion map will be used if the channel is 10:
>>> me.channel = 10
- >>> i = midi.translate.midiEventsToInstrument(me)
- >>> i
+ >>> instrumentObj = midi.translate.midiEventsToInstrument(me)
+ >>> instrumentObj
- >>> i.midiChannel # 0-indexed in music21
+ >>> instrumentObj.midiChannel # 0-indexed in music21
9
- >>> i.midiProgram # 0-indexed in music21
+ >>> instrumentObj.midiProgram # 0-indexed in music21
53
'''
from music21 import midi as midiModule
@@ -1135,7 +1149,7 @@ def midiEventsToTempo(eventList):
return mm
-def tempoToMidiEvents(tempoIndication, includeDeltaTime=True):
+def tempoToMidiEvents(tempoIndication: 'music21.tempo.TempoIndication', includeDeltaTime=True):
# noinspection PyShadowingNames
r'''
Given any TempoIndication, convert it to list of :class:`~music21.midi.MidiEvent`
@@ -1182,7 +1196,7 @@ def tempoToMidiEvents(tempoIndication, includeDeltaTime=True):
True
'''
from music21 import midi as midiModule
- if tempoIndication.number is None:
+ if not hasattr(tempoIndication, 'number') or tempoIndication.number is None:
return
mt = None # use a midi track set to None
eventList = []
@@ -1258,7 +1272,7 @@ def getPacketFromMidiEvent(
# allocate channel later
# post['channel'] = None
if midiEvent.type != midiModule.ChannelVoiceMessages.NOTE_OFF and obj is not None:
- # store duration so as to calculate when the
+ # store duration to calculate when the
# channel/pitch bend can be freed
post['duration'] = durationToMidiTicks(obj.duration)
# note offs will have the same object ref, and seem like the have a
@@ -1337,7 +1351,7 @@ def streamToPackets(
Then, packets to events is called.
'''
from music21 import midi as midiModule
- # store all events by offset by offset without delta times
+ # store all events by offset without delta times
# as (absTime, event)
packetsByOffset = []
lastInstrument = None
@@ -1380,7 +1394,7 @@ def streamToPackets(
lastInstrument=lastInstrument,
)
elementPackets.append(p)
- # if its a note_off, use the duration to shift offset
+ # if it is a note_off, use the duration to shift offset
# midi events have already been created;
else:
p = getPacketFromMidiEvent(
@@ -1506,7 +1520,7 @@ def assignPacketsToChannels(
# only add if unique
if usedChannel not in channelExclude:
channelExclude.append(usedChannel)
- # or if this event has shift, then we can exclude
+ # or if this event has a shift, then we can exclude
# the channel already used without a shift
elif centShift:
if usedChannel not in channelExclude:
@@ -1595,7 +1609,7 @@ def assignPacketsToChannels(
# environLocal.printDebug(['foundChannels', foundChannels])
# environLocal.printDebug(['usedTracks', usedTracks])
- # post processing of entire packet collection
+ # post-processing of entire packet collection
# for all used channels, create a zero pitch bend at time zero
# for ch in foundChannels:
# for each track, places a pitch bend in its initChannel
@@ -1976,7 +1990,7 @@ def midiTrackToStream(
# collect notes with similar start times into chords
# create a composite list of both notes and chords
# composite = []
- chordSub = None
+ chordSub: Optional[List['music21.midi.MidiEvent']] = None
i = 0
iGathered = [] # store a list of indexes of gathered values put into chords
voicesRequired = False
@@ -2034,7 +2048,7 @@ def midiTrackToStream(
# if we can add more elements to this chord group
else: # no more matches; assuming chordSub tones are contiguous
break
- # this comparison must be outside of j loop, as the case where we
+ # this comparison must be outside the j loop, as the case where we
# have the last note in a list of notes and the j loop does not
# execute; chordSub will be None
if chordSub is not None:
@@ -2871,7 +2885,7 @@ def midiFileToStream(
if 'quantizePost' in keywords:
quantizePost = keywords.pop('quantizePost')
- # create a stream for each tracks
+ # create a stream for each track
# may need to check if tracks actually have event data
midiTracksToStreams(mf.tracks,
ticksPerQuarter=mf.ticksPerQuarterNote,
diff --git a/music21/musicxml/m21ToXml.py b/music21/musicxml/m21ToXml.py
index 17135045c9..e52ff6a90e 100644
--- a/music21/musicxml/m21ToXml.py
+++ b/music21/musicxml/m21ToXml.py
@@ -2389,11 +2389,10 @@ def getSupports(self):
'''
- # pylint: disable=redefined-builtin
- def getSupport(attribute, type, value, element): # @ReservedAssignment
+ def getSupport(attribute, supports_type, value, element):
su = Element('supports')
su.set('attribute', attribute)
- su.set('type', type)
+ su.set('type', supports_type)
su.set('value', value)
su.set('element', element)
return su
diff --git a/music21/musicxml/partStaffExporter.py b/music21/musicxml/partStaffExporter.py
index d85ffb81c8..25b5a6c60c 100644
--- a/music21/musicxml/partStaffExporter.py
+++ b/music21/musicxml/partStaffExporter.py
@@ -19,6 +19,7 @@
from xml.etree.ElementTree import Element, SubElement, Comment
from music21.common.misc import flattenList
+from music21.common.types import M21ObjType
from music21.key import KeySignature
from music21.layout import StaffGroup
from music21.meter import TimeSignature
@@ -516,13 +517,14 @@ def setEarliestAttributesAndClefsPartStaff(self, group: StaffGroup):
'''
- def isMultiAttribute(m21Class, comparison: str = '__eq__') -> bool:
+ def isMultiAttribute(m21Class: M21ObjType,
+ comparison: str = '__eq__') -> bool:
'''
Return True if any first instance of m21Class in any subsequent staff
in this StaffGroup does not compare to the first instance of that class
in the earliest staff where found (not necessarily the first) using `comparison`.
'''
- initialM21Instance: Optional[m21Class] = None
+ initialM21Instance: Optional[M21ObjType] = None
# noinspection PyShadowingNames
for ps in group: # ps okay to reuse.
if initialM21Instance is None:
@@ -548,7 +550,7 @@ def isMultiAttribute(m21Class, comparison: str = '__eq__') -> bool:
# Initial PartStaff in group: find earliest mxAttributes, set clef #1 and
if initialPartStaffRoot is None:
initialPartStaffRoot = self.getRootForPartStaff(ps)
- mxAttributes: Element = initialPartStaffRoot.find('measure/attributes')
+ mxAttributes = initialPartStaffRoot.find('measure/attributes')
clef1: Optional[Element] = mxAttributes.find('clef')
if clef1 is not None:
clef1.set('number', '1')
diff --git a/music21/musicxml/xmlToM21.py b/music21/musicxml/xmlToM21.py
index 5a182cdbb0..6d6d2578b5 100644
--- a/music21/musicxml/xmlToM21.py
+++ b/music21/musicxml/xmlToM21.py
@@ -4260,7 +4260,7 @@ def xmlToTuplets(self, mxNote):
remainingTupletAmountToAccountFor = t.tupletMultiplier()
timeModTup = t
- returnTuplets = [None] * 8 # type: List[Optional['music21.duration.Tuplet']]
+ returnTuplets: List[Optional[duration.Tuplet]] = [None] * 8
removeFromActiveTuplets = set()
# a set of tuplets to set to stop...
diff --git a/music21/pitch.py b/music21/pitch.py
index 90f698cd98..3a22eed81a 100644
--- a/music21/pitch.py
+++ b/music21/pitch.py
@@ -13,7 +13,7 @@
Classes for representing and manipulating pitches, pitch-space, and accidentals.
Each :class:`~music21.note.Note` object has a `Pitch` object embedded in it.
-Some of the methods below, such as `Pitch.name`, `Pitch.step`, etc. are
+Some methods below, such as `Pitch.name`, `Pitch.step`, etc. are
made available directly in the `Note` object, so they will seem familiar.
'''
import copy
@@ -21,7 +21,7 @@
import itertools
import unittest
from collections import OrderedDict
-from typing import List, Optional, Union, TypeVar, Tuple, Dict
+from typing import List, Optional, Union, TypeVar, Tuple, Dict, Literal
from music21 import base
from music21 import common
@@ -39,6 +39,8 @@
_MOD = 'pitch'
environLocal = environment.Environment(_MOD)
+PitchClassString = Literal['a', 'A', 't', 'T', 'b', 'B', 'e', 'E']
+
STEPREF = {
'C': 0,
'D': 2,
@@ -123,7 +125,9 @@ def _sortModifiers():
# ------------------------------------------------------------------------------
# utility functions
-def _convertPitchClassToNumber(ps) -> int:
+def _convertPitchClassToNumber(
+ ps: Union[int, PitchClassString]
+) -> int:
'''
Given a pitch class string
return the pitch class representation.
@@ -140,10 +144,10 @@ def _convertPitchClassToNumber(ps) -> int:
'''
if common.isNum(ps):
return ps
- else: # assume is is a string
- if ps in ('a', 'A'):
+ else: # assume it is a string
+ if ps in ('a', 'A', 't', 'T'):
return 10
- if ps in ('b', 'B'):
+ if ps in ('b', 'B', 'e', 'E'):
return 11
# maybe it is a string of an integer?
return int(ps)
@@ -259,7 +263,7 @@ def _convertPsToStep(ps) -> Tuple[str, 'Accidental', 'Microtone', int]:
# if close enough to a quarter tone
if round(micro, 1) == 0.5:
- # if can round to 0.5, than this is a quarter-tone accidental
+ # if we can round to 0.5, then this is a quarter-tone accidental
alter = 0.5
# need to find microtonal alteration around this value
# of alter is 0.5 and micro is 0.7 than micro should be 0.2
@@ -293,7 +297,7 @@ def _convertPsToStep(ps) -> Tuple[str, 'Accidental', 'Microtone', int]:
# the above octave, which may not be represented in ps value
if pc == 11:
octShift = 1
- # its a natural; nothing to do
+ # it is a natural; nothing to do
elif pc in NATURAL_PCS: # 0, 2, 4, 5, 7, 9, 11
acc = Accidental(0 + alter) # alter is usually 0 unless half-sharp.
pcName = pc
@@ -658,8 +662,8 @@ def __init__(self,
self._centShift = centsOrString # specify harmonic in cents
else:
self._parseString(centsOrString)
- # need to additional store a reference to a position in a
- # another pitches overtone series?
+ # need to additional store a reference to a position in
+ # another pitch's overtone series?
# such as: A4(+69c [7thH/C3])?
# SPECIAL METHODS #
@@ -722,7 +726,7 @@ def __str__(self):
>>> m1
'''
- # cent values may be of any resolution, but round to nearest int
+ # cent values may be of any resolution, but round to the nearest int
sub = ''
roundShift = round(self._centShift)
if self._centShift >= 0:
@@ -838,7 +842,6 @@ class Accidental(prebase.ProtoM21Object, style.StyleMixin):
'displayStyle',
)
- # define order to present names in documentation; use strings
_DOC_ORDER = ['name', 'modifier', 'alter', 'set']
# documentation for all attributes (not properties or methods)
@@ -1026,8 +1029,8 @@ def listNames(cls):
def set(self, name, *, allowNonStandardValue=False):
'''
- Change the type of the Accidental. Strings values, numbers, and Lilypond
- Abbreviations are all accepted. All other values will change
+ Change the type of the Accidental. Strings, numbers, and Lilypond (German-like)
+ abbreviations are all accepted. All other values will change
after setting.
>>> a = pitch.Accidental()
@@ -1599,7 +1602,7 @@ class Pitch(prebase.ProtoM21Object):
>>> p3.fullName
'C-sharp in octave 7 (-30c)'
- The full list of supported key words are: `name`, `accidental` (which
+ The full list of supported keywords are: `name`, `accidental` (which
can be a string or an :class:`~music21.pitch.Accidental` object), `octave`,
microtone (which can be a number or a :class:`~music21.pitch.Microtone` object),
`pitchClass` (0-11), `fundamental` (another `Pitch` object representing the
@@ -1657,7 +1660,7 @@ class Pitch(prebase.ProtoM21Object):
A consequence of comparing enharmonics for equality but .ps for comparisons
is that a `Pitch` can be neither less than
- or greater than another `Pitch` without being equal:
+ nor greater than another `Pitch` without being equal:
>>> pitch.Pitch('C#5') == pitch.Pitch('D-5')
False
@@ -1686,11 +1689,11 @@ class Pitch(prebase.ProtoM21Object):
If contradictory keyword attributes (like `name='E-', accidental='#'`) are passed in,
behavior is not defined, but unlikely to make you happy.
- Pitches are ProtoM21Objects, so they retain some of the attributes there
+ Pitches are ProtoM21Objects, so they retain some attributes there
such as .classes and .groups, but they don't have Duration or Sites objects
and cannot be put into Streams
'''
- # define order to present names in documentation; use strings
+ # define order for presenting names in documentation; use strings
_DOC_ORDER = ['name', 'nameWithOctave', 'step', 'pitchClass', 'octave', 'midi', 'german',
'french', 'spanish', 'italian', 'dutch']
# documentation for all attributes (not properties or methods)
@@ -1768,7 +1771,7 @@ def __init__(self,
self._overridden_freq440 = None
# store an Accidental and Microtone objects
- # note that creating an Accidental object is much more time consuming
+ # note that creating an Accidental object is much more time-consuming
# than a microtone
self._accidental = None
self._microtone = None # 5% of pitch creation time; it'll be created in a sec anyhow
@@ -1932,7 +1935,7 @@ def __le__(self, other) -> bool:
'''
Less than or equal. Based on the accidentals' alter function.
Note that to be equal enharmonics must be the same. So two pitches can
- be neither lt or gt and not equal to each other!
+ be neither lt nor gt and not equal to each other!
>>> a = pitch.Pitch('d4')
>>> b = pitch.Pitch('d8')
@@ -1974,7 +1977,7 @@ def __ge__(self, other) -> bool:
'''
Greater than or equal. Based on the accidentals' alter function.
Note that to be equal enharmonics must be the same. So two pitches can
- be neither lt or gt and not equal to each other!
+ be neither lt nor gt and not equal to each other!
>>> a = pitch.Pitch('d4')
>>> b = pitch.Pitch('d8')
@@ -2018,7 +2021,7 @@ def accidental(self) -> Optional[Accidental]:
'''
Stores an optional accidental object contained within the
Pitch object. This might return None, which is different
- than a natural accidental:
+ from a natural accidental:
>>> a = pitch.Pitch('E-')
>>> a.accidental.alter
@@ -2034,20 +2037,6 @@ def accidental(self) -> Optional[Accidental]:
False
>>> b.accidental
-
- Deprecated usage allows setting accidental to
- a number or string. Will be a warning in v.7 and removed in v.8.
-
- >>> b = pitch.Pitch('C4')
- >>> b.accidental = 1.5
- >>> print(b)
- C#4(+50c)
- >>> b.accidental = 1.65
- >>> print(b)
- C#~4(+15c)
- >>> b.accidental = 1.95
- >>> print(b)
- C##4(-5c)
'''
return self._accidental
@@ -2055,7 +2044,7 @@ def accidental(self) -> Optional[Accidental]:
def accidental(self, value: Union[str, int, float, Accidental]):
if isinstance(value, str):
self._accidental = Accidental(value)
- elif common.isNum(value):
+ elif common.isNum(value): # pragma: no cover
# check and add any microtones
alter, cents = _convertCentsToAlterAndCents(value * 100.0)
self._accidental = Accidental(alter)
@@ -2224,7 +2213,7 @@ def alter(self) -> float:
'''
Get or set the number of half-steps shifted
by this pitch, such as 1.0 for a sharp, -1.0 for a flat,
- 0.0 for a natural, 2.0 for a double sharp, and
+ 0.0 for a natural, 2.0 for a double sharp,
and -0.5 for a quarter tone flat.
Thus, the alter value combines the pitch change
@@ -2926,7 +2915,7 @@ def pitchClass(self) -> int:
@pitchClass.setter
def pitchClass(self, value: Union[str, int]):
- # permit the submission of strings, like A an dB
+ # permit the submission of strings, like "A" and "B"
value = _convertPitchClassToNumber(value)
# get step and accidental w/o octave
self.step, self._accidental = _convertPsToStep(value)[0:2]
@@ -2941,7 +2930,7 @@ def pitchClassString(self) -> str:
where integers greater than 10 are replaced by A and B,
respectively. Can be used to set pitch class by a
string representation as well (though this is also
- possible with :attr:`~music21.pitch.Pitch.pitchClass`.
+ possible with :attr:`~music21.pitch.Pitch.pitchClass`).
>>> a = pitch.Pitch('a#3')
>>> a.pitchClass
@@ -3220,6 +3209,7 @@ def spanish(self) -> str:
# noinspection SpellCheckingInspection
@property
def french(self) -> str:
+ # noinspection GrazieInspection
'''
Read-only attribute. Returns the name
of a Pitch in the French system
@@ -3331,7 +3321,7 @@ def frequency(self) -> float:
def frequency(self, value: Union[int, float]):
self.freq440 = value
- # these methods may belong in in a temperament object
+ # these methods may belong in a temperament object
# name of method and property could be more clear
@property
@@ -3765,7 +3755,7 @@ def isEnharmonic(self, other: 'Pitch') -> bool:
>>> pitch.Pitch('C4').isEnharmonic( pitch.Pitch('B#4') )
False
- If either pitch is octaveless, then they a pitch in any octave will match:
+ If either pitch is octaveless, then a pitch in any octave will match:
>>> pitch.Pitch('C#').isEnharmonic( pitch.Pitch('D-9') )
True
@@ -4422,7 +4412,7 @@ def transposeBelowTarget(
>>> pitch.Pitch('g#2').transposeBelowTarget(pitch.Pitch('f#8'))
- But with minimize=True, it will actually RAISE the pitch so it is the closest
+ But with minimize=True, it will actually RAISE the pitch so that it is the closest
pitch to the target
>>> target = pitch.Pitch('f#8')
@@ -4437,8 +4427,8 @@ def transposeBelowTarget(
else:
src = copy.deepcopy(self)
while True:
- # ref 20, min 10, lower ref
- # ref 5, min 10, do not lower
+ # ref 20, min 10, lower ref.
+ # ref 5, min 10, do not lower.
if src.ps - target.ps <= 0:
break
# lower one octave
@@ -4502,8 +4492,8 @@ def transposeAboveTarget(self: _T, target, *, minimize=False, inPlace=False) ->
src = copy.deepcopy(self)
# case where self is below target
while True:
- # ref 20, max 10, do not raise ref
- # ref 5, max 10, raise ref to above max
+ # ref 20, max 10, do not raise ref.
+ # ref 5, max 10, raise ref to above max.
if src.ps - target.ps >= 0:
break
# raise one octave
@@ -4700,7 +4690,7 @@ def updateAccidentalDisplay(
self.accidental.displayStatus = True
return # do not search past
- # pitches in past... first search if last pitch in measure
+ # pitches in the past... first search if last pitch in measure
# at this octave contradicts this pitch. if so then no matter what
# we need an accidental.
for i in reversed(range(len(pitchPast))):
@@ -4713,7 +4703,7 @@ def updateAccidentalDisplay(
return
else: # names are the same, skip this line of questioning
break
- # nope, no conflicting accidentals at this name and octave in past...
+ # nope, no conflicting accidentals at this name and octave in the past...
# here tied and always are treated the same; we assume that
# making ties sets the displayStatus, and thus we would not be
@@ -4850,7 +4840,7 @@ def updateAccidentalDisplay(
else:
if self.accidental is not None:
self.accidental.displayStatus = False
- # if we match the step in a key signature and we want
+ # if we match the step in a key signature, and we want
# cautionary not immediate repeated
elif (self._stepInKeySignature(alteredPitches) is True
and cautionaryNotImmediateRepeat is True):
@@ -5281,7 +5271,7 @@ def compare(past, _result):
proc(pList, [], ks.alteredPitches)
compare(pList, result)
- # non initial scale tones with chromatic alteration
+ # non-initial scale tones with chromatic alteration
pList = [Pitch('a3'), Pitch('c#3'), Pitch('g#3'),
Pitch('g3'), Pitch('c#4'), Pitch('g#4')]
result = [(None, None), ('sharp', False), ('sharp', False),
@@ -5290,7 +5280,7 @@ def compare(past, _result):
proc(pList, [], ks.alteredPitches)
compare(pList, result)
- # non initial scale tones with chromatic alteration
+ # non-initial scale tones with chromatic alteration
pList = [Pitch('a3'), Pitch('c#3'), Pitch('g#3'),
Pitch('g3'), Pitch('c#4'), Pitch('g#4')]
result = [(None, None), ('sharp', False), ('sharp', False),
diff --git a/music21/search/segment.py b/music21/search/segment.py
index 86e93a2f68..ab0808bf71 100644
--- a/music21/search/segment.py
+++ b/music21/search/segment.py
@@ -33,6 +33,7 @@
from collections import OrderedDict
from functools import partial
+from typing import List
from music21 import common
from music21 import converter
@@ -172,6 +173,7 @@ def indexScoreFilePaths(scoreFilePaths,
*args,
runMulticore=True,
**keywords):
+ # noinspection PyShadowingNames
'''
Returns a dictionary of the lists from indexScoreParts for each score in
scoreFilePaths
@@ -290,6 +292,7 @@ def getDifflibOrPyLev(
smObject = difflib.SequenceMatcher(junk, '', seq2)
else:
try:
+ # noinspection PyPackageRequirements
from Levenshtein import StringMatcher as pyLevenshtein
smObject = pyLevenshtein.StringMatcher(junk, '', seq2)
except ImportError:
@@ -304,6 +307,7 @@ def scoreSimilarity(
includeReverse=False,
forceDifflib=False,
):
+ # noinspection PyShadowingNames
r'''
Find the level of similarity between each pair of segments in a scoreDict.
@@ -400,7 +404,7 @@ def doOneSegment(thisSegment):
# ------------------------------------------------------------------------------
# define presented order in documentation
-_DOC_ORDER = []
+_DOC_ORDER: List[type] = []
if __name__ == '__main__':
diff --git a/music21/spanner.py b/music21/spanner.py
index 02a4029708..db6ceeb05f 100644
--- a/music21/spanner.py
+++ b/music21/spanner.py
@@ -20,7 +20,7 @@
'''
import unittest
import copy
-from typing import Any, Dict, Sequence, Union, List, Optional
+from typing import Any, Dict, Sequence, Union, List, Optional, TypedDict
from music21 import exceptions21
from music21 import base
@@ -220,8 +220,8 @@ def __init__(self, *arguments, **keywords):
self.spannerStorage = stream.SpannerStorage(spannerParent=self)
# we do not want to auto sort based on offset or class, as
- # both are meaningless inside of this Stream (and only have meaning
- # in Stream external to this
+ # both are meaningless inside this Stream (and only have meaning
+ # in Stream external to this)
self.spannerStorage.autoSort = False
# add arguments as a list or single item
@@ -278,13 +278,16 @@ def _deepcopySubclassable(self, memo=None, ignoreAttributes=None, removeFromIgno
def __deepcopy__(self, memo=None):
'''
- This produces a new, independent object containing references to the same spannedElements.
- SpannedElements linked in this Spanner must be manually re-set, likely using the
+ This produces a new, independent object containing references
+ to the same spannedElements.
+ SpannedElements linked in this Spanner must be manually re-set,
+ likely using the
replaceSpannedElement() method.
- Notice that we put the references to the same object so that later we can replace them;
+ Notice that we put the references to the same object so that
+ later we can replace them;
otherwise in a deepcopy of a stream, the notes in the stream
- will become independent from the notes in the spanner.
+ will become independent of the notes in the spanner.
>>> import copy
>>> n1 = note.Note('g')
@@ -403,18 +406,19 @@ def getSpannedElementIds(self):
'''
return [id(n) for n in self.spannerStorage._elements]
- def addSpannedElements(self,
- spannedElements: Union[Sequence[base.Music21Object],
- base.Music21Object],
- *arguments,
- **keywords):
+ def addSpannedElements(
+ self,
+ spannedElements: Union[Sequence[base.Music21Object],
+ base.Music21Object],
+ *arguments,
+ **keywords
+ ):
'''
Associate one or more elements with this Spanner.
The order in which elements are added is retained and
may or may not be significant to the spanner.
-
>>> n1 = note.Note('g')
>>> n2 = note.Note('f#')
>>> n3 = note.Note('e')
@@ -435,7 +439,7 @@ def addSpannedElements(self,
if arguments:
# copy
spannedElements = spannedElements[:] # type: ignore[index]
- # assume all other arguments
+ # assume all other arguments are spanners
spannedElements += arguments # type: ignore[operator]
for c in spannedElements: # type: ignore[union-attr]
if c is None:
@@ -451,7 +455,7 @@ def addSpannedElements(self,
self.spannerStorage.coreElementsChanged()
- def hasSpannedElement(self, spannedElement):
+ def hasSpannedElement(self, spannedElement: base.Music21Object) -> bool:
'''
Return True if this Spanner has the spannedElement.
@@ -603,12 +607,17 @@ def getLast(self):
# ------------------------------------------------------------------------------
+class _SpannerRef(TypedDict):
+ # noinspection PyTypedDict
+ spanner: 'Spanner'
+ className: str
+
class SpannerBundle(prebase.ProtoM21Object):
'''
An advanced utility object for collecting and processing
collections of Spanner objects. This is necessary because
often processing routines that happen at many different
- levels need access to the same collection of spanners.
+ levels still need access to the same collection of spanners.
Because SpannerBundles are so commonly used with
:class:`~music21.stream.Stream` objects, the Stream has a
@@ -621,10 +630,9 @@ class SpannerBundle(prebase.ProtoM21Object):
Not to be confused with SpannerStorage (which is a Stream class inside
a spanner that stores Elements that are spanned)
- Changed in v7: only argument must be a List of spanners. Creators of SpannerBundles
- are required to check that this constraint is True
+ Changed in v7: only argument must be a List of spanners.
+ Creators of SpannerBundles are required to check that this constraint is True
'''
-
def __init__(self, spanners: Optional[List[Spanner]] = None):
self._cache: Dict[str, Any] = {} # cache is defined on Music21Object not ProtoM21Object
@@ -638,7 +646,7 @@ def __init__(self, spanners: Optional[List[Spanner]] = None):
# SpannerBundle as missing a spannedElement; the next obj that meets
# the class expectation will then be assigned and the spannedElement
# cleared
- self._pendingSpannedElementAssignment: List[Dict[Spanner, str]] = []
+ self._pendingSpannedElementAssignment: List[_SpannerRef] = []
def append(self, other):
'''
@@ -794,7 +802,11 @@ def getBySpannedElement(self, spannedElement):
self._cache[cacheKey] = post
return self._cache[cacheKey]
- def replaceSpannedElement(self, old, new):
+ def replaceSpannedElement(
+ self,
+ old: base.Music21Object,
+ new: base.Music21Object
+ ) -> List[Spanner]:
# noinspection PyShadowingNames
'''
Given a spanner spannedElement (an object), replace all old spannedElements
@@ -834,13 +846,10 @@ def replaceSpannedElement(self, old, new):
>>> sb.replaceSpannedElement(id(n1), n2)
Traceback (most recent call last):
- TypeError: send elements to replaceSpannedElement(), not ids (deprecated)
+ TypeError: send elements to replaceSpannedElement(), not ids.
'''
- # environLocal.printDebug(['SpannerBundle.replaceSpannedElement()', 'old', old,
- # 'new', new, 'len(self._storage)', len(self._storage)])
- # TODO: remove in v.8 for speed?
if isinstance(old, int):
- raise TypeError('send elements to replaceSpannedElement(), not ids (deprecated)')
+ raise TypeError('send elements to replaceSpannedElement(), not ids.')
replacedSpanners = []
# post = self.__class__() # return a bundle of spanners that had changes
@@ -864,7 +873,7 @@ def replaceSpannedElement(self, old, new):
return replacedSpanners
- def getByClass(self, className):
+ def getByClass(self, className: Union[str, type]) -> 'SpannerBundle':
'''
Given a spanner class, return a new SpannerBundle of all Spanners of the desired class.
@@ -947,20 +956,22 @@ class have been closed. The example below demonstrates that the
def setIdLocals(self):
# noinspection PyShadowingNames
'''
- Utility method for outputting MusicXML (and potentially other formats) for spanners.
+ Utility method for outputting MusicXML (and potentially
+ other formats) for spanners.
- Each Spanner type (slur, line, glissando, etc.) in MusicXML has a number assigned to it.
- We call this number, `idLocal`. idLocal is a number from 1 to 6. This does not mean
- that your piece can only have six slurs total! But it does mean that within a single
- part, only up to 6 slurs can happen simultaneously. But as soon as a slur stops, its
- idLocal can be reused.
+ Each Spanner type (slur, line, glissando, etc.) in MusicXML
+ has a number assigned to it.
+ We call this number, `idLocal`. idLocal is a number from 1 to 6.
+ This does not mean that your piece can only have six slurs total!
+ But it does mean that within a single
+ part, only up to 6 slurs can happen simultaneously.
+ But as soon as a slur stops, its idLocal can be reused.
This method sets all idLocals for all classes in this SpannerBundle.
This will assure that each class has a unique idLocal number.
Calling this method is destructive: existing idLocal values will be lost.
-
>>> su1 = spanner.Slur()
>>> su2 = layout.StaffGroup()
>>> su3 = spanner.Slur()
@@ -977,7 +988,8 @@ def setIdLocals(self):
(, 2)]
:class:`~music21.dynamics.DynamicWedge` objects are commingled. That is,
- :class:`~music21.dynamics.Crescendo` and :class:`~music21.dynamics.Diminuendo`
+ :class:`~music21.dynamics.Crescendo` and
+ :class:`~music21.dynamics.Diminuendo`
are not numbered separately:
>>> sb2 = spanner.SpannerBundle()
@@ -1023,7 +1035,11 @@ def getByClassIdLocalComplete(self, className, idLocal, completeStatus):
return self.getByClass(className).getByIdLocal(
idLocal).getByCompleteStatus(completeStatus)
- def setPendingSpannedElementAssignment(self, sp, className):
+ def setPendingSpannedElementAssignment(
+ self,
+ sp: Spanner,
+ className: str,
+ ):
'''
A SpannerBundle can be set up so that a particular spanner (sp)
is looking for an element of class (className) to complete it. Any future
@@ -1075,7 +1091,7 @@ def setPendingSpannedElementAssignment(self, sp, className):
[]
'''
- ref = {'spanner': sp, 'className': className}
+ ref: _SpannerRef = {'spanner': sp, 'className': className}
self._pendingSpannedElementAssignment.append(ref)
def freePendingSpannedElementAssignment(self, spannedElementCandidate):
@@ -1517,7 +1533,7 @@ def shiftDirection(self, reverse=False):
'''
Returns up or down depending on the type of shift:
'''
- # an 8va means that the notes must be shifted down with the mark
+ # an 8va mark means that the notes must be shifted down with the mark
if self._type.endswith('a'):
if reverse:
return 'down'
diff --git a/music21/stream/base.py b/music21/stream/base.py
index d9d22b0a29..f4019b5821 100644
--- a/music21/stream/base.py
+++ b/music21/stream/base.py
@@ -86,7 +86,7 @@ class StreamDeprecationWarning(UserWarning):
# -----------------------------------------------------------------------------
# Metaclass
-_OffsetMap = collections.namedtuple('OffsetMap', ['element', 'offset', 'endTime', 'voiceIndex'])
+OffsetMap = collections.namedtuple('OffsetMap', ['element', 'offset', 'endTime', 'voiceIndex'])
# -----------------------------------------------------------------------------
@@ -1252,7 +1252,7 @@ def hasElementOfClass(self, className, forceFlat=False):
>>> s.hasElementOfClass('Measure')
False
- To be deprecated in v.7 -- to be removed in version 8, use:
+ To be deprecated in v.8 -- to be removed in version 9, use:
>>> bool(s.getElementsByClass('TimeSignature'))
True
@@ -5297,84 +5297,6 @@ def getInstrument(self,
recurse=recurse)
return post.first()
- @common.deprecated('v7', 'v8', 'use getElementsByClass() or getContextByClass() or bestClef()')
- def getClefs(self, searchActiveSite=False, searchContext=True,
- returnDefault=True): # pragma: no cover
- '''
- DEPRECATED in v7.
-
- Collect all :class:`~music21.clef.Clef` objects in
- this Stream in a list. Optionally search the
- activeSite Stream and/or contexts.
-
- If no Clef objects are defined, get a default
- using :meth:`~music21.clef.bestClef`
-
-
- >>> a = stream.Stream()
- >>> b = clef.AltoClef()
- >>> a.insert(0, b)
- >>> a.repeatInsert(note.Note('C#'), list(range(10)))
- >>> #_DOCS_SHOW c = a.getClefs()
- >>> #_DOCS_SHOW len(c) == 1
- True
- '''
- # TODO: activeSite searching is not yet implemented
- # this may not be useful unless a stream is flat
- post = list(self.getElementsByClass('Clef'))
-
- # environLocal.printDebug(['getClefs(); count of local', len(post), post])
- if not post and searchActiveSite and self.activeSite is not None:
- # environLocal.printDebug(['getClefs(): search activeSite'])
- post = list(self.activeSite.getElementsByClass('Clef'))
-
- if not post and searchContext:
- # returns a single element match
- # post = self.__class__()
- obj = self.getContextByClass('Clef')
- if obj is not None:
- post.append(obj)
-
- # get a default and/or place default at zero if nothing at zero
- if returnDefault and (not post or post[0].offset > 0):
- # environLocal.printDebug(['getClefs(): using bestClef()'])
- post.insert(0, clef.bestClef(self))
- return post
-
- @common.deprecated('v7', 'v8', 'use getElementsByClass() or getContextByClass()')
- def getKeySignatures(self, searchActiveSite=True, searchContext=True): # pragma: no cover
- '''
- Collect all :class:`~music21.key.KeySignature` objects in this
- Stream in a new Stream. Optionally search the activeSite
- stream and/or contexts.
-
- If no KeySignature objects are defined, returns an empty Stream
-
- DEPRECATED in v7.
-
- >>> a = stream.Stream()
- >>> b = key.KeySignature(3)
- >>> a.insert(0, b)
- >>> a.repeatInsert(note.Note('C#'), list(range(10)))
- >>> #_DOCS_SHOW c = a.getKeySignatures()
- >>> #_DOCS_SHOW len(c) == 1
- True
- '''
- # TODO: activeSite searching is not yet implemented
- # this may not be useful unless a stream is flat
- post = self.getElementsByClass('KeySignature')
- if not post and searchContext:
- # returns a single value
- post = self.cloneEmpty(derivationMethod='getKeySignatures')
- obj = self.getContextByClass(key.KeySignature)
- if obj is not None:
- post.append(obj)
-
- # do nothing if empty
- if not post or post[0].offset > 0:
- pass
- return post
-
def invertDiatonic(self, inversionNote=note.Note('C4'), *, inPlace=False):
'''
inverts a stream diatonically around the given note (by default, middle C)
@@ -5766,279 +5688,6 @@ def _uniqueOffsetsAndEndTimes(self, offsetsOnly=False, endTimesOnly=False):
for v in offsetDictValues}
return sorted(offsets.union(endTimes))
- @common.deprecated('v7', 'v8', 'use chordify() instead')
- def makeChords(self,
- minimumWindowSize=0.125,
- includePostWindow=True,
- removeRedundantPitches=True,
- useExactOffsets=False,
- gatherArticulations=True,
- gatherExpressions=True,
- inPlace=False,
- transferGroupsToPitches=False,
- makeRests=True): # pragma: no cover
- '''
- DEPRECATED in v7. Use Chordify instead!
-
- Gathers simultaneously sounding :class:`~music21.note.Note` objects
- into :class:`~music21.chord.Chord` objects, each of which
- contains all the pitches sounding together.
-
- If useExactOffsets is True (default=False), then do an exact
- makeChords using the offsets in the piece.
- If this parameter is set, then minimumWindowSize is ignored.
-
- This first example puts a part with three quarter notes (C4, D4, E4)
- together with a part consisting of a half note (C#5) and a
- quarter note (E#5) to make two Chords, the first containing the
- three :class:`~music21.pitch.Pitch` objects sounding at the
- beginning, the second consisting of the two Pitches sounding
- on offset 2.0 (beat 3):
-
- >>> p1 = stream.Part()
- >>> p1.append([note.Note('C4', type='quarter'),
- ... note.Note('D4', type='quarter'),
- ... note.Note('E4', type='quarter'),
- ... note.Note('B2', type='quarter')])
- >>> p2 = stream.Part()
- >>> p2.append([note.Note('C#5', type='half'),
- ... note.Note('E#5', type='quarter'),
- ... chord.Chord(['E4', 'G5', 'C#7'])])
- >>> sc1 = stream.Score()
- >>> sc1.insert(0, p1)
- >>> sc1.insert(0, p2)
- >>> #_DOCS_SHOW scChords = sc1.flatten().makeChords()
- >>> #_DOCS_SHOW scChords.show('text')
- {0.0}
- {2.0}
- {3.0}
-
- The gathering of elements, starting from offset 0.0, uses
- the `minimumWindowSize`, in quarter lengths, to collect
- all Notes that start between 0.0 and the minimum window
- size (this permits overlaps within a minimum tolerance).
-
- After collection, the maximum duration of collected elements
- is found; this duration is then used to set the new
- starting offset. A possible gap then results between the end
- of the window and offset specified by the maximum duration;
- these additional notes are gathered in a second pass if
- `includePostWindow` is True.
-
- The new start offset is shifted to the larger of either
- the minimum window or the maximum duration found in the
- collected group. The process is repeated until all offsets
- are covered.
-
- Each collection of Notes is formed into a Chord. The Chord
- is given the longest duration of all constituents, and is
- inserted at the start offset of the window from which it
- was gathered.
-
- Chords can gather both articulations and expressions from found Notes
- using `gatherArticulations` and `gatherExpressions`.
-
- If `transferGroupsToPitches` is True, and group defined on the source
- elements Groups object will be transferred to the Pitch
- objects contained in the resulting Chord.
-
- The resulting Stream, if not in-place, can also gather
- additional objects by placing class names in the `collect` list.
- By default, TimeSignature and KeySignature objects are collected.
- '''
- if not inPlace: # make a copy
- # since we do not return Scores, this probably should always be
- # a Stream
- # returnObj = Stream()
- # returnObj = self.__class__() # for output
- returnObj = self.coreCopyAsDerivation('makeChords')
- else:
- returnObj = self
-
- def dealWithSubNotes(chordLength, noteList):
- # environLocal.printDebug(['creating chord from noteList',
- # noteList, 'inPlace', inPlace])
- c = chord.Chord()
- c.duration.quarterLength = chordLength
- # these are references, not copies, for now
- tempComponents = []
- for n in noteList:
- if n.isChord:
- cSub = list(n)
- else:
- cSub = [n]
-
- if transferGroupsToPitches and n.groups:
- for comp in cSub:
- for g in n.groups:
- comp.pitch.groups.append(g)
-
- for comp in cSub:
- tempComponents.append(comp)
-
- c.pitches = [comp.pitch for comp in tempComponents]
- for comp in tempComponents:
- if comp.tie is not None:
- c.setTie(comp.tie.type, comp.pitch)
-
- if gatherArticulations:
- for n in noteList:
- c.articulations += n.articulations
- if gatherExpressions:
- for n in noteList:
- c.expressions += n.expressions
- # always remove all the previous elements
- for n in noteList:
- returnObj.remove(n)
- # remove all rests found in source
- for r in list(returnObj.getElementsByClass('Rest')):
- returnObj.remove(r)
-
- if removeRedundantPitches:
- removedPitches = c.removeRedundantPitches(inPlace=True)
-
- if transferGroupsToPitches:
- for rem_p, cn in itertools.product(removedPitches, c):
- if cn.pitch.nameWithOctave == rem_p.nameWithOctave:
- # print(cn.pitch, rem_p)
- # print(cn.pitch.groups, rem_p.groups)
- cn.pitch.groups.extend(rem_p.groups)
- return c
- # environLocal.printDebug(['makeChords():',
- # 'transferGroupsToPitches', transferGroupsToPitches])
-
- if returnObj.hasMeasures():
- # call on component measures
- for m in returnObj.getElementsByClass('Measure'):
- # offset values are not relative to measure; need to
- # shift by each measure's offset
- m.makeChords(
- minimumWindowSize=minimumWindowSize,
- includePostWindow=includePostWindow,
- removeRedundantPitches=removeRedundantPitches,
- gatherArticulations=gatherArticulations,
- gatherExpressions=gatherExpressions,
- transferGroupsToPitches=transferGroupsToPitches,
- inPlace=True,
- makeRests=makeRests
- )
- return returnObj # exit
-
- if returnObj.hasPartLikeStreams():
- # must get Streams, not Parts here
- for p in returnObj.getElementsByClass('Stream'):
- p.makeChords(
- minimumWindowSize=minimumWindowSize,
- includePostWindow=includePostWindow,
- removeRedundantPitches=removeRedundantPitches,
- gatherArticulations=gatherArticulations,
- gatherExpressions=gatherExpressions,
- transferGroupsToPitches=transferGroupsToPitches,
- inPlace=True,
- makeRests=makeRests
- )
- return returnObj # exit
-
- # TODO: gather lyrics as an option
- # define classes that are gathered; assume they have pitches
- # matchClasses = ['Note', 'Chord', 'Rest']
- matchClasses = ['Note', 'Chord']
- o = 0.0 # start at zero
- oTerminate = returnObj.highestOffset
-
- # get temporary boundaries for making rests
- preHighestTime = returnObj.highestTime
- preLowestOffset = returnObj.lowestOffset
- # environLocal.printDebug(['got preLowest, preHighest', preLowestOffset, preHighestTime])
- if useExactOffsets is False:
- while True: # TODO: Remove while True always...
- # get all notes within the start and the min window size
- oStart = o
- oEnd = oStart + minimumWindowSize
- subNotes = list(returnObj.getElementsByOffset(
- oStart,
- oEnd,
- includeEndBoundary=False,
- mustFinishInSpan=False,
- mustBeginInSpan=True
- ).getElementsByClass(matchClasses)) # get once for speed
- # environLocal.printDebug(['subNotes', subNotes])
- qlMax: Optional[float] = None
- # get the max duration found from within the window
- if subNotes:
- # get largest duration, use for duration of Chord, next span
- qlMax = max([n.quarterLength for n in subNotes])
-
- # if the max duration found in the window is greater than the min
- # window size, it is possible that there are notes that will not
- # be gathered; those starting at the end of this window but before
- # the max found duration (as that will become the start of the next
- # window
- # so: if ql > min window, gather notes between
- # oStart + minimumWindowSize and oStart + qlMax
- if (includePostWindow and qlMax is not None
- and qlMax > minimumWindowSize):
- subAdd = list(returnObj.getElementsByOffset(
- oStart + minimumWindowSize,
- oStart + qlMax,
- includeEndBoundary=False,
- mustFinishInSpan=False,
- mustBeginInSpan=True
- ).getElementsByClass(matchClasses))
- # concatenate any additional notes found
- subNotes += subAdd
-
- # make subNotes into a chord
- if subNotes:
- cOut = dealWithSubNotes(qlMax, subNotes)
- # insert chord at start location
- returnObj.coreInsert(o, cOut)
- # environLocal.printDebug(['len of returnObj', len(returnObj)])
- # shift offset to qlMax or minimumWindowSize
- if qlMax is not None and qlMax >= minimumWindowSize:
- # update start offset to what was old boundary
- # note: this assumes that the start of the longest duration
- # was at oStart; it could have been between oStart and oEnd
- o += qlMax
- else:
- o += minimumWindowSize
- # end While loop conditions
- if o > oTerminate:
- break
- else: # useExactOffsets is True:
- onAndOffOffsets = self.flatten().notesAndRests.stream()._uniqueOffsetsAndEndTimes()
- # environLocal.printDebug(['makeChords: useExactOffsets=True;
- # onAndOffOffsets:', onAndOffOffsets])
-
- for i in range(len(onAndOffOffsets) - 1):
- # get all notes within the start and the min window size
- oStart = onAndOffOffsets[i]
- oEnd = onAndOffOffsets[i + 1]
- subNotes = list(returnObj.getElementsByOffset(
- oStart,
- oEnd,
- includeEndBoundary=False,
- mustFinishInSpan=False,
- mustBeginInSpan=True
- ).getElementsByClass(matchClasses))
- # environLocal.printDebug(['subNotes', subNotes])
- # subNotes.show('t')
-
- # make subNotes into a chord
- if subNotes:
- cOut = dealWithSubNotes(oEnd - oStart, subNotes)
- # insert chord at start location
- returnObj.coreInsert(oStart, cOut)
-
- # makeRests to fill any gaps produced by stripping
- # environLocal.printDebug(['pre makeRests show()'])
- returnObj.coreElementsChanged()
- if makeRests:
- returnObj.makeRests(
- refStreamOrTimeRange=(preLowestOffset, preHighestTime),
- fillGaps=True, inPlace=True)
- return returnObj
-
def chordify(
self,
*,
@@ -6456,7 +6105,7 @@ def offsetMap(self, srcObj=None):
endTime = opFrac(offset + dur)
# NOTE: used to make a copy.copy of elements here;
# this is not necessary b/c making deepcopy of entire Stream
- thisOffsetMap = _OffsetMap(e, offset, endTime, voiceIndex)
+ thisOffsetMap = OffsetMap(e, offset, endTime, voiceIndex)
# environLocal.printDebug(['offsetMap: thisOffsetMap', thisOffsetMap])
offsetMap.append(thisOffsetMap)
# offsetMap.append((offset, offset + dur, e, voiceIndex))
@@ -6933,45 +6582,6 @@ def extendDuration(self, objName, *, inPlace=False):
if not inPlace:
return returnObj
- @common.deprecated('v7', 'v8', 'call extendDurations() and getElementsByClass() separately')
- def extendDurationAndGetBoundaries(self, objName, *, inPlace=False): # pragma: no cover
- '''
- DEPRECATED in v.7 -- to be removed in v.8
-
- Extend the Duration of elements specified by objName;
- then, collect a dictionary for every matched element of objName class,
- where the matched element is the value and the key is the (start, end) offset value.
-
- >>> from pprint import pprint as pp
- >>> s = stream.Stream()
- >>> s.insert(3, dynamics.Dynamic('mf'))
- >>> s.insert(7, dynamics.Dynamic('f'))
- >>> s.insert(12, dynamics.Dynamic('ff'))
- >>> #_DOCS_SHOW pp(s.extendDurationAndGetBoundaries('Dynamic'))
- {(3.0, 7.0): ,
- (7.0, 12.0): ,
- (12.0, 12.0): }
-
-
- TODO: only allow inPlace=True or delete or something, can't return two different things
- '''
- if not inPlace: # make a copy
- returnObj = copy.deepcopy(self)
- else:
- returnObj = self
- returnObj.extendDuration(objName, inPlace=True)
- # TODO: use iteration.
- elements = returnObj.getElementsByClass(objName)
- boundaries = {}
- if not elements:
- raise StreamException('no elements of this class defined in this Stream')
-
- for e in elements:
- start = returnObj.elementOffset(e)
- end = start + e.duration.quarterLength
- boundaries[(start, end)] = e
- return boundaries
-
def stripTies(
self,
inPlace=False,
@@ -14416,7 +14026,8 @@ def testCopyAndDeepcopy(self):
# -----------------------------------------------------------------------------
# define presented order in documentation
-_DOC_ORDER = [Stream, Measure, Part, Score, Opus, Voice]
+_DOC_ORDER = [Stream, Measure, Part, Score, Opus, Voice,
+ SpannerStorage, VariantStorage, OffsetMap]
if __name__ == '__main__':
diff --git a/music21/stream/iterator.py b/music21/stream/iterator.py
index f1c455cdda..9a9687b790 100644
--- a/music21/stream/iterator.py
+++ b/music21/stream/iterator.py
@@ -160,11 +160,10 @@ def __init__(self,
self.filters: List[FilterType] = filterList
self._len = None
self._matchingElements = None
-
# keep track of where we are in the parse.
# esp important for recursive streams...
if activeInformation is not None:
- self.activeInformation = activeInformation
+ self.activeInformation: ActiveInformation = activeInformation
else:
self.activeInformation: ActiveInformation = {}
self.updateActiveInformation()
diff --git a/music21/stream/makeNotation.py b/music21/stream/makeNotation.py
index 05c176605c..beab4bc6f4 100644
--- a/music21/stream/makeNotation.py
+++ b/music21/stream/makeNotation.py
@@ -3,13 +3,13 @@
# Name: makeNotation.py
# Purpose: functionality for manipulating streams
#
-# Authors: Michael Scott Cuthbert
+# Authors: Michael Scott Asato Cuthbert
# Christopher Ariza
# Jacob Walls
# Evan Lynch
#
-# Copyright: Copyright © 2008-2021 Michael Scott Cuthbert and the music21
-# Project
+# Copyright: Copyright © 2008-2022 Michael Scott Asato Cuthbert
+# and the music21 Project
# License: BSD, see license.txt
# -----------------------------------------------------------------------------
@@ -30,7 +30,7 @@
from music21 import pitch
from music21.common.numberTools import opFrac
-
+from music21.common.types import StreamType
from music21.exceptions21 import StreamException
environLocal = environment.Environment(__file__)
@@ -40,12 +40,12 @@
def makeBeams(
- s: 'music21.stream.Stream',
+ s: StreamType,
*,
inPlace=False,
setStemDirections=True,
failOnNoTimeSignature=False,
-):
+) -> Optional[StreamType]:
# noinspection PyShadowingNames
'''
Return a new Measure, or Stream of Measures, with beams applied to all
@@ -122,7 +122,7 @@ def makeBeams(
if not inPlace: # make a copy
returnObj: stream.Stream = s.coreCopyAsDerivation('makeBeams')
else:
- returnObj: stream.Stream = s
+ returnObj = s
# if s.isClass(Measure):
mColl: List[stream.Measure]
@@ -221,7 +221,7 @@ def makeBeams(
def makeMeasures(
- s,
+ s: StreamType,
*,
meterStream=None,
refStreamOrTimeRange=None,
@@ -230,7 +230,7 @@ def makeMeasures(
finalBarline='final',
bestClef=False,
inPlace=False,
-):
+) -> Optional[StreamType]:
'''
Takes a stream and places all of its elements into
measures (:class:`~music21.stream.Measure` objects)
@@ -702,14 +702,14 @@ def makeMeasures(
def makeRests(
- s,
+ s: StreamType,
*,
refStreamOrTimeRange=None,
fillGaps=False,
timeRangeFromBarDuration=False,
inPlace=False,
hideRests=False,
-):
+) -> Optional[StreamType]:
'''
Given a Stream with an offset not equal to zero,
fill with one Rest preceding this offset.
@@ -972,13 +972,13 @@ def oHighTargetForMeasure(
return returnObj
def makeTies(
- s,
+ s: StreamType,
*,
meterStream=None,
inPlace=False,
displayTiedAccidentals=False,
classFilterList=(note.GeneralNote,),
-):
+) -> Optional[StreamType]:
# noinspection PyShadowingNames
'''
Given a stream containing measures, examine each element in the
@@ -1347,7 +1347,7 @@ def makeTies(
return None
-def makeTupletBrackets(s: 'music21.stream.Stream', *, inPlace=False):
+def makeTupletBrackets(s: StreamType, *, inPlace=False) -> Optional[StreamType]:
# noinspection PyShadowingNames
'''
Given a flat Stream of mixed durations, designates the first and last tuplet of any group
@@ -1470,7 +1470,7 @@ def makeTupletBrackets(s: 'music21.stream.Stream', *, inPlace=False):
return returnObj
-def realizeOrnaments(s: 'music21.stream.Stream'):
+def realizeOrnaments(s: StreamType) -> StreamType:
'''
Realize all ornaments on a stream
@@ -1548,7 +1548,8 @@ def realizeElementExpressions(innerElement):
return newStream
-def moveNotesToVoices(source: 'music21.stream.Stream', classFilterList=('GeneralNote',)):
+def moveNotesToVoices(source: StreamType,
+ classFilterList=('GeneralNote',)) -> None:
'''
Move notes into voices. Happens inplace always. Returns None
'''
@@ -1564,7 +1565,7 @@ def moveNotesToVoices(source: 'music21.stream.Stream', classFilterList=('General
source.insert(0, dst)
-def getTiePitchSet(prior: 'music21.note.NotRest'):
+def getTiePitchSet(prior: 'music21.note.NotRest') -> Optional[Set[str]]:
# noinspection PyShadowingNames
'''
helper method for makeAccidentals to get the tie pitch set (or None)
@@ -1622,7 +1623,7 @@ def getTiePitchSet(prior: 'music21.note.NotRest'):
return tiePitchSet
def makeAccidentalsInMeasureStream(
- s: 'music21.stream.Stream',
+ s: StreamType,
*,
pitchPast: Optional[List[pitch.Pitch]] = None,
pitchPastMeasure: Optional[List[pitch.Pitch]] = None,
@@ -1633,7 +1634,7 @@ def makeAccidentalsInMeasureStream(
overrideStatus: bool = False,
cautionaryNotImmediateRepeat: bool = True,
tiePitchSet: Optional[Set[str]] = None
-):
+) -> None:
'''
Makes accidentals in place on a stream consisting of only Measures.
Helper for Stream.makeNotation and Part.makeAccidentals.
@@ -1710,7 +1711,7 @@ def makeAccidentalsInMeasureStream(
)
def iterateBeamGroups(
- s: 'music21.stream.Stream',
+ s: StreamType,
skipNoBeams=True,
recurse=True
) -> Generator[List[note.NotRest], None, None]:
@@ -1781,7 +1782,7 @@ def iterateBeamGroups(
def setStemDirectionForBeamGroups(
- s: 'music21.stream.Stream',
+ s: StreamType,
*,
setNewStems=True,
overrideConsistentStemDirections=False,
diff --git a/music21/stream/streamStatus.py b/music21/stream/streamStatus.py
index 7f72ef698a..b01e52ff2f 100644
--- a/music21/stream/streamStatus.py
+++ b/music21/stream/streamStatus.py
@@ -150,11 +150,11 @@ def haveTupletBracketsBeenMade(self):
>>> s.append(note.Note())
>>> s.streamStatus.haveTupletBracketsBeenMade() is None
True
- >>> n = note.Note(quarterLength=1/3)
- >>> s.append(n)
+ >>> nTuplet = note.Note(quarterLength=1/3)
+ >>> s.append(nTuplet)
>>> s.streamStatus.haveTupletBracketsBeenMade()
False
- >>> n.duration.tuplets[0].type = 'start'
+ >>> nTuplet.duration.tuplets[0].type = 'start'
>>> s.streamStatus.haveTupletBracketsBeenMade()
True
diff --git a/music21/tie.py b/music21/tie.py
index 4a5832f046..4be5bf53d2 100644
--- a/music21/tie.py
+++ b/music21/tie.py
@@ -102,7 +102,7 @@ class Tie(prebase.ProtoM21Object, SlottedObjectMixin):
VALID_TIE_TYPES = ('start', 'stop', 'continue', 'let-ring', 'continue-let-ring')
# pylint: disable=redefined-builtin
- def __init__(self, type='start'): # @ReservedAssignment
+ def __init__(self, type='start'):
# super().__init__() # no need for ProtoM21Object or SlottedObjectMixin
if type not in self.VALID_TIE_TYPES:
raise TieException(
diff --git a/music21/voiceLeading.py b/music21/voiceLeading.py
index 7627c9edc1..3732e0c200 100644
--- a/music21/voiceLeading.py
+++ b/music21/voiceLeading.py
@@ -55,7 +55,7 @@
# create a module level shared cache for intervals of P1, P5, P8
# to be populated the first time a VLQ object is created
-intervalCache = [] # type: List[interval.Interval]
+intervalCache: List[interval.Interval] = []
class MotionType(str, enum.Enum):
@@ -1665,8 +1665,8 @@ def offset(self, leftAlign=True):
returns the overall offset of the Verticality. Typically, this would just be the
offset of each object in the Verticality,
and each object would have the same offset.
- However, if the duration of one object in the slice is different than the duration
- of another,
+ However, if the duration of one object in the slice is different from
+ the duration of another,
and that other starts after the first, but the first is still sounding, then the
offsets would be
different. In this case, specify leftAlign=True to return the lowest valued-offset
@@ -2106,7 +2106,7 @@ def couldBePassingTone(self) -> bool:
are moving in the same direction. Returns True if the tone is
identified as either a chromatic passing tone or a diatonic passing
tone. Only major and minor diatonic passing tones are recognized (not
- pentatonic or scales beyond twelve-notes). Does NOT check if tone is non harmonic
+ pentatonic or scales beyond twelve-notes). Does NOT check if tone is non-harmonic.
Accepts pitch or note objects; method is dependent on octave information
@@ -2212,7 +2212,7 @@ def couldBeChromaticPassingTone(self):
def couldBeNeighborTone(self):
'''
checks if noteToAnalyze could be a neighbor tone, either a diatonic neighbor tone
- or a chromatic neighbor tone. Does NOT check if tone is non harmonic
+ or a chromatic neighbor tone. Does NOT check if tone is non-harmonic.
>>> voiceLeading.ThreeNoteLinearSegment('E3', 'F3', 'E3').couldBeNeighborTone()
True