mirror of
https://github.com/beetbox/beets.git
synced 2026-01-13 19:52:48 +01:00
Stop on invalid queries instead of ignoring them
So far an invalid query won't be applied:
$ beet ls The Beatles year:196a
will be treaded as
$ beet ls The Beatles
With this commit it stops beets, returns 1 and produces
$ invalid query: u'196a' is not an int or a float
This applies to any querying and therefore on many command, plugins and
some configuration options.
Invalid queries exist on numeric fields and on regular expression usage.
Compile regular expression queries upon instantiation instead of upon
each match test.
The reporting can be improved (give more context). Fix #1219.
This commit is contained in:
parent
ccd5e71519
commit
3804eb5b52
3 changed files with 32 additions and 17 deletions
|
|
@ -20,6 +20,14 @@ from beets import util
|
|||
from datetime import datetime, timedelta
|
||||
|
||||
|
||||
class InvalidQuery(ValueError):
|
||||
def __init__(self, what, expected, detail=None):
|
||||
message = "{0!r} is not {1}".format(what, expected)
|
||||
if detail:
|
||||
message = "{0}: {1}".format(message, detail)
|
||||
super(InvalidQuery, self).__init__(message)
|
||||
|
||||
|
||||
class Query(object):
|
||||
"""An abstract class representing a query into the item database.
|
||||
"""
|
||||
|
|
@ -140,14 +148,17 @@ class RegexpQuery(StringFieldQuery):
|
|||
"""A query that matches a regular expression in a specific item
|
||||
field.
|
||||
"""
|
||||
def __init__(self, field, pattern, false=True):
|
||||
super(RegexpQuery, self).__init__(field, pattern, false)
|
||||
try:
|
||||
self.pattern = re.compile(self.pattern)
|
||||
except re.error as exc:
|
||||
# Invalid regular expression.
|
||||
raise InvalidQuery(pattern, "a regular expression", format(exc))
|
||||
|
||||
@classmethod
|
||||
def string_match(cls, pattern, value):
|
||||
try:
|
||||
res = re.search(pattern, value)
|
||||
except re.error:
|
||||
# Invalid regular expression.
|
||||
return False
|
||||
return res is not None
|
||||
return pattern.search(value) is not None
|
||||
|
||||
|
||||
class BooleanQuery(MatchQuery):
|
||||
|
|
@ -203,7 +214,7 @@ class NumericQuery(FieldQuery):
|
|||
try:
|
||||
return float(s)
|
||||
except ValueError:
|
||||
return None
|
||||
raise InvalidQuery(s, "an int or a float")
|
||||
|
||||
def __init__(self, field, pattern, fast=True):
|
||||
super(NumericQuery, self).__init__(field, pattern, fast)
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ from beets.util.functemplate import Template
|
|||
from beets import config
|
||||
from beets.util import confit
|
||||
from beets.autotag import mb
|
||||
from beets.dbcore import query as db_query
|
||||
|
||||
# On Windows platforms, use colorama to support "ANSI" terminal colors.
|
||||
if sys.platform == 'win32':
|
||||
|
|
@ -960,6 +961,9 @@ def main(args=None):
|
|||
except confit.ConfigError as exc:
|
||||
log.error(u'configuration error: {0}', exc)
|
||||
sys.exit(1)
|
||||
except db_query.InvalidQuery as exc:
|
||||
log.error(u'invalid query: {0}', exc)
|
||||
sys.exit(1)
|
||||
except IOError as exc:
|
||||
if exc.errno == errno.EPIPE:
|
||||
# "Broken pipe". End silently.
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import helper
|
|||
import beets.library
|
||||
from beets import dbcore
|
||||
from beets.dbcore import types
|
||||
from beets.dbcore.query import NoneQuery
|
||||
from beets.dbcore.query import NoneQuery, InvalidQuery
|
||||
from beets.library import Library, Item
|
||||
|
||||
|
||||
|
|
@ -218,11 +218,6 @@ class GetTest(DummyDataTestCase):
|
|||
'baz qux',
|
||||
])
|
||||
|
||||
def test_bad_year(self):
|
||||
q = 'year:delete from items'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, [])
|
||||
|
||||
def test_singleton_true(self):
|
||||
q = 'singleton:true'
|
||||
results = self.lib.items(q)
|
||||
|
|
@ -280,10 +275,15 @@ class GetTest(DummyDataTestCase):
|
|||
results = self.lib.items(q)
|
||||
self.assertFalse(results)
|
||||
|
||||
def test_numeric_empty(self):
|
||||
q = dbcore.query.NumericQuery('year', '')
|
||||
results = self.lib.items(q)
|
||||
self.assertTrue(results)
|
||||
def test_invalid_query(self):
|
||||
with self.assertRaises(InvalidQuery) as raised:
|
||||
dbcore.query.NumericQuery('year', '199a')
|
||||
self.assertIn('not an int', str(raised.exception))
|
||||
|
||||
with self.assertRaises(InvalidQuery) as raised:
|
||||
dbcore.query.RegexpQuery('year', '199(')
|
||||
self.assertIn('not a regular expression', str(raised.exception))
|
||||
self.assertIn('unbalanced parenthesis', str(raised.exception))
|
||||
|
||||
|
||||
class MatchTest(_common.TestCase):
|
||||
|
|
|
|||
Loading…
Reference in a new issue