]> git.notmuchmail.org Git - notmuch/blob - cnotmuch/database.py
Implement Message(), Database.find_message(), Database.create()
[notmuch] / cnotmuch / database.py
1 import ctypes
2 from ctypes import c_int, c_char_p, c_void_p
3 from cnotmuch.globals import nmlib, STATUS, NotmuchError
4
5
6 class Database(object):
7     """ Wrapper around a notmuch_database_t
8
9     Do note that as soon as we tear down this object, all derived queries,
10     threads, and messages will be freed as well.
11     """
12     #constants
13     MODE_READ_ONLY = 0
14     MODE_READ_WRITE = 1
15
16     _std_db_path = None
17     """Class attribute of users default database"""
18
19     """notmuch_database_get_path (notmuch_database_t *database)"""
20     _get_path = nmlib.notmuch_database_get_path
21     _get_path.restype = c_char_p
22
23     """notmuch_database_open (const char *path, notmuch_database_mode_t mode)"""
24     _open = nmlib.notmuch_database_open 
25     _open.restype = c_void_p
26
27     """ notmuch_database_find_message """
28     _find_message = nmlib.notmuch_database_find_message
29     _find_message.restype = c_void_p
30
31     """notmuch_database_get_all_tags (notmuch_database_t *database)"""
32     _get_all_tags = nmlib.notmuch_database_get_all_tags
33     _get_all_tags.restype = c_void_p
34
35     """ notmuch_database_create(const char *path):"""
36     _create = nmlib.notmuch_database_create
37     _create.restype = c_void_p
38
39     def __init__(self, path=None, create=False, status= MODE_READ_ONLY):
40         """ Open or create a notmuch database
41
42         If path is None, we will try to read a users notmuch configuration and
43         use his default database.
44         Throws a NotmuchError in case of failure.
45         """
46         self._db = None
47         if path is None:
48             # no path specified. use a user's default database
49             if Database._std_db_path is None:
50                 #the following line throws a NotmuchError if it fails
51                 Database._std_db_path = self._get_user_default_db()
52             path = Database._std_db_path
53
54         if create == False:
55             self.open(path, status)
56         else:
57             self.create(path, status)
58
59     def create(self, path, status=MODE_READ_ONLY):
60         """ notmuch_database_create(const char *path)
61
62         :returns: Raises :exc:`notmuch.NotmuchError` in case
63                   of any failure (after printing an error message on stderr).
64         """
65         res = Database._create(path, status)
66
67         if res is None:
68             raise NotmuchError(
69                 message="Could not create the specified database")
70         self._db = res
71
72     def open(self, path, status= MODE_READ_ONLY): 
73         """calls notmuch_database_open
74
75         :returns: Raises :exc:`notmuch.NotmuchError` in case
76                   of any failure (after printing an error message on stderr).
77         """
78         res = Database._open(path, status)
79
80         if res is None:
81             raise NotmuchError(
82                 message="Could not open the specified database")
83         self._db = res
84
85     def get_path(self):
86         """notmuch_database_get_path (notmuch_database_t *database);  """
87         return Database._get_path(self._db)
88
89     def find_message(self, msgid):
90         """notmuch_database_find_message
91         :param msgid: The message id
92         :ptype msgid: string
93
94         :returns: Message() or None if no message is found or if an
95                   out-of-memory situation occurs.
96         """
97         if self._db is None:
98             raise NotmuchError(STATUS.NOT_INITIALIZED)
99         msg_p = Database._find_message(self._db, msgid)
100         if msg_p is None:
101             return None
102         return Message(msg_p, self)
103
104     def get_all_tags(self):
105         """Return a Tags() object (list of all tags found in the database)
106
107         :returns: Tags() object or raises :exc:`NotmuchError` with 
108                   STATUS.NULL_POINTER on error
109         """
110         if self._db is None:
111             raise NotmuchError(STATUS.NOT_INITIALIZED)
112
113         tags_p = Database._get_all_tags (self._db)
114         if tags_p == None:
115             raise NotmuchError(STATUS.NULL_POINTER)
116         return Tags(tags_p, self)
117
118     def __repr__(self):
119         return "'Notmuch DB " + self.get_path() + "'"
120
121     def __del__(self):
122         """Close and free the notmuch database if needed"""
123         if self._db is not None:
124             print("Freeing the database now")
125             nmlib.notmuch_database_close(self._db)
126
127     def _get_user_default_db(self):
128         """ Reads a user's notmuch config and returns his db location
129
130         Throws a NotmuchError if it cannot find it"""
131         from ConfigParser import SafeConfigParser
132         import os.path
133         config = SafeConfigParser()
134         config.read(os.path.expanduser('~/.notmuch-config'))
135         if not config.has_option('database','path'):
136             raise NotmuchError(message=
137                                "No DB path specified and no user default found")
138         return config.get('database','path')
139
140     @property
141     def db_p(self):
142         """Returns a pointer to the current notmuch_database_t or None"""
143         return self._db
144
145
146 #------------------------------------------------------------------------------
147 class Tags(object):
148     """Wrapper around notmuch_tags_t"""
149     class notmuch_tags_t(ctypes.Structure):
150         """the opaque tags struct that is returned by functions."""
151         pass
152
153     #notmuch_tags_get
154     _get = nmlib.notmuch_tags_get
155     _get.restype = c_char_p
156
157     def __init__(self, tags_p, db=None):
158         """
159         msg_p is a pointer to an notmuch_message_t Structure. If it is None,
160         we will raise an NotmuchError(STATUS.NULL_POINTER).
161
162         Is passed the db these tags are derived from, and saves a
163         reference to it, so we can automatically delete the db object
164         once all derived objects are dead.
165
166         Tags() provides an iterator over all contained tags. However, you will
167         only be able to iterate over the Tags once, because the underlying C
168         function only allows iterating once.
169         #TODO: make the iterator work more than once and cache the tags in 
170                the Python object.
171         """
172         if tags_p is None:
173             NotmuchError(STATUS.NULL_POINTER)
174
175         self._tags = tags_p
176         self._db = db
177         print "Inited Tags derived from %s" %(str(db))
178     
179     def __iter__(self):
180         """ Make Tags an iterator """
181         return self
182
183     def next(self):
184         if self._tags is None:
185             raise StopIteration
186         nmlib.notmuch_tags_move_to_next(self._tags)
187         if not nmlib.notmuch_tags_valid(self._tags):
188             print("Freeing the Tags now")
189             nmlib.notmuch_tags_destroy (self._tags)
190             raise StopIteration
191         return Tags._get (self._tags)
192
193     def __del__(self):
194         """Close and free the notmuch tags"""
195         if self._tags is not None:
196             print("Freeing the Tags now")
197             nmlib.notmuch_tags_destroy (self._tags)
198
199 #------------------------------------------------------------------------------
200 class Message(object):
201     """Wrapper around notmuch_message_t"""
202
203     """notmuch_message_get_filename (notmuch_message_t *message)"""
204     _get_filename = nmlib.notmuch_message_get_filename
205     _get_filename.restype = c_char_p 
206     """notmuch_message_get_message_id (notmuch_message_t *message)"""
207     _get_message_id = nmlib.notmuch_message_get_message_id
208     _get_message_id.restype = c_char_p 
209
210     """notmuch_message_get_tags (notmuch_message_t *message)"""
211     _get_tags = nmlib.notmuch_message_get_tags
212     _get_tags.restype = c_void_p
213
214     def __init__(self, msg_p, parent=None):
215         """
216         msg_p is a pointer to an notmuch_message_t Structure. If it is None,
217         we will raise an NotmuchError(STATUS.NULL_POINTER).
218
219         Is a 'parent' object is passed which this message is derived from,
220         we save a reference to it, so we can automatically delete the parent
221         object once all derived objects are dead.
222         """
223         if msg_p is None:
224             NotmuchError(STATUS.NULL_POINTER)
225         self._msg = msg_p
226         self._parent = parent
227         print "Inited Message derived from %s" %(str(parent))
228
229
230     def get_message_id(self):
231         """ return the msg id
232         
233         Raises NotmuchError(STATUS.NOT_INITIALIZED) if not inited
234         """
235         if self._msg is None:
236             raise NotmuchError(STATUS.NOT_INITIALIZED)
237         return Message._get_message_id(self._msg)
238
239
240     def get_filename(self):
241         """ return the msg filename
242         
243         Raises NotmuchError(STATUS.NOT_INITIALIZED) if not inited
244         """
245         if self._msg is None:
246             raise NotmuchError(STATUS.NOT_INITIALIZED)
247         return Message._get_filename(self._msg)
248
249     def get_tags(self):
250         """ return the msg tags
251         
252         Raises NotmuchError(STATUS.NOT_INITIALIZED) if not inited
253         Raises NotmuchError(STATUS.NULL_POINTER) on error.
254         """
255         if self._msg is None:
256             raise NotmuchError(STATUS.NOT_INITIALIZED)
257
258         tags_p = Message._get_tags(self._msg)
259         if tags_p == None:
260             raise NotmuchError(STATUS.NULL_POINTER)
261         return Tags(tags_p, self)
262
263     def __del__(self):
264         """Close and free the notmuch Message"""
265         if self._msg is not None:
266             print("Freeing the Message now")
267             nmlib.notmuch_message_destroy (self._msg)