mirror of
https://github.com/beetbox/beets.git
synced 2026-01-06 07:53:40 +01:00
Merge branch 'invalidqueryerror'
This commit is contained in:
commit
54abd1a510
5 changed files with 53 additions and 14 deletions
|
|
@ -23,5 +23,6 @@ from .types import Type
|
|||
from .queryparse import query_from_strings
|
||||
from .queryparse import sort_from_strings
|
||||
from .queryparse import parse_sorted_query
|
||||
from .query import InvalidQueryError
|
||||
|
||||
# flake8: noqa
|
||||
|
|
|
|||
|
|
@ -23,12 +23,35 @@ from beets import util
|
|||
from datetime import datetime, timedelta
|
||||
|
||||
|
||||
class InvalidQueryError(ValueError):
|
||||
class ParsingError(ValueError):
|
||||
"""Abstract class for any unparseable user-requested album/query
|
||||
specification.
|
||||
"""
|
||||
|
||||
|
||||
class InvalidQueryError(ParsingError):
|
||||
"""Represent any kind of invalid query.
|
||||
|
||||
The query should be a unicode string or a list, which will be space-joined.
|
||||
"""
|
||||
def __init__(self, query, explanation):
|
||||
if isinstance(query, list):
|
||||
query = " ".join(query)
|
||||
message = "'{0}': {1}".format(query, explanation)
|
||||
super(InvalidQueryError, self).__init__(message)
|
||||
|
||||
|
||||
class InvalidQueryArgumentTypeError(ParsingError):
|
||||
"""Represent a query argument that could not be converted as expected.
|
||||
|
||||
It exists to be caught in upper stack levels so a meaningful (i.e. with the
|
||||
query) InvalidQueryError can be raised.
|
||||
"""
|
||||
def __init__(self, what, expected, detail=None):
|
||||
message = "'{0}' is not {1}".format(what, expected)
|
||||
if detail:
|
||||
message = "{0}: {1}".format(message, detail)
|
||||
super(InvalidQueryError, self).__init__(message)
|
||||
super(InvalidQueryArgumentTypeError, self).__init__(message)
|
||||
|
||||
|
||||
class Query(object):
|
||||
|
|
@ -160,8 +183,9 @@ class RegexpQuery(StringFieldQuery):
|
|||
self.pattern = re.compile(self.pattern)
|
||||
except re.error as exc:
|
||||
# Invalid regular expression.
|
||||
raise InvalidQueryError(pattern, "a regular expression",
|
||||
format(exc))
|
||||
raise InvalidQueryArgumentTypeError(pattern,
|
||||
"a regular expression",
|
||||
format(exc))
|
||||
|
||||
@classmethod
|
||||
def string_match(cls, pattern, value):
|
||||
|
|
@ -228,7 +252,7 @@ class NumericQuery(FieldQuery):
|
|||
try:
|
||||
return float(s)
|
||||
except ValueError:
|
||||
raise InvalidQueryError(s, "an int or a float")
|
||||
raise InvalidQueryArgumentTypeError(s, "an int or a float")
|
||||
|
||||
def __init__(self, field, pattern, fast=True):
|
||||
super(NumericQuery, self).__init__(field, pattern, fast)
|
||||
|
|
|
|||
|
|
@ -1085,7 +1085,7 @@ def parse_query_string(s, model_cls):
|
|||
try:
|
||||
parts = [p.decode('utf8') for p in shlex.split(s)]
|
||||
except ValueError as exc:
|
||||
raise ValueError("Cannot parse {0!r} (error was: {1})".format(s, exc))
|
||||
raise dbcore.InvalidQueryError(s, exc)
|
||||
return parse_query_parts(parts, model_cls)
|
||||
|
||||
|
||||
|
|
@ -1155,11 +1155,14 @@ class Library(dbcore.Database):
|
|||
in the query string the `sort` argument is ignored.
|
||||
"""
|
||||
# Parse the query, if necessary.
|
||||
parsed_sort = None
|
||||
if isinstance(query, basestring):
|
||||
query, parsed_sort = parse_query_string(query, model_cls)
|
||||
elif isinstance(query, (list, tuple)):
|
||||
query, parsed_sort = parse_query_parts(query, model_cls)
|
||||
try:
|
||||
parsed_sort = None
|
||||
if isinstance(query, basestring):
|
||||
query, parsed_sort = parse_query_string(query, model_cls)
|
||||
elif isinstance(query, (list, tuple)):
|
||||
query, parsed_sort = parse_query_parts(query, model_cls)
|
||||
except dbcore.query.InvalidQueryArgumentTypeError as exc:
|
||||
raise dbcore.InvalidQueryError(query, exc)
|
||||
|
||||
# Any non-null sort specified by the parsed query overrides the
|
||||
# provided sort.
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ from test._common import unittest
|
|||
from test._common import item
|
||||
import beets.library
|
||||
import beets.mediafile
|
||||
import beets.dbcore.query
|
||||
from beets import util
|
||||
from beets import plugins
|
||||
from beets import config
|
||||
|
|
@ -1171,6 +1172,14 @@ class ItemReadTest(unittest.TestCase):
|
|||
item.read('/thisfiledoesnotexist')
|
||||
|
||||
|
||||
class ParseQueryTest(unittest.TestCase):
|
||||
def test_parse_invalid_query_string(self):
|
||||
with self.assertRaises(beets.dbcore.InvalidQueryError) as raised:
|
||||
beets.library.parse_query_string('foo"', None)
|
||||
self.assertIsInstance(raised.exception,
|
||||
beets.dbcore.query.ParsingError)
|
||||
|
||||
|
||||
def suite():
|
||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,8 @@ from test import helper
|
|||
import beets.library
|
||||
from beets import dbcore
|
||||
from beets.dbcore import types
|
||||
from beets.dbcore.query import NoneQuery, InvalidQueryError
|
||||
from beets.dbcore.query import (NoneQuery, ParsingError,
|
||||
InvalidQueryArgumentTypeError)
|
||||
from beets.library import Library, Item
|
||||
|
||||
|
||||
|
|
@ -282,14 +283,15 @@ class GetTest(DummyDataTestCase):
|
|||
self.assertFalse(results)
|
||||
|
||||
def test_invalid_query(self):
|
||||
with self.assertRaises(InvalidQueryError) as raised:
|
||||
with self.assertRaises(InvalidQueryArgumentTypeError) as raised:
|
||||
dbcore.query.NumericQuery('year', '199a')
|
||||
self.assertIn('not an int', unicode(raised.exception))
|
||||
|
||||
with self.assertRaises(InvalidQueryError) as raised:
|
||||
with self.assertRaises(InvalidQueryArgumentTypeError) as raised:
|
||||
dbcore.query.RegexpQuery('year', '199(')
|
||||
self.assertIn('not a regular expression', unicode(raised.exception))
|
||||
self.assertIn('unbalanced parenthesis', unicode(raised.exception))
|
||||
self.assertIsInstance(raised.exception, ParsingError)
|
||||
|
||||
|
||||
class MatchTest(_common.TestCase):
|
||||
|
|
|
|||
Loading…
Reference in a new issue