From 3aaa0de10c7eeb0d8a445760dcbfbab0cfe0bf41 Mon Sep 17 00:00:00 2001 From: Myke Cuthbert Date: Thu, 5 May 2022 15:37:36 -1000 Subject: [PATCH 1/2] Misc Cleanup 2022-05 --- .github/workflows/pythonpylint.yml | 4 +- music21/abcFormat/__init__.py | 100 ++++++++++++++++------------- music21/abcFormat/testFiles.py | 2 - music21/abcFormat/translate.py | 2 +- music21/analysis/windowed.py | 2 +- music21/base.py | 15 +++-- music21/common/enums.py | 2 + music21/features/outputFormats.py | 2 +- music21/humdrum/questions.py | 2 +- music21/metadata/testMetadata.py | 2 +- music21/stream/base.py | 2 +- music21/stream/makeNotation.py | 53 +++++++-------- 12 files changed, 98 insertions(+), 90 deletions(-) diff --git a/.github/workflows/pythonpylint.yml b/.github/workflows/pythonpylint.yml index ebcf1da973..d2b80a9d69 100644 --- a/.github/workflows/pythonpylint.yml +++ b/.github/workflows/pythonpylint.yml @@ -63,6 +63,4 @@ jobs: python -m pip install mypy - 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/stream/base.py music21/stream/core.py music21/stream/enums.py music21/stream/filters.py music21/stream/iterator.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/key.py music21/layout.py music21/note.py music21/percussion.py music21/pitch.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/*.py 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/stream music21/test music21/vexflow diff --git a/music21/abcFormat/__init__.py b/music21/abcFormat/__init__.py index 5b6a65265b..4e244bfe32 100644 --- a/music21/abcFormat/__init__.py +++ b/music21/abcFormat/__init__.py @@ -31,7 +31,7 @@ or wherever you have downloaded EasyABC to (PC users might need: 'c:/program files (x86)/easyabc/easyabc.exe') -(Thanks to Norman Schmidt for the heads up) +(Thanks to Norman Schmidt for the heads-up) There is a two-step process in converting ABC files to Music21 Streams. First this module reads in the text-based .abc file and converts all the information into ABCToken objects. Then @@ -57,7 +57,7 @@ import io import re import unittest -from typing import Union, Optional, List, Tuple, Any +from typing import Union, Optional, List, Tuple, Any, TypeVar, Sequence from music21 import common from music21 import environment @@ -66,6 +66,7 @@ from music21.abcFormat import translate +_T = TypeVar('_T') environLocal = environment.Environment('abcFormat') # for implementation @@ -98,10 +99,10 @@ # note inclusion of w: for lyrics reMetadataTag = re.compile('[A-Zw]:') rePitchName = re.compile('[a-gA-Gz]') -reChordSymbol = re.compile('"[^"]*"') # non greedy -reChord = re.compile('[.*?]') # non greedy +reChordSymbol = re.compile('"[^"]*"') # non-greedy +reChord = re.compile('[.*?]') # non-greedy reAbcVersion = re.compile(r'^%abc-((\d+)\.(\d+)\.?(\d+)?)') -reDirective = re.compile(r'^%%([a-z\-]+)\s+([^\s]+)(.*)') +reDirective = re.compile(r'^%%([a-z\-]+)\s+(\S+)(.*)') # ------------------------------------------------------------------------------ @@ -227,7 +228,7 @@ def preParse(self) -> None: ''' div = reMetadataTag.match(self.src).end() strSrc = self.stripComment(self.src) # remove any comments - self.tag = strSrc[:div - 1] # do not get colon, : + self.tag = strSrc[:div - 1] # do not get the colon self.data = strSrc[div:].strip() # remove leading/trailing def parse(self): @@ -1257,10 +1258,10 @@ def __init__(self, src='', carriedAccidental=None): # provide default duration from handler; may change during piece self.activeDefaultQuarterLength = None - # store if a broken symbol applies; pair of symbol, position (left, right) + # store if a broken symbol applies; a pair of symbols, position (left, right) self.brokenRhythmMarker = None - # store key signature for pitch processing; this is an m21 object + # store key signature for pitch processing; this is an M21Object self.activeKeySignature = None # store a tuplet if active @@ -1280,8 +1281,9 @@ def __init__(self, src='', carriedAccidental=None): self.accidentalDisplayStatus = None # determined during parse() based on if pitch chars are present self.isRest = None - # pitch/ duration attributes for m21 conversion - # set with parse() based on all other contextual + + # Pitch and duration attributes for m21 conversion + # they are set via parse() based on other contextual information. self.pitchName = None # if None, a rest or chord self.quarterLength = None @@ -1432,7 +1434,7 @@ def getPitchName( if carriedAccString and accString: raise ABCHandlerException('Carried accidentals not rendered moot.') # if there is an explicit accidental, regardless of key, it should - # be shown: this will works for naturals well + # be shown: this will work for naturals well if carriedAccString: # An accidental carrying through the measure is supposed to be applied. # This will be set iff no explicit accidental is attached to the note. @@ -1443,7 +1445,7 @@ def getPitchName( elif activeKeySignature is None: accidentalDisplayStatus = None # pitches are key dependent: accidentals are not given - # if we have a key and find a name, that does not have a n, must be + # if we have a key and find a name, that does not have an "n", must be # altered else: alteredPitches = activeKeySignature.alteredPitches @@ -1744,7 +1746,8 @@ def __init__(self, abcVersion=None, lineBreaksDefinePhrases=False): self.currentCollectStr = '' @staticmethod - def _getLinearContext(source, i: int) -> Tuple[Any, Any, Any, Any]: + def _getLinearContext(source: Sequence[_T], + i: int) -> Tuple[Optional[_T], _T, Optional[_T], Optional[_T]]: ''' Find the local context of a string or iterable of objects beginning at a particular index. @@ -1828,7 +1831,8 @@ def barlineTokenFilter(token: str) -> List[ABCBar]: Some single barline tokens are better replaced with two tokens. This method, given a token, returns a list of tokens. If there is no change - necessary, the provided token will be returned in the list. + necessary, the provided token will be returned within + the list. A staticmethod. Call on the class itself. @@ -1848,23 +1852,23 @@ def barlineTokenFilter(token: str) -> List[ABCBar]: ''' barTokens: List[ABCBar] = [] if token == '::': - # create a start and and an end + # create a start and an end barTokens.append(ABCBar(':|')) barTokens.append(ABCBar('|:')) elif token == '|1': - # create a start and and an end + # create a start and an end barTokens.append(ABCBar('|')) barTokens.append(ABCBar('[1')) elif token == '|2': - # create a start and and an end + # create a start and an end barTokens.append(ABCBar('|')) barTokens.append(ABCBar('[2')) elif token == ':|1': - # create a start and and an end + # create a start and an end barTokens.append(ABCBar(':|')) barTokens.append(ABCBar('[1')) elif token == ':|2': - # create a start and and an end + # create a start and an end barTokens.append(ABCBar(':|')) barTokens.append(ABCBar('[2')) else: # append unaltered @@ -2018,7 +2022,7 @@ def tokenize(self, strSrc: str) -> None: Walk the abc string, creating ABC objects along the way. This may be called separately from process(), in the case - that pre/post parse processing is not needed. + that pre-/post-parse processing is not needed. >>> abch = abcFormat.ABCHandler() >>> abch.tokens @@ -2315,7 +2319,7 @@ def tokenize(self, strSrc: str) -> None: if self.strSrc[j] in accidentals: accidental += self.strSrc[j] continue - # only allow one pitch alpha to be a continue condition + # only allow one pitch, alpha, to be a "continue" condition elif (not foundPitchAlpha and self.strSrc[j].isalpha() # noinspection SpellCheckingInspection and self.strSrc[j] not in '~wuvhHLTSN'): @@ -2541,7 +2545,8 @@ def tokenProcess(self): t.activeDefaultQuarterLength = lastDefaultQL t.activeKeySignature = lastKeySignature t.applicableSpanners = self.activeSpanners[:] # fast copy of a list - # ends ties one note after they begin + + # This block ends tie objects one note after they begin if lastTieToken is not None: t.tie = 'stop' lastTieToken = None @@ -2646,7 +2651,10 @@ def definesReferenceNumbers(self): >>> abcStr += 'X:6\\nM:6/8\\nL:1/8\\nK:G\\nB3 A3 | G6 | B3 A3 | G6 ||' >>> ah = abcFormat.ABCHandler() >>> junk = ah.process(abcStr) - >>> ah.definesReferenceNumbers() # two tokens so returns True + + There are two tokens, so this returns True + + >>> ah.definesReferenceNumbers() True ''' if not self.tokens: @@ -2861,7 +2869,7 @@ def splitByVoice(self) -> List['ABCHandler']: if t.isVoice(): # if first char is a number # can be V:3 name="Bass" snm="b" clef=bass - if t.data[0].isdigit(): + if t.data and t.data[0].isdigit(): pos.append(i) # store position voiceCount += 1 @@ -3031,7 +3039,7 @@ def splitByMeasure(self) -> List['ABCHandlerBar']: elif isinstance(self.tokens[yTestIndex], ABCMetadata): pass # no change # if y position is a note/chord, and this is the last index, - # must included it + # we must have included it elif not (isinstance(self.tokens[yTestIndex], (ABCNote, ABCChord)) and yTestIndex == len(self.tokens) - 1): # if we find a note in the yClip position, it is likely @@ -3041,7 +3049,7 @@ def splitByMeasure(self) -> List['ABCHandlerBar']: # environLocal.printDebug(['clip boundaries: x,y', xClip, yClip]) # boundaries are inclusive; need to add one here ah.tokens = self.tokens[xClip:yClip + 1] - # after bar assign, if no bars known, reject + # after bar assign, if no bars are known, reject if not ah: continue abcBarHandlers.append(ah) @@ -3067,8 +3075,8 @@ def tokensToBarIndices(self) -> List[int]: except IndexError: tNext = None - # either we get a bar, or we just complete metadata and we - # encounter a note (a pickup) + # either we get a bar, or we just complete metadata and + # possibly encounter a note (a pickup) if isinstance(t, ABCBar): # or (barCount == 0 and noteCount > 0): # environLocal.printDebug(['splitByMeasure()', 'found bar', t]) barIndices.append(i) # store position @@ -3130,7 +3138,7 @@ def getTitle(self) -> Optional[str]: class ABCHandlerBar(ABCHandler): ''' A Handler specialized for storing bars. All left - and right bars are collected and assigned to attributes. + and right bars are collected and assigned to their respective attributes. ''' # divide elements of a character stream into objects and handle # store in a list, and pass global information to components @@ -3187,7 +3195,7 @@ def mergeLeadingMetaData(barHandlers: List[ABCHandlerBar]) -> List[ABCHandlerBar metadataPos.append(i) # environLocal.printDebug(['mergeLeadingMetaData()', # 'metadataPosList', metadataPos, 'mCount', mCount]) - # merge meta data into bars for processing + # merge metadata into bars for processing mergedHandlers = [] if mCount <= 1: # if only one true measure, do not create measures ahb = ABCHandlerBar() @@ -3196,12 +3204,12 @@ def mergeLeadingMetaData(barHandlers: List[ABCHandlerBar]) -> List[ABCHandlerBar mergedHandlers.append(ahb) else: # when we have metadata, we need to pass its tokens with those - # of the measure that follows it; if we have trailing meta data, + # of the measure that follows it; if we have trailing metadata, # we can pass but do not create a measure i = 0 while i < len(barHandlers): - # if we find metadata and it is not the last valid index - # merge into a single handler + # If we find metadata, and it is not the last valid index, + # merge it into a single handler. if i in metadataPos and i != len(barHandlers) - 1: mergedHandlers.append(barHandlers[i] + barHandlers[i + 1]) i += 2 @@ -3308,7 +3316,7 @@ def extractReferenceNumber(strSrc: str, number: int) -> str: if line.strip().startswith('X:') and line.replace(' ', '').rstrip() == f'X:{number}': gather = True elif line.strip().startswith('X:') and not gather: - # some numbers are like X:0490 but we may request them as 490... + # some numbers are like "X:0490", but we may request them as 490... try: forcedNum = int(line.replace(' ', '').rstrip().replace('X:', '')) if forcedNum == int(number): @@ -3336,7 +3344,7 @@ def readstr(self, strSrc: str, number: Optional[int] = None) -> ABCHandler: Returns a ABCHandler instance. ''' if number is not None: - # will raise exception if cannot be found + # will raise exception if number cannot be found strSrc = self.extractReferenceNumber(strSrc, number) handler = ABCHandler(abcVersion=self.abcVersion) @@ -3471,7 +3479,7 @@ def testSplitByMeasure(self): ah.process(testFiles.hectorTheHero) ahm = ah.splitByMeasure() - for i, l, r in [(0, None, None), # meta data + for i, l, r in [(0, None, None), # metadata (2, '|:', '|'), (3, '|', '|'), (-2, '[1', ':|'), @@ -3499,9 +3507,9 @@ def testSplitByMeasure(self): ah.process(testFiles.theBeggerBoy) ahm = ah.splitByMeasure() - for i, l, r in [(0, None, None), # meta data + for i, l, r in [(0, None, None), # metadata (1, None, '|'), - (-1, '||', None), # trailing lyric meta data + (-1, '||', None), # trailing lyric metadata ]: # print(i, l, r, ahm[i].tokens) if l is None: @@ -3519,7 +3527,7 @@ def testSplitByMeasure(self): ah.process('M:6/8\nL:1/8\nK:G\nc1D2') ahm = ah.splitByMeasure() - for i, l, r in [(0, None, None), # meta data + for i, l, r in [(0, None, None), # metadata (-1, None, None), # note data, but no bars ]: # print(i, l, r, ahm[i].tokens) @@ -3536,7 +3544,7 @@ def testSplitByMeasure(self): def testMergeLeadingMetaData(self): from music21.abcFormat import testFiles - # a case of leading and trailing meta data + # a case of leading and trailing metadata ah = ABCHandler() ah.process(testFiles.theBeggerBoy) ahm = ah.splitByMeasure() @@ -3545,7 +3553,7 @@ def testMergeLeadingMetaData(self): mergedHandlers = mergeLeadingMetaData(ahm) - # after merging, one less handler as leading meta data is merged + # after merging, one less handler as leading metadata is merged self.assertEqual(len(mergedHandlers), 13) # the last handler is all trailing metadata self.assertTrue(mergedHandlers[0].hasNotes()) @@ -3554,7 +3562,7 @@ def testMergeLeadingMetaData(self): # these are all ABCHandlerBar instances with bars defined self.assertEqual(mergedHandlers[-2].rightBarToken.src, '||') - # a case of only leading meta data + # a case of only leading metadata ah = ABCHandler() ah.process(testFiles.theAleWifesDaughter) ahm = ah.splitByMeasure() @@ -3562,7 +3570,7 @@ def testMergeLeadingMetaData(self): self.assertEqual(len(ahm), 10) mergedHandlers = mergeLeadingMetaData(ahm) - # after merging, one less handler as leading meta data is merged + # after merging, one less handler as leading metadata is merged self.assertEqual(len(mergedHandlers), 10) # all handlers have notes self.assertTrue(mergedHandlers[0].hasNotes()) @@ -3576,10 +3584,10 @@ def testMergeLeadingMetaData(self): ah.process('M:6/8\nL:1/8\nK:G\nc1D2') ahm = ah.splitByMeasure() - # split by measure divides meta data + # split by measure divides metadata self.assertEqual(len(ahm), 2) mergedHandlers = mergeLeadingMetaData(ahm) - # after merging, meta data is merged back + # after merging, metadata is merged back self.assertEqual(len(mergedHandlers), 1) # and it has notes self.assertTrue(mergedHandlers[0].hasNotes()) @@ -3587,7 +3595,7 @@ def testMergeLeadingMetaData(self): def testSplitByReferenceNumber(self): from music21.abcFormat import testFiles - # a case of leading and trailing meta data + # a case of leading and trailing metadata ah = ABCHandler() ah.process(testFiles.theBeggerBoy) ahs = ah.splitByReferenceNumber() diff --git a/music21/abcFormat/testFiles.py b/music21/abcFormat/testFiles.py index 7047b4bb02..8d04e971b6 100644 --- a/music21/abcFormat/testFiles.py +++ b/music21/abcFormat/testFiles.py @@ -4,8 +4,6 @@ # Purpose: ABC test files # # Authors: Christopher Ariza -# -# License: BSD, see license.txt # ------------------------------------------------------------------------------ import unittest diff --git a/music21/abcFormat/translate.py b/music21/abcFormat/translate.py index 146cba30e7..73b9490008 100644 --- a/music21/abcFormat/translate.py +++ b/music21/abcFormat/translate.py @@ -277,7 +277,7 @@ def parseTokens(mh, dst, p, useMeasures): # add the attached chord symbol if t.chordSymbols: cs_name = t.chordSymbols[0] - cs_name = re.sub('["]', '', cs_name).lstrip().rstrip() + cs_name = re.sub('"', '', cs_name).lstrip().rstrip() cs_name = re.sub('[()]', '', cs_name) cs_name = common.cleanedFlatNotation(cs_name) try: diff --git a/music21/analysis/windowed.py b/music21/analysis/windowed.py index 09e71675fc..e046aaa130 100644 --- a/music21/analysis/windowed.py +++ b/music21/analysis/windowed.py @@ -314,7 +314,7 @@ def process(self, # of all windows of a single size across the entire Stream solutionMatrix = [] colorMatrix = [] - # store meta data about each row as a dictionary + # store metadata about each row as a dictionary metaMatrix = [] if common.isNum(windowStepSize): diff --git a/music21/base.py b/music21/base.py index 5439d578b3..4cd04f50da 100644 --- a/music21/base.py +++ b/music21/base.py @@ -1201,7 +1201,7 @@ def purgeOrphans(self, excludeStorageStreams=True) -> None: ''' # environLocal.printDebug(['purging orphans']) orphans = [] - # TODO: how can this be optimized to not use getSites, so as to + # TODO: how can this be optimized to not use getSites, to # not unwrap weakrefs? for s in self.sites.yieldSites(excludeNone=True): # of the site does not actually have this Music21Object in @@ -3031,18 +3031,18 @@ def splitAtQuarterLength( >>> n = note.Note() >>> n.quarterLength = 0.5 - >>> a, b = n.splitAtQuarterLength(0.5) - >>> b is None + >>> firstPart, secondPart = n.splitAtQuarterLength(0.5) + >>> secondPart is None True - >>> a is n + >>> firstPart is n True (same with retainOrigin off) >>> n = note.Note() >>> n.quarterLength = 0.5 - >>> a, b = n.splitAtQuarterLength(0.5, retainOrigin=False) - >>> a is n + >>> firstPart, secondPart = n.splitAtQuarterLength(0.5, retainOrigin=False) + >>> firstPart is n False @@ -3050,7 +3050,7 @@ def splitAtQuarterLength( >>> n = note.Note() >>> n.quarterLength = 0.5 - >>> a, b = n.splitAtQuarterLength(0.7) + >>> first, second = n.splitAtQuarterLength(0.7) Traceback (most recent call last): music21.duration.DurationException: cannot split a duration (0.5) at this quarterLength (7/10) @@ -4623,6 +4623,7 @@ def testHighestTime(self): b1 = bar.Barline() s.append(n1) self.assertEqual(s.highestTime, 30.0) + # noinspection PyTypeChecker s.coreSetElementOffset(b1, OffsetSpecial.AT_END, addElement=True) self.assertEqual(b1.getOffsetBySite(s), 30.0) diff --git a/music21/common/enums.py b/music21/common/enums.py index da971c7bc6..31075e29de 100644 --- a/music21/common/enums.py +++ b/music21/common/enums.py @@ -139,6 +139,8 @@ class OffsetSpecial(StrEnum): The enum `AT_END` is equal to the string 'highestTime' + In version 9, the string comparisons will be removed. + >>> from music21.common.enums import OffsetSpecial >>> OffsetSpecial.AT_END diff --git a/music21/features/outputFormats.py b/music21/features/outputFormats.py index 1bc754f799..72f505bd03 100644 --- a/music21/features/outputFormats.py +++ b/music21/features/outputFormats.py @@ -78,7 +78,7 @@ def getHeaderLines(self, includeClassLabel=True, includeId=True): post.append(self._dataSet.getAttributeLabels( includeClassLabel=includeClassLabel, includeId=includeId)) - # second row meta data + # second row metadata row = [] for x in self._dataSet.getDiscreteLabels( includeClassLabel=includeClassLabel, includeId=includeId): diff --git a/music21/humdrum/questions.py b/music21/humdrum/questions.py index b60354eeda..89c2964e44 100644 --- a/music21/humdrum/questions.py +++ b/music21/humdrum/questions.py @@ -117,7 +117,7 @@ def xtest004(self): # # titles and other tex annotations # # here, we get these only from the first page # titleCandidates += pageStream[0].getElementsByClass(TextAnnotation) -# # an additional slot might be used to store meta data, also +# # an additional slot might be used to store metadata, also # # as a stream # metaStream = partStream['meta'] # titleCandidates += metaStream.getElementsByClass(TextAnnotation) diff --git a/music21/metadata/testMetadata.py b/music21/metadata/testMetadata.py index f7dcb4617f..32c9fa50e3 100644 --- a/music21/metadata/testMetadata.py +++ b/music21/metadata/testMetadata.py @@ -43,7 +43,7 @@ def testJSONSerializationMetadata(self): self.assertEqual(md.composer, 'Frank') self.assertEqual(md.title, 'Concerto in F') - # test getting meta data from an imported source + # test getting metadata from an imported source c = converter.parse(mTF.mozartTrioK581Excerpt) md = c.metadata diff --git a/music21/stream/base.py b/music21/stream/base.py index 60949a77db..961420a926 100644 --- a/music21/stream/base.py +++ b/music21/stream/base.py @@ -13898,7 +13898,7 @@ def mergeScores(self): md = s.metadata # presently just getting the first of attributes encountered if md is not None: - # environLocal.printDebug(['sub-score meta data', md, + # environLocal.printDebug(['sub-score metadata', md, # 'md.composer', md.composer, 'md.title', md.title]) if md.title is not None and mdNew.title is None: mdNew.title = md.title diff --git a/music21/stream/makeNotation.py b/music21/stream/makeNotation.py index 1866922356..a903866d9f 100644 --- a/music21/stream/makeNotation.py +++ b/music21/stream/makeNotation.py @@ -30,7 +30,7 @@ from music21 import pitch from music21.common.numberTools import opFrac -from music21.common.types import StreamType +from music21.common.types import StreamType, OffsetQL from music21.exceptions21 import StreamException from music21.stream.iterator import StreamIterator # type-checking only @@ -129,7 +129,6 @@ def makeBeams( # if s.isClass(Measure): mColl: List[stream.Measure] if isinstance(returnObj, stream.Measure): - returnObj = cast(stream.Measure, returnObj) mColl = [returnObj] # store a list of measures for processing else: mColl = list(returnObj.getElementsByClass(stream.Measure)) # a list of measures @@ -426,7 +425,7 @@ def makeMeasures( inPlace=True, # copy already made ) if inPlace: - return + return None else: return returnObj else: @@ -529,6 +528,7 @@ def makeMeasures( measureCount = 0 lastTimeSignature = None while True: + # TODO: avoid while True m = stream.Measure() m.number = measureCount + 1 # environLocal.printDebug([ @@ -588,7 +588,8 @@ def makeMeasures( # cache information about each measure (we used to do this once per element) postLen = len(post) postMeasureList = [] - lastTimeSignature = None + lastTimeSignature = meter.TimeSignature('4/4') # default. + for i in range(postLen): m = post[i] if m.timeSignature is not None: @@ -617,9 +618,7 @@ def makeMeasures( continue match = False - lastTimeSignature = None - m = None for i in range(postLen): postMeasureInfo = postMeasureList[i] mStart = postMeasureInfo['mStart'] @@ -850,7 +849,7 @@ def makeRests( timeRangeFromBarDuration=timeRangeFromBarDuration, ) if inPlace: - return + return None else: return returnObj @@ -873,8 +872,8 @@ def oHighTargetForMeasure( post -= m.paddingRight return max(post, 0.0) - oLowTarget = 0.0 - oHighTarget = 0.0 + oLowTarget: OffsetQL = 0.0 + oHighTarget: OffsetQL = 0.0 if timeRangeFromBarDuration: if isinstance(returnObj, stream.Measure): oHighTarget = oHighTargetForMeasure(m=returnObj) @@ -905,10 +904,11 @@ def oHighTargetForMeasure( oLowTarget = min(refStreamOrTimeRange) oHighTarget = max(refStreamOrTimeRange) + bundle: List[StreamType] if returnObj.hasVoices(): bundle = list(returnObj.voices) elif returnObj.hasMeasures(): - bundle = returnObj.getElementsByClass(stream.Measure) + bundle = list(returnObj.getElementsByClass(stream.Measure)) else: bundle = [returnObj] @@ -1212,24 +1212,24 @@ def makeTies( lastTimeSignature = None while True: # TODO: find a way to avoid 'while True' - # update measureStream on each iteration, + # update measureIterator on each iteration, # as new measure may have been added to the returnObj stream - measureStream = returnObj.getElementsByClass(stream.Measure).stream() - if mCount >= len(measureStream): + measureIterator = returnObj.getElementsByClass(stream.Measure) + if mCount >= len(measureIterator): break # reached the end of all measures available or added # get the current measure to look for notes that need ties - m = measureStream[mCount] + m = measureIterator[mCount] if m.timeSignature is not None: lastTimeSignature = m.timeSignature # get next measure; we may not need it, but have it ready - if mCount + 1 < len(measureStream): - mNext = measureStream[mCount + 1] + if mCount + 1 < len(measureIterator): + mNext = measureIterator[mCount + 1] mNextAdd = False # already present; do not append else: # create a new measure mNext = stream.Measure() # set offset to last offset plus total length - mOffset = measureStream.elementOffset(m) + mOffset = m.offset if lastTimeSignature is not None: mNext.offset = (mOffset + lastTimeSignature.barDuration.quarterLength) @@ -1261,12 +1261,12 @@ def makeTies( # if there are voices, we must look at voice id values to only # connect ties to components in the same voice, assuming there # are voices in the next measure - try: + if lastTimeSignature is not None: mEnd = lastTimeSignature.barDuration.quarterLength - except AttributeError: - ts = m.getContextByClass(meter.TimeSignature) - if ts is not None: - lastTimeSignature = ts + else: + possible_ts = m.getContextByClass(meter.TimeSignature) + if possible_ts is not None: + lastTimeSignature = possible_ts mEnd = lastTimeSignature.barDuration.quarterLength else: mEnd = 4.0 # Default @@ -1343,9 +1343,9 @@ def makeTies( # mNext]) returnObj.insert(mNext.offset, mNext) mCount += 1 - for measure in measureStream: + + for measure in returnObj.getElementsByClass(stream.Measure): measure.flattenUnnecessaryVoices(inPlace=True) - del measureStream # clean up unused streams if not inPlace: return returnObj @@ -1848,9 +1848,10 @@ def setStemDirectionOneGroup( has_consistent_stem_directions = False # noinspection PyTypeChecker - clef_context: clef.Clef = group[0].getContextByClass(clef.Clef) - if not clef_context: + optional_clef_context: Optional[clef.Clef] = group[0].getContextByClass(clef.Clef) + if optional_clef_context is None: return + clef_context: clef.Clef = optional_clef_context pitchList: List[pitch.Pitch] = [] for n in group: From 9f0b341c39295cc5715c318ba655dd6ff0b54b14 Mon Sep 17 00:00:00 2001 From: Myke Cuthbert Date: Thu, 5 May 2022 16:11:45 -1000 Subject: [PATCH 2/2] lint --- music21/abcFormat/__init__.py | 115 +++++++++++++++++++-------------- music21/stream/makeNotation.py | 10 +-- 2 files changed, 71 insertions(+), 54 deletions(-) diff --git a/music21/abcFormat/__init__.py b/music21/abcFormat/__init__.py index 4e244bfe32..078d772e20 100644 --- a/music21/abcFormat/__init__.py +++ b/music21/abcFormat/__init__.py @@ -57,7 +57,7 @@ import io import re import unittest -from typing import Union, Optional, List, Tuple, Any, TypeVar, Sequence +from typing import Union, Optional, List, Tuple, Any, TypeVar, Sequence, Literal, Dict from music21 import common from music21 import environment @@ -142,7 +142,7 @@ def _reprInternal(self): return repr(self.src) @staticmethod - def stripComment(strSrc): + def stripComment(strSrc: str) -> str: ''' removes ABC-style comments from a string: @@ -317,7 +317,7 @@ def isTempo(self) -> bool: return True return False - def getTimeSignatureParameters(self): + def getTimeSignatureParameters(self) -> Optional[Tuple[int, int, str]]: ''' If there is a time signature representation available, get a numerator, denominator and an abbreviation symbol. @@ -349,6 +349,9 @@ def getTimeSignatureParameters(self): if not self.isMeter(): raise ABCTokenException('no time signature associated with this metadata') + n: int + d: int + symbol: str if self.data.lower() == 'none': return None elif self.data == 'C': @@ -366,7 +369,7 @@ def getTimeSignatureParameters(self): symbol = 'normal' # m21 compat return n, d, symbol - def getTimeSignatureObject(self): + def getTimeSignatureObject(self) -> Optional['music21.meter.TimeSignature']: ''' Return a music21 :class:`~music21.meter.TimeSignature` object for this metadata tag, if isMeter is True, otherwise raise exception. @@ -382,6 +385,12 @@ def getTimeSignatureObject(self): Traceback (most recent call last): music21.abcFormat.ABCTokenException: no time signature associated with this non-metrical metadata. + + >>> am = abcFormat.ABCMetadata('M:none') + >>> am.preParse() + >>> ts = am.getTimeSignatureObject() + >>> ts is None + True ''' if not self.isMeter(): raise ABCTokenException( @@ -394,11 +403,10 @@ def getTimeSignatureObject(self): numerator, denominator, unused_symbol = parameters return meter.TimeSignature(f'{numerator}/{denominator}') - def getKeySignatureParameters(self): + def getKeySignatureParameters(self) -> Tuple[int, Optional[str]]: # noinspection SpellCheckingInspection ''' - Extract key signature parameters, include indications for mode, - and translate sharps count compatible with m21, + Extract key signature parameters, returning the number of sharps and the mode. >>> from music21 import abcFormat @@ -452,7 +460,6 @@ def getKeySignatureParameters(self): >>> am.preParse() >>> am.getKeySignatureParameters() (-2, 'aeolian') - ''' # placing this import in method for now; key.py may import this module from music21 import key @@ -517,7 +524,9 @@ def getKeySignatureParameters(self): # (key of D) but then mark every c as natural return key.pitchToSharps(standardKeyStr, mode), mode - def getKeySignatureObject(self): + def getKeySignatureObject(self) -> Union['music21.key.Key', + 'music21.key.KeySignature', + None]: # noinspection SpellCheckingInspection,PyShadowingNames ''' Return a music21 :class:`~music21.key.KeySignature` or :class:`~music21.key.Key` @@ -620,7 +629,6 @@ def getMetronomeMarkObject(self) -> Optional['music21.tempo.MetronomeMark']: >>> am.preParse() >>> am.getMetronomeMarkObject() - ''' if not self.isTempo(): raise ABCTokenException('no tempo associated with this metadata') @@ -826,7 +834,7 @@ def parse(self): if len(barTypeComponents) > 2: self.repeatForm = barTypeComponents[3] - def isRepeat(self): + def isRepeat(self) -> bool: if self.barType == 'repeat': return True else: @@ -846,7 +854,7 @@ def isRegular(self) -> bool: else: return False - def isRepeatBracket(self) -> Union[int, bool]: + def isRepeatBracket(self) -> Union[int, Literal[False]]: ''' Return a number if this defines a repeat bracket for an alternate ending otherwise returns False. @@ -902,7 +910,7 @@ class ABCTuplet(ABCToken): In ABCHandler.tokenProcess(), rhythms are adjusted. ''' - def __init__(self, src): + def __init__(self, src: str = ''): super().__init__(src) # self.qlRemain = None # how many ql are left of this tuplets activity @@ -920,7 +928,7 @@ def __init__(self, src): # store an m21 tuplet object self.tupletObj = None - def updateRatio(self, keySignatureObj=None): + def updateRatio(self, timeSignatureObj: Optional['music21.meter.TimeSignature'] = None) -> None: # noinspection PyShadowingNames ''' Cannot be called until local meter context @@ -993,9 +1001,9 @@ def updateRatio(self, keySignatureObj=None): Traceback (most recent call last): music21.abcFormat.ABCTokenException: cannot handle tuplet of form: '(10' ''' - if keySignatureObj is None: + if timeSignatureObj is None: normalSwitch = 2 # 4/4 - elif keySignatureObj.beatDivisionCount == 3: # if compound + elif timeSignatureObj.beatDivisionCount == 3: # if compound normalSwitch = 3 else: normalSwitch = 2 @@ -1035,7 +1043,7 @@ def updateRatio(self, keySignatureObj=None): self.numberNotesActual = a self.numberNotesNormal = normalNotes - def updateNoteCount(self): + def updateNoteCount(self) -> None: ''' Update the note count of notes that are affected by this tuplet. Can be set by p:q:r style tuplets. @@ -1088,7 +1096,7 @@ class ABCTie(ABCToken): Ties are treated as an attribute of the note before the '-'; the note after is marked as the end of the tie. ''' - def __init__(self, src): + def __init__(self, src=''): super().__init__(src) self.noteObj = None @@ -1098,9 +1106,9 @@ class ABCSlurStart(ABCToken): ABCSlurStart tokens always precede the notes in a slur. For nested slurs, each open parenthesis gets its own token. ''' - def __init__(self, src): + def __init__(self, src=''): super().__init__(src) - self.slurObj = None + self.slurObj: Optional['music21.spanner.Slur'] = None def fillSlur(self): ''' @@ -1125,9 +1133,9 @@ class ABCCrescStart(ABCToken): the closing string "!crescendo)" counts as an ABCParenStop. ''' - def __init__(self, src): + def __init__(self, src=''): super().__init__(src) - self.crescObj = None + self.crescObj: Optional['music21.dynamics.Crescendo'] = None def fillCresc(self): from music21 import dynamics @@ -1139,9 +1147,9 @@ class ABCDimStart(ABCToken): ABCDimStart tokens always precede the notes in a diminuendo. They function identically to ABCCrescStart tokens. ''' - def __init__(self, src): # previous typo?: used to be __init + def __init__(self, src=''): super().__init__(src) - self.dimObj = None + self.dimObj: Optional['music21.dynamics.Diminuendo'] = None def fillDim(self): from music21 import dynamics @@ -1208,10 +1216,9 @@ class ABCBrokenRhythmMarker(ABCToken): ''' Marks that rhythm is broken with '>>>' ''' - - def __init__(self, src): + def __init__(self, src=''): super().__init__(src) - self.data = None + self.data: Optional[str] = None def preParse(self): '''Called before context adjustments: need to have access to data @@ -1239,14 +1246,14 @@ class ABCNote(ABCToken): these guitar chords) associated with this note. This attribute is updated when parse() is called. ''' - def __init__(self, src='', carriedAccidental=None): + def __init__(self, src='', carriedAccidental: Optional[str] = None): super().__init__(src) # store the ABC accidental string propagated in the measure that # must be applied to this note. Note must not be set if the # note already has an explicit accidental attached. (The explicit # accidental is now the one that will be carried forward.) - self.carriedAccidental = carriedAccidental + self.carriedAccidental: Optional[str] = carriedAccidental # store chord string if connected to this note self.chordSymbols = [] @@ -1257,7 +1264,7 @@ def __init__(self, src='', carriedAccidental=None): self.inGrace = None # provide default duration from handler; may change during piece - self.activeDefaultQuarterLength = None + self.activeDefaultQuarterLength: Optional[float] = None # store if a broken symbol applies; a pair of symbols, position (left, right) self.brokenRhythmMarker = None @@ -1279,8 +1286,9 @@ def __init__(self, src='', carriedAccidental=None): # set to True if a modification of key signature # set to False if an altered tone part of a Key self.accidentalDisplayStatus = None + # determined during parse() based on if pitch chars are present - self.isRest = None + self.isRest: bool = False # Pitch and duration attributes for m21 conversion # they are set via parse() based on other contextual information. @@ -1288,10 +1296,10 @@ def __init__(self, src='', carriedAccidental=None): self.quarterLength = None @staticmethod - def _splitChordSymbols(strSrc): + def _splitChordSymbols(strSrc: str) -> Tuple[List[str], str]: ''' Splits chord symbols from other string characteristics. - Return list of chord symbols and clean, remain chars + Returns a 2-tuple of a list of chord symbols, and clean remaining chars Staticmethod: @@ -1367,7 +1375,6 @@ def getPitchName( >>> an.getPitchName('c') ('C#5', False) - Illegal pitch names raise an ABCHandlerException >>> an.getPitchName('x') @@ -1477,7 +1484,9 @@ def getPitchName( _pitchTranslationCache[_cacheKey] = (pStr, accidentalDisplayStatus) return (pStr, accidentalDisplayStatus) - def getQuarterLength(self, strSrc, forceDefaultQuarterLength=None) -> float: + def getQuarterLength(self, + strSrc: str, + forceDefaultQuarterLength: Optional[float] = None) -> float: ''' Called with parse(), after context processing, to calculate duration @@ -1522,7 +1531,7 @@ def getQuarterLength(self, strSrc, forceDefaultQuarterLength=None) -> float: >>> an.getQuarterLength('A') 0.9375 - >>> an.getQuarterLength('A', forceDefaultQuarterLength=1) + >>> an.getQuarterLength('A', forceDefaultQuarterLength=1.0) 1.875 ''' if forceDefaultQuarterLength is not None: @@ -1603,14 +1612,16 @@ def getQuarterLength(self, strSrc, forceDefaultQuarterLength=None) -> float: def parse( self, - forceDefaultQuarterLength=None, - forceKeySignature=None + forceDefaultQuarterLength: Optional[float] = None, + forceKeySignature: Optional['music21.key.KeySignature'] = None ) -> None: # environLocal.printDebug(['parse', self.src]) self.chordSymbols, nonChordSymStr = self._splitChordSymbols(self.src) # get pitch name form remaining string # rests will have a pitch name of None + pn: str + accDisp: Union[bool, None] try: pn, accDisp = self.getPitchName(nonChordSymStr, forceKeySignature=forceKeySignature) @@ -1730,19 +1741,21 @@ class ABCHandler: New in v6.3 -- lineBreaksDefinePhrases -- does not yet do anything ''' - def __init__(self, abcVersion=None, lineBreaksDefinePhrases=False): + def __init__(self, + abcVersion: Optional[Tuple[int, ...]] = None, + lineBreaksDefinePhrases=False): # tokens are ABC objects import n a linear stream - self.abcVersion = abcVersion - self.abcDirectives = {} - self.tokens = [] - self.activeParens = [] - self.activeSpanners = [] - self.lineBreaksDefinePhrases = lineBreaksDefinePhrases + self.abcVersion: Optional[Tuple[int, ...]] = abcVersion + self.abcDirectives: Dict[str, str] = {} + self.tokens: List[ABCToken] = [] + self.activeParens: List[str] = [] # e.g. ['Crescendo', 'Slur'] + self.activeSpanners: List['music21.spanner.Spanner'] = [] + self.lineBreaksDefinePhrases: bool = lineBreaksDefinePhrases self.pos = -1 self.skipAhead = 0 self.isFirstComment = True self.strSrc = '' - self.srcLen = len(self.strSrc) # just documenting this. + self.srcLen: int = len(self.strSrc) # just documenting this. self.currentCollectStr = '' @staticmethod @@ -1896,7 +1909,7 @@ def _accidentalPropagation(self) -> str: return self.abcDirectives['propagate-accidentals'] return 'pitch' # Default per abc 2.1 standard - def parseCommentForVersionInformation(self, commentLine: str): + def parseCommentForVersionInformation(self, commentLine: str) -> Optional[Tuple[int, ...]]: ''' If this is the first comment then searches for a version match and set it as .abcVersion @@ -1969,7 +1982,9 @@ def processComment(self): # environLocal.printDebug(['got comment:', repr(self.strSrc[i:j + 1])]) @staticmethod - def startsMetadata(c: str, cNext: Optional[str], cNextNext: Optional[str]) -> bool: + def startsMetadata(c: str, + cNext: Optional[str], + cNextNext: Optional[str]) -> bool: ''' Returns True if this context describes the start of a metadata section, like @@ -2424,8 +2439,8 @@ def tokenProcess(self): lastDefaultQL = None lastKeySignature = None lastTimeSignatureObj = None # an m21 object - lastTupletToken = None # a token obj; keeps count of usage - lastTieToken = None + lastTupletToken: Optional[ABCTuplet] = None # a token obj; keeps count of usage + lastTieToken: Optional[ABCTie] = None lastStaccToken = None lastUpToken = None lastDownToken = None diff --git a/music21/stream/makeNotation.py b/music21/stream/makeNotation.py index a903866d9f..fe961c4bfe 100644 --- a/music21/stream/makeNotation.py +++ b/music21/stream/makeNotation.py @@ -15,7 +15,7 @@ import copy import unittest -from typing import List, Generator, Optional, Set, Union, cast +from typing import List, Generator, Optional, Set, Union from fractions import Fraction # typing only from music21 import beam @@ -1211,7 +1211,8 @@ def makeTies( mCount = 0 lastTimeSignature = None - while True: # TODO: find a way to avoid 'while True' + while True: # pylint: disable=too-many-nested-blocks + # TODO: find a way to avoid 'while True' # update measureIterator on each iteration, # as new measure may have been added to the returnObj stream measureIterator = returnObj.getElementsByClass(stream.Measure) @@ -1236,8 +1237,9 @@ def makeTies( else: mNext.offset = mOffset if not meterStream: # in case no meters are defined - ts = meter.TimeSignature() - ts.load(f'{defaults.meterNumerator}/{defaults.meterDenominatorBeatType}') + ts = meter.TimeSignature( + f'{defaults.meterNumerator}/{defaults.meterDenominatorBeatType}' + ) else: # get the last encountered meter ts = meterStream.getElementAtOrBefore(mNext.offset) # only copy and assign if not the same as the last