mirror of
https://git.notmuchmail.org/git/notmuch
synced 2024-12-22 17:34:54 +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 codecs
|
||||
from ctypes import c_char_p, c_void_p, c_uint, c_long, byref, POINTER
|
||||
from notmuch.globals import (nmlib, STATUS, NotmuchError, NotInitializedError,
|
||||
NullPointerError, Enum, _str,
|
||||
NotmuchDatabaseP, NotmuchDirectoryP, NotmuchMessageP, NotmuchTagsP,
|
||||
NotmuchQueryP, NotmuchMessagesP, NotmuchThreadsP, NotmuchFilenamesP)
|
||||
from notmuch.thread import Threads
|
||||
from notmuch.message import Messages, Message
|
||||
from notmuch.globals import (
|
||||
nmlib,
|
||||
STATUS,
|
||||
NotmuchError,
|
||||
NotInitializedError,
|
||||
Enum,
|
||||
_str,
|
||||
NotmuchDatabaseP,
|
||||
NotmuchDirectoryP,
|
||||
NotmuchMessageP,
|
||||
NotmuchTagsP,
|
||||
NotmuchFilenamesP
|
||||
)
|
||||
from notmuch.message import Message
|
||||
from notmuch.tag import Tags
|
||||
|
||||
from .query import Query
|
||||
|
||||
class Database(object):
|
||||
"""The :class:`Database` is the highest-level object that notmuch
|
||||
|
@ -590,165 +598,6 @@ class Database(object):
|
|||
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):
|
||||
"""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