From 913e9e0512a124c4f9e624fae71cf68b198813b6 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Wed, 18 Sep 2024 11:19:06 -1000 Subject: [PATCH 1/3] Some Note/TinyNotation typing This is a bit of cleanup before fixing a bigger TN problem of Issue #1733. --- music21/meter/base.py | 1 - music21/meter/tools.py | 2 - music21/note.py | 135 ++++++++++++++++++++-------------------- music21/tinyNotation.py | 22 ++++--- 4 files changed, 80 insertions(+), 80 deletions(-) diff --git a/music21/meter/base.py b/music21/meter/base.py index ce280bd5a..b41babc49 100644 --- a/music21/meter/base.py +++ b/music21/meter/base.py @@ -265,7 +265,6 @@ class TimeSignatureBase(base.Music21Object): pass class TimeSignature(TimeSignatureBase): - # noinspection GrazieInspection r''' The `TimeSignature` object represents time signatures in musical scores (4/4, 3/8, 2/4+5/16, Cut, etc.). diff --git a/music21/meter/tools.py b/music21/meter/tools.py index 33abacee6..3585cea5c 100644 --- a/music21/meter/tools.py +++ b/music21/meter/tools.py @@ -101,8 +101,6 @@ def slashCompoundToFraction(value: str) -> NumDenomTuple: return tuple(post) -# Pycharm is having trouble with the unmatched parentheses in the docs -# noinspection GrazieInspection @lru_cache(512) def slashMixedToFraction(valueSrc: str) -> tuple[NumDenomTuple, bool]: ''' diff --git a/music21/note.py b/music21/note.py index 52b2c22b9..d80697e39 100644 --- a/music21/note.py +++ b/music21/note.py @@ -680,32 +680,9 @@ def tie(self) -> tie.Tie|None: def tie(self, value: tie.Tie|None): self._tie = value - def _getLyric(self) -> str|None: - if not self.lyrics: - return None - - allText = [ly.text for ly in self.lyrics] - return '\n'.join([textStr for textStr in allText if textStr is not None]) - - def _setLyric(self, value: str|Lyric|None) -> None: - self.lyrics = [] - if value is None: - return - - if isinstance(value, Lyric): - self.lyrics.append(value) - return - - if not isinstance(value, str): - value = str(value) - - values = value.split('\n') - for i, v in enumerate(values): - self.lyrics.append(Lyric(v, number=i + 1)) - - lyric = property(_getLyric, - _setLyric, - doc=r''' + @property + def lyric(self) -> str|None: + r''' The lyric property can be used to get and set a lyric for this Note, Chord, or Rest. This is a simplified version of the more general @@ -749,7 +726,30 @@ def _setLyric(self, value: str|Lyric|None) -> None: * Changed in v6.7: added setting to a Lyric object. Removed undocumented setting to False instead of setting to None - ''') + ''' + if not self.lyrics: + return None + + allText = [ly.text for ly in self.lyrics] + return '\n'.join([textStr for textStr in allText if textStr is not None]) + + @lyric.setter + def lyric(self, value: str|Lyric|None) -> None: + self.lyrics = [] + if value is None: + return + + if isinstance(value, Lyric): + self.lyrics.append(value) + return + + if not isinstance(value, str): + value = str(value) + + values = value.split('\n') + for i, v in enumerate(values): + self.lyrics.append(Lyric(v, number=i + 1)) + def addLyric(self, text, @@ -1076,21 +1076,10 @@ def __deepcopy__(self, memo=None): # environLocal.printDebug(['calling NotRest.__deepcopy__', self]) return self._deepcopySubclassable(memo=memo) - def _getStemDirection(self) -> str: - return self._stemDirection - def _setStemDirection(self, direction): - if direction is None: - direction = 'unspecified' # allow setting to None meaning - elif direction == 'none': - direction = 'noStem' # allow setting to none or None - elif direction not in stemDirectionNames: - raise NotRestException(f'not a valid stem direction name: {direction}') - self._stemDirection = direction - - stemDirection = property(_getStemDirection, - _setStemDirection, - doc=''' + @property + def stemDirection(self) -> str: + r''' Get or set the stem direction of this NotRest object. Valid stem direction names are found in note.stemDirectionNames (see below). @@ -1124,7 +1113,18 @@ def _setStemDirection(self, direction): >>> n.stemDirection = None >>> n.stemDirection 'unspecified' - ''') + ''' + return self._stemDirection + + @stemDirection.setter + def stemDirection(self, direction: None|str): + if direction is None: + direction = 'unspecified' # allow setting to None meaning + elif direction == 'none': + direction = 'noStem' # allow setting to none or None + elif direction not in stemDirectionNames: + raise NotRestException(f'not a valid stem direction name: {direction}') + self._stemDirection = direction @property def notehead(self) -> str: @@ -1617,32 +1617,30 @@ def __deepcopy__(self: Note, memo=None) -> Note: # -------------------------------------------------------------------------- # property access - def _getName(self) -> str: + @property + def name(self) -> str: + ''' + Return or set the pitch name from the :class:`~music21.pitch.Pitch` object. + See `Pitch`'s attribute :attr:`~music21.pitch.Pitch.name`. + ''' return self.pitch.name - def _setName(self, value: str): + @name.setter + def name(self, value: str): self.pitch.name = value - name = property(_getName, - _setName, - doc=''' - Return or set the pitch name from the :class:`~music21.pitch.Pitch` object. - See `Pitch`'s attribute :attr:`~music21.pitch.Pitch.name`. - ''') - - def _getNameWithOctave(self) -> str: + @property + def nameWithOctave(self) -> str: + ''' + Return or set the pitch name with octave from the :class:`~music21.pitch.Pitch` object. + See `Pitch`'s attribute :attr:`~music21.pitch.Pitch.nameWithOctave`. + ''' return self.pitch.nameWithOctave - def _setNameWithOctave(self, value: str): + @nameWithOctave.setter + def nameWithOctave(self, value: str): self.pitch.nameWithOctave = value - nameWithOctave = property(_getNameWithOctave, - _setNameWithOctave, - doc=''' - Return or set the pitch name with octave from the :class:`~music21.pitch.Pitch` object. - See `Pitch`'s attribute :attr:`~music21.pitch.Pitch.nameWithOctave`. - ''') - @property def step(self) -> StepName: ''' @@ -1655,19 +1653,18 @@ def step(self) -> StepName: def step(self, value: StepName): self.pitch.step = value - def _getOctave(self) -> int|None: + @property + def octave(self) -> int|None: + ''' + Return or set the octave value from the :class:`~music21.pitch.Pitch` object. + See :attr:`~music21.pitch.Pitch.octave`. + ''' return self.pitch.octave - def _setOctave(self, value: int|None): + @octave.setter + def octave(self, value: int|None): self.pitch.octave = value - octave = property(_getOctave, - _setOctave, - doc=''' - Return or set the octave value from the :class:`~music21.pitch.Pitch` object. - See :attr:`~music21.pitch.Pitch.octave`. - ''') - @property def pitches(self) -> tuple[Pitch, ...]: ''' diff --git a/music21/tinyNotation.py b/music21/tinyNotation.py index 224e3702f..3eff7b0fe 100644 --- a/music21/tinyNotation.py +++ b/music21/tinyNotation.py @@ -275,10 +275,10 @@ class State: # expires after N tokens or never. autoExpires: typing.Literal[False]|int = False - def __init__(self, parent=None, stateInfo=None): + def __init__(self, parent: Converter|None = None, stateInfo: str|None = None): self.affectedTokens = [] - self.parent = parent - self.stateInfo = stateInfo + self.parent: Converter|None = parent + self.stateInfo: str|None = stateInfo # print('Adding state', self, parent.activeStates) def start(self): @@ -293,19 +293,25 @@ def end(self) -> Music21Object|None: ''' return None - def affectTokenBeforeParse(self, tokenStr): + def affectTokenBeforeParse(self, tokenStr: str) -> str: ''' called to modify the string of a token. ''' return tokenStr - def affectTokenAfterParseBeforeModifiers(self, m21Obj): + def affectTokenAfterParseBeforeModifiers( + self, + m21Obj: base.Music21Object + ) -> base.Music21Object: ''' called after the object has been acquired but before modifiers have been applied. ''' return m21Obj - def affectTokenAfterParse(self, m21Obj): + def affectTokenAfterParse( + self, + m21Obj: base.Music21Object + ) -> base.Music21Object: ''' called to modify the tokenObj after parsing @@ -545,7 +551,7 @@ class RestToken(NoteOrRestToken): ''' A token starting with 'r', representing a rest. ''' - def parse(self, parent=None): + def parse(self, parent: Converter|None = None): r = note.Rest() self.applyDuration(r, self.token, parent) return r @@ -586,7 +592,7 @@ def __init__(self, token=''): super().__init__(token) self.isEditorial = False - def parse(self, parent=None): + def parse(self, parent: Converter|None = None): ''' Extract the pitch from the note and then returns the Note. ''' From 955224c4ffa0213f104fd4f0d219857c9f66c32b Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Wed, 18 Sep 2024 12:31:30 -1000 Subject: [PATCH 2/3] Fix lint (and readme, thanks Greg!) --- README.md | 2 +- .../usersGuide/usersGuide_14_timeSignatures.ipynb | 2 +- music21/tinyNotation.py | 10 ++++++---- music21/tree/verticality.py | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index f5f18328c..5be6d9915 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ Maintainers will respect confidentiality with regard to reports. ## Acknowledgements ## -The early development of `music21` was been supported by +The early development of `music21` was supported by the generosity of the Seaver Institute and the National Endowment for the Humanities, along with MIT's Music and Theater Arts Section and the School of Humanities, Arts, and Social Sciences. diff --git a/documentation/source/usersGuide/usersGuide_14_timeSignatures.ipynb b/documentation/source/usersGuide/usersGuide_14_timeSignatures.ipynb index 24b7d6fc4..b36a5d166 100644 --- a/documentation/source/usersGuide/usersGuide_14_timeSignatures.ipynb +++ b/documentation/source/usersGuide/usersGuide_14_timeSignatures.ipynb @@ -913,7 +913,7 @@ ], "source": [ "for n in alto.recurse().notes:\n", - " n.stemDirection = None\n", + " n.stemDirection = 'unspecified'\n", "\n", "alto.show()" ] diff --git a/music21/tinyNotation.py b/music21/tinyNotation.py index 3eff7b0fe..e4c15633a 100644 --- a/music21/tinyNotation.py +++ b/music21/tinyNotation.py @@ -301,8 +301,8 @@ def affectTokenBeforeParse(self, tokenStr: str) -> str: def affectTokenAfterParseBeforeModifiers( self, - m21Obj: base.Music21Object - ) -> base.Music21Object: + m21Obj: Music21Object + ) -> Music21Object: ''' called after the object has been acquired but before modifiers have been applied. ''' @@ -310,8 +310,8 @@ def affectTokenAfterParseBeforeModifiers( def affectTokenAfterParse( self, - m21Obj: base.Music21Object - ) -> base.Music21Object: + m21Obj: Music21Object + ) -> Music21Object: ''' called to modify the tokenObj after parsing @@ -324,6 +324,8 @@ def affectTokenAfterParse( self.end() # this is a hack that should be done away with... p = self.parent + if p is None: + return m21Obj # I thought this was potentially O(n^2) but it's actually # just O(n) on activeStates and on pop() operation. diff --git a/music21/tree/verticality.py b/music21/tree/verticality.py index 0f5fd9f41..fba1046cd 100644 --- a/music21/tree/verticality.py +++ b/music21/tree/verticality.py @@ -786,7 +786,7 @@ def newNote(ts: spans.PitchedTimespan, n: note.Note) -> note.Note: nNew.pitch = n.pitch if nNew.stemDirection != 'noStem': - nNew.stemDirection = None + nNew.stemDirection = 'unspecified' if not addTies: return nNew From 123cb0d1556a3d3a530bb979bd4ce3b06a7d60b1 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Wed, 18 Sep 2024 12:39:09 -1000 Subject: [PATCH 3/3] I think affectedTokens is always m21obj? --- music21/tinyNotation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/music21/tinyNotation.py b/music21/tinyNotation.py index e4c15633a..65b3b69de 100644 --- a/music21/tinyNotation.py +++ b/music21/tinyNotation.py @@ -276,7 +276,7 @@ class State: autoExpires: typing.Literal[False]|int = False def __init__(self, parent: Converter|None = None, stateInfo: str|None = None): - self.affectedTokens = [] + self.affectedTokens: list[Music21Object] = [] self.parent: Converter|None = parent self.stateInfo: str|None = stateInfo # print('Adding state', self, parent.activeStates)