2 This file is part of notmuch.
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.
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
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/>.
17 Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>'
22 from ctypes import c_char_p, c_void_p, c_uint, c_long, byref, POINTER
23 from notmuch.globals import (
30 ReadOnlyDatabaseError,
39 from notmuch.message import Message
40 from notmuch.tag import Tags
41 from .query import Query
43 class Database(object):
44 """The :class:`Database` is the highest-level object that notmuch
45 provides. It references a notmuch database, and can be opened in
46 read-only or read-write mode. A :class:`Query` can be derived from
47 or be applied to a specific database to find messages. Also adding
48 and removing messages to the database happens via this
49 object. Modifications to the database are not atmic by default (see
50 :meth:`begin_atomic`) and once a database has been modified, all
51 other database objects pointing to the same data-base will throw an
52 :exc:`XapianError` as the underlying database has been
53 modified. Close and reopen the database to continue working with it.
55 :class:`Database` objects implement the context manager protocol
56 so you can use the :keyword:`with` statement to ensure that the
57 database is properly closed.
61 Any function in this class can and will throw an
62 :exc:`NotInitializedError` if the database was not intitialized
67 Do remember that as soon as we tear down (e.g. via `del db`) this
68 object, all underlying derived objects such as queries, threads,
69 messages, tags etc will be freed by the underlying library as well.
70 Accessing these objects will lead to segfaults and other unexpected
71 behavior. See above for more details.
74 """Class attribute to cache user's default database"""
76 MODE = Enum(['READ_ONLY', 'READ_WRITE'])
77 """Constants: Mode in which to open the database"""
79 """notmuch_database_get_directory"""
80 _get_directory = nmlib.notmuch_database_get_directory
81 _get_directory.argtypes = [NotmuchDatabaseP, c_char_p]
82 _get_directory.restype = NotmuchDirectoryP
84 """notmuch_database_get_path"""
85 _get_path = nmlib.notmuch_database_get_path
86 _get_path.argtypes = [NotmuchDatabaseP]
87 _get_path.restype = c_char_p
89 """notmuch_database_get_version"""
90 _get_version = nmlib.notmuch_database_get_version
91 _get_version.argtypes = [NotmuchDatabaseP]
92 _get_version.restype = c_uint
94 """notmuch_database_open"""
95 _open = nmlib.notmuch_database_open
96 _open.argtypes = [c_char_p, c_uint]
97 _open.restype = NotmuchDatabaseP
99 """notmuch_database_upgrade"""
100 _upgrade = nmlib.notmuch_database_upgrade
101 _upgrade.argtypes = [NotmuchDatabaseP, c_void_p, c_void_p]
102 _upgrade.restype = c_uint
104 """ notmuch_database_find_message"""
105 _find_message = nmlib.notmuch_database_find_message
106 _find_message.argtypes = [NotmuchDatabaseP, c_char_p,
107 POINTER(NotmuchMessageP)]
108 _find_message.restype = c_uint
110 """notmuch_database_find_message_by_filename"""
111 _find_message_by_filename = nmlib.notmuch_database_find_message_by_filename
112 _find_message_by_filename.argtypes = [NotmuchDatabaseP, c_char_p,
113 POINTER(NotmuchMessageP)]
114 _find_message_by_filename.restype = c_uint
116 """notmuch_database_get_all_tags"""
117 _get_all_tags = nmlib.notmuch_database_get_all_tags
118 _get_all_tags.argtypes = [NotmuchDatabaseP]
119 _get_all_tags.restype = NotmuchTagsP
121 """notmuch_database_create"""
122 _create = nmlib.notmuch_database_create
123 _create.argtypes = [c_char_p]
124 _create.restype = NotmuchDatabaseP
126 def __init__(self, path = None, create = False,
127 mode = MODE.READ_ONLY):
128 """If *path* is `None`, we will try to read a users notmuch
129 configuration and use his configured database. The location of the
130 configuration file can be specified through the environment variable
131 *NOTMUCH_CONFIG*, falling back to the default `~/.notmuch-config`.
133 If *create* is `True`, the database will always be created in
134 :attr:`MODE`.READ_WRITE mode. Default mode for opening is READ_ONLY.
136 :param path: Directory to open/create the database in (see
137 above for behavior if `None`)
138 :type path: `str` or `None`
139 :param create: Pass `False` to open an existing, `True` to create a new
142 :param mode: Mode to open a database in. Is always
143 :attr:`MODE`.READ_WRITE when creating a new one.
144 :type mode: :attr:`MODE`
145 :raises: :exc:`NotmuchError` or derived exception in case of
151 # no path specified. use a user's default database
152 if Database._std_db_path is None:
153 #the following line throws a NotmuchError if it fails
154 Database._std_db_path = self._get_user_default_db()
155 path = Database._std_db_path
158 self.open(path, mode)
165 def _assert_db_is_initialized(self):
166 """Raises :exc:`NotInitializedError` if self._db is `None`"""
168 raise NotInitializedError()
170 def create(self, path):
171 """Creates a new notmuch database
173 This function is used by __init__() and usually does not need
174 to be called directly. It wraps the underlying
175 *notmuch_database_create* function and creates a new notmuch
176 database at *path*. It will always return a database in :attr:`MODE`
177 .READ_WRITE mode as creating an empty database for
178 reading only does not make a great deal of sense.
180 :param path: A directory in which we should create the database.
182 :raises: :exc:`NotmuchError` in case of any failure
183 (possibly after printing an error message on stderr).
185 if self._db is not None:
186 raise NotmuchError(message="Cannot create db, this Database() "
187 "already has an open one.")
189 res = Database._create(_str(path), Database.MODE.READ_WRITE)
193 message="Could not create the specified database")
196 def open(self, path, mode=0):
197 """Opens an existing database
199 This function is used by __init__() and usually does not need
200 to be called directly. It wraps the underlying
201 *notmuch_database_open* function.
203 :param status: Open the database in read-only or read-write mode
204 :type status: :attr:`MODE`
205 :raises: Raises :exc:`NotmuchError` in case of any failure
206 (possibly after printing an error message on stderr).
208 res = Database._open(_str(path), mode)
211 raise NotmuchError(message="Could not open the specified database")
214 _close = nmlib.notmuch_database_close
215 _close.argtypes = [NotmuchDatabaseP]
216 _close.restype = None
219 """Close and free the notmuch database if needed"""
220 if self._db is not None:
221 self._close(self._db)
226 Implements the context manager protocol.
230 def __exit__(self, exc_type, exc_value, traceback):
232 Implements the context manager protocol.
237 """Returns the file path of an open database"""
238 self._assert_db_is_initialized()
239 return Database._get_path(self._db).decode('utf-8')
241 def get_version(self):
242 """Returns the database format version
244 :returns: The database version as positive integer
246 self._assert_db_is_initialized()
247 return Database._get_version(self._db)
249 _needs_upgrade = nmlib.notmuch_database_needs_upgrade
250 _needs_upgrade.argtypes = [NotmuchDatabaseP]
251 _needs_upgrade.restype = bool
253 def needs_upgrade(self):
254 """Does this database need to be upgraded before writing to it?
256 If this function returns `True` then no functions that modify the
257 database (:meth:`add_message`,
258 :meth:`Message.add_tag`, :meth:`Directory.set_mtime`,
259 etc.) will work unless :meth:`upgrade` is called successfully first.
261 :returns: `True` or `False`
263 self._assert_db_is_initialized()
264 return self._needs_upgrade(self._db)
267 """Upgrades the current database
269 After opening a database in read-write mode, the client should
270 check if an upgrade is needed (notmuch_database_needs_upgrade) and
271 if so, upgrade with this function before making any modifications.
273 NOT IMPLEMENTED: The optional progress_notify callback can be
274 used by the caller to provide progress indication to the
275 user. If non-NULL it will be called periodically with
276 'progress' as a floating-point value in the range of [0.0..1.0]
277 indicating the progress made so far in the upgrade process.
279 :TODO: catch exceptions, document return values and etc...
281 self._assert_db_is_initialized()
282 status = Database._upgrade(self._db, None, None)
283 #TODO: catch exceptions, document return values and etc
286 _begin_atomic = nmlib.notmuch_database_begin_atomic
287 _begin_atomic.argtypes = [NotmuchDatabaseP]
288 _begin_atomic.restype = c_uint
290 def begin_atomic(self):
291 """Begin an atomic database operation
293 Any modifications performed between a successful
294 :meth:`begin_atomic` and a :meth:`end_atomic` will be applied to
295 the database atomically. Note that, unlike a typical database
296 transaction, this only ensures atomicity, not durability;
297 neither begin nor end necessarily flush modifications to disk.
299 :returns: :attr:`STATUS`.SUCCESS or raises
300 :raises: :exc:`NotmuchError`: :attr:`STATUS`.XAPIAN_EXCEPTION
301 Xapian exception occurred; atomic section not entered.
303 *Added in notmuch 0.9*"""
304 self._assert_db_is_initialized()
305 status = self._begin_atomic(self._db)
306 if status != STATUS.SUCCESS:
307 raise NotmuchError(status)
310 _end_atomic = nmlib.notmuch_database_end_atomic
311 _end_atomic.argtypes = [NotmuchDatabaseP]
312 _end_atomic.restype = c_uint
314 def end_atomic(self):
315 """Indicate the end of an atomic database operation
317 See :meth:`begin_atomic` for details.
319 :returns: :attr:`STATUS`.SUCCESS or raises
323 :attr:`STATUS`.XAPIAN_EXCEPTION
324 A Xapian exception occurred; atomic section not
326 :attr:`STATUS`.UNBALANCED_ATOMIC:
327 end_atomic has been called more times than begin_atomic.
329 *Added in notmuch 0.9*"""
330 self._assert_db_is_initialized()
331 status = self._end_atomic(self._db)
332 if status != STATUS.SUCCESS:
333 raise NotmuchError(status)
336 def get_directory(self, path):
337 """Returns a :class:`Directory` of path,
338 (creating it if it does not exist(?))
340 :param path: An unicode string containing the path relative to the path
341 of database (see :meth:`get_path`), or else should be an absolute
342 path with initial components that match the path of 'database'.
343 :returns: :class:`Directory` or raises an exception.
344 :raises: :exc:`FileError` if path is not relative database or absolute
345 with initial components same as database.
346 :raises: :exc:`ReadOnlyDatabaseError` if the database has not been
347 opened in read-write mode
349 self._assert_db_is_initialized()
351 # work around libnotmuch calling exit(3), see
352 # id:20120221002921.8534.57091@thinkbox.jade-hamburg.de
353 # TODO: remove once this issue is resolved
354 if self.mode != Database.MODE.READ_WRITE:
355 raise ReadOnlyDatabaseError('The database has to be opened in '
356 'read-write mode for get_directory')
358 # sanity checking if path is valid, and make path absolute
359 if path and path[0] == os.sep:
360 # we got an absolute path
361 if not path.startswith(self.get_path()):
362 # but its initial components are not equal to the db path
363 raise FileError('Database().get_directory() called '
364 'with a wrong absolute path')
367 #we got a relative path, make it absolute
368 abs_dirpath = os.path.abspath(os.path.join(self.get_path(), path))
370 dir_p = Database._get_directory(self._db, _str(path))
372 # return the Directory, init it with the absolute path
373 return Directory(abs_dirpath, dir_p, self)
375 _add_message = nmlib.notmuch_database_add_message
376 _add_message.argtypes = [NotmuchDatabaseP, c_char_p,
377 POINTER(NotmuchMessageP)]
378 _add_message.restype = c_uint
380 def add_message(self, filename, sync_maildir_flags=False):
381 """Adds a new message to the database
383 :param filename: should be a path relative to the path of the
384 open database (see :meth:`get_path`), or else should be an
385 absolute filename with initial components that match the
386 path of the database.
388 The file should be a single mail message (not a
389 multi-message mbox) that is expected to remain at its
390 current location, since the notmuch database will reference
391 the filename, and will not copy the entire contents of the
394 :param sync_maildir_flags: If the message contains Maildir
395 flags, we will -depending on the notmuch configuration- sync
396 those tags to initial notmuch tags, if set to `True`. It is
397 `False` by default to remain consistent with the libnotmuch
398 API. You might want to look into the underlying method
399 :meth:`Message.maildir_flags_to_tags`.
401 :returns: On success, we return
403 1) a :class:`Message` object that can be used for things
404 such as adding tags to the just-added message.
405 2) one of the following :attr:`STATUS` values:
407 :attr:`STATUS`.SUCCESS
408 Message successfully added to database.
409 :attr:`STATUS`.DUPLICATE_MESSAGE_ID
410 Message has the same message ID as another message already
411 in the database. The new filename was successfully added
412 to the list of the filenames for the existing message.
414 :rtype: 2-tuple(:class:`Message`, :attr:`STATUS`)
416 :raises: Raises a :exc:`NotmuchError` with the following meaning.
417 If such an exception occurs, nothing was added to the database.
419 :attr:`STATUS`.FILE_ERROR
420 An error occurred trying to open the file, (such as
421 permission denied, or file not found, etc.).
422 :attr:`STATUS`.FILE_NOT_EMAIL
423 The contents of filename don't look like an email
425 :attr:`STATUS`.READ_ONLY_DATABASE
426 Database was opened in read-only mode so no message can
429 self._assert_db_is_initialized()
430 msg_p = NotmuchMessageP()
431 status = self._add_message(self._db, _str(filename), byref(msg_p))
433 if not status in [STATUS.SUCCESS, STATUS.DUPLICATE_MESSAGE_ID]:
434 raise NotmuchError(status)
436 #construct Message() and return
437 msg = Message(msg_p, self)
438 #automatic sync initial tags from Maildir flags
439 if sync_maildir_flags:
440 msg.maildir_flags_to_tags()
443 _remove_message = nmlib.notmuch_database_remove_message
444 _remove_message.argtypes = [NotmuchDatabaseP, c_char_p]
445 _remove_message.restype = c_uint
447 def remove_message(self, filename):
448 """Removes a message (filename) from the given notmuch database
450 Note that only this particular filename association is removed from
451 the database. If the same message (as determined by the message ID)
452 is still available via other filenames, then the message will
453 persist in the database for those filenames. When the last filename
454 is removed for a particular message, the database content for that
455 message will be entirely removed.
457 :returns: A :attr:`STATUS` value with the following meaning:
459 :attr:`STATUS`.SUCCESS
460 The last filename was removed and the message was removed
462 :attr:`STATUS`.DUPLICATE_MESSAGE_ID
463 This filename was removed but the message persists in the
464 database with at least one other filename.
466 :raises: Raises a :exc:`NotmuchError` with the following meaning.
467 If such an exception occurs, nothing was removed from the
470 :attr:`STATUS`.READ_ONLY_DATABASE
471 Database was opened in read-only mode so no message can be
474 self._assert_db_is_initialized()
475 return self._remove_message(self._db, _str(filename))
477 def find_message(self, msgid):
478 """Returns a :class:`Message` as identified by its message ID
480 Wraps the underlying *notmuch_database_find_message* function.
482 :param msgid: The message ID
483 :type msgid: unicode or str
484 :returns: :class:`Message` or `None` if no message is found.
486 :exc:`OutOfMemoryError`
487 If an Out-of-memory occured while constructing the message.
489 In case of a Xapian Exception. These exceptions
490 include "Database modified" situations, e.g. when the
491 notmuch database has been modified by another program
492 in the meantime. In this case, you should close and
493 reopen the database and retry.
494 :exc:`NotInitializedError` if
495 the database was not intitialized.
497 self._assert_db_is_initialized()
498 msg_p = NotmuchMessageP()
499 status = Database._find_message(self._db, _str(msgid), byref(msg_p))
500 if status != STATUS.SUCCESS:
501 raise NotmuchError(status)
502 return msg_p and Message(msg_p, self) or None
504 def find_message_by_filename(self, filename):
505 """Find a message with the given filename
509 This call needs a writeable database in
510 :attr:`Database.MODE`.READ_WRITE mode. The underlying library will
511 exit the program if this method is used on a read-only database!
513 :returns: If the database contains a message with the given
514 filename, then a class:`Message:` is returned. This
515 function returns None if no message is found with the given
519 :exc:`OutOfMemoryError`
520 If an Out-of-memory occured while constructing the message.
522 In case of a Xapian Exception. These exceptions
523 include "Database modified" situations, e.g. when the
524 notmuch database has been modified by another program
525 in the meantime. In this case, you should close and
526 reopen the database and retry.
527 :exc:`NotInitializedError` if
528 the database was not intitialized.
530 *Added in notmuch 0.9*"""
531 self._assert_db_is_initialized()
532 msg_p = NotmuchMessageP()
533 status = Database._find_message_by_filename(self._db, _str(filename),
535 if status != STATUS.SUCCESS:
536 raise NotmuchError(status)
537 return msg_p and Message(msg_p, self) or None
539 def get_all_tags(self):
540 """Returns :class:`Tags` with a list of all tags found in the database
542 :returns: :class:`Tags`
543 :execption: :exc:`NotmuchError` with :attr:`STATUS`.NULL_POINTER
546 self._assert_db_is_initialized()
547 tags_p = Database._get_all_tags(self._db)
549 raise NullPointerError()
550 return Tags(tags_p, self)
552 def create_query(self, querystring):
553 """Returns a :class:`Query` derived from this database
555 This is a shorthand method for doing::
558 # Automatically frees the Database() when 'q' is deleted
560 q = Database(dbpath).create_query('from:"Biene Maja"')
562 # long version, which is functionally equivalent but will keep the
563 # Database in the 'db' variable around after we delete 'q':
565 db = Database(dbpath)
566 q = Query(db,'from:"Biene Maja"')
568 This function is a python extension and not in the underlying C API.
570 return Query(self, querystring)
573 return "'Notmuch DB " + self.get_path() + "'"
575 def _get_user_default_db(self):
576 """ Reads a user's notmuch config and returns his db location
578 Throws a NotmuchError if it cannot find it"""
581 from configparser import SafeConfigParser
584 from ConfigParser import SafeConfigParser
586 config = SafeConfigParser()
587 conf_f = os.getenv('NOTMUCH_CONFIG',
588 os.path.expanduser('~/.notmuch-config'))
589 config.readfp(codecs.open(conf_f, 'r', 'utf-8'))
590 if not config.has_option('database', 'path'):
591 raise NotmuchError(message="No DB path specified"
592 " and no user default found")
593 return config.get('database', 'path')
597 """Property returning a pointer to `notmuch_database_t` or `None`
599 This should normally not be needed by a user (and is not yet
600 guaranteed to remain stable in future versions).
605 class Directory(object):
606 """Represents a directory entry in the notmuch directory
608 Modifying attributes of this object will modify the
609 database, not the real directory attributes.
611 The Directory object is usually derived from another object
612 e.g. via :meth:`Database.get_directory`, and will automatically be
613 become invalid whenever that parent is deleted. You should
614 therefore initialized this object handing it a reference to the
615 parent, preventing the parent from automatically being garbage
619 """notmuch_directory_get_mtime"""
620 _get_mtime = nmlib.notmuch_directory_get_mtime
621 _get_mtime.argtypes = [NotmuchDirectoryP]
622 _get_mtime.restype = c_long
624 """notmuch_directory_set_mtime"""
625 _set_mtime = nmlib.notmuch_directory_set_mtime
626 _set_mtime.argtypes = [NotmuchDirectoryP, c_long]
627 _set_mtime.restype = c_uint
629 """notmuch_directory_get_child_files"""
630 _get_child_files = nmlib.notmuch_directory_get_child_files
631 _get_child_files.argtypes = [NotmuchDirectoryP]
632 _get_child_files.restype = NotmuchFilenamesP
634 """notmuch_directory_get_child_directories"""
635 _get_child_directories = nmlib.notmuch_directory_get_child_directories
636 _get_child_directories.argtypes = [NotmuchDirectoryP]
637 _get_child_directories.restype = NotmuchFilenamesP
639 def _assert_dir_is_initialized(self):
640 """Raises a NotmuchError(:attr:`STATUS`.NOT_INITIALIZED)
643 raise NotInitializedError()
645 def __init__(self, path, dir_p, parent):
647 :param path: The absolute path of the directory object.
648 :param dir_p: The pointer to an internal notmuch_directory_t object.
649 :param parent: The object this Directory is derived from
650 (usually a :class:`Database`). We do not directly use
651 this, but store a reference to it as long as
652 this Directory object lives. This keeps the
657 self._parent = parent
659 def set_mtime(self, mtime):
660 """Sets the mtime value of this directory in the database
662 The intention is for the caller to use the mtime to allow efficient
663 identification of new messages to be added to the database. The
664 recommended usage is as follows:
666 * Read the mtime of a directory from the filesystem
668 * Call :meth:`Database.add_message` for all mail files in
671 * Call notmuch_directory_set_mtime with the mtime read from the
672 filesystem. Then, when wanting to check for updates to the
673 directory in the future, the client can call :meth:`get_mtime`
674 and know that it only needs to add files if the mtime of the
675 directory and files are newer than the stored timestamp.
679 :meth:`get_mtime` function does not allow the caller to
680 distinguish a timestamp of 0 from a non-existent timestamp. So
681 don't store a timestamp of 0 unless you are comfortable with
684 :param mtime: A (time_t) timestamp
685 :raises: :exc:`XapianError` a Xapian exception occurred, mtime
687 :raises: :exc:`ReadOnlyDatabaseError` the database was opened
688 in read-only mode so directory mtime cannot be modified
689 :raises: :exc:`NotInitializedError` the directory object has not
692 self._assert_dir_is_initialized()
693 status = Directory._set_mtime(self._dir_p, mtime)
695 if status != STATUS.SUCCESS:
696 raise NotmuchError(status)
699 """Gets the mtime value of this directory in the database
701 Retrieves a previously stored mtime for this directory.
703 :param mtime: A (time_t) timestamp
704 :raises: :exc:`NotmuchError`:
706 :attr:`STATUS`.NOT_INITIALIZED
707 The directory has not been initialized
709 self._assert_dir_is_initialized()
710 return Directory._get_mtime(self._dir_p)
712 # Make mtime attribute a property of Directory()
713 mtime = property(get_mtime, set_mtime, doc="""Property that allows getting
714 and setting of the Directory *mtime* (read-write)
716 See :meth:`get_mtime` and :meth:`set_mtime` for usage and
717 possible exceptions.""")
719 def get_child_files(self):
720 """Gets a Filenames iterator listing all the filenames of
721 messages in the database within the given directory.
723 The returned filenames will be the basename-entries only (not
726 self._assert_dir_is_initialized()
727 files_p = Directory._get_child_files(self._dir_p)
728 return Filenames(files_p, self)
730 def get_child_directories(self):
731 """Gets a :class:`Filenames` iterator listing all the filenames of
732 sub-directories in the database within the given directory
734 The returned filenames will be the basename-entries only (not
737 self._assert_dir_is_initialized()
738 files_p = Directory._get_child_directories(self._dir_p)
739 return Filenames(files_p, self)
743 """Returns the absolute path of this Directory (read-only)"""
747 """Object representation"""
748 return "<notmuch Directory object '%s'>" % self._path
750 _destroy = nmlib.notmuch_directory_destroy
751 _destroy.argtypes = [NotmuchDirectoryP]
752 _destroy.argtypes = None
755 """Close and free the Directory"""
756 if self._dir_p is not None:
757 self._destroy(self._dir_p)
760 class Filenames(object):
761 """An iterator over File- or Directory names stored in the database"""
763 #notmuch_filenames_get
764 _get = nmlib.notmuch_filenames_get
765 _get.argtypes = [NotmuchFilenamesP]
766 _get.restype = c_char_p
768 def __init__(self, files_p, parent):
770 :param files_p: The pointer to an internal notmuch_filenames_t object.
771 :param parent: The object this Directory is derived from
772 (usually a Directory()). We do not directly use
773 this, but store a reference to it as long as
774 this Directory object lives. This keeps the
777 self._files_p = files_p
778 self._parent = parent
781 """ Make Filenames an iterator """
784 _valid = nmlib.notmuch_filenames_valid
785 _valid.argtypes = [NotmuchFilenamesP]
786 _valid.restype = bool
788 _move_to_next = nmlib.notmuch_filenames_move_to_next
789 _move_to_next.argtypes = [NotmuchFilenamesP]
790 _move_to_next.restype = None
793 if not self._files_p:
794 raise NotInitializedError()
796 if not self._valid(self._files_p):
800 file_ = Filenames._get(self._files_p)
801 self._move_to_next(self._files_p)
802 return file_.decode('utf-8', 'ignore')
803 next = __next__ # python2.x iterator protocol compatibility
806 """len(:class:`Filenames`) returns the number of contained files
810 As this iterates over the files, we will not be able to
811 iterate over them again! So this will fail::
814 files = Database().get_directory('').get_child_files()
815 if len(files) > 0: # this 'exhausts' msgs
817 # NotmuchError(:attr:`STATUS`.NOT_INITIALIZED)
818 for file in files: print file
820 if not self._files_p:
821 raise NotInitializedError()
824 while self._valid(self._files_p):
825 self._move_to_next(self._files_p)
830 _destroy = nmlib.notmuch_filenames_destroy
831 _destroy.argtypes = [NotmuchFilenamesP]
832 _destroy.restype = None
835 """Close and free Filenames"""
836 if self._files_p is not None:
837 self._destroy(self._files_p)