mirror of
https://github.com/fergalmoran/picard.git
synced 2025-12-24 02:09:56 +00:00
PICARD-2218: Support setting regex flags in $performer pattern
This commit is contained in:
@@ -764,15 +764,16 @@ def func_lenmulti(parser, multi, separator=MULTI_VALUED_JOINER):
|
|||||||
"""`$performer(pattern="",join=", ")`
|
"""`$performer(pattern="",join=", ")`
|
||||||
|
|
||||||
Returns the performers where the performance type (e.g. "vocal") matches `pattern`, joined by `join`.
|
Returns the performers where the performance type (e.g. "vocal") matches `pattern`, joined by `join`.
|
||||||
You can specify a regular expression by surrounding the pattern with `/.../`. For example
|
You can specify a regular expression in the format `/pattern/flags`. `flags` are optional. Currently
|
||||||
`/^guitars?$/` matches the performance type "guitar" or "guitars", but not e.g. "bass guitar".
|
the only supported flag is "i" (ignore case). For example `$performer(/^guitars?$/i)` matches the
|
||||||
|
performance type "guitar" or "Guitars", but not e.g. "bass guitar".
|
||||||
|
|
||||||
_Since Picard 0.10_"""
|
_Since Picard 0.10_"""
|
||||||
))
|
))
|
||||||
def func_performer(parser, pattern="", join=", "):
|
def func_performer(parser, pattern="", join=", "):
|
||||||
values = []
|
values = []
|
||||||
try:
|
try:
|
||||||
regex = pattern_as_regex(pattern, allow_wildcards=False, flags=re.IGNORECASE)
|
regex = pattern_as_regex(pattern, allow_wildcards=False)
|
||||||
except re.error:
|
except re.error:
|
||||||
return ''
|
return ''
|
||||||
for name, value in parser.context.items():
|
for name, value in parser.context.items():
|
||||||
|
|||||||
@@ -734,7 +734,9 @@ def extract_year_from_date(dt):
|
|||||||
def pattern_as_regex(pattern, allow_wildcards=False, flags=0):
|
def pattern_as_regex(pattern, allow_wildcards=False, flags=0):
|
||||||
"""Parses a string and interprets it as a matching pattern.
|
"""Parses a string and interprets it as a matching pattern.
|
||||||
|
|
||||||
- If pattern starts and ends with / it is interpreted as a regular expression (e.g. `/foo.*/`)
|
- If pattern is of the form /pattern/flags it is interpreted as a regular expression (e.g. `/foo.*/`).
|
||||||
|
The flags are optional and in addition to the flags passed in the `flags` function parameter. Supported
|
||||||
|
flags in the expression are "i" (ignore case) and "m" (multiline)
|
||||||
- Otherwise if `allow_wildcards` is True, it is interpreted as a pattern that allows wildcard matching (see below)
|
- Otherwise if `allow_wildcards` is True, it is interpreted as a pattern that allows wildcard matching (see below)
|
||||||
- If `allow_wildcards` is False a regex matching the literal string is returned
|
- If `allow_wildcards` is False a regex matching the literal string is returned
|
||||||
|
|
||||||
@@ -750,9 +752,14 @@ def pattern_as_regex(pattern, allow_wildcards=False, flags=0):
|
|||||||
|
|
||||||
Raises: `re.error` if the regular expression could not be parsed
|
Raises: `re.error` if the regular expression could not be parsed
|
||||||
"""
|
"""
|
||||||
if len(pattern) > 2 and pattern[0] == '/' and pattern[-1] == '/':
|
plain_pattern = pattern.rstrip('im')
|
||||||
pattern = pattern[1:-1]
|
if len(plain_pattern) > 2 and plain_pattern[0] == '/' and plain_pattern[-1] == '/':
|
||||||
return re.compile(pattern, flags)
|
extra_flags = pattern[len(plain_pattern):]
|
||||||
|
if 'i' in extra_flags:
|
||||||
|
flags |= re.IGNORECASE
|
||||||
|
if 'm' in extra_flags:
|
||||||
|
flags |= re.MULTILINE
|
||||||
|
return re.compile(plain_pattern[1:-1], flags)
|
||||||
elif allow_wildcards:
|
elif allow_wildcards:
|
||||||
# FIXME?: only support '*' (not '?' or '[abc]')
|
# FIXME?: only support '*' (not '?' or '[abc]')
|
||||||
# replace multiple '*' by one
|
# replace multiple '*' by one
|
||||||
|
|||||||
@@ -959,6 +959,14 @@ class ScriptParserTest(PicardTestCase):
|
|||||||
self.assertScriptResultEquals(r"$performer(/drums \(/)", "", context)
|
self.assertScriptResultEquals(r"$performer(/drums \(/)", "", context)
|
||||||
self.assertScriptResultEquals(r"$performer(drums \()", "Drummer", context)
|
self.assertScriptResultEquals(r"$performer(drums \()", "Drummer", context)
|
||||||
|
|
||||||
|
def test_cmd_performer_regex_ignore_case(self):
|
||||||
|
context = Metadata()
|
||||||
|
context['performer:guitar'] = 'Foo1'
|
||||||
|
context['performer:GUITARS'] = 'Foo2'
|
||||||
|
context['performer:rhythm-guitar'] = 'Foo3'
|
||||||
|
result = self.parser.eval(r"$performer(/^guitars?/i)", context=context)
|
||||||
|
self.assertEqual({'Foo1', 'Foo2'}, set(result.split(', ')))
|
||||||
|
|
||||||
def test_cmd_performer_custom_join(self):
|
def test_cmd_performer_custom_join(self):
|
||||||
context = Metadata()
|
context = Metadata()
|
||||||
context['performer:guitar'] = 'Foo1'
|
context['performer:guitar'] = 'Foo1'
|
||||||
|
|||||||
@@ -531,6 +531,13 @@ class PatternAsRegexTest(PicardTestCase):
|
|||||||
self.assertTrue(regex.flags & re.IGNORECASE)
|
self.assertTrue(regex.flags & re.IGNORECASE)
|
||||||
self.assertTrue(regex.flags & re.MULTILINE)
|
self.assertTrue(regex.flags & re.MULTILINE)
|
||||||
|
|
||||||
|
def test_regex_extra_flags(self):
|
||||||
|
regex = pattern_as_regex(r'/^foo.*/im', flags=re.VERBOSE)
|
||||||
|
self.assertEqual(r'^foo.*', regex.pattern)
|
||||||
|
self.assertTrue(regex.flags & re.VERBOSE)
|
||||||
|
self.assertTrue(regex.flags & re.IGNORECASE)
|
||||||
|
self.assertTrue(regex.flags & re.MULTILINE)
|
||||||
|
|
||||||
def test_regex_raises(self):
|
def test_regex_raises(self):
|
||||||
with self.assertRaises(re.error):
|
with self.assertRaises(re.error):
|
||||||
pattern_as_regex(r'/^foo(.*/')
|
pattern_as_regex(r'/^foo(.*/')
|
||||||
|
|||||||
Reference in New Issue
Block a user