diff --git a/picard/script/__init__.py b/picard/script/__init__.py index beb02cad0..ff8db2bef 100644 --- a/picard/script/__init__.py +++ b/picard/script/__init__.py @@ -44,8 +44,6 @@ from enum import ( import json import uuid -import yaml - from picard.config import get_config from picard.const import ( DEFAULT_FILE_NAMING_FORMAT, @@ -128,7 +126,7 @@ class PicardScriptType(IntEnum): FILENAMING = 2 -class ScriptYamlImportError(Exception): +class ScriptImportError(Exception): def __init__(self, *args): super().__init__(*args) @@ -244,15 +242,17 @@ class PicardScript(): new_object._set_new_id() return new_object - def to_yaml(self): - """Converts the properties of the script object to a YAML formatted string. Note that only property - names listed in `OUTPUT_FIELDS` will be included in the output. + # TODO: Enable once PyYAML requirement resolved with Python 3.8 + # + # def to_yaml(self): + # """Converts the properties of the script object to a YAML formatted string. Note that only property + # names listed in `OUTPUT_FIELDS` will be included in the output. - Returns: - str: The properties of the script object formatted as a YAML string. - """ - items = {key: getattr(self, key) for key in dir(self) if key in self.OUTPUT_FIELDS} - return yaml.dump(items) + # Returns: + # str: The properties of the script object formatted as a YAML string. + # """ + # items = {key: getattr(self, key) for key in dir(self) if key in self.OUTPUT_FIELDS} + # return yaml.dump(items) def to_json(self, indent=None): """Converts the properties of the script object to a JSON formatted string. Note that only property @@ -267,25 +267,27 @@ class PicardScript(): items = {key: getattr(self, key) for key in dir(self) if key in self.OUTPUT_FIELDS} return json.dumps(items, indent=indent, sort_keys=True) - @classmethod - def create_from_yaml(cls, yaml_string): - """Creates an instance based on the contents of the YAML string provided. - Properties in the YAML string that are not found in the script object are ignored. + # TODO: Enable once PyYAML requirement resolved with Python 3.8 + # + # @classmethod + # def create_from_yaml(cls, yaml_string): + # """Creates an instance based on the contents of the YAML string provided. + # Properties in the YAML string that are not found in the script object are ignored. - Args: - yaml_string (str): YAML string containing the property settings. + # Args: + # yaml_string (str): YAML string containing the property settings. - Returns: - object: An instance of the class, populated from the property settings in the YAML string. - """ - new_object = cls() - yaml_dict = yaml.safe_load(yaml_string) - if not isinstance(yaml_dict, dict): - raise ScriptYamlImportError(N_("File content not a dictionary")) - if 'title' not in yaml_dict or 'script' not in yaml_dict: - raise ScriptYamlImportError(N_('Invalid script package')) - new_object._update_from_dict(yaml_dict) - return new_object + # Returns: + # object: An instance of the class, populated from the property settings in the YAML string. + # """ + # new_object = cls() + # yaml_dict = yaml.safe_load(yaml_string) + # if not isinstance(yaml_dict, dict): + # raise ScriptImportError(N_("File content not a dictionary")) + # if 'title' not in yaml_dict or 'script' not in yaml_dict: + # raise ScriptImportError(N_('Invalid script package')) + # new_object._update_from_dict(yaml_dict) + # return new_object @classmethod def create_from_json(cls, json_string): @@ -299,7 +301,10 @@ class PicardScript(): object: An instance of the class, populated from the property settings in the JSON string. """ new_object = cls() + new_object.title = '' new_object.update_from_json(json_string) + if not (new_object['title'] and new_object['script']): + raise ScriptImportError(N_('Invalid script package')) return new_object def update_from_json(self, json_string): @@ -309,7 +314,11 @@ class PicardScript(): Args: json_string (str): JSON string containing the property settings. """ - self._update_from_dict(json.loads(json_string)) + try: + decoded_string = json.loads(json_string) + except json.decoder.JSONDecodeError: + raise ScriptImportError(N_("Unable to decode JSON string")) + self._update_from_dict(decoded_string) class FileNamingScript(PicardScript): diff --git a/picard/ui/scripteditor.py b/picard/ui/scripteditor.py index b3c12ae48..0081885ed 100644 --- a/picard/ui/scripteditor.py +++ b/picard/ui/scripteditor.py @@ -41,8 +41,8 @@ from picard.file import File from picard.script import ( FileNamingScript, ScriptError, + ScriptImportError, ScriptParser, - ScriptYamlImportError, get_file_naming_script_presets, ) from picard.util.settingsoverride import SettingsOverride @@ -58,11 +58,7 @@ from picard.ui.ui_scripteditor_details import Ui_ScriptDetails from picard.ui.widgets.scriptdocumentation import ScriptingDocumentationWidget -class ScriptImportError(OptionsCheckError): - pass - - -class ScriptExportError(OptionsCheckError): +class ScriptFileError(OptionsCheckError): pass @@ -271,7 +267,7 @@ class ScriptEditorPage(PicardDialog): self.FILE_TYPE_ALL = _("All Files") + " (*)" self.FILE_TYPE_SCRIPT = _("Picard Script Files") + " (*.pts *.txt)" - self.FILE_TYPE_PACKAGE = _("Picard Naming Script Package") + " (*.pnsp *.yaml)" + self.FILE_TYPE_PACKAGE = _("Picard Naming Script Package") + " (*.pnsp *.json)" self.SCRIPT_TITLE_SYSTEM = _("System: %s") self.SCRIPT_TITLE_USER = _("User: %s") @@ -619,7 +615,7 @@ class ScriptEditorPage(PicardDialog): """ log.error(fmt, filename, msg) error_message = _(fmt) % (filename, _(msg)) - self.display_error(ScriptImportError(_(title), error_message)) + self.display_error(ScriptFileError(_(title), error_message)) def output_file_error(self, fmt, filename, msg): """Log file error and display error message dialog. @@ -656,8 +652,8 @@ class ScriptEditorPage(PicardDialog): return if file_type == self.FILE_TYPE_PACKAGE: try: - script_item = FileNamingScript().create_from_yaml(file_content) - except ScriptYamlImportError as error: + script_item = FileNamingScript().create_from_json(file_content) + except ScriptImportError as error: self.output_file_error(FILE_ERROR_DECODE, filename, error) return else: @@ -690,7 +686,7 @@ class ScriptEditorPage(PicardDialog): filename = name log.debug('Exporting naming script file: %s' % filename) if file_type == self.FILE_TYPE_PACKAGE: - script_text = script_item.to_yaml() + script_text = script_item.to_json(indent=4) try: with open(filename, 'w', encoding='utf8') as o_file: o_file.write(script_text) diff --git a/requirements-macos-10.12.txt b/requirements-macos-10.12.txt index bc6772e23..8536f4c67 100644 --- a/requirements-macos-10.12.txt +++ b/requirements-macos-10.12.txt @@ -6,4 +6,3 @@ mutagen==1.45.1 pyobjc-core==6.2.2 pyobjc-framework-Cocoa==6.2.2 PyQt5==5.13.1 -PyYAML==5.4.1 diff --git a/requirements-macos-10.14.txt b/requirements-macos-10.14.txt index faf9a216e..e5b07118a 100644 --- a/requirements-macos-10.14.txt +++ b/requirements-macos-10.14.txt @@ -6,4 +6,3 @@ mutagen==1.45.1 pyobjc-core==6.2.2 pyobjc-framework-Cocoa==6.2.2 PyQt5==5.15.4 -PyYAML==5.4.1 diff --git a/requirements-win.txt b/requirements-win.txt index ab978f4d6..0ff957813 100644 --- a/requirements-win.txt +++ b/requirements-win.txt @@ -5,4 +5,3 @@ markdown==3.3.4 mutagen==1.45.1 PyQt5==5.15.4 pywin32==300 -PyYAML==5.4.1 diff --git a/requirements.txt b/requirements.txt index dc24bd548..70e4bf549 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,4 +7,3 @@ pyobjc-core<7.0; sys_platform == 'darwin' pyobjc-framework-Cocoa<7.0; sys_platform == 'darwin' PyQt5>=5.10 pywin32; sys_platform == 'win32' -PyYAML==5.4.1 diff --git a/test/test_scriptclasses.py b/test/test_scriptclasses.py index 264be582f..882b40224 100644 --- a/test/test_scriptclasses.py +++ b/test/test_scriptclasses.py @@ -27,6 +27,7 @@ from test.picardtestcase import PicardTestCase from picard.script import ( FileNamingScript, PicardScript, + ScriptImportError, ) @@ -125,10 +126,10 @@ class ScriptClassesTest(PicardTestCase): def test_script_object_9(self): # Test that an exception is raised when creating or updating using an invalid JSON string - with self.assertRaises(JSONDecodeError): + with self.assertRaises(ScriptImportError): test_script = PicardScript().create_from_json('Not a JSON string') test_script = PicardScript(title='Script 1', script='Script text', id='12345', last_updated='2021-04-26', script_language_version='1.0') - with self.assertRaises(JSONDecodeError): + with self.assertRaises(ScriptImportError): test_script.update_from_json('Not a JSON string') def test_naming_script_object_1(self):