mirror of
https://github.com/fergalmoran/picard.git
synced 2025-12-22 09:18:18 +00:00
Move script functions extension point to extension_points
This commit is contained in:
@@ -30,10 +30,14 @@ from picard.plugin import ExtensionPoint
|
|||||||
|
|
||||||
|
|
||||||
ext_point_formats = ExtensionPoint(label='formats')
|
ext_point_formats = ExtensionPoint(label='formats')
|
||||||
formats_extensions = {}
|
_formats_extensions = {}
|
||||||
|
|
||||||
|
|
||||||
def register_format(file_format):
|
def register_format(file_format):
|
||||||
ext_point_formats.register(file_format.__module__, file_format)
|
ext_point_formats.register(file_format.__module__, file_format)
|
||||||
for ext in file_format.EXTENSIONS:
|
for ext in file_format.EXTENSIONS:
|
||||||
formats_extensions[ext[1:]] = file_format
|
_formats_extensions[ext[1:]] = file_format
|
||||||
|
|
||||||
|
|
||||||
|
def ext_to_format(ext):
|
||||||
|
return _formats_extensions.get(ext, None)
|
||||||
|
|||||||
162
picard/extension_points/script_functions.py
Normal file
162
picard/extension_points/script_functions.py
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Picard, the next-generation MusicBrainz tagger
|
||||||
|
#
|
||||||
|
# Copyright (C) 2006-2009, 2012 Lukáš Lalinský
|
||||||
|
# Copyright (C) 2007 Javier Kohen
|
||||||
|
# Copyright (C) 2008-2011, 2014-2015, 2018-2021, 2023 Philipp Wolfer
|
||||||
|
# Copyright (C) 2009 Carlin Mangar
|
||||||
|
# Copyright (C) 2009 Nikolai Prokoschenko
|
||||||
|
# Copyright (C) 2011-2012 Michael Wiencek
|
||||||
|
# Copyright (C) 2012 Chad Wilson
|
||||||
|
# Copyright (C) 2012 stephen
|
||||||
|
# Copyright (C) 2012, 2014, 2017, 2021 Wieland Hoffmann
|
||||||
|
# Copyright (C) 2013-2014, 2017-2024 Laurent Monin
|
||||||
|
# Copyright (C) 2014, 2017, 2021 Sophist-UK
|
||||||
|
# Copyright (C) 2016-2017 Sambhav Kothari
|
||||||
|
# Copyright (C) 2016-2017 Ville Skyttä
|
||||||
|
# Copyright (C) 2017-2018 Antonio Larrosa
|
||||||
|
# Copyright (C) 2018 Calvin Walton
|
||||||
|
# Copyright (C) 2018 virusMac
|
||||||
|
# Copyright (C) 2020-2023 Bob Swift
|
||||||
|
# Copyright (C) 2021 Adam James
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License
|
||||||
|
# as published by the Free Software Foundation; either version 2
|
||||||
|
# of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
from collections import namedtuple
|
||||||
|
from inspect import getfullargspec
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
from markdown import markdown
|
||||||
|
except ImportError:
|
||||||
|
markdown = None
|
||||||
|
|
||||||
|
from picard.i18n import gettext as _
|
||||||
|
from picard.plugin import ExtensionPoint
|
||||||
|
|
||||||
|
|
||||||
|
ext_point_script_functions = ExtensionPoint(label='script_functions')
|
||||||
|
|
||||||
|
|
||||||
|
Bound = namedtuple('Bound', ['lower', 'upper'])
|
||||||
|
|
||||||
|
|
||||||
|
class FunctionRegistryItem:
|
||||||
|
def __init__(self, function, eval_args, argcount, documentation=None,
|
||||||
|
name=None, module=None):
|
||||||
|
self.function = function
|
||||||
|
self.eval_args = eval_args
|
||||||
|
self.argcount = argcount
|
||||||
|
self.documentation = documentation
|
||||||
|
self.name = name
|
||||||
|
self.module = module
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '{classname}({me.function}, {me.eval_args}, {me.argcount}, {doc})'.format(
|
||||||
|
classname=self.__class__.__name__,
|
||||||
|
me=self,
|
||||||
|
doc='"""{0}"""'.format(self.documentation) if self.documentation else None
|
||||||
|
)
|
||||||
|
|
||||||
|
def _postprocess(self, data, postprocessor):
|
||||||
|
if postprocessor is not None:
|
||||||
|
data = postprocessor(data, function=self)
|
||||||
|
return data
|
||||||
|
|
||||||
|
def markdowndoc(self, postprocessor=None):
|
||||||
|
if self.documentation is not None:
|
||||||
|
ret = _(self.documentation)
|
||||||
|
else:
|
||||||
|
ret = ''
|
||||||
|
return self._postprocess(ret, postprocessor)
|
||||||
|
|
||||||
|
def htmldoc(self, postprocessor=None):
|
||||||
|
if markdown is not None:
|
||||||
|
ret = markdown(self.markdowndoc())
|
||||||
|
else:
|
||||||
|
ret = ''
|
||||||
|
return self._postprocess(ret, postprocessor)
|
||||||
|
|
||||||
|
|
||||||
|
def register_script_function(function, name=None, eval_args=True,
|
||||||
|
check_argcount=True, documentation=None):
|
||||||
|
"""Registers a script function. If ``name`` is ``None``,
|
||||||
|
``function.__name__`` will be used.
|
||||||
|
If ``eval_args`` is ``False``, the arguments will not be evaluated before being
|
||||||
|
passed to ``function``.
|
||||||
|
If ``check_argcount`` is ``False`` the number of arguments passed to the
|
||||||
|
function will not be verified."""
|
||||||
|
|
||||||
|
args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, annotations = getfullargspec(function)
|
||||||
|
|
||||||
|
required_kwonlyargs = len(kwonlyargs)
|
||||||
|
if kwonlydefaults is not None:
|
||||||
|
required_kwonlyargs -= len(kwonlydefaults.keys())
|
||||||
|
if required_kwonlyargs:
|
||||||
|
raise TypeError("Functions with required keyword-only parameters are not supported")
|
||||||
|
|
||||||
|
args = len(args) - 1 # -1 for the parser
|
||||||
|
varargs = varargs is not None
|
||||||
|
defaults = len(defaults) if defaults else 0
|
||||||
|
|
||||||
|
argcount = Bound(args - defaults, args if not varargs else None)
|
||||||
|
|
||||||
|
if name is None:
|
||||||
|
name = function.__name__
|
||||||
|
ext_point_script_functions.register(
|
||||||
|
function.__module__,
|
||||||
|
(
|
||||||
|
name,
|
||||||
|
FunctionRegistryItem(
|
||||||
|
function,
|
||||||
|
eval_args,
|
||||||
|
argcount if argcount and check_argcount else False,
|
||||||
|
documentation=documentation,
|
||||||
|
name=name,
|
||||||
|
module=function.__module__,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def script_function(name=None, eval_args=True, check_argcount=True, prefix='func_', documentation=None):
|
||||||
|
"""Decorator helper to register script functions
|
||||||
|
|
||||||
|
It calls ``register_script_function()`` and share same arguments
|
||||||
|
Extra optional arguments:
|
||||||
|
``prefix``: define the prefix to be removed from defined function to name script function
|
||||||
|
By default, ``func_foo`` will create ``foo`` script function
|
||||||
|
|
||||||
|
Example:
|
||||||
|
@script_function(eval_args=False)
|
||||||
|
def func_myscriptfunc():
|
||||||
|
...
|
||||||
|
"""
|
||||||
|
def script_function_decorator(func):
|
||||||
|
fname = func.__name__
|
||||||
|
if name is None and prefix and fname.startswith(prefix):
|
||||||
|
sname = fname[len(prefix):]
|
||||||
|
else:
|
||||||
|
sname = name
|
||||||
|
register_script_function(
|
||||||
|
func,
|
||||||
|
name=sname,
|
||||||
|
eval_args=eval_args,
|
||||||
|
check_argcount=check_argcount,
|
||||||
|
documentation=documentation
|
||||||
|
)
|
||||||
|
return func
|
||||||
|
return script_function_decorator
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
from picard import log
|
from picard import log
|
||||||
from picard.extension_points.formats import (
|
from picard.extension_points.formats import (
|
||||||
ext_point_formats,
|
ext_point_formats,
|
||||||
formats_extensions,
|
ext_to_format,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -43,10 +43,6 @@ def supported_extensions():
|
|||||||
return [ext for exts, name in supported_formats() for ext in exts]
|
return [ext for exts, name in supported_formats() for ext in exts]
|
||||||
|
|
||||||
|
|
||||||
def ext_to_format(ext):
|
|
||||||
return formats_extensions.get(ext, None)
|
|
||||||
|
|
||||||
|
|
||||||
def guess_format(filename, options=None):
|
def guess_format(filename, options=None):
|
||||||
"""Select the best matching file type amongst supported formats."""
|
"""Select the best matching file type amongst supported formats."""
|
||||||
if options is None:
|
if options is None:
|
||||||
@@ -79,7 +75,10 @@ def open_(filename):
|
|||||||
i = filename.rfind(".")
|
i = filename.rfind(".")
|
||||||
if i >= 0:
|
if i >= 0:
|
||||||
ext = filename[i+1:].lower()
|
ext = filename[i+1:].lower()
|
||||||
audio_file = formats_extensions[ext](filename)
|
file_format = ext_to_format(ext)
|
||||||
|
if file_format is None:
|
||||||
|
return None
|
||||||
|
audio_file = file_format(filename)
|
||||||
else:
|
else:
|
||||||
# If there is no extension, try to guess the format based on file headers
|
# If there is no extension, try to guess the format based on file headers
|
||||||
audio_file = guess_format(filename)
|
audio_file = guess_format(filename)
|
||||||
|
|||||||
@@ -41,14 +41,13 @@ from picard.const.defaults import (
|
|||||||
DEFAULT_FILE_NAMING_FORMAT,
|
DEFAULT_FILE_NAMING_FORMAT,
|
||||||
DEFAULT_NAMING_PRESET_ID,
|
DEFAULT_NAMING_PRESET_ID,
|
||||||
)
|
)
|
||||||
|
from picard.extension_points import script_functions
|
||||||
from picard.i18n import (
|
from picard.i18n import (
|
||||||
N_,
|
N_,
|
||||||
gettext as _,
|
gettext as _,
|
||||||
)
|
)
|
||||||
from picard.script.functions import ( # noqa: F401 # pylint: disable=unused-import
|
# Those imports are required to actually parse the code and interpret decorators
|
||||||
register_script_function,
|
import picard.script.functions # noqa: F401 # pylint: disable=unused-import
|
||||||
script_function,
|
|
||||||
)
|
|
||||||
from picard.script.parser import ( # noqa: F401 # pylint: disable=unused-import
|
from picard.script.parser import ( # noqa: F401 # pylint: disable=unused-import
|
||||||
MultiValue,
|
MultiValue,
|
||||||
ScriptEndOfFile,
|
ScriptEndOfFile,
|
||||||
@@ -73,7 +72,7 @@ class ScriptFunctionDocError(Exception):
|
|||||||
|
|
||||||
def script_function_documentation(name, fmt, functions=None, postprocessor=None):
|
def script_function_documentation(name, fmt, functions=None, postprocessor=None):
|
||||||
if functions is None:
|
if functions is None:
|
||||||
functions = dict(ScriptParser._function_registry)
|
functions = dict(script_functions.ext_point_script_functions)
|
||||||
if name not in functions:
|
if name not in functions:
|
||||||
raise ScriptFunctionDocError("no such function: %s (known functions: %r)" % (name, [name for name in functions]))
|
raise ScriptFunctionDocError("no such function: %s (known functions: %r)" % (name, [name for name in functions]))
|
||||||
|
|
||||||
@@ -87,13 +86,13 @@ def script_function_documentation(name, fmt, functions=None, postprocessor=None)
|
|||||||
|
|
||||||
def script_function_names(functions=None):
|
def script_function_names(functions=None):
|
||||||
if functions is None:
|
if functions is None:
|
||||||
functions = dict(ScriptParser._function_registry)
|
functions = dict(script_functions.ext_point_script_functions)
|
||||||
yield from sorted(functions)
|
yield from sorted(functions)
|
||||||
|
|
||||||
|
|
||||||
def script_function_documentation_all(fmt='markdown', pre='',
|
def script_function_documentation_all(fmt='markdown', pre='',
|
||||||
post='', postprocessor=None):
|
post='', postprocessor=None):
|
||||||
functions = dict(ScriptParser._function_registry)
|
functions = dict(script_functions.ext_point_script_functions)
|
||||||
doc_elements = []
|
doc_elements = []
|
||||||
for name in script_function_names(functions):
|
for name in script_function_names(functions):
|
||||||
doc_element = script_function_documentation(name, fmt,
|
doc_element = script_function_documentation(name, fmt,
|
||||||
|
|||||||
@@ -39,21 +39,19 @@
|
|||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
import datetime
|
import datetime
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
from inspect import getfullargspec
|
|
||||||
import operator
|
import operator
|
||||||
import re
|
import re
|
||||||
import unicodedata
|
import unicodedata
|
||||||
|
|
||||||
from picard.const.countries import RELEASE_COUNTRIES
|
from picard.const.countries import RELEASE_COUNTRIES
|
||||||
|
from picard.extension_points.script_functions import script_function
|
||||||
from picard.i18n import (
|
from picard.i18n import (
|
||||||
N_,
|
N_,
|
||||||
gettext as _,
|
|
||||||
gettext_countries,
|
gettext_countries,
|
||||||
)
|
)
|
||||||
from picard.metadata import MULTI_VALUED_JOINER
|
from picard.metadata import MULTI_VALUED_JOINER
|
||||||
from picard.script.parser import (
|
from picard.script.parser import (
|
||||||
MultiValue,
|
MultiValue,
|
||||||
ScriptParser,
|
|
||||||
ScriptRuntimeError,
|
ScriptRuntimeError,
|
||||||
normalize_tagname,
|
normalize_tagname,
|
||||||
)
|
)
|
||||||
@@ -63,123 +61,6 @@ from picard.util import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
try:
|
|
||||||
from markdown import markdown
|
|
||||||
except ImportError:
|
|
||||||
markdown = None
|
|
||||||
|
|
||||||
|
|
||||||
Bound = namedtuple('Bound', ['lower', 'upper'])
|
|
||||||
|
|
||||||
|
|
||||||
class FunctionRegistryItem:
|
|
||||||
def __init__(self, function, eval_args, argcount, documentation=None,
|
|
||||||
name=None, module=None):
|
|
||||||
self.function = function
|
|
||||||
self.eval_args = eval_args
|
|
||||||
self.argcount = argcount
|
|
||||||
self.documentation = documentation
|
|
||||||
self.name = name
|
|
||||||
self.module = module
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return '{classname}({me.function}, {me.eval_args}, {me.argcount}, {doc})'.format(
|
|
||||||
classname=self.__class__.__name__,
|
|
||||||
me=self,
|
|
||||||
doc='"""{0}"""'.format(self.documentation) if self.documentation else None
|
|
||||||
)
|
|
||||||
|
|
||||||
def _postprocess(self, data, postprocessor):
|
|
||||||
if postprocessor is not None:
|
|
||||||
data = postprocessor(data, function=self)
|
|
||||||
return data
|
|
||||||
|
|
||||||
def markdowndoc(self, postprocessor=None):
|
|
||||||
if self.documentation is not None:
|
|
||||||
ret = _(self.documentation)
|
|
||||||
else:
|
|
||||||
ret = ''
|
|
||||||
return self._postprocess(ret, postprocessor)
|
|
||||||
|
|
||||||
def htmldoc(self, postprocessor=None):
|
|
||||||
if markdown is not None:
|
|
||||||
ret = markdown(self.markdowndoc())
|
|
||||||
else:
|
|
||||||
ret = ''
|
|
||||||
return self._postprocess(ret, postprocessor)
|
|
||||||
|
|
||||||
|
|
||||||
def register_script_function(function, name=None, eval_args=True,
|
|
||||||
check_argcount=True, documentation=None):
|
|
||||||
"""Registers a script function. If ``name`` is ``None``,
|
|
||||||
``function.__name__`` will be used.
|
|
||||||
If ``eval_args`` is ``False``, the arguments will not be evaluated before being
|
|
||||||
passed to ``function``.
|
|
||||||
If ``check_argcount`` is ``False`` the number of arguments passed to the
|
|
||||||
function will not be verified."""
|
|
||||||
|
|
||||||
args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, annotations = getfullargspec(function)
|
|
||||||
|
|
||||||
required_kwonlyargs = len(kwonlyargs)
|
|
||||||
if kwonlydefaults is not None:
|
|
||||||
required_kwonlyargs -= len(kwonlydefaults.keys())
|
|
||||||
if required_kwonlyargs:
|
|
||||||
raise TypeError("Functions with required keyword-only parameters are not supported")
|
|
||||||
|
|
||||||
args = len(args) - 1 # -1 for the parser
|
|
||||||
varargs = varargs is not None
|
|
||||||
defaults = len(defaults) if defaults else 0
|
|
||||||
|
|
||||||
argcount = Bound(args - defaults, args if not varargs else None)
|
|
||||||
|
|
||||||
if name is None:
|
|
||||||
name = function.__name__
|
|
||||||
ScriptParser._function_registry.register(
|
|
||||||
function.__module__,
|
|
||||||
(
|
|
||||||
name,
|
|
||||||
FunctionRegistryItem(
|
|
||||||
function,
|
|
||||||
eval_args,
|
|
||||||
argcount if argcount and check_argcount else False,
|
|
||||||
documentation=documentation,
|
|
||||||
name=name,
|
|
||||||
module=function.__module__,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def script_function(name=None, eval_args=True, check_argcount=True, prefix='func_', documentation=None):
|
|
||||||
"""Decorator helper to register script functions
|
|
||||||
|
|
||||||
It calls ``register_script_function()`` and share same arguments
|
|
||||||
Extra optional arguments:
|
|
||||||
``prefix``: define the prefix to be removed from defined function to name script function
|
|
||||||
By default, ``func_foo`` will create ``foo`` script function
|
|
||||||
|
|
||||||
Example:
|
|
||||||
@script_function(eval_args=False)
|
|
||||||
def func_myscriptfunc():
|
|
||||||
...
|
|
||||||
"""
|
|
||||||
def script_function_decorator(func):
|
|
||||||
fname = func.__name__
|
|
||||||
if name is None and prefix and fname.startswith(prefix):
|
|
||||||
sname = fname[len(prefix):]
|
|
||||||
else:
|
|
||||||
sname = name
|
|
||||||
register_script_function(
|
|
||||||
func,
|
|
||||||
name=sname,
|
|
||||||
eval_args=eval_args,
|
|
||||||
check_argcount=check_argcount,
|
|
||||||
documentation=documentation
|
|
||||||
)
|
|
||||||
return func
|
|
||||||
return script_function_decorator
|
|
||||||
|
|
||||||
|
|
||||||
def _compute_int(operation, *args):
|
def _compute_int(operation, *args):
|
||||||
return str(reduce(operation, map(int, args)))
|
return str(reduce(operation, map(int, args)))
|
||||||
|
|
||||||
|
|||||||
@@ -38,11 +38,11 @@
|
|||||||
from collections.abc import MutableSequence
|
from collections.abc import MutableSequence
|
||||||
from queue import LifoQueue
|
from queue import LifoQueue
|
||||||
|
|
||||||
|
from picard.extension_points import script_functions
|
||||||
from picard.metadata import (
|
from picard.metadata import (
|
||||||
MULTI_VALUED_JOINER,
|
MULTI_VALUED_JOINER,
|
||||||
Metadata,
|
Metadata,
|
||||||
)
|
)
|
||||||
from picard.plugin import ExtensionPoint
|
|
||||||
|
|
||||||
|
|
||||||
class ScriptError(Exception):
|
class ScriptError(Exception):
|
||||||
@@ -216,7 +216,6 @@ Grammar:
|
|||||||
argument ::= (variable | function | argtext)*
|
argument ::= (variable | function | argtext)*
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_function_registry = ExtensionPoint(label='function_registry')
|
|
||||||
_cache = {}
|
_cache = {}
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -362,9 +361,7 @@ Grammar:
|
|||||||
return (tokens, ch)
|
return (tokens, ch)
|
||||||
|
|
||||||
def load_functions(self):
|
def load_functions(self):
|
||||||
self.functions = {}
|
self.functions = dict(script_functions.ext_point_script_functions)
|
||||||
for name, item in ScriptParser._function_registry:
|
|
||||||
self.functions[name] = item
|
|
||||||
|
|
||||||
def parse(self, script, functions=False):
|
def parse(self, script, functions=False):
|
||||||
"""Parse the script."""
|
"""Parse the script."""
|
||||||
|
|||||||
@@ -43,6 +43,11 @@ from test.picardtestcase import PicardTestCase
|
|||||||
|
|
||||||
from picard.cluster import Cluster
|
from picard.cluster import Cluster
|
||||||
from picard.const.defaults import DEFAULT_FILE_NAMING_FORMAT
|
from picard.const.defaults import DEFAULT_FILE_NAMING_FORMAT
|
||||||
|
from picard.extension_points.script_functions import (
|
||||||
|
FunctionRegistryItem,
|
||||||
|
register_script_function,
|
||||||
|
script_function,
|
||||||
|
)
|
||||||
from picard.metadata import (
|
from picard.metadata import (
|
||||||
MULTI_VALUED_JOINER,
|
MULTI_VALUED_JOINER,
|
||||||
Metadata,
|
Metadata,
|
||||||
@@ -60,12 +65,9 @@ from picard.script import (
|
|||||||
ScriptSyntaxError,
|
ScriptSyntaxError,
|
||||||
ScriptUnicodeError,
|
ScriptUnicodeError,
|
||||||
ScriptUnknownFunction,
|
ScriptUnknownFunction,
|
||||||
register_script_function,
|
|
||||||
script_function,
|
|
||||||
script_function_documentation,
|
script_function_documentation,
|
||||||
script_function_documentation_all,
|
script_function_documentation_all,
|
||||||
)
|
)
|
||||||
from picard.script.functions import FunctionRegistryItem
|
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -170,77 +172,77 @@ class ScriptParserTest(PicardTestCase):
|
|||||||
with self.assertRaisesRegex(ScriptUnicodeError, areg):
|
with self.assertRaisesRegex(ScriptUnicodeError, areg):
|
||||||
self.parser.eval("\\ufffg")
|
self.parser.eval("\\ufffg")
|
||||||
|
|
||||||
|
@patch('picard.extension_points.script_functions.ext_point_script_functions', ExtensionPoint(label='test_script'))
|
||||||
def test_script_function_decorator_default(self):
|
def test_script_function_decorator_default(self):
|
||||||
# test default decorator and default prefix
|
# test default decorator and default prefix
|
||||||
with patch.object(ScriptParser, '_function_registry', ExtensionPoint()):
|
@script_function()
|
||||||
@script_function()
|
def func_somefunc(parser):
|
||||||
def func_somefunc(parser):
|
return "x"
|
||||||
return "x"
|
self.assertScriptResultEquals("$somefunc()", "x")
|
||||||
self.assertScriptResultEquals("$somefunc()", "x")
|
|
||||||
|
|
||||||
|
@patch('picard.extension_points.script_functions.ext_point_script_functions', ExtensionPoint(label='test_script'))
|
||||||
def test_script_function_decorator_no_prefix(self):
|
def test_script_function_decorator_no_prefix(self):
|
||||||
# function without prefix
|
# function without prefix
|
||||||
with patch.object(ScriptParser, '_function_registry', ExtensionPoint()):
|
@script_function()
|
||||||
@script_function()
|
def somefunc(parser):
|
||||||
def somefunc(parser):
|
return "x"
|
||||||
return "x"
|
self.assertScriptResultEquals("$somefunc()", "x")
|
||||||
self.assertScriptResultEquals("$somefunc()", "x")
|
|
||||||
|
|
||||||
|
@patch('picard.extension_points.script_functions.ext_point_script_functions', ExtensionPoint(label='test_script'))
|
||||||
def test_script_function_decorator_arg(self):
|
def test_script_function_decorator_arg(self):
|
||||||
# function with argument
|
# function with argument
|
||||||
with patch.object(ScriptParser, '_function_registry', ExtensionPoint()):
|
@script_function()
|
||||||
@script_function()
|
def somefunc(parser, arg):
|
||||||
def somefunc(parser, arg):
|
return arg
|
||||||
return arg
|
|
||||||
|
|
||||||
@script_function()
|
@script_function()
|
||||||
def title(parser, arg):
|
def title(parser, arg):
|
||||||
return arg.upper()
|
return arg.upper()
|
||||||
|
|
||||||
self.assertScriptResultEquals("$somefunc($title(x))", "X")
|
self.assertScriptResultEquals("$somefunc($title(x))", "X")
|
||||||
areg = r"^\d+:\d+:\$somefunc: Wrong number of arguments for \$somefunc: Expected exactly 1"
|
areg = r"^\d+:\d+:\$somefunc: Wrong number of arguments for \$somefunc: Expected exactly 1"
|
||||||
with self.assertRaisesRegex(ScriptError, areg):
|
with self.assertRaisesRegex(ScriptError, areg):
|
||||||
self.parser.eval("$somefunc()")
|
self.parser.eval("$somefunc()")
|
||||||
|
|
||||||
|
@patch('picard.extension_points.script_functions.ext_point_script_functions', ExtensionPoint(label='test_script'))
|
||||||
def test_script_function_decorator_argcount(self):
|
def test_script_function_decorator_argcount(self):
|
||||||
# ignore argument count
|
# ignore argument count
|
||||||
with patch.object(ScriptParser, '_function_registry', ExtensionPoint()):
|
@script_function(check_argcount=False)
|
||||||
@script_function(check_argcount=False)
|
def somefunc(parser, *arg):
|
||||||
def somefunc(parser, *arg):
|
return str(len(arg))
|
||||||
return str(len(arg))
|
self.assertScriptResultEquals("$somefunc(a,b,c)", "3")
|
||||||
self.assertScriptResultEquals("$somefunc(a,b,c)", "3")
|
|
||||||
|
|
||||||
|
@patch('picard.extension_points.script_functions.ext_point_script_functions', ExtensionPoint(label='test_script'))
|
||||||
def test_script_function_decorator_altname(self):
|
def test_script_function_decorator_altname(self):
|
||||||
# alternative name
|
# alternative name
|
||||||
with patch.object(ScriptParser, '_function_registry', ExtensionPoint()):
|
@script_function(name="otherfunc")
|
||||||
@script_function(name="otherfunc")
|
def somefunc4(parser):
|
||||||
def somefunc4(parser):
|
return "x"
|
||||||
return "x"
|
self.assertScriptResultEquals("$otherfunc()", "x")
|
||||||
self.assertScriptResultEquals("$otherfunc()", "x")
|
areg = r"^\d+:\d+:\$somefunc: Unknown function '\$somefunc'"
|
||||||
areg = r"^\d+:\d+:\$somefunc: Unknown function '\$somefunc'"
|
with self.assertRaisesRegex(ScriptError, areg):
|
||||||
with self.assertRaisesRegex(ScriptError, areg):
|
self.parser.eval("$somefunc()")
|
||||||
self.parser.eval("$somefunc()")
|
|
||||||
|
|
||||||
|
@patch('picard.extension_points.script_functions.ext_point_script_functions', ExtensionPoint(label='test_script'))
|
||||||
def test_script_function_decorator_altprefix(self):
|
def test_script_function_decorator_altprefix(self):
|
||||||
# alternative prefix
|
# alternative prefix
|
||||||
with patch.object(ScriptParser, '_function_registry', ExtensionPoint()):
|
@script_function(prefix='theprefix_')
|
||||||
@script_function(prefix='theprefix_')
|
def theprefix_somefunc(parser):
|
||||||
def theprefix_somefunc(parser):
|
return "x"
|
||||||
return "x"
|
self.assertScriptResultEquals("$somefunc()", "x")
|
||||||
self.assertScriptResultEquals("$somefunc()", "x")
|
|
||||||
|
|
||||||
|
@patch('picard.extension_points.script_functions.ext_point_script_functions', ExtensionPoint(label='test_script'))
|
||||||
def test_script_function_decorator_eval_args(self):
|
def test_script_function_decorator_eval_args(self):
|
||||||
# disable argument evaluation
|
# disable argument evaluation
|
||||||
with patch.object(ScriptParser, '_function_registry', ExtensionPoint()):
|
@script_function(eval_args=False)
|
||||||
@script_function(eval_args=False)
|
def somefunc(parser, arg):
|
||||||
def somefunc(parser, arg):
|
return arg.eval(parser)
|
||||||
return arg.eval(parser)
|
|
||||||
|
|
||||||
@script_function()
|
@script_function()
|
||||||
def title(parser, arg):
|
def title(parser, arg):
|
||||||
return arg.upper()
|
return arg.upper()
|
||||||
|
|
||||||
self.assertScriptResultEquals("$somefunc($title(x))", "X")
|
self.assertScriptResultEquals("$somefunc($title(x))", "X")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def assertStartswith(text, expect):
|
def assertStartswith(text, expect):
|
||||||
@@ -252,98 +254,95 @@ class ScriptParserTest(PicardTestCase):
|
|||||||
if not text.endswith(expect):
|
if not text.endswith(expect):
|
||||||
raise AssertionError("do not end with %r but with %r" % (expect, text[-len(expect):]))
|
raise AssertionError("do not end with %r but with %r" % (expect, text[-len(expect):]))
|
||||||
|
|
||||||
|
@patch('picard.extension_points.script_functions.ext_point_script_functions', ExtensionPoint(label='test_script'))
|
||||||
def test_script_function_documentation_nodoc(self):
|
def test_script_function_documentation_nodoc(self):
|
||||||
"""test script_function_documentation() with a function without documentation"""
|
"""test script_function_documentation() with a function without documentation"""
|
||||||
with patch.object(ScriptParser, '_function_registry', ExtensionPoint()):
|
@script_function()
|
||||||
|
def func_nodocfunc(parser):
|
||||||
|
return ""
|
||||||
|
|
||||||
@script_function()
|
doc = script_function_documentation('nodocfunc', 'markdown')
|
||||||
def func_nodocfunc(parser):
|
self.assertEqual(doc, '')
|
||||||
return ""
|
doc = script_function_documentation('nodocfunc', 'html')
|
||||||
|
self.assertEqual(doc, '')
|
||||||
doc = script_function_documentation('nodocfunc', 'markdown')
|
|
||||||
self.assertEqual(doc, '')
|
|
||||||
doc = script_function_documentation('nodocfunc', 'html')
|
|
||||||
self.assertEqual(doc, '')
|
|
||||||
|
|
||||||
|
@patch('picard.extension_points.script_functions.ext_point_script_functions', ExtensionPoint(label='test_script'))
|
||||||
def test_script_function_documentation(self):
|
def test_script_function_documentation(self):
|
||||||
"""test script_function_documentation() with a function with documentation"""
|
"""test script_function_documentation() with a function with documentation"""
|
||||||
with patch.object(ScriptParser, '_function_registry', ExtensionPoint()):
|
# the documentation used to test includes backquotes
|
||||||
# the documentation used to test includes backquotes
|
testdoc = '`$somefunc()`'
|
||||||
testdoc = '`$somefunc()`'
|
|
||||||
|
|
||||||
@script_function(documentation=testdoc)
|
@script_function(documentation=testdoc)
|
||||||
def func_somefunc(parser):
|
def func_somefunc(parser):
|
||||||
return "x"
|
return "x"
|
||||||
|
|
||||||
doc = script_function_documentation('somefunc', 'markdown')
|
doc = script_function_documentation('somefunc', 'markdown')
|
||||||
self.assertEqual(doc, testdoc)
|
self.assertEqual(doc, testdoc)
|
||||||
areg = r"^no such documentation format: unknownformat"
|
areg = r"^no such documentation format: unknownformat"
|
||||||
with self.assertRaisesRegex(ScriptFunctionDocError, areg):
|
with self.assertRaisesRegex(ScriptFunctionDocError, areg):
|
||||||
script_function_documentation('somefunc', 'unknownformat')
|
script_function_documentation('somefunc', 'unknownformat')
|
||||||
|
|
||||||
@unittest.skipUnless(markdown, "markdown module missing")
|
@unittest.skipUnless(markdown, "markdown module missing")
|
||||||
|
@patch('picard.extension_points.script_functions.ext_point_script_functions', ExtensionPoint(label='test_script'))
|
||||||
def test_script_function_documentation_html(self):
|
def test_script_function_documentation_html(self):
|
||||||
"""test script_function_documentation() with a function with documentation"""
|
"""test script_function_documentation() with a function with documentation"""
|
||||||
with patch.object(ScriptParser, '_function_registry', ExtensionPoint()):
|
# get html code as generated by markdown
|
||||||
# get html code as generated by markdown
|
pre, post = markdown('`XXX`').split('XXX')
|
||||||
pre, post = markdown('`XXX`').split('XXX')
|
|
||||||
|
|
||||||
# the documentation used to test includes backquotes
|
# the documentation used to test includes backquotes
|
||||||
testdoc = '`$somefunc()`'
|
testdoc = '`$somefunc()`'
|
||||||
|
|
||||||
@script_function(documentation=testdoc)
|
@script_function(documentation=testdoc)
|
||||||
def func_somefunc(parser):
|
def func_somefunc(parser):
|
||||||
return "x"
|
return "x"
|
||||||
|
|
||||||
doc = script_function_documentation('somefunc', 'html')
|
doc = script_function_documentation('somefunc', 'html')
|
||||||
self.assertEqual(doc, pre + '$somefunc()' + post)
|
self.assertEqual(doc, pre + '$somefunc()' + post)
|
||||||
|
|
||||||
|
@patch('picard.extension_points.script_functions.ext_point_script_functions', ExtensionPoint(label='test_script'))
|
||||||
def test_script_function_documentation_unknown_function(self):
|
def test_script_function_documentation_unknown_function(self):
|
||||||
"""test script_function_documentation() with an unknown function"""
|
"""test script_function_documentation() with an unknown function"""
|
||||||
with patch.object(ScriptParser, '_function_registry', ExtensionPoint()):
|
areg = r"^no such function: unknownfunc"
|
||||||
areg = r"^no such function: unknownfunc"
|
with self.assertRaisesRegex(ScriptFunctionDocError, areg):
|
||||||
with self.assertRaisesRegex(ScriptFunctionDocError, areg):
|
script_function_documentation('unknownfunc', 'html')
|
||||||
script_function_documentation('unknownfunc', 'html')
|
|
||||||
|
|
||||||
|
@patch('picard.extension_points.script_functions.ext_point_script_functions', ExtensionPoint(label='test_script'))
|
||||||
def test_script_function_documentation_all(self):
|
def test_script_function_documentation_all(self):
|
||||||
"""test script_function_documentation_all() with markdown format"""
|
"""test script_function_documentation_all() with markdown format"""
|
||||||
with patch.object(ScriptParser, '_function_registry', ExtensionPoint()):
|
@script_function(documentation='somedoc2')
|
||||||
|
def func_somefunc2(parser):
|
||||||
|
return "x"
|
||||||
|
|
||||||
@script_function(documentation='somedoc2')
|
@script_function(documentation='somedoc1')
|
||||||
def func_somefunc2(parser):
|
def func_somefunc1(parser):
|
||||||
return "x"
|
return "x"
|
||||||
|
|
||||||
@script_function(documentation='somedoc1')
|
docall = script_function_documentation_all()
|
||||||
def func_somefunc1(parser):
|
self.assertEqual(docall, 'somedoc1\nsomedoc2')
|
||||||
return "x"
|
|
||||||
|
|
||||||
docall = script_function_documentation_all()
|
|
||||||
self.assertEqual(docall, 'somedoc1\nsomedoc2')
|
|
||||||
|
|
||||||
@unittest.skipUnless(markdown, "markdown module missing")
|
@unittest.skipUnless(markdown, "markdown module missing")
|
||||||
|
@patch('picard.extension_points.script_functions.ext_point_script_functions', ExtensionPoint(label='test_script'))
|
||||||
def test_script_function_documentation_all_html(self):
|
def test_script_function_documentation_all_html(self):
|
||||||
"""test script_function_documentation_all() with html format"""
|
"""test script_function_documentation_all() with html format"""
|
||||||
with patch.object(ScriptParser, '_function_registry', ExtensionPoint()):
|
# get html code as generated by markdown
|
||||||
|
pre, post = markdown('XXX').split('XXX')
|
||||||
|
|
||||||
# get html code as generated by markdown
|
@script_function(documentation='somedoc')
|
||||||
pre, post = markdown('XXX').split('XXX')
|
def func_somefunc(parser):
|
||||||
|
return "x"
|
||||||
|
|
||||||
@script_function(documentation='somedoc')
|
def postprocessor(data, function):
|
||||||
def func_somefunc(parser):
|
return 'w' + data + function.name + 'y'
|
||||||
return "x"
|
|
||||||
|
|
||||||
def postprocessor(data, function):
|
docall = script_function_documentation_all(
|
||||||
return 'w' + data + function.name + 'y'
|
fmt='html',
|
||||||
|
pre='<div id="test">',
|
||||||
|
post="</div>\n",
|
||||||
|
postprocessor=postprocessor,
|
||||||
|
)
|
||||||
|
|
||||||
docall = script_function_documentation_all(
|
self.assertStartswith(docall, '<div id="test">w' + pre)
|
||||||
fmt='html',
|
self.assertEndswith(docall, post + 'somefuncy</div>\n')
|
||||||
pre='<div id="test">',
|
|
||||||
post="</div>\n",
|
|
||||||
postprocessor=postprocessor,
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertStartswith(docall, '<div id="test">w' + pre)
|
|
||||||
self.assertEndswith(docall, post + 'somefuncy</div>\n')
|
|
||||||
|
|
||||||
def test_unknown_function(self):
|
def test_unknown_function(self):
|
||||||
areg = r"^\d+:\d+:\$unknownfunction: Unknown function '\$unknownfunction'"
|
areg = r"^\d+:\d+:\$unknownfunction: Unknown function '\$unknownfunction'"
|
||||||
|
|||||||
@@ -26,9 +26,9 @@ from test.picardtestcase import PicardTestCase
|
|||||||
|
|
||||||
from picard import config
|
from picard import config
|
||||||
from picard.const.sys import IS_WIN
|
from picard.const.sys import IS_WIN
|
||||||
|
from picard.extension_points.script_functions import register_script_function
|
||||||
from picard.file import File
|
from picard.file import File
|
||||||
from picard.metadata import Metadata
|
from picard.metadata import Metadata
|
||||||
from picard.script import register_script_function
|
|
||||||
from picard.util.scripttofilename import (
|
from picard.util.scripttofilename import (
|
||||||
script_to_filename,
|
script_to_filename,
|
||||||
script_to_filename_with_metadata,
|
script_to_filename_with_metadata,
|
||||||
|
|||||||
Reference in New Issue
Block a user