diff --git a/mscxyz/__init__.py b/mscxyz/__init__.py
index efb1533..c3b80cb 100644
--- a/mscxyz/__init__.py
+++ b/mscxyz/__init__.py
@@ -7,8 +7,8 @@
.. code ::
- MscoreFile
- MscoreXmlTree
+ MuseScoreFile
+ MuseScoreFile
MscoreLyricsInterface
MscoreMetaInterface
MscoreStyleInterface
@@ -37,9 +37,8 @@
from mscxyz.meta import Meta
from mscxyz.rename import rename_filename
from mscxyz.score_file_classes import (
- MscoreFile,
MscoreStyleInterface,
- MscoreXmlTree,
+ MuseScoreFile,
list_scores,
)
from mscxyz.settings import DefaultArguments
@@ -54,17 +53,10 @@
# Classes
-# Level 1
-MscoreFile
+MuseScoreFile
"""see submodule ``score_file_classes.py``"""
-# Level 2
-
-MscoreXmlTree
-"""see submodule ``score_file_classes.py``"""
-
-# Level 3
MscoreLyricsInterface
"""see submodule ``lyrics.py``"""
@@ -216,13 +208,11 @@ def execute(args: typing.Sequence[str] | None = None):
print("\n" + color(file, "red"))
if args.general_backup:
- from mscxyz.score_file_classes import MscoreFile
-
- score = MscoreFile(file)
+ score = MuseScoreFile(file)
score.backup()
if args.subcommand == "clean":
- score = MscoreXmlTree(file)
+ score = MuseScoreFile(file)
print(score.filename)
score.clean()
if args.clean_style:
@@ -269,9 +259,7 @@ def execute(args: typing.Sequence[str] | None = None):
score = rename_filename(file)
elif args.subcommand == "export":
- from mscxyz.score_file_classes import MscoreFile
-
- score = MscoreFile(file)
+ score = MuseScoreFile(file)
score.export(extension=args.export_extension)
report_errors(score.errors)
diff --git a/mscxyz/lyrics.py b/mscxyz/lyrics.py
index 2168505..67e468a 100644
--- a/mscxyz/lyrics.py
+++ b/mscxyz/lyrics.py
@@ -4,10 +4,10 @@
import lxml.etree as etree
-from mscxyz.score_file_classes import MscoreXmlTree
+from mscxyz.score_file_classes import MuseScoreFile
-class MscoreLyricsInterface(MscoreXmlTree):
+class MscoreLyricsInterface(MuseScoreFile):
def __init__(self, relpath: str):
super(MscoreLyricsInterface, self).__init__(relpath)
self.lyrics = self.normalize_lyrics()
diff --git a/mscxyz/meta.py b/mscxyz/meta.py
index 5826605..6e7975e 100644
--- a/mscxyz/meta.py
+++ b/mscxyz/meta.py
@@ -11,7 +11,7 @@
import tmep
from lxml.etree import _Element
-from mscxyz.score_file_classes import MscoreXmlTree
+from mscxyz.score_file_classes import MuseScoreFile
from mscxyz.utils import color, get_args
if typing.TYPE_CHECKING:
@@ -286,7 +286,7 @@ def __setattr__(self, field, value):
self._set_text(field.title(), value)
-class Combined(MscoreXmlTree):
+class Combined(MuseScoreFile):
fields = (
"composer",
"lyricist",
@@ -398,9 +398,9 @@ class InterfaceReadOnly:
"readonly_relpath_backup",
]
- xml_tree: MscoreXmlTree
+ xml_tree: MuseScoreFile
- def __init__(self, tree: MscoreXmlTree):
+ def __init__(self, tree: MuseScoreFile):
self.xml_tree = tree
@property
@@ -433,9 +433,9 @@ def readonly_relpath_backup(self) -> str:
class Interface:
- xml_tree: MscoreXmlTree
+ xml_tree: MuseScoreFile
- def __init__(self, tree: MscoreXmlTree):
+ def __init__(self, tree: MuseScoreFile):
self.xml_tree = tree
self.read_only = InterfaceReadOnly(tree)
self.read_write = InterfaceReadWrite(tree.xml_root)
@@ -463,7 +463,7 @@ def __setattr__(self, field: str, value):
raise ReadOnlyFieldError(field)
-class Meta(MscoreXmlTree):
+class Meta(MuseScoreFile):
def __init__(self, relpath: str):
super(Meta, self).__init__(relpath)
diff --git a/mscxyz/score_file_classes.py b/mscxyz/score_file_classes.py
index f15a4f8..cdc15fc 100644
--- a/mscxyz/score_file_classes.py
+++ b/mscxyz/score_file_classes.py
@@ -1,13 +1,10 @@
"""A collection of classes intended to represent one MuseScore file.
-The classes build on each other hierarchically. The class hierarchy:
-
.. code ::
- MscoreFile
- MscoreXmlTree
- MscoreStyleInterface
- MscoreLyricsInterface
+ MuseScoreFile
+ MscoreStyleInterface
+ MscoreLyricsInterface
"""
from __future__ import annotations
@@ -74,92 +71,6 @@ def list_zero_alphabet() -> List[str]:
return score_dirs
-###############################################################################
-# Class hierarchy level 1
-###############################################################################
-
-
-class MscoreFile:
- """This class holds basic file properties of the MuseScore score file.
-
- :param relpath: The relative (or absolute) path of a MuseScore
- file.
- """
-
- errors: List[Exception]
- """A list to store errors."""
-
- path: Path
- """The absolute path of the input file."""
-
- loadpath: str
- """The path of the uncompressed MuseScore file in XML format file.
- This path may be located in the temporary directory."""
-
- relpath: str
- """The relative path of the score file, for example:
- ``files/by_version/2/simple.mscx``.
- """
-
- abspath: str
- """The absolute path of the score file, for example:
- ``/home/jf/test/files/by_version/2/simple.mscx``."""
-
- relpath_backup: str
-
- dirname: str
- """The name of the containing directory of the MuseScore file, for
- example: ``files/by_version/2``."""
-
- basename: str
- """The basename of the score file, for example: ``simple``."""
-
- zip_container: Optional[ZipContainer]
-
- def __init__(self, relpath: str) -> None:
- self.errors = []
- self.relpath = relpath
- self.path = Path(relpath).resolve()
- self.abspath = os.path.abspath(relpath)
- self.relpath_backup = relpath.replace(
- "." + self.extension, "_bak." + self.extension
- )
- self.dirname = os.path.dirname(relpath)
- self.basename = self.filename.replace(".mscx", "")
-
- if self.extension == "mscz":
- self.zip_container = ZipContainer(self.path)
- self.loadpath = str(self.zip_container.mscx_file)
- else:
- self.loadpath = self.abspath
-
- @property
- def filename(self) -> str:
- """The filename of the MuseScore file, for example:
- ``simple.mscx``."""
- return self.path.name
-
- @property
- def extension(self) -> str:
- """The extension (``mscx`` or ``mscz``) of the score file, for
- example: ``mscx``."""
- return self.filename.split(".")[-1].lower()
-
- def backup(self) -> None:
- """Make a copy of the MuseScore file."""
- shutil.copy2(self.relpath, self.relpath_backup)
-
- def export(self, extension: str = "pdf") -> None:
- """Export the score to the specifed file type.
-
- :param extension: The extension (default: pdf)
- """
- score: str = self.relpath
- mscore(
- ["--export-to", score.replace("." + self.extension, "." + extension), score]
- )
-
-
class ZipContainer:
"""Container for the file paths of the different files in an unzipped MuseScore file
@@ -236,17 +147,41 @@ def save(self, dest: str | Path) -> None:
zip.close()
-###############################################################################
-# Class hierarchy level 2
-###############################################################################
+class MuseScoreFile:
+ """This class holds basic file properties of the MuseScore score file.
+
+ :param relpath: The relative (or absolute) path of a MuseScore
+ file.
+ """
+ errors: List[Exception]
+ """A list to store errors."""
+
+ path: Path
+ """The absolute path of the input file."""
-class MscoreXmlTree(MscoreFile):
- """XML tree manipulation
+ loadpath: str
+ """The path of the uncompressed MuseScore file in XML format file.
+ This path may be located in the temporary directory."""
- :param relpath: The relative (or absolute) path of a MuseScore file.
+ relpath: str
+ """The relative path of the score file, for example:
+ ``files/by_version/2/simple.mscx``.
"""
+ abspath: str
+ """The absolute path of the score file, for example:
+ ``/home/jf/test/files/by_version/2/simple.mscx``."""
+
+ relpath_backup: str
+
+ dirname: str
+ """The name of the containing directory of the MuseScore file, for
+ example: ``files/by_version/2``."""
+
+ basename: str
+ """The basename of the score file, for example: ``simple``."""
+
xml_tree: _ElementTree
version_major: int
@@ -255,8 +190,25 @@ class MscoreXmlTree(MscoreFile):
version: float
"""The MuseScore version, for example 2.03 or 3.01"""
+ zip_container: Optional[ZipContainer]
+
def __init__(self, relpath: str) -> None:
- super(MscoreXmlTree, self).__init__(relpath)
+ self.errors = []
+ self.relpath = relpath
+ self.path = Path(relpath).resolve()
+ self.abspath = os.path.abspath(relpath)
+ self.relpath_backup = relpath.replace(
+ "." + self.extension, "_bak." + self.extension
+ )
+ self.dirname = os.path.dirname(relpath)
+ self.basename = self.filename.replace(".mscx", "")
+
+ if self.extension == "mscz":
+ self.zip_container = ZipContainer(self.path)
+ self.loadpath = str(self.zip_container.mscx_file)
+ else:
+ self.loadpath = self.abspath
+
try:
self.xml_tree = lxml.etree.parse(self.loadpath)
except lxml.etree.XMLSyntaxError as e:
@@ -266,6 +218,32 @@ def __init__(self, relpath: str) -> None:
self.version = self.get_version()
self.version_major = int(self.version)
+ @property
+ def filename(self) -> str:
+ """The filename of the MuseScore file, for example:
+ ``simple.mscx``."""
+ return self.path.name
+
+ @property
+ def extension(self) -> str:
+ """The extension (``mscx`` or ``mscz``) of the score file, for
+ example: ``mscx``."""
+ return self.filename.split(".")[-1].lower()
+
+ def backup(self) -> None:
+ """Make a copy of the MuseScore file."""
+ shutil.copy2(self.relpath, self.relpath_backup)
+
+ def export(self, extension: str = "pdf") -> None:
+ """Export the score to the specifed file type.
+
+ :param extension: The extension (default: pdf)
+ """
+ score: str = self.relpath
+ mscore(
+ ["--export-to", score.replace("." + self.extension, "." + extension), score]
+ )
+
def get_version(self) -> float:
"""
Get the version number of the MuseScore file.
@@ -434,7 +412,7 @@ def save(self, new_name: str = "", mscore: bool = False):
###############################################################################
-class MscoreStyleInterface(MscoreXmlTree):
+class MscoreStyleInterface(MuseScoreFile):
"""
Interface specialized for the style manipulation.
diff --git a/tests/helper.py b/tests/helper.py
index e9f7bc1..e9d8d30 100644
--- a/tests/helper.py
+++ b/tests/helper.py
@@ -1,4 +1,4 @@
-"""MscoreFile for various tests"""
+"""MuseScoreFile for various tests"""
from __future__ import annotations
@@ -10,7 +10,7 @@
from lxml.etree import _ElementTree
-from mscxyz import MscoreXmlTree
+from mscxyz import MuseScoreFile
# if typing.TYPE_CHECKING:
@@ -66,7 +66,7 @@ def get_xml_tree(filename: str, version: int = 2) -> _ElementTree:
:param version: The version of the file (default is 2).
:return: The XML tree.
"""
- return MscoreXmlTree(get_file(filename, version)).xml_tree
+ return MuseScoreFile(get_file(filename, version)).xml_tree
def read_file(filename: str) -> str:
diff --git a/tests/test_api.py b/tests/test_api.py
index 4eb96fb..2c5b3d9 100644
--- a/tests/test_api.py
+++ b/tests/test_api.py
@@ -3,19 +3,18 @@
from mscxyz import (
- MscoreFile,
MscoreLyricsInterface,
MscoreMetaInterface,
MscoreStyleInterface,
- MscoreXmlTree,
+ MuseScoreFile,
exec_mscore_binary,
)
class TestApi:
def test_api(self):
- assert MscoreFile
- assert MscoreXmlTree
+ assert MuseScoreFile
+ assert MuseScoreFile
assert MscoreLyricsInterface
assert MscoreMetaInterface
assert MscoreStyleInterface
diff --git a/tests/test_meta.py b/tests/test_meta.py
index 3ba7438..a7880da 100644
--- a/tests/test_meta.py
+++ b/tests/test_meta.py
@@ -23,7 +23,7 @@
export_to_dict,
to_underscore,
)
-from mscxyz.score_file_classes import MscoreXmlTree
+from mscxyz.score_file_classes import MuseScoreFile
from tests import helper
from tests.helper import ini_file
@@ -104,7 +104,7 @@ def setup_method(self) -> None:
def _init_class(self, filename: str, version: int = 2):
tmp = helper.get_file(filename, version)
- tree = MscoreXmlTree(tmp)
+ tree = MuseScoreFile(tmp)
interface = InterfaceReadWrite(tree.xml_root)
return interface, tree, tmp
@@ -158,7 +158,7 @@ def _test_set_all_values(self, version: int):
assert getattr(interface, field) == field + "_test"
tree.save()
- tree = MscoreXmlTree(tmp)
+ tree = MuseScoreFile(tmp)
interface = InterfaceReadWrite(tree.xml_root)
assert interface.combined_composer == "vbox_composer_test"
@@ -228,7 +228,7 @@ def setup_method(self):
"readonly_relpath_backup",
)
self.tmp = helper.get_file("simple.mscx")
- self.xml_tree = MscoreXmlTree(self.tmp)
+ self.xml_tree = MuseScoreFile(self.tmp)
self.interface = InterfaceReadOnly(self.xml_tree)
def test_exception(self):
@@ -290,7 +290,7 @@ def setup_method(self):
]
self.tmp = helper.get_file("meta-all-values.mscx")
- self.xml_tree = MscoreXmlTree(self.tmp)
+ self.xml_tree = MuseScoreFile(self.tmp)
self.interface = Interface(self.xml_tree)
def test_static_method_get_all_fields(self):
@@ -312,7 +312,7 @@ def test_exception(self):
class TestClassMetaTag:
def _init_class(self, filename: str, version: int = 2):
tmp = helper.get_file(filename, version)
- tree = MscoreXmlTree(tmp)
+ tree = MuseScoreFile(tmp)
meta = MetaTag(tree.xml_root)
return meta, tree, tmp
@@ -342,7 +342,7 @@ def test_set2(self) -> None:
meta.workTitle = "WT"
meta.movement_title = "MT"
tree.save()
- tree = MscoreXmlTree(tmp)
+ tree = MuseScoreFile(tmp)
meta = MetaTag(tree.xml_root)
assert meta.work_title == "WT"
assert meta.movementTitle == "MT"
@@ -354,7 +354,7 @@ def test_set3(self) -> None:
meta.workTitle = "WT"
meta.movement_title = "MT"
tree.save()
- tree = MscoreXmlTree(tmp)
+ tree = MuseScoreFile(tmp)
meta = MetaTag(tree.xml_root)
assert meta.work_title == "WT"
assert meta.movementTitle == "MT"
@@ -382,9 +382,9 @@ def test_clean(self) -> None:
class TestClassVbox:
def _init_class(
self, filename: str, version: int = 2
- ) -> tuple[Vbox, MscoreXmlTree, str]:
+ ) -> tuple[Vbox, MuseScoreFile, str]:
tmp = helper.get_file(filename, version)
- tree = MscoreXmlTree(tmp)
+ tree = MuseScoreFile(tmp)
vbox = Vbox(tree.xml_root)
return vbox, tree, tmp
@@ -421,12 +421,12 @@ def test_get_exception(self) -> None:
def _assert_set(self, filename: str, version: int = 2) -> None:
tmp = helper.get_file(filename, version)
- tree = MscoreXmlTree(tmp)
+ tree = MuseScoreFile(tmp)
vbox = Vbox(tree.xml_root)
vbox.Title = "lol"
vbox.composer = "lol"
tree.save()
- tree = MscoreXmlTree(tmp)
+ tree = MuseScoreFile(tmp)
vbox = Vbox(tree.xml_root)
assert vbox.title == "lol"
assert vbox.Composer == "lol"
@@ -452,9 +452,9 @@ def test_set_exception(self) -> None:
class TestClassCombined:
- def _init_class(self, filename: str) -> tuple[Combined, MscoreXmlTree, str]:
+ def _init_class(self, filename: str) -> tuple[Combined, MuseScoreFile, str]:
tmp = helper.get_file(filename)
- tree = MscoreXmlTree(tmp)
+ tree = MuseScoreFile(tmp)
combined = Combined(tree.xml_root)
return combined, tree, tmp
diff --git a/tests/test_score_file_classes.py b/tests/test_score_file_classes.py
index adad8bd..40e16e9 100644
--- a/tests/test_score_file_classes.py
+++ b/tests/test_score_file_classes.py
@@ -15,9 +15,8 @@
import mscxyz
from mscxyz.score_file_classes import (
- MscoreFile,
MscoreStyleInterface,
- MscoreXmlTree,
+ MuseScoreFile,
ZipContainer,
list_scores,
list_zero_alphabet,
@@ -88,9 +87,9 @@ def test_function_list_zero_alphabet(self):
assert result[26] == "z"
-class TestMscoreFile:
+class TestClassMuseScoreFile:
def setup_method(self) -> None:
- self.file = MscoreFile(helper.get_file("simple.mscx"))
+ self.file = MuseScoreFile(helper.get_file("simple.mscx"))
def test_attribute_path(self) -> None:
assert self.file.relpath
@@ -114,11 +113,76 @@ def test_attribute_extension(self) -> None:
def test_attribute_basename(self) -> None:
assert self.file.basename == "simple"
+ def test_method_merge_style(self) -> None:
+ tree = MuseScoreFile(helper.get_file("simple.mscx"))
+ styles = """
+
+ center
+ bottom
+ 0
+ -1
+ spatium
+ Form Section
+ Alegreya Sans
+ 12
+ 1
+ 1
+ 1
+ 0.1
+ 0.2
+ 0
+
+
+ """
+ tree.clean()
+ tree.merge_style(styles)
+
+ xml_tree = tree.xml_tree
+ result = xml_tree.xpath("/museScore/Score/Style")
+ assert result[0][0][0].tag == "halign"
+ assert result[0][0][0].text == "center"
+
+ def test_method_clean(self) -> None:
+ tmp = helper.get_file("clean.mscx", version=3)
+ tree = MuseScoreFile(tmp)
+ tree.clean()
+ tree.save()
+ tree = MuseScoreFile(tmp)
+ xml_tree = tree.xml_tree
+ assert xml_tree.xpath("/museScore/Score/Style") == []
+ assert xml_tree.xpath("//LayoutBreak") == []
+ assert xml_tree.xpath("//StemDirection") == []
+ assert xml_tree.xpath("//font") == []
+ assert xml_tree.xpath("//b") == []
+ assert xml_tree.xpath("//i") == []
+ assert xml_tree.xpath("//pos") == []
+ assert xml_tree.xpath("//offset") == []
+
+ def test_method_save(self) -> None:
+ tmp = helper.get_file("simple.mscx")
+ tree = MuseScoreFile(tmp)
+ tree.save()
+ result = helper.read_file(tmp)
+ assert '' in result
+
+ def test_method_save_new_name(self):
+ tmp = helper.get_file("simple.mscx")
+ tree = MuseScoreFile(tmp)
+ tree.save(new_name=tmp)
+ result = helper.read_file(tmp)
+ assert '' in result
+
+ def test_mscz(self):
+ tmp = helper.get_file("simple.mscz")
+ tree = MuseScoreFile(tmp)
+ result = tree.xml_tree.xpath("/museScore/Score/Style")
+ assert result[0].tag == "Style"
+
@pytest.mark.skip("Not implemented yet")
-class TestMscoreFileMscz:
+class TestMuseScoreFileMscz:
def setup_method(self):
- self.file = MscoreFile(helper.get_file("simple.mscz"))
+ self.file = MuseScoreFile(helper.get_file("simple.mscz"))
def test_attribute_extension(self):
assert self.file.extension == "mscz"
@@ -127,9 +191,9 @@ def test_attribute_loadpath(self):
assert "simple.mscx" in self.file.loadpath
-class TestMscoreFileMscz4:
+class TestMuseScoreFileMscz4:
def setup_method(self):
- self.file = MscoreFile(
+ self.file = MuseScoreFile(
helper.get_file("test.mscz", version=4),
)
@@ -176,8 +240,8 @@ def test_method_save(self) -> None:
assert container.mscx_file.exists()
-class TestMscoreXmlTreeVersion2:
- tree = MscoreXmlTree(helper.get_file("simple.mscz", 2))
+class TestMuseScoreFileVersion2:
+ tree = MuseScoreFile(helper.get_file("simple.mscz", 2))
def test_property_version(self) -> None:
assert self.tree.version == 2.06
@@ -189,8 +253,8 @@ def test_method_get_version(self) -> None:
assert self.tree.get_version() == 2.06
-class TestMscoreXmlTreeVersion3:
- tree = MscoreXmlTree(helper.get_file("simple.mscz", 3))
+class TestMuseScoreFileVersion3:
+ tree = MuseScoreFile(helper.get_file("simple.mscz", 3))
def test_property_version(self) -> None:
assert self.tree.version == 3.01
@@ -202,8 +266,8 @@ def test_method_get_version(self) -> None:
assert self.tree.get_version() == 3.01
-class TestMscoreXmlTreeVersion4:
- tree = MscoreXmlTree(helper.get_file("simple.mscz", 4))
+class TestMuseScoreFileVersion4:
+ tree = MuseScoreFile(helper.get_file("simple.mscz", 4))
def test_property_version(self) -> None:
assert self.tree.version == 4.2
@@ -215,73 +279,6 @@ def test_method_get_version(self) -> None:
assert self.tree.get_version() == 4.2
-class TestClassMscoreXmlTree:
- def test_method_merge_style(self) -> None:
- tree = MscoreXmlTree(helper.get_file("simple.mscx"))
- styles = """
-
- center
- bottom
- 0
- -1
- spatium
- Form Section
- Alegreya Sans
- 12
- 1
- 1
- 1
- 0.1
- 0.2
- 0
-
-
- """
- tree.clean()
- tree.merge_style(styles)
-
- xml_tree = tree.xml_tree
- result = xml_tree.xpath("/museScore/Score/Style")
- assert result[0][0][0].tag == "halign"
- assert result[0][0][0].text == "center"
-
- def test_method_clean(self) -> None:
- tmp = helper.get_file("clean.mscx", version=3)
- tree = MscoreXmlTree(tmp)
- tree.clean()
- tree.save()
- tree = MscoreXmlTree(tmp)
- xml_tree = tree.xml_tree
- assert xml_tree.xpath("/museScore/Score/Style") == []
- assert xml_tree.xpath("//LayoutBreak") == []
- assert xml_tree.xpath("//StemDirection") == []
- assert xml_tree.xpath("//font") == []
- assert xml_tree.xpath("//b") == []
- assert xml_tree.xpath("//i") == []
- assert xml_tree.xpath("//pos") == []
- assert xml_tree.xpath("//offset") == []
-
- def test_method_save(self) -> None:
- tmp = helper.get_file("simple.mscx")
- tree = MscoreXmlTree(tmp)
- tree.save()
- result = helper.read_file(tmp)
- assert '' in result
-
- def test_method_save_new_name(self):
- tmp = helper.get_file("simple.mscx")
- tree = MscoreXmlTree(tmp)
- tree.save(new_name=tmp)
- result = helper.read_file(tmp)
- assert '' in result
-
- def test_mscz(self):
- tmp = helper.get_file("simple.mscz")
- tree = MscoreXmlTree(tmp)
- result = tree.xml_tree.xpath("/museScore/Score/Style")
- assert result[0].tag == "Style"
-
-
class TestClean:
def _test_clean(self, version: int = 2) -> None:
tmp: str = helper.get_file("formats.mscx", version)
@@ -454,7 +451,7 @@ def assertDiff(self, filename: str, version: int = 2):
saved = orig.replace(".mscx", "_saved.mscx")
tmp = helper.get_file(filename, version=version)
shutil.copy2(tmp, orig)
- tree = MscoreXmlTree(tmp)
+ tree = MuseScoreFile(tmp)
tree.save(new_name=saved)
assert filecmp.cmp(orig, saved)
os.remove(orig)