Skip to content

Commit

Permalink
Merge pull request #1520 from cuthbertLab/register_subconverters
Browse files Browse the repository at this point in the history
New subconverters register above default
  • Loading branch information
mscuthbert authored Jan 30, 2023
2 parents aa7b068 + ef1d98e commit b474fd4
Showing 1 changed file with 57 additions and 16 deletions.
73 changes: 57 additions & 16 deletions music21/converter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
'''
from __future__ import annotations

from collections import deque
import collections.abc
import copy
from http.client import responses
Expand Down Expand Up @@ -378,24 +379,24 @@ def status(self) -> tuple[pathlib.Path, bool, pathlib.Path | None]:


# ------------------------------------------------------------------------------
_registeredSubconverters: list[type[subConverters.SubConverter]] = []
# a deque of additional subconverters to use (in addition to the default ones)
_registeredSubconverters: deque[type[subConverters.SubConverter]] = deque()

# default subconverters to skip
_deregisteredSubconverters: list[
_deregisteredSubconverters: deque[
type[subConverters.SubConverter] | t.Literal['all']
] = []
] = deque()


def resetSubconverters():
'''
Reset state to default (removing all registered and deregistered subconverters).
'''
global _registeredSubconverters # pylint: disable=global-statement
global _deregisteredSubconverters # pylint: disable=global-statement
_registeredSubconverters = []
_deregisteredSubconverters = []
_registeredSubconverters.clear()
_deregisteredSubconverters.clear()


def registerSubconverter(newSubConverter) -> None:
def registerSubconverter(newSubConverter: type[subConverters.SubConverter]) -> None:
'''
Add a Subconverter to the list of registered subconverters.
Expand All @@ -418,8 +419,9 @@ def registerSubconverter(newSubConverter) -> None:
>>> converter.resetSubconverters() #_DOCS_HIDE
Changed in v.9 -- custom subconverters are registered above default subconverters.
'''
_registeredSubconverters.append(newSubConverter)
_registeredSubconverters.appendleft(newSubConverter)


def unregisterSubconverter(
Expand Down Expand Up @@ -457,13 +459,11 @@ def unregisterSubconverter(
[]
>>> converter.resetSubconverters() #_DOCS_HIDE
'''
global _registeredSubconverters # pylint: disable=global-statement
global _deregisteredSubconverters # pylint: disable=global-statement
if removeSubconverter == 'all':
_registeredSubconverters = []
_deregisteredSubconverters = ['all']
_registeredSubconverters.clear()
_deregisteredSubconverters.clear()
_deregisteredSubconverters.append('all')
return

try:
Expand Down Expand Up @@ -539,7 +539,6 @@ def parseFileNoPickle(

if useFormat is None:
useFormat = self.getFormatFromFileExtension(fpPathlib)

self.setSubconverterFromFormat(useFormat)
if t.TYPE_CHECKING:
assert isinstance(self.subConverter, subConverters.SubConverter)
Expand Down Expand Up @@ -792,6 +791,7 @@ def subconvertersList(
self,
converterType: t.Literal['any', 'input', 'output'] = 'any'
) -> list[type[subConverters.SubConverter]]:
# noinspection PyAttributeOutsideInit
'''
Gives a list of all the subconverter classes that are registered.
Expand Down Expand Up @@ -844,6 +844,45 @@ def subconvertersList(
>>> ConverterSonix in c.subconvertersList()
True
Newly registered subconveters appear first, so they will be used instead
of any default subconverters that work on the same format or extension.
>>> class BadMusicXMLConverter(converter.subConverters.SubConverter):
... registerFormats = ('musicxml',)
... registerInputExtensions = ('xml', 'mxl', 'musicxml')
... def parseData(self, strData, number=None):
... self.stream = stream.Score(id='empty')
>>> converter.registerSubconverter(BadMusicXMLConverter)
>>> c.subconvertersList()
[<class 'music21.BadMusicXMLConverter'>,
...
<class 'music21.converter.subConverters.ConverterMusicXML'>,
...]
Show that this musicxml file by Amy Beach is now parsed by BadMusicXMLConverter:
>>> #_DOCS_SHOW s = corpus.parse('beach/prayer_of_a_tired_child')
>>> #_DOCS_HIDE -- we cannot know if the piece is already parsed or not.
>>> s = corpus.parse('beach/prayer_of_a_tired_child', forceSource=True) #_DOCS_HIDE
>>> s.id
'empty'
>>> len(s.parts)
0
Note that if the file has already been parsed by another subconverter format
the parameter `forceSource` is required to force the file to be parsed by the
newly registered subconverter:
>>> converter.unregisterSubconverter(BadMusicXMLConverter)
>>> #_DOCS_HIDE -- the forceSource will not have created a pickle.
>>> #_DOCS_SHOW s = corpus.parse('beach/prayer_of_a_tired_child')
>>> s.id
'empty'
>>> s = corpus.parse('beach/prayer_of_a_tired_child', forceSource=True)
>>> len(s.parts)
6
>>> converter.resetSubconverters() #_DOCS_HIDE
'''
subConverterList = []
Expand Down Expand Up @@ -960,7 +999,9 @@ def getSubConverterFormats(self):
if hasattr(name, 'registerFormats'):
formatsTuple = name.registerFormats
for f in formatsTuple:
converterFormats[f.lower()] = name
f = f.lower()
if f not in converterFormats:
converterFormats[f] = name
return converterFormats

def setSubconverterFromFormat(self, converterFormat: str):
Expand Down

0 comments on commit b474fd4

Please sign in to comment.