]> git.notmuchmail.org Git - notmuch/blob - cnotmuch/database.py
82c4f2bc2a86975ec09fc86748f2e523e56f87f1
[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 class Query(object):
147     """ Wrapper around a notmuch_query_t
148
149     Do note that as soon as we tear down this object, all derived
150     threads, and messages will be freed as well.
151     """
152     def __init__(self, db, querystr):
153         pass
154
155 #------------------------------------------------------------------------------
156 class Tags(object):
157     """Wrapper around notmuch_tags_t"""
158     class notmuch_tags_t(ctypes.Structure):
159         """the opaque tags struct that is returned by functions."""
160         pass
161
162     #notmuch_tags_get
163     _get = nmlib.notmuch_tags_get
164     _get.restype = c_char_p
165
166     def __init__(self, tags_p, db=None):
167         """
168         msg_p is a pointer to an notmuch_message_t Structure. If it is None,
169         we will raise an NotmuchError(STATUS.NULL_POINTER).
170
171         Is passed the db these tags are derived from, and saves a
172         reference to it, so we can automatically delete the db object
173         once all derived objects are dead.
174
175         Tags() provides an iterator over all contained tags. However, you will
176         only be able to iterate over the Tags once, because the underlying C
177         function only allows iterating once.
178         #TODO: make the iterator work more than once and cache the tags in 
179                the Python object.
180         """
181         if tags_p is None:
182             NotmuchError(STATUS.NULL_POINTER)
183
184         self._tags = tags_p
185         self._db = db
186         print "Inited Tags derived from %s" %(str(db))
187     
188     def __iter__(self):
189         """ Make Tags an iterator """
190         return self
191
192     def next(self):
193         if self._tags is None:
194             raise StopIteration
195         nmlib.notmuch_tags_move_to_next(self._tags)
196         if not nmlib.notmuch_tags_valid(self._tags):
197             print("Freeing the Tags now")
198             nmlib.notmuch_tags_destroy (self._tags)
199             raise StopIteration
200         return Tags._get (self._tags)
201
202     def __del__(self):
203         """Close and free the notmuch tags"""
204         if self._tags is not None:
205             print("Freeing the Tags now")
206             nmlib.notmuch_tags_destroy (self._tags)
207
208 #------------------------------------------------------------------------------
209 class Message(object):
210     """Wrapper around notmuch_message_t"""
211
212     """notmuch_message_get_filename (notmuch_message_t *message)"""
213     _get_filename = nmlib.notmuch_message_get_filename
214     _get_filename.restype = c_char_p 
215     """notmuch_message_get_message_id (notmuch_message_t *message)"""
216     _get_message_id = nmlib.notmuch_message_get_message_id
217     _get_message_id.restype = c_char_p 
218
219     """notmuch_message_get_tags (notmuch_message_t *message)"""
220     _get_tags = nmlib.notmuch_message_get_tags
221     _get_tags.restype = c_void_p
222
223     def __init__(self, msg_p, parent=None):
224         """
225         msg_p is a pointer to an notmuch_message_t Structure. If it is None,
226         we will raise an NotmuchError(STATUS.NULL_POINTER).
227
228         Is a 'parent' object is passed which this message is derived from,
229         we save a reference to it, so we can automatically delete the parent
230         object once all derived objects are dead.
231         """
232         if msg_p is None:
233             NotmuchError(STATUS.NULL_POINTER)
234         self._msg = msg_p
235         self._parent = parent
236         print "Inited Message derived from %s" %(str(parent))
237
238
239     def get_message_id(self):
240         """ return the msg id
241         
242         Raises NotmuchError(STATUS.NOT_INITIALIZED) if not inited
243         """
244         if self._msg is None:
245             raise NotmuchError(STATUS.NOT_INITIALIZED)
246         return Message._get_message_id(self._msg)
247
248
249     def get_filename(self):
250         """ return the msg filename
251         
252         Raises NotmuchError(STATUS.NOT_INITIALIZED) if not inited
253         """
254         if self._msg is None:
255             raise NotmuchError(STATUS.NOT_INITIALIZED)
256         return Message._get_filename(self._msg)
257
258     def get_tags(self):
259         """ return the msg tags
260         
261         Raises NotmuchError(STATUS.NOT_INITIALIZED) if not inited
262         Raises NotmuchError(STATUS.NULL_POINTER) on error.
263         """
264         if self._msg is None:
265             raise NotmuchError(STATUS.NOT_INITIALIZED)
266
267         tags_p = Message._get_tags(self._msg)
268         if tags_p == None:
269             raise NotmuchError(STATUS.NULL_POINTER)
270         return Tags(tags_p, self)
271
272     def __del__(self):
273         """Close and free the notmuch Message"""
274         if self._msg is not None:
275             print("Freeing the Message now")
276             nmlib.notmuch_message_destroy (self._msg)