mirror of
https://github.com/fergalmoran/picard.git
synced 2026-02-13 19:24:01 +00:00
Merge pull request #710 from Sophist-UK/PICARD-922_improve-inmulti-as-per-1.4.1
PICARD-922: Make multi-value script functions work correctly
This commit is contained in:
@@ -28,6 +28,7 @@ from inspect import getfullargspec
|
||||
from picard.metadata import Metadata
|
||||
from picard.metadata import MULTI_VALUED_JOINER
|
||||
from picard.plugin import ExtensionPoint
|
||||
from picard.util import uniqify
|
||||
|
||||
|
||||
class ScriptError(Exception):
|
||||
@@ -324,6 +325,31 @@ def _compute_logic(operation, *args):
|
||||
return operation(args)
|
||||
|
||||
|
||||
def _get_multi_values(parser, multi, separator):
|
||||
if isinstance(separator, ScriptExpression):
|
||||
separator = separator.eval(parser)
|
||||
|
||||
if separator == MULTI_VALUED_JOINER:
|
||||
# Convert ScriptExpression containing only a single variable into variable
|
||||
if (isinstance(multi, ScriptExpression) and
|
||||
len(multi) == 1 and
|
||||
isinstance(multi[0], ScriptVariable)):
|
||||
multi = multi[0]
|
||||
|
||||
# If a variable, return multi-values
|
||||
if isinstance(multi, ScriptVariable):
|
||||
if multi.name.startswith("_"):
|
||||
name = "~" + multi.name[1:]
|
||||
else:
|
||||
name = multi.name
|
||||
return parser.context.getall(name)
|
||||
|
||||
# Fall-back to converting to a string and splitting if haystack is an expression
|
||||
# or user has overridden the separator character.
|
||||
multi = multi.eval(parser)
|
||||
return multi.split(separator) if separator else [multi]
|
||||
|
||||
|
||||
def func_if(parser, _if, _then, _else=None):
|
||||
"""If ``if`` is not empty, it returns ``then``, otherwise it returns ``else``."""
|
||||
if _if.eval(parser):
|
||||
@@ -395,9 +421,14 @@ def func_in(parser, text, needle):
|
||||
return ""
|
||||
|
||||
|
||||
def func_inmulti(parser, text, value, separator=MULTI_VALUED_JOINER):
|
||||
"""Splits ``text`` by ``separator``, and returns true if the resulting list contains ``value``."""
|
||||
return func_in(parser, text.split(separator) if separator else [text], value)
|
||||
def func_inmulti(parser, haystack, needle, separator=MULTI_VALUED_JOINER):
|
||||
"""Searches for ``needle`` in ``haystack``, supporting a list variable for
|
||||
``haystack``. If a string is used instead, then a ``separator`` can be
|
||||
used to split it. In both cases, it returns true if the resulting list
|
||||
contains exactly ``needle`` as a member."""
|
||||
|
||||
needle = needle.eval(parser)
|
||||
return func_in(parser, _get_multi_values(parser, haystack, separator), needle)
|
||||
|
||||
|
||||
def func_rreplace(parser, text, old, new):
|
||||
@@ -486,7 +517,7 @@ def func_copymerge(parser, new, old):
|
||||
old = "~" + old[1:]
|
||||
newvals = parser.context.getall(new)
|
||||
oldvals = parser.context.getall(old)
|
||||
parser.context[new] = newvals + list(set(oldvals) - set(newvals))
|
||||
parser.context[new] = uniqify(newvals + oldvals)
|
||||
return ""
|
||||
|
||||
|
||||
@@ -644,6 +675,10 @@ def func_len(parser, text=""):
|
||||
return string_(len(text))
|
||||
|
||||
|
||||
def func_lenmulti(parser, multi, separator=MULTI_VALUED_JOINER):
|
||||
return func_len(parser, _get_multi_values(parser, multi, separator))
|
||||
|
||||
|
||||
def func_performer(parser, pattern="", join=", "):
|
||||
values = []
|
||||
for name, value in parser.context.items():
|
||||
@@ -827,10 +862,11 @@ register_script_function(func_lte, "lte")
|
||||
register_script_function(func_gt, "gt")
|
||||
register_script_function(func_gte, "gte")
|
||||
register_script_function(func_in, "in")
|
||||
register_script_function(func_inmulti, "inmulti")
|
||||
register_script_function(func_inmulti, "inmulti", eval_args=False)
|
||||
register_script_function(func_copy, "copy")
|
||||
register_script_function(func_copymerge, "copymerge")
|
||||
register_script_function(func_len, "len")
|
||||
register_script_function(func_lenmulti, "lenmulti", eval_args=False)
|
||||
register_script_function(func_performer, "performer")
|
||||
register_script_function(func_matchedtracks, "matchedtracks")
|
||||
register_script_function(func_is_complete, "is_complete")
|
||||
|
||||
@@ -11,9 +11,12 @@ class ScriptParserTest(unittest.TestCase):
|
||||
config.setting = {
|
||||
'enabled_plugins': '',
|
||||
}
|
||||
|
||||
self.parser = ScriptParser()
|
||||
|
||||
def func_noargstest(parser):
|
||||
return ""
|
||||
|
||||
register_script_function(func_noargstest, "noargstest")
|
||||
|
||||
def assertScriptResultEquals(self, script, expected, context=None):
|
||||
@@ -172,16 +175,10 @@ class ScriptParserTest(unittest.TestCase):
|
||||
self.assertScriptResultEquals("$upper(AbeCeDA)", "ABECEDA")
|
||||
|
||||
def test_cmd_rreplace(self):
|
||||
self.assertEqual(
|
||||
self.parser.eval(r'''$rreplace(test \(disc 1\),\\s\\\(disc \\d+\\\),)'''),
|
||||
"test"
|
||||
)
|
||||
self.assertScriptResultEquals(r'''$rreplace(test \(disc 1\),\\s\\\(disc \\d+\\\),)''', "test")
|
||||
|
||||
def test_cmd_rsearch(self):
|
||||
self.assertEqual(
|
||||
self.parser.eval(r"$rsearch(test \(disc 1\),\\\(disc \(\\d+\)\\\))"),
|
||||
"1"
|
||||
)
|
||||
self.assertScriptResultEquals(r"$rsearch(test \(disc 1\),\\\(disc \(\\d+\)\\\))", "1")
|
||||
|
||||
def test_arguments(self):
|
||||
self.assertTrue(
|
||||
@@ -271,7 +268,7 @@ class ScriptParserTest(unittest.TestCase):
|
||||
|
||||
def _eval_and_check_copymerge(self, context, expected):
|
||||
self.parser.eval("$copymerge(target,source)", context)
|
||||
self.assertEqual(sorted(self.parser.context.getall("target")), sorted(expected))
|
||||
self.assertEqual(self.parser.context.getall("target"), expected)
|
||||
|
||||
def test_cmd_copymerge_notarget(self):
|
||||
context = Metadata()
|
||||
@@ -287,8 +284,8 @@ class ScriptParserTest(unittest.TestCase):
|
||||
|
||||
def test_cmd_copymerge_removedupes(self):
|
||||
context = Metadata()
|
||||
context["target"] = ["tag1", "tag2"]
|
||||
context["source"] = ["tag2", "tag3"]
|
||||
context["target"] = ["tag1", "tag2", "tag1"]
|
||||
context["source"] = ["tag2", "tag3", "tag2"]
|
||||
self._eval_and_check_copymerge(context, ["tag1", "tag2", "tag3"])
|
||||
|
||||
def test_cmd_copymerge_nonlist(self):
|
||||
@@ -385,6 +382,75 @@ class ScriptParserTest(unittest.TestCase):
|
||||
self.assertNotIn('performer:bar', context)
|
||||
self.assertNotIn('performer:foo', context)
|
||||
|
||||
def test_cmd_inmulti(self):
|
||||
context = Metadata()
|
||||
|
||||
# Test with single-value string
|
||||
context["foo"] = "First:A; Second:B; Third:C"
|
||||
# Tests with $in for comparison purposes
|
||||
self.assertScriptResultEquals("$in(%foo%,Second:B)", "1", context)
|
||||
self.assertScriptResultEquals("$in(%foo%,irst:A; Second:B; Thi)", "1", context)
|
||||
self.assertScriptResultEquals("$in(%foo%,First:A; Second:B; Third:C)", "1", context)
|
||||
# Base $inmulti tests
|
||||
self.assertScriptResultEquals("$inmulti(%foo%,Second:B)", "", context)
|
||||
self.assertScriptResultEquals("$inmulti(%foo%,irst:A; Second:B; Thi)", "", context)
|
||||
self.assertScriptResultEquals("$inmulti(%foo%,First:A; Second:B; Third:C)", "1", context)
|
||||
# Test separator override but with existing separator - results should be same as base
|
||||
self.assertScriptResultEquals("$inmulti(%foo%,Second:B,; )", "", context)
|
||||
self.assertScriptResultEquals("$inmulti(%foo%,irst:A; Second:B; Thi,; )", "", context)
|
||||
self.assertScriptResultEquals("$inmulti(%foo%,First:A; Second:B; Third:C,; )", "1", context)
|
||||
# Test separator override
|
||||
self.assertScriptResultEquals("$inmulti(%foo%,First:A,:)", "", context)
|
||||
self.assertScriptResultEquals("$inmulti(%foo%,Second:B,:)", "", context)
|
||||
self.assertScriptResultEquals("$inmulti(%foo%,Third:C,:)", "", context)
|
||||
self.assertScriptResultEquals("$inmulti(%foo%,First,:)", "1", context)
|
||||
self.assertScriptResultEquals("$inmulti(%foo%,A; Second,:)", "1", context)
|
||||
self.assertScriptResultEquals("$inmulti(%foo%,B; Third,:)", "1", context)
|
||||
self.assertScriptResultEquals("$inmulti(%foo%,C,:)", "1", context)
|
||||
|
||||
# Test with multi-values
|
||||
context["foo"] = ["First:A", "Second:B", "Third:C"]
|
||||
# Tests with $in for comparison purposes
|
||||
self.assertScriptResultEquals("$in(%foo%,Second:B)", "1", context)
|
||||
self.assertScriptResultEquals("$in(%foo%,irst:A; Second:B; Thi)", "1", context)
|
||||
self.assertScriptResultEquals("$in(%foo%,First:A; Second:B; Third:C)", "1", context)
|
||||
# Base $inmulti tests
|
||||
self.assertScriptResultEquals("$inmulti(%foo%,Second:B)", "1", context)
|
||||
self.assertScriptResultEquals("$inmulti(%foo%,irst:A; Second:B; Thi)", "", context)
|
||||
self.assertScriptResultEquals("$inmulti(%foo%,First:A; Second:B; Third:C)", "", context)
|
||||
# Test separator override but with existing separator - results should be same as base
|
||||
self.assertScriptResultEquals("$inmulti(%foo%,Second:B,; )", "1", context)
|
||||
self.assertScriptResultEquals("$inmulti(%foo%,irst:A; Second:B; Thi,; )", "", context)
|
||||
self.assertScriptResultEquals("$inmulti(%foo%,First:A; Second:B; Third:C,; )", "", context)
|
||||
# Test separator override
|
||||
self.assertScriptResultEquals("$inmulti(%foo%,First:A,:)", "", context)
|
||||
self.assertScriptResultEquals("$inmulti(%foo%,Second:B,:)", "", context)
|
||||
self.assertScriptResultEquals("$inmulti(%foo%,Third:C,:)", "", context)
|
||||
self.assertScriptResultEquals("$inmulti(%foo%,First,:)", "1", context)
|
||||
self.assertScriptResultEquals("$inmulti(%foo%,A; Second,:)", "1", context)
|
||||
self.assertScriptResultEquals("$inmulti(%foo%,B; Third,:)", "1", context)
|
||||
self.assertScriptResultEquals("$inmulti(%foo%,C,:)", "1", context)
|
||||
|
||||
def test_cmd_lenmulti(self):
|
||||
context = Metadata()
|
||||
context["foo"] = "First:A; Second:B; Third:C"
|
||||
context["bar"] = ["First:A", "Second:B", "Third:C"]
|
||||
# Tests with $len for comparison purposes
|
||||
self.assertScriptResultEquals("$len(%foo%)", "26", context)
|
||||
self.assertScriptResultEquals("$len(%bar%)", "26", context)
|
||||
# Base $lenmulti tests
|
||||
self.assertScriptResultEquals("$lenmulti(%foo%)", "1", context)
|
||||
self.assertScriptResultEquals("$lenmulti(%bar%)", "3", context)
|
||||
self.assertScriptResultEquals("$lenmulti(%foo%.)", "3", context)
|
||||
# Test separator override but with existing separator - results should be same as base
|
||||
self.assertScriptResultEquals("$lenmulti(%foo%,; )", "1", context)
|
||||
self.assertScriptResultEquals("$lenmulti(%bar%,; )", "3", context)
|
||||
self.assertScriptResultEquals("$lenmulti(%foo%.,; )", "3", context)
|
||||
# Test separator override
|
||||
self.assertScriptResultEquals("$lenmulti(%foo%,:)", "4", context)
|
||||
self.assertScriptResultEquals("$lenmulti(%bar%,:)", "4", context)
|
||||
self.assertScriptResultEquals("$lenmulti(%foo%.,:)", "4", context)
|
||||
|
||||
def test_required_kwonly_parameters(self):
|
||||
def func(a, *, required_kwarg):
|
||||
pass
|
||||
|
||||
Reference in New Issue
Block a user