diff --git a/.github/workflows/pythonpylint.yml b/.github/workflows/pythonpylint.yml index 31c07f4680..7f702b8868 100644 --- a/.github/workflows/pythonpylint.yml +++ b/.github/workflows/pythonpylint.yml @@ -64,4 +64,4 @@ jobs: - name: Type-check certain modules with mypy run: | mypy --follow-imports=silent music21/capella music21/common music21/corpus music21/features music21/figuredBass music21/humdrum music21/ipython21 music21/languageExcerpts music21/lily music21/mei music21/metadata music21/musedata music21/noteworthy music21/omr music21/romanText music21/test music21/vexflow - mypy --follow-imports=silent music21/articulations.py music21/bar.py music21/beam.py music21/clef.py music21/configure.py music21/defaults.py music21/derivation.py music21/duration.py music21/dynamics.py music21/editorial.py music21/environment.py music21/exceptions21.py music21/expressions.py music21/freezeThaw.py music21/harmony.py music21/instrument.py music21/interval.py music21/layout.py music21/percussion.py music21/prebase.py music21/repeat.py music21/roman.py music21/serial.py music21/sieve.py music21/sites.py music21/sorting.py music21/spanner.py music21/style.py music21/tablature.py music21/tempo.py music21/text.py music21/tie.py music21/tinyNotation.py music21/variant.py music21/voiceLeading.py music21/volpiano.py music21/volume.py + mypy --follow-imports=silent music21/articulations.py music21/bar.py music21/base.py music21/beam.py music21/clef.py music21/configure.py music21/defaults.py music21/derivation.py music21/duration.py music21/dynamics.py music21/editorial.py music21/environment.py music21/exceptions21.py music21/expressions.py music21/freezeThaw.py music21/harmony.py music21/instrument.py music21/interval.py music21/layout.py music21/percussion.py music21/prebase.py music21/repeat.py music21/roman.py music21/serial.py music21/sieve.py music21/sites.py music21/sorting.py music21/spanner.py music21/style.py music21/tablature.py music21/tempo.py music21/text.py music21/tie.py music21/tinyNotation.py music21/variant.py music21/voiceLeading.py music21/volpiano.py music21/volume.py diff --git a/documentation/testDocumentation.py b/documentation/testDocumentation.py index a97c4acfae..5636c1e85d 100644 --- a/documentation/testDocumentation.py +++ b/documentation/testDocumentation.py @@ -30,7 +30,8 @@ from music21.test import testRunner -ModTuple = namedtuple('ModTuple', 'module fullModulePath moduleNoExtension autoGen') +ModTuple = namedtuple('ModTuple', + ['module', 'fullModulePath', 'moduleNoExtension', 'autoGen']) class Unbuffered: diff --git a/music21/abcFormat/translate.py b/music21/abcFormat/translate.py index 0ef1ecf1a2..146cba30e7 100644 --- a/music21/abcFormat/translate.py +++ b/music21/abcFormat/translate.py @@ -24,18 +24,20 @@ import unittest import re +from music21 import articulations +from music21 import bar from music21 import clef +from music21 import chord from music21 import common from music21 import environment from music21 import exceptions21 +from music21 import harmony from music21 import meter -from music21 import stream -from music21 import tie -from music21 import articulations from music21 import note -from music21 import chord from music21 import spanner -from music21 import harmony +from music21 import stream +from music21 import tempo +from music21 import tie environLocal = environment.Environment('abcFormat.translate') @@ -52,7 +54,7 @@ def abcToStreamPart(abcHandler, inputM21=None, spannerBundle=None): ''' - Handler conversion of a single Part of a multi-part score. + Handler conversion of a single Part of a Score with multiple Parts. Results are added into the provided inputM21 object or a newly created Part object @@ -79,7 +81,7 @@ def abcToStreamPart(abcHandler, inputM21=None, spannerBundle=None): # one measure, that means that no measures are defined barHandlers = abcHandler.splitByMeasure() # environLocal.printDebug(['barHandlers', len(barHandlers)]) - # merge loading meta data with each bar that precedes it + # merge loading metadata with each bar that precedes it mergedHandlers = abcFormat.mergeLeadingMetaData(barHandlers) # environLocal.printDebug(['mergedHandlers', len(mergedHandlers)]) else: # simply stick in a single list @@ -92,14 +94,14 @@ def abcToStreamPart(abcHandler, inputM21=None, spannerBundle=None): useMeasures = True # each unit in merged handlers defines possible a Measure (w/ or w/o metadata), - # trailing meta data, or a single collection of metadata and note data + # trailing metadata, or a single collection of metadata and note data barCount = 0 measureNumber = 1 # merged handler are ABCHandlerBar objects, defining attributes for barlines for mh in mergedHandlers: - # if use measures and the handler has notes; otherwise add to part + # if "use measures" is True and the handler has notes; otherwise add to part # environLocal.printDebug(['abcToStreamPart', 'handler', 'left:', mh.leftBarToken, # 'right:', mh.rightBarToken, 'len(mh)', len(mh)]) @@ -121,7 +123,7 @@ def abcToStreamPart(abcHandler, inputM21=None, spannerBundle=None): rbSpanners = spannerBundle.getByClass('RepeatBracket' ).getByCompleteStatus(False) # this indication is most likely an opening, as ABC does - # not encode second ending ending boundaries + # not encode second ending boundaries # we can still check thought: if not rbSpanners: # add this measure as a component @@ -168,7 +170,7 @@ def abcToStreamPart(abcHandler, inputM21=None, spannerBundle=None): postTransposition, clefSet = parseTokens(mh, dst, p, useMeasures) - # append measure to part; in the case of trailing meta data + # append measure to part; in the case of trailing metadata # dst may be part, even though useMeasures is True if useMeasures and isinstance(dst, stream.Measure): # check for incomplete bars @@ -195,7 +197,7 @@ def abcToStreamPart(abcHandler, inputM21=None, spannerBundle=None): except (ABCTranslateException, meter.MeterException, ZeroDivisionError): pass # clefs are not typically defined, but if so, are set to the first measure - # following the meta data, or in the open stream + # following the metadata, or in the open stream if not clefSet and not p[clef.Clef]: if useMeasures: # assume at start of measures p.getElementsByClass(stream.Measure).first().clef = clef.bestClef(p, recurse=True) @@ -390,7 +392,7 @@ def abcToStreamScore(abcHandler, inputM21=None): else: s = inputM21 - # meta data can be first + # metadata can be first md = metadata.Metadata() s.insert(0, md) @@ -448,7 +450,7 @@ def abcToStreamScore(abcHandler, inputM21=None): return s def abcToStreamOpus(abcHandler, inputM21=None, number=None): - '''Convert a multi-work stream into one or more complete works packed into a an Opus Stream. + '''Convert a multi-work stream into one or more complete works packed into an Opus Stream. If a `number` argument is given, and a work is defined by that number, that work is returned. @@ -470,7 +472,7 @@ def abcToStreamOpus(abcHandler, inputM21=None, number=None): scoreList = [] for key, value in sorted(abcDict.items()): # do not need to set work number, as that will be gathered - # with meta data in abcToStreamScore + # with metadata in abcToStreamScore try: sc = abcToStreamScore(value) scoreList.append(sc) @@ -485,8 +487,8 @@ def abcToStreamOpus(abcHandler, inputM21=None, number=None): return opus -# noinspection SpellCheckingInspection def reBar(music21Part, *, inPlace=False): + # noinspection PyShadowingNames,SpellCheckingInspection ''' Re-bar overflow measures using the last known time signature. @@ -495,7 +497,6 @@ def reBar(music21Part, *, inPlace=False): 'Aililiu na Gamhna, S.35' >>> music21Part = irl2[1] - The whole part is in 2/4 time, but there are some measures expressed in 4/4 time without an explicit time signature change, an error in abc parsing due to the omission of barlines. The method will split those measures such that they conform @@ -664,11 +665,11 @@ def testChords(self): # check pitches in chords; sharps are applied due to key signature match = [p.nameWithOctave for p in s.parts[1].flatten().getElementsByClass( - 'Chord')[4].pitches] + chord.Chord)[4].pitches] self.assertEqual(match, ['F#4', 'D4', 'B3']) match = [p.nameWithOctave for p in s.parts[1].flatten().getElementsByClass( - 'Chord')[3].pitches] + chord.Chord)[3].pitches] self.assertEqual(match, ['E4', 'C#4', 'A3']) # s.show() @@ -877,17 +878,15 @@ def testNoChord(self): ''' score = converter.parse(target_str, format='abc') - self.assertEqual(len(list(score.flatten().getElementsByClass( - 'ChordSymbol'))), 9) - self.assertEqual(len(list(score.flatten().getElementsByClass( - 'NoChord'))), 4) + self.assertEqual(len(score[harmony.ChordSymbol]), 9) + self.assertEqual(len(score[harmony.NoChord]), 4) score = harmony.realizeChordSymbolDurations(score) - self.assertEqual(8, score.getElementsByClass(harmony.ChordSymbol)[ - -1].quarterLength) - self.assertEqual(4, score.getElementsByClass(harmony.ChordSymbol)[ - 0].quarterLength) + self.assertEqual(8, score.getElementsByClass(harmony.ChordSymbol) + .last().quarterLength) + self.assertEqual(4, score.getElementsByClass(harmony.ChordSymbol) + .first().quarterLength) def testAbcKeyImport(self): from music21 import abcFormat @@ -944,15 +943,14 @@ def testRepeatBracketsA(self): # s.show() # one start, one end # s.parts[0].show('t') - self.assertEqual(len(s['Repeat']), 2) + self.assertEqual(len(s[bar.Repeat]), 2) # s.show() # this has a 1 note pickup # has three repeat bars; first one is implied s = converter.parse(testFiles.draughtOfAle) - self.assertEqual(len(s['Repeat']), 3) - self.assertEqual(s.parts[0].getElementsByClass( - 'Measure')[0].notes[0].pitch.nameWithOctave, 'D4') + self.assertEqual(len(s[bar.Repeat]), 3) + self.assertEqual(s[note.Note].first().pitch.nameWithOctave, 'D4') # new problem case: s = converter.parse(testFiles.hectorTheHero) @@ -978,19 +976,19 @@ def testMetronomeMarkA(self): from music21.abcFormat import testFiles from music21 import converter s = converter.parse(testFiles.fullRiggedShip) - mmStream = s.flatten().getElementsByClass('TempoIndication') + mmStream = s.flatten().getElementsByClass(tempo.TempoIndication) self.assertEqual(len(mmStream), 1) self.assertEqual(str(mmStream[0]), '') s = converter.parse(testFiles.aleIsDear) - mmStream = s.flatten().getElementsByClass('TempoIndication') + mmStream = s.flatten().getElementsByClass(tempo.TempoIndication) # this is a two-part pieces, and this is being added for each part # not sure if this is a problem self.assertEqual(len(mmStream), 2) self.assertEqual(str(mmStream[0]), '') s = converter.parse(testFiles.theBeggerBoy) - mmStream = s.flatten().getElementsByClass('TempoIndication') + mmStream = s[tempo.TempoIndication] # this is a two-part pieces, and this is being added for each part # not sure if this is a problem self.assertEqual(len(mmStream), 1) diff --git a/music21/alpha/analysis/fixer.py b/music21/alpha/analysis/fixer.py index aa72dd44f7..b2075bcc9e 100644 --- a/music21/alpha/analysis/fixer.py +++ b/music21/alpha/analysis/fixer.py @@ -23,8 +23,7 @@ from music21 import stream # noinspection PyShadowingBuiltins -_T = TypeVar('_T') - +OMRMidiFixerType = TypeVar('OMRMidiFixerType', bound='OMRMidiFixer') class OMRMidiFixer: ''' @@ -392,7 +391,7 @@ def addOrnament(self, return True return False - def fix(self: _T, *, show=False, inPlace=True) -> Optional[_T]: + def fix(self: OMRMidiFixerType, *, show=False, inPlace=True) -> Optional[OMRMidiFixerType]: ''' Corrects missed ornaments in omrStream according to midiStream :param show: Whether to show results @@ -443,6 +442,8 @@ def fix(self: _T, *, show=False, inPlace=True) -> Optional[_T]: if not inPlace: return TrillFixer(sa.changes, sa.targetStream, sa.sourceStream) + else: + return None def getNotesWithinDuration(startingGeneralNote, totalDuration, referenceStream=None): ''' diff --git a/music21/alpha/analysis/testFiles.py b/music21/alpha/analysis/testFiles.py index 2c39841250..8aee720801 100644 --- a/music21/alpha/analysis/testFiles.py +++ b/music21/alpha/analysis/testFiles.py @@ -11,7 +11,11 @@ import os import inspect -pathName = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) +def _dummy(): + pass # something for getting our path name + + +pathName = os.path.dirname(os.path.abspath(inspect.getfile(_dummy))) K525_short_dir = pathName + os.sep + 'testfiles' + os.sep + 'K525' K525_short_midi_path = K525_short_dir + os.sep + 'k525short_midi_ms_parsed.xml' diff --git a/music21/analysis/correlate.py b/music21/analysis/correlate.py index dcff0dc4eb..21bf9db417 100644 --- a/music21/analysis/correlate.py +++ b/music21/analysis/correlate.py @@ -21,8 +21,7 @@ from music21 import dynamics from music21 import environment -_MOD = 'analysis.correlate' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('analysis.correlate') # ------------------------------------------------------------------------------ @@ -82,7 +81,6 @@ def _findActive(self, objNameSrc=None, objNameDst=None): # dst object is within the source objects boundaries # if so, append it to the source object's dictionary for element in streamFlat.getElementsByClass(objNameDst): - # print(_MOD, 'dst', element) dstStart = element.offset dstEnd = dstStart + element.duration.quarterLength diff --git a/music21/analysis/discrete.py b/music21/analysis/discrete.py index b987ebff03..71dffac873 100644 --- a/music21/analysis/discrete.py +++ b/music21/analysis/discrete.py @@ -39,8 +39,7 @@ from music21 import environment -_MOD = 'analysis.discrete' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('analysis.discrete') # ----------------------------------------------------------------------------- diff --git a/music21/analysis/enharmonics.py b/music21/analysis/enharmonics.py index 1ff5c28641..33eba52656 100644 --- a/music21/analysis/enharmonics.py +++ b/music21/analysis/enharmonics.py @@ -20,8 +20,7 @@ from music21 import musedata from music21 import environment -_MOD = 'analysis.enharmonics' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('analysis.enharmonics') class EnharmonicsException(exceptions21.Music21Exception): diff --git a/music21/analysis/metrical.py b/music21/analysis/metrical.py index d49d9fbc53..6b9419b52a 100644 --- a/music21/analysis/metrical.py +++ b/music21/analysis/metrical.py @@ -22,8 +22,8 @@ from music21 import stream from music21 import environment -_MOD = "analysis.metrical" -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('analysis.metrical') + def labelBeatDepth(streamIn): r''' diff --git a/music21/analysis/neoRiemannian.py b/music21/analysis/neoRiemannian.py index 86a757966d..7aff1df987 100644 --- a/music21/analysis/neoRiemannian.py +++ b/music21/analysis/neoRiemannian.py @@ -23,8 +23,7 @@ from music21.analysis import enharmonics from music21 import environment -_MOD = 'analysis.neoRiemannian' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('analysis.neoRiemannian') # TODO: change doctests from passing on exceptions to raising them and trapping them. diff --git a/music21/analysis/reduceChordsOld.py b/music21/analysis/reduceChordsOld.py index 0c53814f1f..fbe321b0c8 100644 --- a/music21/analysis/reduceChordsOld.py +++ b/music21/analysis/reduceChordsOld.py @@ -294,7 +294,7 @@ def reduceThisMeasure(self, mI, measureIndex, maxChords, closedPosition, forceOc cLastEnd = newOffset + cElCopy.quarterLength m.coreInsert(newOffset, cElCopy, ignoreSort=True) - tsContext = mI.parts.first().getContextByClass('TimeSignature') + tsContext = mI.parts.first().getContextByClass(meter.TimeSignature) if tsContext is not None: if round(tsContext.barDuration.quarterLength - cLastEnd, 6) != 0.0: cLast.quarterLength += tsContext.barDuration.quarterLength - cLastEnd diff --git a/music21/analysis/reduction.py b/music21/analysis/reduction.py index 5b8e8323a1..55d68fbdc2 100644 --- a/music21/analysis/reduction.py +++ b/music21/analysis/reduction.py @@ -31,9 +31,7 @@ from music21 import stream from music21 import environment -_MOD = "analysis.reduction" -environLocal = environment.Environment(_MOD) - +environLocal = environment.Environment('analysis.reduction') # ------------------------------------------------------------------------------ diff --git a/music21/analysis/segmentByRests.py b/music21/analysis/segmentByRests.py index 0056518759..81d41e54fd 100644 --- a/music21/analysis/segmentByRests.py +++ b/music21/analysis/segmentByRests.py @@ -20,8 +20,7 @@ from music21 import environment -_MOD = 'analysis.segmentByRests' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('analysis.segmentByRests') # ------------------------------------------------------------------------------ diff --git a/music21/analysis/transposition.py b/music21/analysis/transposition.py index c66fa9b0d3..2d5cd52d59 100644 --- a/music21/analysis/transposition.py +++ b/music21/analysis/transposition.py @@ -17,8 +17,7 @@ from music21 import chord from music21 import environment -_MOD = 'analysis.transposition' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('analysis.transposition') class TranspositionException(exceptions21.Music21Exception): diff --git a/music21/analysis/windowed.py b/music21/analysis/windowed.py index 3a6d4fb245..09e71675fc 100644 --- a/music21/analysis/windowed.py +++ b/music21/analysis/windowed.py @@ -33,8 +33,7 @@ from music21 import environment -_MOD = 'analysis.windowed' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('analysis.windowed') # ----------------------------------------------------------------------------- diff --git a/music21/articulations.py b/music21/articulations.py index be6a332304..c8acaf3125 100644 --- a/music21/articulations.py +++ b/music21/articulations.py @@ -88,8 +88,7 @@ from music21 import environment from music21 import style -_MOD = 'articulations' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('articulations') diff --git a/music21/audioSearch/__init__.py b/music21/audioSearch/__init__.py index 2350e03ddb..1e0e238c69 100644 --- a/music21/audioSearch/__init__.py +++ b/music21/audioSearch/__init__.py @@ -59,8 +59,7 @@ from music21.audioSearch import transcriber from music21 import environment -_MOD = 'audioSearch' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('audioSearch') audioChunkLength = 1024 recordSampleRate = 44100 diff --git a/music21/audioSearch/recording.py b/music21/audioSearch/recording.py index 15edb6ef04..e6941ba16f 100644 --- a/music21/audioSearch/recording.py +++ b/music21/audioSearch/recording.py @@ -28,8 +28,7 @@ from music21 import exceptions21 from music21 import environment -_MOD = "audioSearch.recording" -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('audioSearch.recording') ### diff --git a/music21/audioSearch/scoreFollower.py b/music21/audioSearch/scoreFollower.py index 9e28a9d46b..bb41b7b4a9 100644 --- a/music21/audioSearch/scoreFollower.py +++ b/music21/audioSearch/scoreFollower.py @@ -20,12 +20,10 @@ from music21 import stream from music21 import environment -_MOD = 'audioSearch.scoreFollower' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('audioSearch.scoreFollower') class ScoreFollower: - def __init__(self, scoreStream=None): self.scoreStream = scoreStream if scoreStream is not None: diff --git a/music21/audioSearch/transcriber.py b/music21/audioSearch/transcriber.py index 96842c8100..c7cefd91d0 100644 --- a/music21/audioSearch/transcriber.py +++ b/music21/audioSearch/transcriber.py @@ -17,8 +17,7 @@ from music21 import scale from music21 import environment -_MOD = 'audioSearch.transcriber' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('audioSearch.transcriber') def runTranscribe(show=True, plot=True, useMic=True, diff --git a/music21/bar.py b/music21/bar.py index 6d9345ead6..c29f442a90 100644 --- a/music21/bar.py +++ b/music21/bar.py @@ -23,8 +23,7 @@ from music21 import environment -_MOD = 'bar' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('bar') # ------------------------------------------------------------------------------ diff --git a/music21/base.py b/music21/base.py index f58b9ea7a1..6aa67ed361 100644 --- a/music21/base.py +++ b/music21/base.py @@ -36,6 +36,8 @@ >>> base.Music21Object ''' +from __future__ import annotations + import copy import sys import types @@ -53,11 +55,14 @@ FrozenSet, Iterable, List, + Literal, Optional, Union, Tuple, Type, TypeVar, + cast, + overload, ) from music21 import common @@ -114,11 +119,7 @@ Music21Exception = exceptions21.Music21Exception - -# ?? pylint does not think that this was used... - -_MOD = 'base' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('base') _missingImport = [] for modName in ('matplotlib', 'numpy'): @@ -145,7 +146,7 @@ class ElementException(exceptions21.Music21Exception): # ----------------------------------------------------------------------------- # for contextSites searches... -ContextTuple = namedtuple('ContextTuple', 'site offset recurseType') +ContextTuple = namedtuple('ContextTuple', ['site', 'offset', 'recurseType']) # pseudo class for returning splitAtX() type commands. @@ -199,7 +200,7 @@ class Groups(list): # no need to inherit from slotted object >>> g ['hello'] - >>> g.append(5) + >>> g.append(5) # type: ignore Traceback (most recent call last): music21.exceptions21.GroupException: Only strings can be used as group names, not 5 ''' @@ -217,16 +218,16 @@ def _validName(self, value: str): # if ' ' in value: # raise exceptions21.GroupException('Spaces are not allowed as group names') - def append(self, value: Union[int, str]) -> None: + def append(self, value: str) -> None: self._validName(value) if not list.__contains__(self, value): list.append(self, value) - def __setitem__(self, i: int, y: Union[int, str]): + def __setitem__(self, i, y): self._validName(y) super().__setitem__(i, y) - def __eq__(self, other: 'Groups'): + def __eq__(self, other: object): ''' Test Group equality. In normal lists, order matters; here it does not. More like a set. @@ -313,7 +314,7 @@ class Music21Object(prebase.ProtoM21Object): _styleClass: Type[style.Style] = style.Style # define order for presenting names in documentation; use strings - _DOC_ORDER = [] + _DOC_ORDER: List[str] = [] # documentation for all attributes (not properties or methods) _DOC_ATTR = { @@ -490,10 +491,10 @@ def _deepcopySubclassable(self: _M21T, # this can be done much faster in most cases... d = self._duration if d is not None: - clientStore = self._duration._client - self._duration._client = None - newValue = copy.deepcopy(self._duration, memo) - self._duration._client = clientStore + clientStore = d._client + d._client = None + newValue = copy.deepcopy(d, memo) + d._client = clientStore newValue.client = new setattr(new, '_duration', newValue) @@ -636,7 +637,7 @@ def _reprInternal(self) -> str: return super()._reprInternal() reprId = self.id try: - reprId = hex(reprId) + reprId = hex(int(reprId)) except (ValueError, TypeError): pass return f'id={reprId}' @@ -747,6 +748,7 @@ def style(self) -> 'music21.style.Style': if not self.hasStyleInformation: StyleClass = self._styleClass self._style = StyleClass() + assert self._style is not None # for mypy. return self._style @style.setter @@ -832,11 +834,30 @@ def clearCache(self, **keywords): ''' self._cache = {} + @overload + def getOffsetBySite( + self, + site: Union['music21.stream.Stream', None], + *, + returnSpecial: Literal[False] = False, + ) -> Union[float, fractions.Fraction]: + return 0.0 # dummy until Astroid #1015 is fixed. Replace with ... + + @overload + def getOffsetBySite( + self, + site: Union['music21.stream.Stream', None], + *, + returnSpecial: bool = False, + ) -> Union[float, fractions.Fraction, str]: + return 0.0 # dummy until Astroid #1015 is fixed. Replace with ... + # using bool instead of Literal[True] because of + def getOffsetBySite( self, - site: 'music21.stream.Stream', + site: Union['music21.stream.Stream', None], *, - returnSpecial=False, + returnSpecial: bool = False, ) -> Union[float, fractions.Fraction, str]: ''' If this class has been registered in a container such as a Stream, @@ -917,7 +938,7 @@ def getOffsetBySite( try: a = None - tryOrigin = self + tryOrigin: Music21Object = self originMemo = set() maxSearch = 100 while a is None: @@ -934,12 +955,16 @@ def getOffsetBySite( else: return site.highestTime - tryOrigin = self.derivation.origin + possiblyNoneTryOrigin = self.derivation.origin + if possiblyNoneTryOrigin is None: + raise e + tryOrigin = possiblyNoneTryOrigin + if id(tryOrigin) in originMemo: raise e originMemo.add(id(tryOrigin)) maxSearch -= 1 # prevent infinite recursive searches... - if tryOrigin is None or maxSearch < 0: + if maxSearch < 0: raise e return a except SitesException as se: @@ -1207,15 +1232,40 @@ def purgeLocations(self, rescanIsDead=False) -> None: # -------------------------------------------------------------------------------- # contexts... + @overload + def getContextByClass( + self, + className: Type[_M21T], + *, + getElementMethod=ElementSearch.AT_OR_BEFORE, + sortByCreationTime=False, + followDerivation=True, + priorityTargetOnly=False, + ) -> Union[_M21T, None]: + return None # until Astroid #1015 + + @overload def getContextByClass( self, - className, + className: Union[str, None], *, getElementMethod=ElementSearch.AT_OR_BEFORE, sortByCreationTime=False, followDerivation=True, priorityTargetOnly=False, - ) -> Optional[_M21T]: + ) -> Union[Music21Object, None]: + return None # until Astroid #1015 + + + def getContextByClass( + self, + className: Union[Type[_M21T], str, None], + *, + getElementMethod: ElementSearch = ElementSearch.AT_OR_BEFORE, + sortByCreationTime=False, + followDerivation=True, + priorityTargetOnly=False, + ) -> Union[_M21T, Music21Object, None]: # noinspection PyShadowingNames ''' A very powerful method in music21 of fundamental importance: Returns @@ -1259,10 +1309,10 @@ def getContextByClass( >>> b - Now when we run `getContextByClass('TimeSignature')` on c, we get a + Now when we run `getContextByClass(meter.TimeSignature)` on c, we get a time signature of 1/4. - >>> c.getContextByClass('TimeSignature') + >>> c.getContextByClass(meter.TimeSignature) Doing what we just did wouldn't be hard to do with other methods, @@ -1272,9 +1322,20 @@ def getContextByClass( there's nothing there. It goes to the previous measure and searches that one backwards until it gets the proper TimeSignature of 2/4: + >>> b.getContextByClass(meter.TimeSignature) + + + For backwards compatibility you can also pass in a string of the + class name: + >>> b.getContextByClass('TimeSignature') + But if you use Python typing or a typing-aware IDE, then the first call + (with class name) will signal that it is returning a TimeSignature object + and allow for error detection, autocomplete, etc. The latter call + (with string) will only know that some Music21Object was returned. + The method is smart enough to stop when it gets to the beginning of the part. This is all you need to know for most uses. The rest of the docs are for advanced uses: @@ -1313,33 +1374,33 @@ def getContextByClass( Demonstrations of these keywords: - Because `b` is a `Note`, `.getContextByClass('Note')` will only find itself: + Because `b` is a `Note`, `.getContextByClass(note.Note)` will only find itself: - >>> b.getContextByClass('Note') is b + >>> b.getContextByClass(note.Note) is b True To get the previous `Note`, use `getElementMethod=ElementSearch.BEFORE`: - >>> a = b.getContextByClass('Note', getElementMethod=ElementSearch.BEFORE) + >>> a = b.getContextByClass(note.Note, getElementMethod=ElementSearch.BEFORE) >>> a - This is similar to `.previous('Note')`, though that method is a bit more + This is similar to `.previous(note.Note)`, though that method is a bit more sophisticated: - >>> b.previous('Note') + >>> b.previous(note.Note) To get the following `Note` use `getElementMethod=ElementSearch.AFTER`: - >>> c = b.getContextByClass('Note', getElementMethod=ElementSearch.AFTER) + >>> c = b.getContextByClass(note.Note, getElementMethod=ElementSearch.AFTER) >>> c - This is similar to `.next('Note')`, though, again, that method is a bit more + This is similar to `.next(note.Note)`, though, again, that method is a bit more sophisticated: - >>> b.next('Note') + >>> b.next(note.Note) A Stream might contain several elements at the same offset, leading to @@ -1374,7 +1435,7 @@ def getContextByClass( >>> n = note.Note('D') >>> m.insert(2.0, n) >>> try: - ... n.getContextByClass('Part').elementOffset(n) + ... n.getContextByClass(stream.Part).elementOffset(n) ... except Music21Exception: ... print('not there') not there @@ -1384,35 +1445,37 @@ def getContextByClass( >>> import copy >>> n2 = copy.deepcopy(n) >>> try: - ... n2.getContextByClass('Measure').elementOffset(n2) + ... n2.getContextByClass(stream.Measure).elementOffset(n2) ... except Music21Exception: ... print('not there') not there A measure context is being found, but only through the derivation chain. - >>> n2.getContextByClass('Measure') + >>> n2.getContextByClass(stream.Measure) To prevent this error, use the `followDerivation=False` setting - >>> print(n2.getContextByClass('Measure', followDerivation=False)) + >>> print(n2.getContextByClass(stream.Measure, followDerivation=False)) None Or if you want the offset of the element following the derivation chain, call `getOffsetBySite()` on the object: - >>> n2.getOffsetBySite(n2.getContextByClass('Measure')) + >>> n2.getOffsetBySite(n2.getContextByClass(stream.Measure)) 2.0 * changed in v.5.7 -- added followDerivation=False and made everything but the class keyword only * added in v.6 -- added priorityTargetOnly -- see contextSites for description. * added in v.7 -- added getElementMethod `all` and `ElementSearch` enum. + * changed in v.8 -- class-based calls return properly typed items. Putting + multiple types into className (never documented) is no longer allowed. Raises `ValueError` if `getElementMethod` is not a value in `ElementSearch`. - >>> n2.getContextByClass('TextExpression', getElementMethod='invalid') + >>> n2.getContextByClass(expressions.TextExpression, getElementMethod='invalid') Traceback (most recent call last): ValueError: Invalid getElementMethod: invalid @@ -1474,7 +1537,8 @@ def payloadExtractor(checkSite, flatten, innerPositionStart): flatten can be True, 'semiFlat', or False. ''' - siteTree = checkSite.asTree(flatten=flatten, classList=className) + classList = None if not className else (className,) + siteTree = checkSite.asTree(flatten=flatten, classList=classList) if getElementMethod in OFFSET_METHODS: # these methods match only by offset. Used in .getBeat among other places if getElementMethod in (ElementSearch.BEFORE_OFFSET, @@ -1606,10 +1670,7 @@ def wellFormed(checkContextEl, checkSite) -> bool: if priorityTargetOnly and followDerivation: raise ValueError('priorityTargetOnly and followDerivation cannot both be True') - if className and not common.isListLike(className): - className = (className,) - - if getElementMethod in AT_METHODS and not self.classSet.isdisjoint(className): + if getElementMethod in AT_METHODS and className in self.classSet: return self for site, positionStart, searchType in self.contextSites( @@ -1634,7 +1695,7 @@ def wellFormed(checkContextEl, checkSite) -> bool: if searchType != 'elementsOnly': # flatten or elementsFirst if (getElementMethod in AFTER_METHODS and (not className - or not site.classSet.isdisjoint(className))): + or className in site.classSet)): if getElementMethod in NOT_SELF_METHODS and self is site: pass elif getElementMethod not in NOT_SELF_METHODS: # for 'After' we can't do the @@ -1653,7 +1714,7 @@ def wellFormed(checkContextEl, checkSite) -> bool: if (getElementMethod in BEFORE_METHODS and (not className - or not site.classSet.isdisjoint(className))): + or className in site.classSet)): if getElementMethod in NOT_SELF_METHODS and self is site: pass else: @@ -1859,18 +1920,19 @@ def contextSites( if callerFirst is None: callerFirst = self if self.isStream and self not in memo: - recursionType = self.recursionType + streamSelf = cast('music21.stream.Stream', self) + recursionType = streamSelf.recursionType environLocal.printDebug( f'Caller first is {callerFirst} with offsetAppend {offsetAppend}') if returnSortTuples: - selfSortTuple = self.sortTuple().modify( + selfSortTuple = streamSelf.sortTuple().modify( offset=0.0, priority=float('-inf') ) - yield ContextTuple(self, selfSortTuple, recursionType) + yield ContextTuple(streamSelf, selfSortTuple, recursionType) else: - yield ContextTuple(self, 0.0, recursionType) - memo.append(self) + yield ContextTuple(streamSelf, 0.0, recursionType) + memo.append(streamSelf) if priorityTarget is None and sortByCreationTime is False: priorityTarget = self.activeSite @@ -1985,7 +2047,7 @@ def getAllContextsByClass(self, className): >>> s.append(n) - >>> for ts in n.getAllContextsByClass('TimeSignature'): + >>> for ts in n.getAllContextsByClass(meter.TimeSignature): ... print(ts, ts.offset) 1.0 0.0 @@ -2001,7 +2063,10 @@ def getAllContextsByClass(self, className): # ------------------------------------------------------------------------- - def next(self, className=None, *, activeSiteOnly=False): + def next(self, + className: Union[Type[Music21Object], str, None] = None, + *, + activeSiteOnly=False): ''' Get the next element found in the activeSite (or other Sites) of this Music21Object. @@ -2066,7 +2131,7 @@ def next(self, className=None, *, activeSiteOnly=False): print its Part for more information... >>> while o is not None: - ... print(o, o.getContextByClass('Part')) + ... print(o, o.getContextByClass(stream.Part)) ... o = o.next() @@ -2091,7 +2156,7 @@ def next(self, className=None, *, activeSiteOnly=False): while maxRecurse: nextEl = thisElForNext.getContextByClass( className=className, - getElementMethod='getElementAfterNotSelf', + getElementMethod=ElementSearch.AFTER_NOT_SELF, followDerivation=not activeSiteOnly, priorityTargetOnly=activeSiteOnly, ) @@ -2099,6 +2164,7 @@ def next(self, className=None, *, activeSiteOnly=False): callContinue = False for singleSiteContext, unused_positionInContext, unused_recurseType in allSiteContexts: if nextEl is singleSiteContext: + nextEl = cast('music21.stream.Stream', nextEl) if nextEl and nextEl[0] is not self: # has elements return nextEl[0] @@ -2117,7 +2183,10 @@ def next(self, className=None, *, activeSiteOnly=False): if maxRecurse == 0: raise Music21Exception('Maximum recursion!') - def previous(self, className=None, *, activeSiteOnly=False): + def previous(self, + className: Union[Type[Music21Object], str, None] = None, + *, + activeSiteOnly=False): ''' Get the previous element found in the activeSite or other .sites of this Music21Object. @@ -2194,12 +2263,10 @@ def previous(self, className=None, *, activeSiteOnly=False): activeS = self.activeSite # might be None... if activeS is None: return None - if className is not None and not common.isListLike(className): - className = (className,) - asTree = activeS.asTree(classList=className, flatten=False) + asTree = activeS.asTree(classList=[className], flatten=False) prevNode = asTree.getNodeBefore(self.sortTuple()) if prevNode is None: - if className is None or not activeS.classSet.isdisjoint(className): + if className is None or className in activeS.classSet: return activeS else: return None @@ -2380,6 +2447,8 @@ def offset(self) -> OffsetQL: When in doubt, use `.getOffsetBySite(streamObj)` which is safer or streamObj.elementOffset(self) which is 3x faster. + + Changed in v.8 -- using a Duration object as an offset is not allowed. ''' # There is a branch that does slow searches. # See test/testSerialization to have it active. @@ -2418,10 +2487,6 @@ def offset(self, value: OffsetQLIn): except TypeError: offset = value - if hasattr(value, 'quarterLength'): - # probably a Duration object, but could be something else -- in any case, we'll take it. - offset = value.quarterLength - if self.activeSite is not None: self.activeSite.setElementOffset(self, offset) else: @@ -3007,9 +3072,10 @@ def splitAtQuarterLength( eRemain = copy.deepcopy(self) # clear lyrics from remaining parts - if hasattr(eRemain, 'lyrics') and not callable(eRemain.lyrics): - # lyrics is a function on Streams... - eRemain.lyrics = [] # pylint: disable=attribute-defined-outside-init + if isinstance(eRemain, note.GeneralNote): + emptyLyrics: List['music21.note.Lyric'] = [] + # not sure why isinstance check is not picking this up. + eRemain.lyrics = emptyLyrics # pylint: disable=attribute-defined-outside-init spannerList = [] for listType in ('expressions', 'articulations'): @@ -3059,7 +3125,7 @@ def splitAtQuarterLength( # set ties if addTies and isinstance(e, (note.Note, note.Unpitched)): forceEndTieType = 'stop' - if hasattr(e, 'tie') and e.tie is not None: + if e.tie is not None: # the last tie of what was formally a start should # continue if e.tie.type == 'start': @@ -3073,15 +3139,17 @@ def splitAtQuarterLength( elif e.tie.type == 'continue': forceEndTieType = 'continue' # keep continue if already set - elif hasattr(e, 'tie'): + else: + # not sure why this is not being picked up by isinstance check e.tie = tie.Tie('start') # pylint: disable=attribute-defined-outside-init - # #need a tie object - if hasattr(eRemain, 'tie'): - # pylint: disable=attribute-defined-outside-init - eRemain.tie = tie.Tie(forceEndTieType) + if isinstance(eRemain, (note.Note, note.Unpitched)): + # not sure why this is not being picked up by isinstance check + newTie = tie.Tie(forceEndTieType) + eRemain.tie = newTie # pylint: disable=attribute-defined-outside-init - elif addTies and isinstance(e, chord.Chord): + elif addTies and isinstance(e, chord.Chord) and isinstance(eRemain, chord.Chord): + # the last isinstance is redundant, but MyPy needs it. for i in range(len(e.notes)): component = e.notes[i] remainComponent = eRemain.notes[i] @@ -3106,10 +3174,12 @@ def splitAtQuarterLength( # hide accidentals on tied notes where previous note # had an accidental that was shown - if addTies and hasattr(e, 'pitches'): + if addTies and isinstance(e, note.NotRest) and isinstance(eRemain, note.NotRest): + # again -- second isinstance check is redundant for i, p in enumerate(e.pitches): remainP = eRemain.pitches[i] - if hasattr(p, 'accidental') and p.accidental is not None: + if p.accidental is not None and remainP.accidental is not None: + # again -- second remainP.accidental is not None check is redundant if not displayTiedAccidentals: # if False if p.accidental.displayType != 'even-tied': remainP.accidental.displayStatus = False @@ -3181,7 +3251,7 @@ def splitByQuarterLengths( stOut.spannerList = spannerList return stOut - def splitAtDurations(self: _M21T) -> _SplitTuple: + def splitAtDurations(self) -> _SplitTuple: ''' Takes a Music21Object (e.g., a note.Note) and returns a list of similar objects with only a single duration.DurationTuple in each. @@ -3397,7 +3467,9 @@ def _getMeasureOffset(self, includeMeasurePadding=True) -> Union[float, fraction >>> [n._getMeasureOffset(includeMeasurePadding=False) for n in m.notes] [0.0, 0.5, 1.0, 1.5] ''' - # TODO: v7 -- expose as public. + from music21 import stream + + # TODO: v8 -- expose as public. activeS = self.activeSite if activeS is not None and activeS.isMeasure: # environLocal.printDebug(['found activeSite as Measure, using for offset']) @@ -3409,7 +3481,7 @@ def _getMeasureOffset(self, includeMeasurePadding=True) -> Union[float, fraction # doing context search', 'self.activeSite', self.activeSite]) # testing sortByCreationTime == true; this may be necessary # as we often want the most recent measure - m = self.getContextByClass('Measure', sortByCreationTime=True) + m = self.getContextByClass(stream.Measure, sortByCreationTime=True) if m is not None: # environLocal.printDebug(['using found Measure for offset access']) try: @@ -3434,8 +3506,9 @@ def _getTimeSignatureForBeat(self) -> 'music21.meter.TimeSignature': extracted to make sure that all three of the routines use the same one. ''' - ts: Optional['music21.meter.TimeSignature'] = self.getContextByClass( - 'TimeSignature', + from music21 import meter + ts: Optional[meter.TimeSignature] = self.getContextByClass( + meter.TimeSignature, getElementMethod=ElementSearch.AT_OR_BEFORE_OFFSET ) if ts is None: @@ -3735,11 +3808,12 @@ def beatStrength(self) -> float: return float('nan') def _getSeconds(self) -> float: + from music21 import tempo # do not search of duration is zero if self.duration.quarterLength == 0.0: return 0.0 - ti = self.getContextByClass('TempoIndication') + ti = self.getContextByClass(tempo.TempoIndication) if ti is None: return float('nan') mm = ti.getSoundingMetronomeMark() @@ -3747,7 +3821,8 @@ def _getSeconds(self) -> float: return mm.durationToSeconds(self.duration) def _setSeconds(self, value: Union[int, float]) -> None: - ti = self.getContextByClass('TempoIndication') + from music21 import tempo + ti = self.getContextByClass(tempo.TempoIndication) if ti is None: raise Music21ObjectException('this object does not have a TempoIndication in Sites') mm = ti.getSoundingMetronomeMark() @@ -4634,7 +4709,7 @@ def testGetContextByClassA(self): # p.show('t') # if done with default args, we get the same object, as we are using # getElementAtOrBefore - self.assertEqual(str(mm2.getContextByClass('MetronomeMark')), + self.assertEqual(str(mm2.getContextByClass(tempo.MetronomeMark)), '') # if we provide the getElementMethod parameter, we can use # getElementBeforeOffset @@ -4854,10 +4929,10 @@ def testGetContextByClassB(self): n2 = m4[-1] # last element is a note # environLocal.printDebug(['getContextByClass()']) - # self.assertEqual(str(n1.getContextByClass('TimeSignature')), + # self.assertEqual(str(n1.getContextByClass(meter.TimeSignature)), # '') environLocal.printDebug(['getContextByClass()']) - self.assertEqual(str(n2.getContextByClass('TimeSignature')), + self.assertEqual(str(n2.getContextByClass(meter.TimeSignature)), '') def testNextA(self): @@ -4907,20 +4982,22 @@ def testNextB(self): p1.append(m2) n1next = n1.next() self.assertEqual(n1next, m2) - self.assertEqual(n1.next('Note'), n2) + self.assertEqual(n1.next(note.Note), n2) def testNextC(self): from music21 import corpus from music21 import stream + from music21 import meter + from music21 import key s = corpus.parse('bwv66.6') # getting time signature and key sig p1 = s.parts[0] nLast = p1.flatten().notes[-1] - self.assertEqual(str(nLast.previous('TimeSignature')), + self.assertEqual(str(nLast.previous(meter.TimeSignature)), '') - self.assertEqual(str(nLast.previous('KeySignature')), + self.assertEqual(str(nLast.previous(key.KeySignature)), 'f# minor') # iterating at the Measure level, showing usage of flattenLocalSites @@ -5130,7 +5207,10 @@ def testContextInconsistentArguments(self): obj = Music21Object() with self.assertRaises(ValueError): obj.getContextByClass( - editorial.Editorial, priorityTargetOnly=True, followDerivation=True) + ElementWrapper, + priorityTargetOnly=True, + followDerivation=True + ) # great isolation test, but no asserts for now... # def testPreviousA(self): diff --git a/music21/beam.py b/music21/beam.py index c4848b0878..1c3b408e6b 100644 --- a/music21/beam.py +++ b/music21/beam.py @@ -82,8 +82,7 @@ from music21 import style from music21.common.objects import EqualSlottedObjectMixin -_MOD = 'meter' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('beam') class BeamException(exceptions21.Music21Exception): diff --git a/music21/braille/segment.py b/music21/braille/segment.py index 96c66d98b4..49f181f97b 100644 --- a/music21/braille/segment.py +++ b/music21/braille/segment.py @@ -135,9 +135,10 @@ def setGroupingGlobals(): MAX_ELEMENTS_IN_SEGMENT = 48 # 8 measures of 6 notes, etc. each -_ThreeDigitNumber = collections.namedtuple('_ThreeDigitNumber', 'hundreds tens ones') +_ThreeDigitNumber = collections.namedtuple('_ThreeDigitNumber', ['hundreds', 'tens', 'ones']) -SegmentKey = collections.namedtuple('SegmentKey', 'measure ordinal affinity hand') +SegmentKey = collections.namedtuple('SegmentKey', + ['measure', 'ordinal', 'affinity', 'hand']) SegmentKey.__new__.__defaults__ = (0, 0, None, None) diff --git a/music21/braille/test.py b/music21/braille/test.py index bb755d480f..bca6b3d200 100644 --- a/music21/braille/test.py +++ b/music21/braille/test.py @@ -1239,7 +1239,7 @@ def test_drill08_1(self): "tinynotation: 4/4 a2 g8 f8 e4 d4 e4 f8 g8 a4 g4 c'8 b8 a4 g4 f4. e8 d2 " "c4 e4 a4 e'4 e'4 d'4 c'8 b8 a4 a'4 g'8 f'8 e'4 d'4 c'4 a8 b8 c'2", makeNotation=False) - bm.replace(bm.getElementsByClass('TimeSignature').first(), meter.TimeSignature('c')) + bm.replace(bm.getElementsByClass(meter.TimeSignature).first(), meter.TimeSignature('c')) bm.insert(0, tempo.TempoText('Andante maestoso')) bm.insert(0, tempo.MetronomeMark(number=92, referent=note.Note(type='quarter'))) bm.makeNotation(inPlace=True, cautionaryNotImmediateRepeat=False) @@ -1311,7 +1311,7 @@ def test_drill08_5(self): "a2 d'4 c'4 b-4 a4 b-4 c'4 d'2 e'-4 f'4 " "g'2 e'-4 c'4 f'2 d'4 b-4 e'-2 c'4 f4 d'2 b-4 b-4 c'2 " "b-4 c'4 d'4 b-4 c'4 d'4 b-4 c'4 b-4 a4 b-2", makeNotation=False) - bm.replace(bm.getElementsByClass('TimeSignature').first(), meter.TimeSignature('cut')) + bm.replace(bm.getElementsByClass(meter.TimeSignature).first(), meter.TimeSignature('cut')) bm.insert(0, key.KeySignature(-2)) bm.insert(0, tempo.TempoText('Ben marcato')) bm.insert(0, tempo.MetronomeMark(number=112, referent=note.Note(type='half'))) @@ -1549,7 +1549,7 @@ def test_drill09_2(self): "e'4~ e'8 f' e' b c' d' a b c' g a2~ a8 f " "g c' b a d' c' b e' d'2~ d'8 g' f' c' d' e' b c' d'4 a8 g a2.~ a8 r", makeNotation=False) - bm.replace(bm.getElementsByClass('TimeSignature').first(), meter.TimeSignature('c')) + bm.replace(bm.getElementsByClass(meter.TimeSignature).first(), meter.TimeSignature('c')) bm.insert(0, tempo.TempoText('Adagio e molto legato')) bm.makeNotation(inPlace=True, cautionaryNotImmediateRepeat=False) @@ -2963,7 +2963,7 @@ def test_example14_3(self): def test_example14_5(self): bm = converter.parse("tinynotation: 2/2 D8 r F r A r d r B-2 A4 r", makeNotation=False) - bm.getElementsByClass('TimeSignature').first().symbol = 'cut' + bm.getElementsByClass(meter.TimeSignature).first().symbol = 'cut' bm.insert(0, key.KeySignature(-1)) bm.makeNotation(inPlace=True, cautionaryNotImmediateRepeat=False) m0 = bm.getElementsByClass(stream.Measure).first() diff --git a/music21/braille/translate.py b/music21/braille/translate.py index 700afc60ce..0479c06249 100644 --- a/music21/braille/translate.py +++ b/music21/braille/translate.py @@ -465,7 +465,7 @@ def metadataToString(music21Metadata, returnBrailleUnicode=False): ''' >>> from music21.braille import translate >>> corelli = corpus.parse('monteverdi/madrigal.3.1.rntxt') - >>> mdObject = corelli.getElementsByClass('Metadata').first() + >>> mdObject = corelli.getElementsByClass(metadata.Metadata).first() >>> mdObject.__class__ >>> print(translate.metadataToString(mdObject)) diff --git a/music21/capella/fromCapellaXML.py b/music21/capella/fromCapellaXML.py index 8dfa825189..e25bccaa9b 100644 --- a/music21/capella/fromCapellaXML.py +++ b/music21/capella/fromCapellaXML.py @@ -206,7 +206,7 @@ def partScoreFromSystemScore(self, systemScore): print('part entries do not match partDict!') continue clefs = p.getElementsByClass(clef.Clef) - keySignatures = p.getElementsByClass('KeySignature') + keySignatures = p.getElementsByClass(key.KeySignature) lastClef = None lastKeySignature = None for c in clefs: diff --git a/music21/chord/__init__.py b/music21/chord/__init__.py index 02ef08f690..4d70fbd0d0 100644 --- a/music21/chord/__init__.py +++ b/music21/chord/__init__.py @@ -34,8 +34,7 @@ from music21.chord import tables from music21.common.decorators import cacheMethod -_MOD = 'chord' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('chord') _ChordType = TypeVar('_ChordType') diff --git a/music21/chord/tables.py b/music21/chord/tables.py index ee7250a54c..b1e835f069 100644 --- a/music21/chord/tables.py +++ b/music21/chord/tables.py @@ -19,14 +19,13 @@ from collections import namedtuple import unittest -from music21 import exceptions21 - from music21 import environment -_MOD = 'chord.tables' -environLocal = environment.Environment(_MOD) +from music21 import exceptions21 +environLocal = environment.Environment('chord.tables') -ChordTableAddress = namedtuple('ChordTableAddress', 'cardinality forteClass inversion pcOriginal') +ChordTableAddress = namedtuple('ChordTableAddress', + ['cardinality', 'forteClass', 'inversion', 'pcOriginal']) # ------------------------------------------------------------------------------ diff --git a/music21/clef.py b/music21/clef.py index c1f66b2663..f7335f7148 100644 --- a/music21/clef.py +++ b/music21/clef.py @@ -27,8 +27,7 @@ from music21 import pitch # for typing only from music21 import style -_MOD = 'clef' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('clef') class ClefException(exceptions21.Music21Exception): diff --git a/music21/common/pathTools.py b/music21/common/pathTools.py index 4a4c42ade5..bf6cdcbe07 100644 --- a/music21/common/pathTools.py +++ b/music21/common/pathTools.py @@ -19,7 +19,7 @@ 'cleanpath', ] -from typing import List, Union, Optional +from typing import List, Union, Optional, overload, Literal import inspect import os import pathlib @@ -144,7 +144,28 @@ def relativepath(path: str, start: Optional[str] = None) -> str: return os.path.relpath(path, start) -def cleanpath(path: Union[str, pathlib.Path], *, returnPathlib=None) -> Union[str, pathlib.Path]: +@overload +def cleanpath(path: pathlib.Path, *, + returnPathlib: Literal[None]) -> pathlib.Path: + return pathlib.Path('/') # dummy until Astroid #1015 is fixed. + +@overload +def cleanpath(path: str, *, + returnPathlib: Literal[None]) -> str: + return '/' # dummy until Astroid #1015 is fixed. + +@overload +def cleanpath(path: Union[str, pathlib.Path], *, + returnPathlib: Literal[True]) -> pathlib.Path: + return pathlib.Path('/') # dummy until Astroid #1015 is fixed. + +@overload +def cleanpath(path: Union[str, pathlib.Path], *, + returnPathlib: Literal[False]) -> str: + return '/' # dummy until Astroid #1015 is fixed. + +def cleanpath(path: Union[str, pathlib.Path], *, + returnPathlib: Union[bool, None] = 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 diff --git a/music21/configure.py b/music21/configure.py index a8864692b3..8bafbe3e19 100644 --- a/music21/configure.py +++ b/music21/configure.py @@ -30,8 +30,7 @@ from music21 import environment from music21 import exceptions21 -_MOD = 'configure' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('configure') _DOC_IGNORE_MODULE_OR_PACKAGE = True IGNORECASE = re.RegexFlag.IGNORECASE diff --git a/music21/converter/__init__.py b/music21/converter/__init__.py index 1b330a129b..d30af5138d 100644 --- a/music21/converter/__init__.py +++ b/music21/converter/__init__.py @@ -70,8 +70,7 @@ from music21 import _version from music21 import environment -_MOD = 'converter' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('converter') # ------------------------------------------------------------------------------ @@ -1645,7 +1644,7 @@ def testConversionMXTies(self): countTies = 0 countStartTies = 0 for p in a.parts: - post = p.recurse().notes[0].getContextByClass('Clef') + post = p.recurse().notes[0].getContextByClass(clef.Clef) self.assertIsInstance(post, clef.TenorClef) for n in p.recurse().notes: if n.tie is not None: diff --git a/music21/converter/subConverters.py b/music21/converter/subConverters.py index 4cfc76ed59..6fa2e075b1 100644 --- a/music21/converter/subConverters.py +++ b/music21/converter/subConverters.py @@ -34,8 +34,7 @@ from music21 import exceptions21 from music21 import environment -_MOD = 'converter.subConverters' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('converter.subConverters') # pylint complains when abstract methods are not overwritten, but that's okay. # pylint: disable=abstract-method diff --git a/music21/corpus/__init__.py b/music21/corpus/__init__.py index 009c2adde8..584b057ec1 100644 --- a/music21/corpus/__init__.py +++ b/music21/corpus/__init__.py @@ -67,8 +67,7 @@ from music21.exceptions21 import CorpusException from music21 import environment -_MOD = 'corpus' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('corpus') # ----------------------------------------------------------------------------- diff --git a/music21/corpus/chorales.py b/music21/corpus/chorales.py index d7d8e0cbc4..7b279e5383 100644 --- a/music21/corpus/chorales.py +++ b/music21/corpus/chorales.py @@ -21,8 +21,7 @@ class for easily iterating through the chorale collection. from music21 import exceptions21 from music21 import environment from music21 import metadata -_MOD = 'corpus.chorales' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('corpus.chorales') class ChoraleList: diff --git a/music21/corpus/virtual.py b/music21/corpus/virtual.py index 329b661420..b428735261 100644 --- a/music21/corpus/virtual.py +++ b/music21/corpus/virtual.py @@ -25,8 +25,7 @@ from music21 import common from music21 import environment -_MOD = 'converter.virtual' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('corpus.virtual') class VirtualWork: diff --git a/music21/corpus/work.py b/music21/corpus/work.py index 12909e7baa..481835b0f0 100644 --- a/music21/corpus/work.py +++ b/music21/corpus/work.py @@ -18,9 +18,9 @@ from music21 import prebase # ----------------------------------------------------------------------------- -CorpusWork = namedtuple('CorpusWork', 'title files virtual') -CorpusFile = namedtuple('CorpusFile', 'path title filename format ext') -# VirtualCorpusFile = namedtuple('VirtualCorpusFile', 'path title url format') +CorpusWork = namedtuple('CorpusWork', ['title', 'files', 'virtual']) +CorpusFile = namedtuple('CorpusFile', ['path', 'title', 'filename', 'format', 'ext']) +# VirtualCorpusFile = namedtuple('VirtualCorpusFile', ['path', 'title', 'url', 'format']) class DirectoryInformation(prebase.ProtoM21Object): diff --git a/music21/defaults.py b/music21/defaults.py index b11b788bf6..223f59c061 100644 --- a/music21/defaults.py +++ b/music21/defaults.py @@ -12,8 +12,6 @@ ''' Simple storage for data defaults used throughout music21. ''' - - import unittest from music21 import _version diff --git a/music21/derivation.py b/music21/derivation.py index 12566c4191..e340549915 100644 --- a/music21/derivation.py +++ b/music21/derivation.py @@ -25,8 +25,7 @@ # imported by stream from music21 import environment -_MOD = 'derivation' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('derivation') def derivationMethod(function): diff --git a/music21/duration.py b/music21/duration.py index f0300d1191..d247fcaebb 100644 --- a/music21/duration.py +++ b/music21/duration.py @@ -158,7 +158,7 @@ class TupletException(exceptions21.Music21Exception): ) -QuarterLengthConversion = namedtuple('QuarterLengthConversion', 'components tuplet') +QuarterLengthConversion = namedtuple('QuarterLengthConversion', ['components', 'tuplet']) def unitSpec(durationObjectOrObjects): diff --git a/music21/dynamics.py b/music21/dynamics.py index aff6a7fc72..39e2544049 100644 --- a/music21/dynamics.py +++ b/music21/dynamics.py @@ -24,8 +24,7 @@ from music21 import style from music21 import environment -_MOD = 'dynamics' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('dynamics') shortNames = ['pppppp', 'ppppp', 'pppp', 'ppp', 'pp', 'p', 'mp', diff --git a/music21/features/base.py b/music21/features/base.py index 2e881f036c..48e6b3543c 100644 --- a/music21/features/base.py +++ b/music21/features/base.py @@ -27,9 +27,7 @@ from music21.metadata.bundles import MetadataEntry from music21 import environment -_MOD = 'features.base' -environLocal = environment.Environment(_MOD) - +environLocal = environment.Environment('features.base') # ------------------------------------------------------------------------------ @@ -347,7 +345,7 @@ def __getitem__(self, key): if lastKey in self.keysToMethods: prepared = self.keysToMethods[lastKey](self, prepared) elif lastKey.startswith('getElementsByClass('): - classToGet = lastKey[len('getElementsByClass('):-1] + classToGet: str = lastKey[len('getElementsByClass('):-1] prepared = prepared.getElementsByClass(classToGet) else: raise AttributeError(f'no such attribute: {lastKey} in {key}') diff --git a/music21/features/jSymbolic.py b/music21/features/jSymbolic.py index b4f2e70de9..fdc929d230 100644 --- a/music21/features/jSymbolic.py +++ b/music21/features/jSymbolic.py @@ -27,8 +27,7 @@ from music21.instrument import Instrument from music21 import environment -_MOD = 'features.jSymbolic' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('features.jSymbolic') # ------------------------------------------------------------------------------ diff --git a/music21/features/native.py b/music21/features/native.py index 3f6f536a87..83d8a54d01 100644 --- a/music21/features/native.py +++ b/music21/features/native.py @@ -17,8 +17,7 @@ from music21.features import base as featuresModule from music21 import text from music21 import environment -_MOD = 'features.native' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('features.native') # ------------------------------------------------------------------------------ # ideas for other music21 features extractors diff --git a/music21/freezeThaw.py b/music21/freezeThaw.py index e6ef627724..17aac80694 100644 --- a/music21/freezeThaw.py +++ b/music21/freezeThaw.py @@ -86,8 +86,7 @@ # from music21.tree.trees import ElementTree from music21 import environment -_MOD = 'freezeThaw' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('freezeThaw') # ----------------------------------------------------------------------------- diff --git a/music21/graph/__init__.py b/music21/graph/__init__.py index f81f355f80..d3dc300e97 100644 --- a/music21/graph/__init__.py +++ b/music21/graph/__init__.py @@ -55,8 +55,7 @@ from music21.graph import utilities from music21 import environment -_MOD = 'graph' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('graph') def plotStream( diff --git a/music21/graph/axis.py b/music21/graph/axis.py index fbcba25c0d..ae9df15f45 100644 --- a/music21/graph/axis.py +++ b/music21/graph/axis.py @@ -886,7 +886,7 @@ def ticks(self): >>> ax.mostMeasureTicksToShow = 4 >>> ax.ticks() [(0.0, '0'), (9.0, '3'), (21.0, '6'), (29.0, '8')] - >>> m5 = soprano.getElementsByClass('Measure')[5] + >>> m5 = soprano.getElementsByClass(stream.Measure)[5] >>> m5.number 5 >>> m5.rightBarline = bar.Barline('double') @@ -1043,8 +1043,8 @@ def getOffsetMap(self): # if we have part-like sub streams; we can assume that all parts # have parallel measures start times here for simplicity # take the top part - offsetMap = s.getElementsByClass( - 'Stream')[0].measureOffsetMap([stream.Measure]) + offsetMap = (s.getElementsByClass(stream.Stream).first() + .measureOffsetMap([stream.Measure])) elif s.hasMeasures(): offsetMap = s.measureOffsetMap([stream.Measure]) else: diff --git a/music21/graph/plot.py b/music21/graph/plot.py index feee666144..69ff416d10 100644 --- a/music21/graph/plot.py +++ b/music21/graph/plot.py @@ -48,8 +48,7 @@ from music21.analysis import windowed from music21 import environment -_MOD = 'graph.plot' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('graph.plot') def _mergeDicts(a, b): diff --git a/music21/graph/primitives.py b/music21/graph/primitives.py index c4a25e8a63..1700288817 100644 --- a/music21/graph/primitives.py +++ b/music21/graph/primitives.py @@ -39,8 +39,7 @@ from music21.converter.subConverters import SubConverter from music21 import environment -_MOD = 'graph.primitives' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('graph.primitives') # ------------------------------------------------------------------------------ diff --git a/music21/graph/utilities.py b/music21/graph/utilities.py index abbbf9e0bb..bce2898b40 100644 --- a/music21/graph/utilities.py +++ b/music21/graph/utilities.py @@ -27,8 +27,7 @@ from music21 import pitch from music21 import environment -_MOD = 'graph.utilities' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('graph.utilities') ExtendedModules = namedtuple('ExtendedModules', diff --git a/music21/humdrum/spineParser.py b/music21/humdrum/spineParser.py index af57bb2d9c..d71572e866 100644 --- a/music21/humdrum/spineParser.py +++ b/music21/humdrum/spineParser.py @@ -60,6 +60,7 @@ from music21 import duration from music21 import exceptions21 from music21 import expressions +from music21 import instrument from music21 import key from music21 import note from music21 import meter @@ -75,8 +76,7 @@ from music21.humdrum import instruments from music21 import environment -_MOD = 'humdrum.spineParser' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('humdrum.spineParser') flavors = {'JRP': False} @@ -165,7 +165,7 @@ def parse(self): raise HumdrumException('Need a list of lines (dataStream) to parse!') hasOpus, dataCollections = self.determineIfDataStreamIsOpus(dataStream) - if hasOpus is True: # Palestrina data collection, maybe others + if hasOpus is True: # True for the Palestrina data collection, maybe others return self.parseOpusDataCollections(dataCollections) else: return self.parseNonOpus(dataStream) @@ -423,7 +423,7 @@ def parseProtoSpinesAndEventCollections(self): So self.eventCollections and self.protoSpines can each be thought of as a two-dimensional sheet of cells, but where the first index of the former is the vertical position in - the dataStream and the first index of the later is the + the dataStream and the first index of the latter is the horizontal position in the dataStream. The contents of each cell is a SpineEvent object or None (if there's no data at that point). Even '.' (continuation events) get @@ -751,7 +751,7 @@ def insertGlobalEvents(self): # '(well, maybe it is...file a bug report if you ' + # 'have doubled checked your data)') # elif self.spineCollection.spines[0].stream is None: -# raise HumdrumException('okay, you got at least one spine, but it aint got ' + +# raise HumdrumException('okay, you got at least one spine, but it ain\'t got ' + # 'a stream in it; (check your data or file a bug report)') # else: # masterStream = stream.Score() @@ -1536,7 +1536,7 @@ class SpineEvent(prebase.ProtoM21Object): ''' A SpineEvent is an event in a HumdrumSpine or ProtoSpine. - It's .contents property contains the contents of the spine or + It's .contents property contains the contents of the spine, or it could be '.', in which case it means that a particular event appears after the last event in a different spine. It could also be "None" indicating that there is no event at all @@ -1580,7 +1580,7 @@ def __str__(self): def toNote(self, convertString=None): r''' - parse the object as a \*\*kern note and return the a + parse the object as a \*\*kern note and return a :class:`~music21.note.Note` object (or Rest, or Chord) @@ -1756,7 +1756,7 @@ def assignIds(self): firstStreamForEachInstrument = {} for thisSpine in self.spines: spineStream = thisSpine.stream - instrumentsStream = spineStream.getElementsByClass('Instrument') + instrumentsStream = spineStream.getElementsByClass(instrument.Instrument) if not instrumentsStream: spineInstrument = None else: @@ -1916,7 +1916,7 @@ def moveDynamicsAndLyricsToStreams(self): continue if thisSpine.spineType != 'kern': continue - for tandem in thisSpine.stream.getElementsByClass('MiscTandem'): + for tandem in thisSpine.stream.getElementsByClass(MiscTandem): if not tandem.tandem.startswith('*staff'): continue staffInfo = int(tandem.tandem[6:]) # single staff @@ -2355,7 +2355,7 @@ def hdStringToNote(contents): newTup.durationActual = duration.durationTupleFromTypeDots(thisObject.duration.type, 0) newTup.durationNormal = duration.durationTupleFromTypeDots(thisObject.duration.type, 0) - gcd = common.euclidGCD(int(dT), baseValue) + gcd = common.euclidGCD(int(dT), int(baseValue)) newTup.numberNotesActual = int(dT / gcd) newTup.numberNotesNormal = int(float(baseValue) / gcd) @@ -2584,12 +2584,12 @@ def kernTandemToObject(tandem): return MiscTandem(tandem) # TODO: DO SOMETHING WITH TRANSPOSING INSTRUMENTS; not in hum2xml elif tandem.startswith('*I'): # order has to be last - instrument = tandem[2:] + instrumentStr = tandem[2:] try: - iObj = instruments.fromHumdrumInstrument(instrument) + iObj = instruments.fromHumdrumInstrument(instrumentStr) return iObj except instruments.HumdrumInstrumentException: - return MiscTandem(instrument) + return MiscTandem(instrumentStr) elif tandem.startswith('*k'): numSharps = tandem.count('#') if numSharps == 0: @@ -2932,7 +2932,7 @@ def testSpineComments(self): s = hf1.stream # .show() p = s.parts[2] # last part has a comment comments = [] - for c in p.flatten().getElementsByClass('SpineComment'): + for c in p[SpineComment]: comments.append(c.comment) self.assertTrue('spine comment' in comments) # s.show('text') @@ -2969,7 +2969,7 @@ def testHarmSpineDegrees(self): 32.0: ('V in c minor', [7, 11, 2], 'G', 'G', 53, False), 33.0: ('I in c minor', [0, 4, 7], 'C', 'C', 53, False) } - for harm in s.flatten().getElementsByClass('RomanNumeral'): + for harm in s.flatten().getElementsByClass(roman.RomanNumeral): figureAndKey = harm.figureAndKey pitchClasses = harm.pitchClasses root = harm.root().name @@ -3024,7 +3024,7 @@ def testHarmSpineSevenths(self): 42.0: ('V43 in a minor', [11, 2, 4, 8], 'E', 'B', 43, True), 43.0: ('i in a minor', [9, 0, 4], 'A', 'A', 53, False) } - for harm in s.flatten().getElementsByClass('RomanNumeral'): + for harm in s.flatten().getElementsByClass(roman.RomanNumeral): figureAndKey = harm.figureAndKey pitchClasses = harm.pitchClasses root = harm.root().name @@ -3046,6 +3046,7 @@ def testHarmSpineAugmentedSixths(self): hf1.parse() s = hf1.stream groundTruth = { + # Aug6, Italian, French, German 0.0: (False, False, False, False), 1.0: (False, False, False, False), 2.0: (False, False, False, False), @@ -3073,7 +3074,7 @@ def testHarmSpineAugmentedSixths(self): 32.0: (False, False, False, False), 33.0: (False, False, False, False) } - for harm in s.flatten().getElementsByClass('RomanNumeral'): + for harm in s.flatten().getElementsByClass(roman.RomanNumeral): isAugmentedSixth = harm.isAugmentedSixth() isItalianAugmentedSixth = harm.isItalianAugmentedSixth() isFrenchAugmentedSixth = harm.isFrenchAugmentedSixth() diff --git a/music21/instrument.py b/music21/instrument.py index 53e32d7ba8..36a42905a4 100644 --- a/music21/instrument.py +++ b/music21/instrument.py @@ -33,15 +33,12 @@ from music21 import interval from music21 import note from music21 import pitch -from music21 import stream from music21.tree.trees import OffsetTree from music21.exceptions21 import InstrumentException from music21 import environment - -_MOD = 'instrument' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('instrument') def unbundleInstruments(streamIn: 'music21.stream.Stream', @@ -1865,15 +1862,17 @@ def deduplicate(s: 'music21.stream.Stream', inPlace: bool = False) -> 'music21.s >>> list(p2.getInstruments()) [] ''' + from music21 import stream + if inPlace: returnObj = s else: returnObj = s.coreCopyAsDerivation('instrument.deduplicate') if not returnObj.hasPartLikeStreams(): - substreams: Iterable['music21.stream.Stream'] = [returnObj] + substreams: Iterable[stream.Stream] = [returnObj] else: - substreams = returnObj.getElementsByClass('Stream') + substreams = returnObj.getElementsByClass(stream.Stream) for sub in substreams: oTree = OffsetTree(sub[Instrument].stream()) @@ -2216,6 +2215,7 @@ def partitionByInstrument(streamObj): TODO: use proper recursion to make a copy of the stream. TODO: final barlines should be aligned. ''' + from music21 import stream if not streamObj.hasPartLikeStreams(): # place in a score for uniform operations s = stream.Score() @@ -2594,6 +2594,8 @@ def testCopyAndDeepcopy(self): j = copy.deepcopy(obj) def testMusicXMLExport(self): + from music21 import stream + s1 = stream.Stream() i1 = Violin() i1.partName = 'test' @@ -2615,6 +2617,7 @@ def testMusicXMLExport(self): def testPartitionByInstrumentA(self): from music21 import instrument + from music21 import stream # basic case of instruments in Parts s = stream.Score() @@ -2628,7 +2631,7 @@ def testPartitionByInstrumentA(self): post = instrument.partitionByInstrument(s) self.assertEqual(len(post), 2) - self.assertEqual(len(post.flatten().getElementsByClass('Instrument')), 2) + self.assertEqual(len(post.flatten().getElementsByClass(instrument.Instrument)), 2) # post.show('t') @@ -2639,11 +2642,12 @@ def testPartitionByInstrumentA(self): post = instrument.partitionByInstrument(s) self.assertEqual(len(post), 2) - self.assertEqual(len(post.flatten().getElementsByClass('Instrument')), 2) + self.assertEqual(len(post[instrument.Instrument]), 2) # post.show('t') def testPartitionByInstrumentB(self): from music21 import instrument + from music21 import stream # basic case of instruments in Parts s = stream.Score() @@ -2659,12 +2663,13 @@ def testPartitionByInstrumentB(self): post = instrument.partitionByInstrument(s) self.assertEqual(len(post), 2) - self.assertEqual(len(post.flatten().getElementsByClass('Instrument')), 2) + self.assertEqual(len(post[instrument.Instrument]), 2) self.assertEqual(len(post.parts[0].notes), 6) self.assertEqual(len(post.parts[1].notes), 12) def testPartitionByInstrumentC(self): from music21 import instrument + from music21 import stream # basic case of instruments in Parts s = stream.Score() @@ -2686,7 +2691,7 @@ def testPartitionByInstrumentC(self): post = instrument.partitionByInstrument(s) self.assertEqual(len(post), 4) # 4 instruments - self.assertEqual(len(post.flatten().getElementsByClass('Instrument')), 4) + self.assertEqual(len(post[instrument.Instrument]), 4) self.assertEqual(post.parts[0].getInstrument().instrumentName, 'Piano') self.assertEqual(len(post.parts[0].notes), 6) self.assertEqual(post.parts[1].getInstrument().instrumentName, 'Acoustic Guitar') @@ -2701,6 +2706,7 @@ def testPartitionByInstrumentC(self): def testPartitionByInstrumentD(self): from music21 import instrument + from music21 import stream # basic case of instruments in Parts s = stream.Score() @@ -2726,7 +2732,7 @@ def testPartitionByInstrumentD(self): post = instrument.partitionByInstrument(s) self.assertEqual(len(post), 4) # 4 instruments - self.assertEqual(len(post.flatten().getElementsByClass('Instrument')), 4) + self.assertEqual(len(post[instrument.Instrument]), 4) # piano spans are joined together self.assertEqual(post.parts[0].getInstrument().instrumentName, 'Piano') self.assertEqual(len(post.parts[0].notes), 12) @@ -2739,6 +2745,7 @@ def testPartitionByInstrumentD(self): def testPartitionByInstrumentE(self): from music21 import instrument + from music21 import stream # basic case of instruments in Parts # s = stream.Score() @@ -2762,7 +2769,7 @@ def testPartitionByInstrumentE(self): post = instrument.partitionByInstrument(s) self.assertEqual(len(post), 4) # 4 instruments - self.assertEqual(len(post.flatten().getElementsByClass('Instrument')), 4) + self.assertEqual(len(post[instrument.Instrument]), 4) # piano spans are joined together self.assertEqual(post.parts[0].getInstrument().instrumentName, 'Piano') @@ -2777,6 +2784,7 @@ def testPartitionByInstrumentE(self): def testPartitionByInstrumentF(self): from music21 import instrument + from music21 import stream s1 = stream.Stream() s1.append(instrument.AcousticGuitar()) diff --git a/music21/interval.py b/music21/interval.py index 8d4b823215..cccc6b928c 100644 --- a/music21/interval.py +++ b/music21/interval.py @@ -35,8 +35,7 @@ from music21.common.decorators import cacheMethod from music21 import environment -_MOD = 'interval' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('interval') # ------------------------------------------------------------------------------ # constants @@ -1404,7 +1403,7 @@ def transposePitchKeyAware(self, p, k=None, *, inPlace=False): >>> bPitch - But if a key or keySignature (such as one from .getContextByClass('KeySignature') + But if a key or keySignature (such as one from .getContextByClass(key.KeySignature) is given, then the fun begins... >>> fis = pitch.Pitch('F#4') diff --git a/music21/key.py b/music21/key.py index 2d98cdf224..cc701bc98c 100644 --- a/music21/key.py +++ b/music21/key.py @@ -33,8 +33,7 @@ from music21.common.decorators import cacheMethod from music21 import environment -_MOD = 'key' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('key') # ------------------------------------------------------------------------------ diff --git a/music21/layout.py b/music21/layout.py index 8a831e80fc..0a334e4258 100644 --- a/music21/layout.py +++ b/music21/layout.py @@ -97,11 +97,11 @@ from music21 import exceptions21 from music21 import spanner from music21 import stream +from music21.common.enums import GatherSpanners from music21.stream.enums import StaffType from music21 import environment -_MOD = 'layout' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('layout') SystemSize = namedtuple('SystemSize', ['top', 'left', 'right', 'bottom']) @@ -654,7 +654,9 @@ def getRichSystemLayout(inner_allSystemLayouts): thisPage.measureEnd = pageEndM thisPage.pageNumber = pageNumber if fastMeasures is True: - thisPageAll = scoreIn.measures(pageStartM, pageEndM, collect=[], gatherSpanners=False) + thisPageAll = scoreIn.measures(pageStartM, pageEndM, + collect=[], + gatherSpanners=GatherSpanners.NONE) else: thisPageAll = scoreIn.measures(pageStartM, pageEndM) thisPage.systemStart = systemNumber + 1 @@ -676,7 +678,7 @@ def getRichSystemLayout(inner_allSystemLayouts): if fastMeasures is True: measureStacks = scoreIn.measures(systemStartM, systemEndM, collect=[], - gatherSpanners=False) + gatherSpanners=GatherSpanners.NONE) else: measureStacks = scoreIn.measures(systemStartM, systemEndM) thisSystem = System() @@ -703,7 +705,7 @@ def getRichSystemLayout(inner_allSystemLayouts): staffObject.elements = p thisSystem.replace(p, staffObject) - allStaffLayouts: List[StaffLayout] = p.recurse().getElementsByClass('StaffLayout') + allStaffLayouts: List[StaffLayout] = list(p[StaffLayout]) if not allStaffLayouts: continue # else: @@ -864,7 +866,7 @@ def getMarginsAndSizeForPageId(self, pageId): >>> #_DOCS_SHOW g = corpus.parse('luca/gloria') >>> #_DOCS_SHOW m22 = g.parts[0].getElementsByClass(stream.Measure)[22] - >>> #_DOCS_SHOW m22.getElementsByClass('PageLayout').first().leftMargin = 204.0 + >>> #_DOCS_SHOW m22.getElementsByClass(layout.PageLayout).first().leftMargin = 204.0 >>> #_DOCS_SHOW gl = layout.divideByPages(g) >>> #_DOCS_SHOW gl.getMarginsAndSizeForPageId(1) >>> layout.PageSize(171.0, 204.0, 171.0, 171.0, 1457.0, 1886.0) #_DOCS_HIDE @@ -1697,8 +1699,9 @@ def testGetStaffLayoutFromStaff(self): we have had problems with attributes disappearing. ''' from music21 import corpus + from music21 import layout lt = corpus.parse('demos/layoutTest.xml') - ls = divideByPages(lt, fastMeasures=True) + ls = layout.divideByPages(lt, fastMeasures=True) hiddenStaff = ls.pages[0].systems[3].staves[1] self.assertTrue(repr(hiddenStaff).endswith('Staff 11: p.1, sys.4, st.2>'), diff --git a/music21/lily/translate.py b/music21/lily/translate.py index 994f832d13..bd25f84784 100644 --- a/music21/lily/translate.py +++ b/music21/lily/translate.py @@ -24,6 +24,7 @@ from collections import OrderedDict from importlib.util import find_spec +from music21 import clef from music21 import common from music21.converter.subConverters import SubConverter from music21 import corpus @@ -36,8 +37,7 @@ from music21 import variant from music21.lily import lilyObjects as lyo -_MOD = 'lily.translate' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('lily.translate') try: if find_spec('PIL.Image') and find_spec('PIL.ImageOps'): @@ -1971,9 +1971,9 @@ def findOffsetOfFirstNonSpacerElement(inputStream): # Stuff that can be done on the first element only (clef, new/old, id, color) replacedElements = variantList[0].replacedElements(activeSite) re0 = replacedElements[0] - replacedElementsClef = re0.clef or re0.getContextByClass('Clef') + replacedElementsClef = re0.clef or re0.getContextByClass(clef.Clef) - variantContainerStream = variantList[0].getContextByClass('Part') + variantContainerStream = variantList[0].getContextByClass(stream.Part) if variantContainerStream is None: variantContainerStream = variantList[0].getContextByClass('Stream') @@ -2118,9 +2118,9 @@ def lyPrefixCompositeMusicFromVariant(self, ['london'] ''' - replacedElementsClef = replacedElements[0].getContextByClass('Clef') + replacedElementsClef = replacedElements[0].getContextByClass(clef.Clef) - variantContainerStream = variantObject.getContextByClass('Part') + variantContainerStream = variantObject.getContextByClass(stream.Part) if variantContainerStream is None: variantContainerStream = variantObject.getContextByClass('Stream') diff --git a/music21/mei/base.py b/music21/mei/base.py index adf471687f..65c89d6f5f 100644 --- a/music21/mei/base.py +++ b/music21/mei/base.py @@ -200,8 +200,7 @@ from music21 import spanner from music21 import tie -_MOD = 'mei.base' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('mei.base') # Module-Level Constants diff --git a/music21/metadata/__init__.py b/music21/metadata/__init__.py index 8c0a90aeb8..5ee10d26ca 100644 --- a/music21/metadata/__init__.py +++ b/music21/metadata/__init__.py @@ -71,7 +71,8 @@ from music21 import environment environLocal = environment.Environment(os.path.basename(__file__)) -AmbitusShort = namedtuple('AmbitusShort', 'semitones diatonic pitchLowest pitchHighest') +AmbitusShort = namedtuple('AmbitusShort', + ['semitones', 'diatonic', 'pitchLowest', 'pitchHighest']) # ----------------------------------------------------------------------------- diff --git a/music21/meter/base.py b/music21/meter/base.py index 43d06c39f1..51e0211668 100644 --- a/music21/meter/base.py +++ b/music21/meter/base.py @@ -1852,7 +1852,7 @@ def getOffsetFromBeat(self, beat): >>> c = corpus.parse('bwv1.6') >>> for m in c.parts.first().getElementsByClass(stream.Measure): - ... ts = m.timeSignature or m.getContextByClass('TimeSignature') + ... ts = m.timeSignature or m.getContextByClass(meter.TimeSignature) ... print('%s %s' % (m.number, ts.getOffsetFromBeat(4.5) - m.paddingLeft)) 0 0.5 1 3.5 diff --git a/music21/meter/tools.py b/music21/meter/tools.py index 15c6ccd94f..f766feec7e 100644 --- a/music21/meter/tools.py +++ b/music21/meter/tools.py @@ -24,7 +24,7 @@ environLocal = environment.Environment('meter.tools') MeterTerminalTuple = collections.namedtuple('MeterTerminalTuple', - 'numerator denominator division') + ['numerator', 'denominator', 'division']) NumDenom = Tuple[int, int] NumDenomTuple = Tuple[NumDenom, ...] MeterOptions = Tuple[Tuple[str, ...], ...] diff --git a/music21/midi/__init__.py b/music21/midi/__init__.py index 82cbf99258..b6a3ba6d7e 100644 --- a/music21/midi/__init__.py +++ b/music21/midi/__init__.py @@ -49,8 +49,7 @@ from music21.midi import realtime from music21.midi import percussion -_MOD = 'midi' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('midi') # good midi reference: diff --git a/music21/midi/translate.py b/music21/midi/translate.py index aefd530ead..8ac484c40d 100644 --- a/music21/midi/translate.py +++ b/music21/midi/translate.py @@ -31,12 +31,12 @@ from music21 import percussion from music21 import pitch from music21 import stream +from music21 import tempo from music21.instrument import Conductor, deduplicate from music21.midi.percussion import MIDIPercussionException, PercussionMapper -_MOD = 'midi.translate' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('midi.translate') PERCUSSION_MAPPER = PercussionMapper() @@ -1134,7 +1134,6 @@ def midiEventsToTempo(eventList): TODO: Need Tests ''' from music21 import midi as midiModule - from music21 import tempo if not common.isListLike(eventList): event = eventList @@ -2222,7 +2221,6 @@ def conductorStream(s: stream.Stream) -> stream.Part: {0.0} {0.0} ''' - from music21 import tempo from music21 import meter partsList = list(s.getElementsByClass(stream.Stream).getElementsByOffset(0)) minPriority = min(p.priority for p in partsList) if partsList else 0 @@ -2246,9 +2244,9 @@ def conductorStream(s: stream.Stream) -> stream.Part: conductorPart.coreElementsChanged() # Defaults - if not conductorPart.getElementsByClass('MetronomeMark'): + if not conductorPart.getElementsByClass(tempo.MetronomeMark): conductorPart.insert(tempo.MetronomeMark(number=120)) - if not conductorPart.getElementsByClass('TimeSignature'): + if not conductorPart.getElementsByClass(meter.TimeSignature): conductorPart.insert(meter.TimeSignature('4/4')) return conductorPart @@ -2454,7 +2452,7 @@ def packetStorageFromSubstreamList( subs = subs.flatten() # get a first instrument; iterate over rest - instrumentStream = subs.getElementsByClass('Instrument') + instrumentStream = subs.getElementsByClass(instrument.Instrument) # if there is an Instrument object at the start, make instObj that instrument # this may be a Conductor object if prepareStreamForMidi() was run @@ -2994,7 +2992,7 @@ def testTimeSignature(self): # s.show('midi') # get and compare just the conductor tracks - # mtAlt = streamHierarchyToMidiTracks(s.getElementsByClass('TimeSignature').stream())[0] + # mtAlt = streamHierarchyToMidiTracks(s.getElementsByClass(meter.TimeSignature).stream())[0] conductorEvents = repr(mt.events) match = '''[, @@ -3528,14 +3526,14 @@ def testMicrotonalOutputG(self): from music21 import interval s = corpus.parse('bwv66.6') p1 = s.parts[0] - p1.remove(p1.getElementsByClass('Instrument').first()) + p1.remove(p1.getElementsByClass(instrument.Instrument).first()) p2 = copy.deepcopy(p1) p3 = copy.deepcopy(p1) t1 = interval.Interval(12.5) # a sharp p4 t2 = interval.Interval(-7.25) # a sharp p4 - p2.transpose(t1, inPlace=True, classFilterList=('Note', 'Chord')) - p3.transpose(t2, inPlace=True, classFilterList=('Note', 'Chord')) + p2.transpose(t1, inPlace=True, classFilterList=(note.Note, chord.Chord)) + p3.transpose(t2, inPlace=True, classFilterList=(note.Note, chord.Chord)) post = stream.Score() p1.insert(0, instrument.Dulcimer()) post.insert(0, p1) @@ -3566,7 +3564,7 @@ def testMidiTempoImportA(self): # a simple file created in athenacl fp = dirLib / 'test10.mid' s = converter.parse(fp) - mmStream = s.flatten().getElementsByClass('MetronomeMark') + mmStream = s.flatten().getElementsByClass(tempo.MetronomeMark) self.assertEqual(len(mmStream), 4) self.assertEqual(mmStream[0].number, 120.0) self.assertEqual(mmStream[1].number, 110.0) @@ -3575,19 +3573,18 @@ def testMidiTempoImportA(self): fp = dirLib / 'test06.mid' s = converter.parse(fp) - mmStream = s.flatten().getElementsByClass('MetronomeMark') + mmStream = s.flatten().getElementsByClass(tempo.MetronomeMark) self.assertEqual(len(mmStream), 1) self.assertEqual(mmStream[0].number, 120.0) fp = dirLib / 'test07.mid' s = converter.parse(fp) - mmStream = s.flatten().getElementsByClass('MetronomeMark') + mmStream = s.flatten().getElementsByClass(tempo.MetronomeMark) self.assertEqual(len(mmStream), 1) self.assertEqual(mmStream[0].number, 180.0) def testMidiTempoImportB(self): from music21 import converter - from music21 import tempo dirLib = common.getSourceFilePath() / 'midi' / 'testPrimitive' # a file with three tracks and one conductor track with four tempo marks @@ -3646,7 +3643,6 @@ def testMidiImportImplicitMeter(self): def testMidiExportConductorA(self): '''Export conductor data to MIDI conductor track.''' from music21 import meter - from music21 import tempo p1 = stream.Part() p1.repeatAppend(note.Note('c4'), 12) @@ -3677,7 +3673,6 @@ def testMidiExportConductorA(self): # s.show('midi', app='Logic Express') def testMidiExportConductorB(self): - from music21 import tempo from music21 import corpus s = corpus.parse('bwv66.6') s.insert(0, tempo.MetronomeMark(number=240)) @@ -3694,7 +3689,6 @@ def testMidiExportConductorB(self): self.assertEqual(musicTrkRepr.count('SET_TEMPO'), 0) def testMidiExportConductorC(self): - from music21 import tempo minTempo = 60 maxTempo = 600 period = 50 @@ -3724,7 +3718,6 @@ def testMidiExportConductorD(self): def testMidiExportConductorE(self): '''The conductor only gets the first element at an offset.''' from music21 import converter - from music21 import tempo s = stream.Stream() p1 = converter.parse('tinynotation: c1') @@ -3736,8 +3729,8 @@ def testMidiExportConductorE(self): s.insert(0, p2) conductor = conductorStream(s) - tempos = conductor.getElementsByClass('MetronomeMark') - keySignatures = conductor.getElementsByClass('KeySignature') + tempos = conductor.getElementsByClass(tempo.MetronomeMark) + keySignatures = conductor.getElementsByClass(key.KeySignature) self.assertEqual(len(tempos), 1) self.assertEqual(tempos[0].number, 44) self.assertEqual(len(keySignatures), 1) @@ -3925,7 +3918,8 @@ def testMidiInstrumentToStream(self): s = converter.parse(testPrimitive.transposing01) mf = streamToMidiFile(s) out = midiFileToStream(mf) - first_instrument = out.parts.first().measure(1).getElementsByClass('Instrument').first() + first_instrument = out.parts.first().measure(1).getElementsByClass( + instrument.Instrument).first() self.assertIsInstance(first_instrument, instrument.Oboe) self.assertEqual(first_instrument.quarterLength, 0) diff --git a/music21/musedata/__init__.py b/music21/musedata/__init__.py index f1c0cc47cf..6a635d0e7b 100644 --- a/music21/musedata/__init__.py +++ b/music21/musedata/__init__.py @@ -38,8 +38,7 @@ from music21 import prebase from music21 import environment -_MOD = 'musedata' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('musedata') # for implementation # see http://www.ccarh.org/publications/books/beyondmidi/online/musedata/ diff --git a/music21/musedata/translate.py b/music21/musedata/translate.py index 468c790c93..6babb53fec 100644 --- a/music21/musedata/translate.py +++ b/music21/musedata/translate.py @@ -27,8 +27,7 @@ from music21 import clef from music21 import environment from music21 import exceptions21 -_MOD = 'musedata.translate' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('musedata.translate') # ------------------------------------------------------------------------------ @@ -590,7 +589,7 @@ def testBackBasic(self): # s = corpus.parse('symphony94', 3) # sFlat = s.flatten() # # s.show() -# self.assertEqual(len(sFlat.getElementsByClass('Dynamic')), 79) +# self.assertEqual(len(sFlat.getElementsByClass(dynamics.Dynamic)), 79) # # # def testMuseDataImportErrorA(self): diff --git a/music21/musicxml/archiveTools.py b/music21/musicxml/archiveTools.py index e7d6974b73..1392ceb0c4 100644 --- a/music21/musicxml/archiveTools.py +++ b/music21/musicxml/archiveTools.py @@ -19,8 +19,7 @@ from music21 import common from music21 import environment -_MOD = 'musicxml.archiveTools' -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('musicxml.archiveTools') # ----------------------------------------------------------------------------- diff --git a/music21/musicxml/helpers.py b/music21/musicxml/helpers.py index 8cf69f651f..0f3ce45333 100644 --- a/music21/musicxml/helpers.py +++ b/music21/musicxml/helpers.py @@ -11,6 +11,7 @@ # ------------------------------------------------------------------------------ import copy from xml.etree.ElementTree import tostring as et_tostring +from music21 import meter def dumpString(obj, *, noCopy=False) -> str: r''' @@ -185,7 +186,7 @@ def isFullMeasureRest(r: 'music21.note.Rest') -> bool: if r.fullMeasure in (True, 'always'): isFullMeasure = True elif r.fullMeasure == 'auto': - tsContext = r.getContextByClass('TimeSignature') + tsContext = r.getContextByClass(meter.TimeSignature) if tsContext and tsContext.barDuration.quarterLength == r.duration.quarterLength: isFullMeasure = True return isFullMeasure diff --git a/music21/musicxml/m21ToXml.py b/music21/musicxml/m21ToXml.py index ffc5fb7406..0782a5a38b 100644 --- a/music21/musicxml/m21ToXml.py +++ b/music21/musicxml/m21ToXml.py @@ -23,7 +23,7 @@ from xml.etree.ElementTree import ( Element, SubElement, Comment ) -from typing import Dict, List, Mapping, Optional, Union +from typing import Dict, List, Mapping, Optional, Union, cast # external dependencies import webcolors @@ -42,6 +42,7 @@ from music21 import duration from music21 import harmony from music21 import instrument +from music21 import key from music21 import layout from music21 import metadata from music21 import note @@ -60,8 +61,7 @@ from music21.musicxml.xmlObjects import MusicXMLWarning from music21 import environment -_MOD = "musicxml.m21ToXml" -environLocal = environment.Environment(_MOD) +environLocal = environment.Environment('musicxml.m21ToXml') # ------------------------------------------------------------------------------ @@ -1637,7 +1637,7 @@ def setScoreLayouts(self): ''' s = self.stream - scoreLayouts = s.getElementsByClass('ScoreLayout').stream() + scoreLayouts = s.getElementsByClass(layout.ScoreLayout).stream() if scoreLayouts: scoreLayout = scoreLayouts[0] else: @@ -2110,9 +2110,9 @@ def setPartList(self): # Handle last part in the StaffGroup if sg.isLast(p): # find the spanner in the dictionary already-assigned - for key, value in partGroupIndexRef.items(): + for k, value in partGroupIndexRef.items(): if value is sg: - activeIndex = key + activeIndex = k break mxPartGroup = Element('part-group') mxPartGroup.set('type', 'stop') @@ -2757,13 +2757,13 @@ def fixupNotationMeasured(self): if hasattr(first_measure, 'keySignature') and first_measure.keySignature is None: first_measure.makeMutable() # must mutate - outerKeySignatures = part.getElementsByClass('KeySignature') + outerKeySignatures = part.getElementsByClass(key.KeySignature) if outerKeySignatures: first_measure.keySignature = outerKeySignatures.first() if hasattr(first_measure, 'timeSignature') and first_measure.timeSignature is None: first_measure.makeMutable() # must mutate - outerTimeSignatures = part.getElementsByClass('TimeSignature') + outerTimeSignatures = part.getElementsByClass(meter.TimeSignature) if outerTimeSignatures: first_measure.timeSignature = outerTimeSignatures.first() @@ -3025,7 +3025,7 @@ def mainElementsParse(self): self.parseFlatElements(m, backupAfterwards=False) return - nonVoiceMeasureItems = m.getElementsNotOfClass('Voice').stream() + nonVoiceMeasureItems = m.getElementsNotOfClass(stream.Voice).stream() self.parseFlatElements(nonVoiceMeasureItems, backupAfterwards=True) allVoices = list(m.voices) @@ -3624,7 +3624,7 @@ def noteToXml(self, n: note.GeneralNote, noteIndexInChord=0, chordParent=None): SubElement(mxNote, 'chord') if hasattr(n, 'pitch'): - n: note.Note + n = cast(note.Note, n) mxPitch = self.pitchToXml(n.pitch) mxNote.append(mxPitch) elif n.isRest: @@ -3694,14 +3694,14 @@ def noteToXml(self, n: note.GeneralNote, noteIndexInChord=0, chordParent=None): if (addChordTag is False and hasattr(chordOrN, 'stemDirection') and chordOrN.stemDirection != 'unspecified'): - chordOrN: note.NotRest + chordOrN = cast(note.NotRest, chordOrN) stemDirection = chordOrN.stemDirection # or if we are in a chord, but the sub-note has its own stem direction, # record that. elif (chordOrN is not n and hasattr(n, 'stemDirection') and n.stemDirection != 'unspecified'): - n: note.NotRest + n = cast(note.NotRest, n) stemDirection = n.stemDirection if stemDirection is not None: @@ -3724,7 +3724,7 @@ def noteToXml(self, n: note.GeneralNote, noteIndexInChord=0, chordParent=None): # beam if addChordTag is False: if hasattr(chordOrN, 'beams') and chordOrN.beams is not None: - chordOrN: note.NotRest + chordOrN = cast(note.NotRest, chordOrN) nBeamsList = self.beamsToXml(chordOrN.beams) for mxB in nBeamsList: mxNote.append(mxB) @@ -3892,7 +3892,7 @@ def restToXml(self, r: note.Rest): if r.stepShift != 0: mxDisplayStep = SubElement(mxRestTag, 'display-step') mxDisplayOctave = SubElement(mxRestTag, 'display-octave') - currentClef = r.getContextByClass('Clef') + currentClef = r.getContextByClass(clef.Clef) if currentClef is None or not hasattr(currentClef, 'lowestLine'): currentClef = clef.TrebleClef() # this should not be common enough to # worry about the overhead @@ -4296,7 +4296,7 @@ def dealWithNotehead( or n.noteheadParenthesis or n.noteheadFill is not None or (n.hasStyleInformation and n.style.color not in (None, '')))): - n: note.NotRest + n = cast(note.NotRest, n) foundANotehead = True mxNotehead = self.noteheadToXml(n) mxNote.append(mxNotehead) @@ -6067,7 +6067,7 @@ def setMxAttributesObjectForStartOfMeasure(self): m.timeSignature, 'timeSignature', comparison='ratioEqual' )): mxAttributes.append(self.timeSignatureToXml(m.timeSignature)) - smts = list(m.getElementsByClass('SenzaMisuraTimeSignature')) + smts = list(m.getElementsByClass(meter.SenzaMisuraTimeSignature)) if smts: mxAttributes.append(self.timeSignatureToXml(smts[0])) @@ -6077,7 +6077,7 @@ def setMxAttributesObjectForStartOfMeasure(self): if m.clef is not None: mxAttributes.append(self.clefToXml(m.clef)) - found = m.getElementsByClass('StaffLayout') + found = m.getElementsByClass(layout.StaffLayout) if found: sl = found[0] # assume only one per measure mxAttributes.append(self.staffLayoutToXmlStaffDetails(sl)) @@ -6169,7 +6169,7 @@ def staffLayoutToXmlStaffDetails(self, staffLayout): # TODO: staff-size return mxStaffDetails - def timeSignatureToXml(self, ts): + def timeSignatureToXml(self, ts: Union[meter.TimeSignature, meter.SenzaMisuraTimeSignature]): ''' Returns a single