]> git.notmuchmail.org Git - notmuch/blob - bindings/python/notmuch/query.py
python: update bindings for new count API
[notmuch] / bindings / python / notmuch / query.py
1 """
2 This file is part of notmuch.
3
4 Notmuch is free software: you can redistribute it and/or modify it
5 under the terms of the GNU General Public License as published by the
6 Free Software Foundation, either version 3 of the License, or (at your
7 option) any later version.
8
9 Notmuch is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with notmuch.  If not, see <http://www.gnu.org/licenses/>.
16
17 Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>
18 """
19
20 from ctypes import c_char_p, c_uint, POINTER, byref
21 from .globals import (
22     nmlib,
23     Enum,
24     _str,
25     NotmuchQueryP,
26     NotmuchThreadsP,
27     NotmuchDatabaseP,
28     NotmuchMessagesP,
29 )
30 from .errors import (
31     NullPointerError,
32     NotInitializedError,
33 )
34 from .threads import Threads
35 from .messages import Messages
36
37
38 class Query(object):
39     """Represents a search query on an opened :class:`Database`.
40
41     A query selects and filters a subset of messages from the notmuch
42     database we derive from.
43
44     :class:`Query` provides an instance attribute :attr:`sort`, which
45     contains the sort order (if specified via :meth:`set_sort`) or
46     `None`.
47
48     Any function in this class may throw an :exc:`NotInitializedError`
49     in case the underlying query object was not set up correctly.
50
51     .. note:: Do remember that as soon as we tear down this object,
52            all underlying derived objects such as threads,
53            messages, tags etc will be freed by the underlying library
54            as well. Accessing these objects will lead to segfaults and
55            other unexpected behavior. See above for more details.
56     """
57     # constants
58     SORT = Enum(['OLDEST_FIRST', 'NEWEST_FIRST', 'MESSAGE_ID', 'UNSORTED'])
59     """Constants: Sort order in which to return results"""
60
61     def __init__(self, db, querystr):
62         """
63         :param db: An open database which we derive the Query from.
64         :type db: :class:`Database`
65         :param querystr: The query string for the message.
66         :type querystr: utf-8 encoded str or unicode
67         """
68         self._db = None
69         self._query = None
70         self.sort = None
71         self.create(db, querystr)
72
73     def _assert_query_is_initialized(self):
74         """Raises :exc:`NotInitializedError` if self._query is `None`"""
75         if not self._query:
76             raise NotInitializedError()
77
78     """notmuch_query_create"""
79     _create = nmlib.notmuch_query_create
80     _create.argtypes = [NotmuchDatabaseP, c_char_p]
81     _create.restype = NotmuchQueryP
82
83     def create(self, db, querystr):
84         """Creates a new query derived from a Database
85
86         This function is utilized by __init__() and usually does not need to
87         be called directly.
88
89         :param db: Database to create the query from.
90         :type db: :class:`Database`
91         :param querystr: The query string
92         :type querystr: utf-8 encoded str or unicode
93         :raises:
94             :exc:`NullPointerError` if the query creation failed
95                 (e.g. too little memory).
96             :exc:`NotInitializedError` if the underlying db was not
97                 intitialized.
98         """
99         db._assert_db_is_initialized()
100         # create reference to parent db to keep it alive
101         self._db = db
102         # create query, return None if too little mem available
103         query_p = Query._create(db._db, _str(querystr))
104         if not query_p:
105             raise NullPointerError
106         self._query = query_p
107
108     _set_sort = nmlib.notmuch_query_set_sort
109     _set_sort.argtypes = [NotmuchQueryP, c_uint]
110     _set_sort.argtypes = None
111
112     def set_sort(self, sort):
113         """Set the sort order future results will be delivered in
114
115         :param sort: Sort order (see :attr:`Query.SORT`)
116         """
117         self._assert_query_is_initialized()
118         self.sort = sort
119         self._set_sort(self._query, sort)
120
121     _exclude_tag = nmlib.notmuch_query_add_tag_exclude
122     _exclude_tag.argtypes = [NotmuchQueryP, c_char_p]
123     _exclude_tag.resttype = None
124
125     def exclude_tag(self, tagname):
126         """Add a tag that will be excluded from the query results by default.
127
128         This exclusion will be overridden if this tag appears explicitly in the
129         query.
130
131         :param tagname: Name of the tag to be excluded
132         """
133         self._assert_query_is_initialized()
134         self._exclude_tag(self._query, _str(tagname))
135
136     """notmuch_query_search_threads"""
137     _search_threads = nmlib.notmuch_query_search_threads
138     _search_threads.argtypes = [NotmuchQueryP]
139     _search_threads.restype = NotmuchThreadsP
140
141     def search_threads(self):
142         """Execute a query for threads
143
144         Execute a query for threads, returning a :class:`Threads` iterator.
145         The returned threads are owned by the query and as such, will only be
146         valid until the Query is deleted.
147
148         The method sets :attr:`Message.FLAG`\.MATCH for those messages that
149         match the query. The method :meth:`Message.get_flag` allows us
150         to get the value of this flag.
151
152         :returns: :class:`Threads`
153         :raises: :exc:`NullPointerError` if search_threads failed
154         """
155         self._assert_query_is_initialized()
156         threads_p = Query._search_threads(self._query)
157
158         if not threads_p:
159             raise NullPointerError
160         return Threads(threads_p, self)
161
162     """notmuch_query_search_messages"""
163     _search_messages = nmlib.notmuch_query_search_messages
164     _search_messages.argtypes = [NotmuchQueryP]
165     _search_messages.restype = NotmuchMessagesP
166
167     def search_messages(self):
168         """Filter messages according to the query and return
169         :class:`Messages` in the defined sort order
170
171         :returns: :class:`Messages`
172         :raises: :exc:`NullPointerError` if search_messages failed
173         """
174         self._assert_query_is_initialized()
175         msgs_p = Query._search_messages(self._query)
176
177         if not msgs_p:
178             raise NullPointerError
179         return Messages(msgs_p, self)
180
181     _count_messages = nmlib.notmuch_query_count_messages_st
182     _count_messages.argtypes = [NotmuchQueryP, POINTER(c_uint)]
183     _count_messages.restype = c_uint
184
185     def count_messages(self):
186         '''
187         This function performs a search and returns Xapian's best
188         guess as to the number of matching messages.
189
190         :returns: the estimated number of messages matching this query
191         :rtype:   int
192         '''
193         self._assert_query_is_initialized()
194         count = c_uint(0)
195         status = Query._count_messages(self._query, byref(count))
196         if status != 0:
197             raise NotmuchError(status)
198         return count.value
199
200     _count_threads = nmlib.notmuch_query_count_threads_st
201     _count_threads.argtypes = [NotmuchQueryP, POINTER(c_uint)]
202     _count_threads.restype = c_uint
203
204     def count_threads(self):
205         '''
206         This function performs a search and returns the number of
207         unique thread IDs in the matching messages. This is the same
208         as number of threads matching a search.
209
210         Note that this is a significantly heavier operation than
211         meth:`Query.count_messages`.
212
213         :returns: the number of threads returned by this query
214         :rtype:   int
215         '''
216         self._assert_query_is_initialized()
217         count = c_uint(0)
218         status = Query._count_threads(self._query, byref(count))
219         if status != 0:
220             raise NotmuchError(status)
221         return count.value
222
223     _destroy = nmlib.notmuch_query_destroy
224     _destroy.argtypes = [NotmuchQueryP]
225     _destroy.restype = None
226
227     def __del__(self):
228         """Close and free the Query"""
229         if self._query:
230             self._destroy(self._query)