mirror of
https://git.notmuchmail.org/git/notmuch
synced 2025-01-09 02:01:42 +01:00
python: move Query class to its own file
Signed-off-by: Justus Winter <4winter@informatik.uni-hamburg.de>
This commit is contained in:
parent
ff287531ca
commit
5d69d272c3
2 changed files with 207 additions and 166 deletions
|
@ -20,14 +20,22 @@ Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>'
|
||||||
import os
|
import os
|
||||||
import codecs
|
import codecs
|
||||||
from ctypes import c_char_p, c_void_p, c_uint, c_long, byref, POINTER
|
from ctypes import c_char_p, c_void_p, c_uint, c_long, byref, POINTER
|
||||||
from notmuch.globals import (nmlib, STATUS, NotmuchError, NotInitializedError,
|
from notmuch.globals import (
|
||||||
NullPointerError, Enum, _str,
|
nmlib,
|
||||||
NotmuchDatabaseP, NotmuchDirectoryP, NotmuchMessageP, NotmuchTagsP,
|
STATUS,
|
||||||
NotmuchQueryP, NotmuchMessagesP, NotmuchThreadsP, NotmuchFilenamesP)
|
NotmuchError,
|
||||||
from notmuch.thread import Threads
|
NotInitializedError,
|
||||||
from notmuch.message import Messages, Message
|
Enum,
|
||||||
|
_str,
|
||||||
|
NotmuchDatabaseP,
|
||||||
|
NotmuchDirectoryP,
|
||||||
|
NotmuchMessageP,
|
||||||
|
NotmuchTagsP,
|
||||||
|
NotmuchFilenamesP
|
||||||
|
)
|
||||||
|
from notmuch.message import Message
|
||||||
from notmuch.tag import Tags
|
from notmuch.tag import Tags
|
||||||
|
from .query import Query
|
||||||
|
|
||||||
class Database(object):
|
class Database(object):
|
||||||
"""The :class:`Database` is the highest-level object that notmuch
|
"""The :class:`Database` is the highest-level object that notmuch
|
||||||
|
@ -590,165 +598,6 @@ class Database(object):
|
||||||
return self._db
|
return self._db
|
||||||
|
|
||||||
|
|
||||||
class Query(object):
|
|
||||||
"""Represents a search query on an opened :class:`Database`.
|
|
||||||
|
|
||||||
A query selects and filters a subset of messages from the notmuch
|
|
||||||
database we derive from.
|
|
||||||
|
|
||||||
:class:`Query` provides an instance attribute :attr:`sort`, which
|
|
||||||
contains the sort order (if specified via :meth:`set_sort`) or
|
|
||||||
`None`.
|
|
||||||
|
|
||||||
Any function in this class may throw an :exc:`NotInitializedError`
|
|
||||||
in case the underlying query object was not set up correctly.
|
|
||||||
|
|
||||||
.. note:: Do remember that as soon as we tear down this object,
|
|
||||||
all underlying derived objects such as threads,
|
|
||||||
messages, tags etc will be freed by the underlying library
|
|
||||||
as well. Accessing these objects will lead to segfaults and
|
|
||||||
other unexpected behavior. See above for more details.
|
|
||||||
"""
|
|
||||||
# constants
|
|
||||||
SORT = Enum(['OLDEST_FIRST', 'NEWEST_FIRST', 'MESSAGE_ID', 'UNSORTED'])
|
|
||||||
"""Constants: Sort order in which to return results"""
|
|
||||||
|
|
||||||
"""notmuch_query_create"""
|
|
||||||
_create = nmlib.notmuch_query_create
|
|
||||||
_create.argtypes = [NotmuchDatabaseP, c_char_p]
|
|
||||||
_create.restype = NotmuchQueryP
|
|
||||||
|
|
||||||
"""notmuch_query_search_threads"""
|
|
||||||
_search_threads = nmlib.notmuch_query_search_threads
|
|
||||||
_search_threads.argtypes = [NotmuchQueryP]
|
|
||||||
_search_threads.restype = NotmuchThreadsP
|
|
||||||
|
|
||||||
"""notmuch_query_search_messages"""
|
|
||||||
_search_messages = nmlib.notmuch_query_search_messages
|
|
||||||
_search_messages.argtypes = [NotmuchQueryP]
|
|
||||||
_search_messages.restype = NotmuchMessagesP
|
|
||||||
|
|
||||||
"""notmuch_query_count_messages"""
|
|
||||||
_count_messages = nmlib.notmuch_query_count_messages
|
|
||||||
_count_messages.argtypes = [NotmuchQueryP]
|
|
||||||
_count_messages.restype = c_uint
|
|
||||||
|
|
||||||
def __init__(self, db, querystr):
|
|
||||||
"""
|
|
||||||
:param db: An open database which we derive the Query from.
|
|
||||||
:type db: :class:`Database`
|
|
||||||
:param querystr: The query string for the message.
|
|
||||||
:type querystr: utf-8 encoded str or unicode
|
|
||||||
"""
|
|
||||||
self._db = None
|
|
||||||
self._query = None
|
|
||||||
self.sort = None
|
|
||||||
self.create(db, querystr)
|
|
||||||
|
|
||||||
def _assert_query_is_initialized(self):
|
|
||||||
"""Raises :exc:`NotInitializedError` if self._query is `None`"""
|
|
||||||
if self._query is None:
|
|
||||||
raise NotInitializedError()
|
|
||||||
|
|
||||||
def create(self, db, querystr):
|
|
||||||
"""Creates a new query derived from a Database
|
|
||||||
|
|
||||||
This function is utilized by __init__() and usually does not need to
|
|
||||||
be called directly.
|
|
||||||
|
|
||||||
:param db: Database to create the query from.
|
|
||||||
:type db: :class:`Database`
|
|
||||||
:param querystr: The query string
|
|
||||||
:type querystr: utf-8 encoded str or unicode
|
|
||||||
:returns: Nothing
|
|
||||||
:exception:
|
|
||||||
:exc:`NullPointerError` if the query creation failed
|
|
||||||
(e.g. too little memory).
|
|
||||||
:exc:`NotInitializedError` if the underlying db was not
|
|
||||||
intitialized.
|
|
||||||
"""
|
|
||||||
db._assert_db_is_initialized()
|
|
||||||
# create reference to parent db to keep it alive
|
|
||||||
self._db = db
|
|
||||||
# create query, return None if too little mem available
|
|
||||||
query_p = Query._create(db.db_p, _str(querystr))
|
|
||||||
if not query_p:
|
|
||||||
raise NullPointerError
|
|
||||||
self._query = query_p
|
|
||||||
|
|
||||||
_set_sort = nmlib.notmuch_query_set_sort
|
|
||||||
_set_sort.argtypes = [NotmuchQueryP, c_uint]
|
|
||||||
_set_sort.argtypes = None
|
|
||||||
|
|
||||||
def set_sort(self, sort):
|
|
||||||
"""Set the sort order future results will be delivered in
|
|
||||||
|
|
||||||
:param sort: Sort order (see :attr:`Query.SORT`)
|
|
||||||
"""
|
|
||||||
self._assert_query_is_initialized()
|
|
||||||
self.sort = sort
|
|
||||||
self._set_sort(self._query, sort)
|
|
||||||
|
|
||||||
def search_threads(self):
|
|
||||||
"""Execute a query for threads
|
|
||||||
|
|
||||||
Execute a query for threads, returning a :class:`Threads` iterator.
|
|
||||||
The returned threads are owned by the query and as such, will only be
|
|
||||||
valid until the Query is deleted.
|
|
||||||
|
|
||||||
The method sets :attr:`Message.FLAG`\.MATCH for those messages that
|
|
||||||
match the query. The method :meth:`Message.get_flag` allows us
|
|
||||||
to get the value of this flag.
|
|
||||||
|
|
||||||
:returns: :class:`Threads`
|
|
||||||
:exception: :exc:`NullPointerError` if search_threads failed
|
|
||||||
"""
|
|
||||||
self._assert_query_is_initialized()
|
|
||||||
threads_p = Query._search_threads(self._query)
|
|
||||||
|
|
||||||
if not threads_p:
|
|
||||||
raise NullPointerError
|
|
||||||
return Threads(threads_p, self)
|
|
||||||
|
|
||||||
def search_messages(self):
|
|
||||||
"""Filter messages according to the query and return
|
|
||||||
:class:`Messages` in the defined sort order
|
|
||||||
|
|
||||||
:returns: :class:`Messages`
|
|
||||||
:exception: :exc:`NullPointerError` if search_messages failed
|
|
||||||
"""
|
|
||||||
self._assert_query_is_initialized()
|
|
||||||
msgs_p = Query._search_messages(self._query)
|
|
||||||
|
|
||||||
if not msgs_p:
|
|
||||||
raise NullPointerError
|
|
||||||
return Messages(msgs_p, self)
|
|
||||||
|
|
||||||
def count_messages(self):
|
|
||||||
"""Estimate the number of messages matching the query
|
|
||||||
|
|
||||||
This function performs a search and returns Xapian's best
|
|
||||||
guess as to the number of matching messages. It is much faster
|
|
||||||
than performing :meth:`search_messages` and counting the
|
|
||||||
result with `len()` (although it always returned the same
|
|
||||||
result in my tests). Technically, it wraps the underlying
|
|
||||||
*notmuch_query_count_messages* function.
|
|
||||||
|
|
||||||
:returns: :class:`Messages`
|
|
||||||
"""
|
|
||||||
self._assert_query_is_initialized()
|
|
||||||
return Query._count_messages(self._query)
|
|
||||||
|
|
||||||
_destroy = nmlib.notmuch_query_destroy
|
|
||||||
_destroy.argtypes = [NotmuchQueryP]
|
|
||||||
_destroy.restype = None
|
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
"""Close and free the Query"""
|
|
||||||
if self._query is not None:
|
|
||||||
self._destroy(self._query)
|
|
||||||
|
|
||||||
|
|
||||||
class Directory(object):
|
class Directory(object):
|
||||||
"""Represents a directory entry in the notmuch directory
|
"""Represents a directory entry in the notmuch directory
|
||||||
|
|
||||||
|
|
192
bindings/python/notmuch/query.py
Normal file
192
bindings/python/notmuch/query.py
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
"""
|
||||||
|
This file is part of notmuch.
|
||||||
|
|
||||||
|
Notmuch is free software: you can redistribute it and/or modify it
|
||||||
|
under the terms of the GNU General Public License as published by the
|
||||||
|
Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
option) any later version.
|
||||||
|
|
||||||
|
Notmuch is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with notmuch. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>'
|
||||||
|
"""
|
||||||
|
|
||||||
|
from ctypes import c_char_p, c_uint
|
||||||
|
from notmuch.globals import (
|
||||||
|
nmlib,
|
||||||
|
Enum,
|
||||||
|
_str,
|
||||||
|
NotmuchQueryP,
|
||||||
|
NotmuchThreadsP,
|
||||||
|
NotmuchDatabaseP,
|
||||||
|
NotmuchMessagesP,
|
||||||
|
NullPointerError,
|
||||||
|
NotInitializedError,
|
||||||
|
)
|
||||||
|
from notmuch.thread import Threads
|
||||||
|
from notmuch.message import Messages
|
||||||
|
|
||||||
|
|
||||||
|
class Query(object):
|
||||||
|
"""Represents a search query on an opened :class:`Database`.
|
||||||
|
|
||||||
|
A query selects and filters a subset of messages from the notmuch
|
||||||
|
database we derive from.
|
||||||
|
|
||||||
|
:class:`Query` provides an instance attribute :attr:`sort`, which
|
||||||
|
contains the sort order (if specified via :meth:`set_sort`) or
|
||||||
|
`None`.
|
||||||
|
|
||||||
|
Any function in this class may throw an :exc:`NotInitializedError`
|
||||||
|
in case the underlying query object was not set up correctly.
|
||||||
|
|
||||||
|
.. note:: Do remember that as soon as we tear down this object,
|
||||||
|
all underlying derived objects such as threads,
|
||||||
|
messages, tags etc will be freed by the underlying library
|
||||||
|
as well. Accessing these objects will lead to segfaults and
|
||||||
|
other unexpected behavior. See above for more details.
|
||||||
|
"""
|
||||||
|
# constants
|
||||||
|
SORT = Enum(['OLDEST_FIRST', 'NEWEST_FIRST', 'MESSAGE_ID', 'UNSORTED'])
|
||||||
|
"""Constants: Sort order in which to return results"""
|
||||||
|
|
||||||
|
"""notmuch_query_create"""
|
||||||
|
_create = nmlib.notmuch_query_create
|
||||||
|
_create.argtypes = [NotmuchDatabaseP, c_char_p]
|
||||||
|
_create.restype = NotmuchQueryP
|
||||||
|
|
||||||
|
"""notmuch_query_search_threads"""
|
||||||
|
_search_threads = nmlib.notmuch_query_search_threads
|
||||||
|
_search_threads.argtypes = [NotmuchQueryP]
|
||||||
|
_search_threads.restype = NotmuchThreadsP
|
||||||
|
|
||||||
|
"""notmuch_query_search_messages"""
|
||||||
|
_search_messages = nmlib.notmuch_query_search_messages
|
||||||
|
_search_messages.argtypes = [NotmuchQueryP]
|
||||||
|
_search_messages.restype = NotmuchMessagesP
|
||||||
|
|
||||||
|
"""notmuch_query_count_messages"""
|
||||||
|
_count_messages = nmlib.notmuch_query_count_messages
|
||||||
|
_count_messages.argtypes = [NotmuchQueryP]
|
||||||
|
_count_messages.restype = c_uint
|
||||||
|
|
||||||
|
def __init__(self, db, querystr):
|
||||||
|
"""
|
||||||
|
:param db: An open database which we derive the Query from.
|
||||||
|
:type db: :class:`Database`
|
||||||
|
:param querystr: The query string for the message.
|
||||||
|
:type querystr: utf-8 encoded str or unicode
|
||||||
|
"""
|
||||||
|
self._db = None
|
||||||
|
self._query = None
|
||||||
|
self.sort = None
|
||||||
|
self.create(db, querystr)
|
||||||
|
|
||||||
|
def _assert_query_is_initialized(self):
|
||||||
|
"""Raises :exc:`NotInitializedError` if self._query is `None`"""
|
||||||
|
if self._query is None:
|
||||||
|
raise NotInitializedError()
|
||||||
|
|
||||||
|
def create(self, db, querystr):
|
||||||
|
"""Creates a new query derived from a Database
|
||||||
|
|
||||||
|
This function is utilized by __init__() and usually does not need to
|
||||||
|
be called directly.
|
||||||
|
|
||||||
|
:param db: Database to create the query from.
|
||||||
|
:type db: :class:`Database`
|
||||||
|
:param querystr: The query string
|
||||||
|
:type querystr: utf-8 encoded str or unicode
|
||||||
|
:returns: Nothing
|
||||||
|
:exception:
|
||||||
|
:exc:`NullPointerError` if the query creation failed
|
||||||
|
(e.g. too little memory).
|
||||||
|
:exc:`NotInitializedError` if the underlying db was not
|
||||||
|
intitialized.
|
||||||
|
"""
|
||||||
|
db._assert_db_is_initialized()
|
||||||
|
# create reference to parent db to keep it alive
|
||||||
|
self._db = db
|
||||||
|
# create query, return None if too little mem available
|
||||||
|
query_p = Query._create(db.db_p, _str(querystr))
|
||||||
|
if not query_p:
|
||||||
|
raise NullPointerError
|
||||||
|
self._query = query_p
|
||||||
|
|
||||||
|
_set_sort = nmlib.notmuch_query_set_sort
|
||||||
|
_set_sort.argtypes = [NotmuchQueryP, c_uint]
|
||||||
|
_set_sort.argtypes = None
|
||||||
|
|
||||||
|
def set_sort(self, sort):
|
||||||
|
"""Set the sort order future results will be delivered in
|
||||||
|
|
||||||
|
:param sort: Sort order (see :attr:`Query.SORT`)
|
||||||
|
"""
|
||||||
|
self._assert_query_is_initialized()
|
||||||
|
self.sort = sort
|
||||||
|
self._set_sort(self._query, sort)
|
||||||
|
|
||||||
|
def search_threads(self):
|
||||||
|
"""Execute a query for threads
|
||||||
|
|
||||||
|
Execute a query for threads, returning a :class:`Threads` iterator.
|
||||||
|
The returned threads are owned by the query and as such, will only be
|
||||||
|
valid until the Query is deleted.
|
||||||
|
|
||||||
|
The method sets :attr:`Message.FLAG`\.MATCH for those messages that
|
||||||
|
match the query. The method :meth:`Message.get_flag` allows us
|
||||||
|
to get the value of this flag.
|
||||||
|
|
||||||
|
:returns: :class:`Threads`
|
||||||
|
:exception: :exc:`NullPointerError` if search_threads failed
|
||||||
|
"""
|
||||||
|
self._assert_query_is_initialized()
|
||||||
|
threads_p = Query._search_threads(self._query)
|
||||||
|
|
||||||
|
if not threads_p:
|
||||||
|
raise NullPointerError
|
||||||
|
return Threads(threads_p, self)
|
||||||
|
|
||||||
|
def search_messages(self):
|
||||||
|
"""Filter messages according to the query and return
|
||||||
|
:class:`Messages` in the defined sort order
|
||||||
|
|
||||||
|
:returns: :class:`Messages`
|
||||||
|
:exception: :exc:`NullPointerError` if search_messages failed
|
||||||
|
"""
|
||||||
|
self._assert_query_is_initialized()
|
||||||
|
msgs_p = Query._search_messages(self._query)
|
||||||
|
|
||||||
|
if not msgs_p:
|
||||||
|
raise NullPointerError
|
||||||
|
return Messages(msgs_p, self)
|
||||||
|
|
||||||
|
def count_messages(self):
|
||||||
|
"""Estimate the number of messages matching the query
|
||||||
|
|
||||||
|
This function performs a search and returns Xapian's best
|
||||||
|
guess as to the number of matching messages. It is much faster
|
||||||
|
than performing :meth:`search_messages` and counting the
|
||||||
|
result with `len()` (although it always returned the same
|
||||||
|
result in my tests). Technically, it wraps the underlying
|
||||||
|
*notmuch_query_count_messages* function.
|
||||||
|
|
||||||
|
:returns: :class:`Messages`
|
||||||
|
"""
|
||||||
|
self._assert_query_is_initialized()
|
||||||
|
return Query._count_messages(self._query)
|
||||||
|
|
||||||
|
_destroy = nmlib.notmuch_query_destroy
|
||||||
|
_destroy.argtypes = [NotmuchQueryP]
|
||||||
|
_destroy.restype = None
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
"""Close and free the Query"""
|
||||||
|
if self._query is not None:
|
||||||
|
self._destroy(self._query)
|
Loading…
Reference in a new issue