diff --git a/beets/library.py b/beets/library.py index b17538f5a..b3b0eb760 100644 --- a/beets/library.py +++ b/beets/library.py @@ -517,6 +517,10 @@ class RegexpQuery(FieldQuery): class PluginQuery(FieldQuery): + """The base class to add queries using beets plugins. Plugins can add + special queries by defining a subclass of PluginQuery and overriding + the match method. + """ def __init__(self, field, pattern): super(PluginQuery, self).__init__(field, pattern) @@ -589,19 +593,19 @@ class CollectionQuery(Query): @classmethod def _parse_query_part(cls, part): """Takes a query in the form of a key/value pair separated by a - colon. An additional colon before the value indicates that the - value is a regular expression. Returns tuple (key, term, - is_regexp) where key is None if the search term has no key and - is_regexp indicates whether term is a regular expression or an - ordinary substring match. + colon. The value part is matched against a list of prefixes that can be + extended by plugins to add custom query types. For example, the colon + prefix denotes a regular exporession query. + + The function returns a tuple of(key, value, Query) For instance, - parse_query('stapler') == (None, 'stapler', false) - parse_query('color:red') == ('color', 'red', false) - parse_query(':^Quiet') == (None, '^Quiet', true) - parse_query('color::b..e') == ('color', 'b..e', true) + parse_query('stapler') == (None, 'stapler', None) + parse_query('color:red') == ('color', 'red', None) + parse_query(':^Quiet') == (None, '^Quiet', RegexpQuery) + parse_query('color::b..e') == ('color', 'b..e', RegexpQuery) - Colons may be 'escaped' with a backslash to disable the keying + Prefixes may be 'escaped' with a backslash to disable the keying behavior. """ part = part.strip() @@ -613,10 +617,11 @@ class CollectionQuery(Query): if match: key = match.group(1) term = match.group(2).replace('\:', ':') - for p, q in cls.prefixes.items(): - if term.startswith(p): - return (key, term[len(p):], q) - return (key, term, None) + # match the search term against the list of prefixes + for pre, query in cls.prefixes.items(): + if term.startswith(pre): + return (key, term[len(pre):], query) + return (key, term, None) # None means a normal query @classmethod def from_strings(cls, query_parts, default_fields=None, @@ -751,6 +756,10 @@ class AnyRegexpQuery(CollectionQuery): return False class AnyPluginQuery(CollectionQuery): + """A query that dispatch the matching function to the match method of + the cls provided to the contstructor using a list of metadata fields. + """ + def __init__(self, pattern, fields=None, cls=PluginQuery): subqueries = [] self.pattern = pattern diff --git a/beets/plugins.py b/beets/plugins.py index 9cb4a41ed..3749a464b 100755 --- a/beets/plugins.py +++ b/beets/plugins.py @@ -54,6 +54,10 @@ class BeetsPlugin(object): commands that should be added to beets' CLI. """ return () + + def queries(self): + """Should return a dict of {prefix : beets.library.PluginQuery}""" + return {} def track_distance(self, item, info): """Should return a (distance, distance_max) pair to be added @@ -93,9 +97,6 @@ class BeetsPlugin(object): """ return {} - def queries(self): - """Should return a dict of {prefix : beets.library.PluginQuery}""" - return {} listeners = None @@ -214,8 +215,8 @@ def commands(): return out def queries(): - """Returns a dict of {prefix: beet.library.PluginQuery} objects from all loaded plugins. - """ + """Returns a dict of {prefix: beet.library.PluginQuery} objects from all + loaded plugins. """ out = {} for plugin in find_plugins(): out.update(plugin.queries())