python: annotate all calls into libnotmuch with types

Add type information to the ctypes._FuncPtr wrappers and
use the wrapper classes instead of c_void_p for pointers
to notmuch_*_t.

This enables the ctypes library to type check parameters
being handed to functions from the notmuch library.

Signed-off-by: Justus Winter <4winter@informatik.uni-hamburg.de>
This commit is contained in:
Justus Winter 2011-10-10 00:12:54 +02:00 committed by Sebastian Spaeth
parent e92b438f46
commit 3434d19402
5 changed files with 255 additions and 71 deletions

View file

@ -18,9 +18,11 @@ Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>'
"""
import os
from ctypes import c_int, c_char_p, c_void_p, c_uint, c_long, byref
from ctypes import c_int, c_char_p, c_void_p, c_uint, c_long, byref, POINTER
from notmuch.globals import (nmlib, STATUS, NotmuchError, NotInitializedError,
NullPointerError, OutOfMemoryError, XapianError, Enum, _str)
NullPointerError, OutOfMemoryError, XapianError, Enum, _str,
NotmuchDatabaseP, NotmuchDirectoryP, NotmuchMessageP, NotmuchTagsP,
NotmuchQueryP, NotmuchMessagesP, NotmuchThreadsP, NotmuchFilenamesP)
from notmuch.thread import Threads
from notmuch.message import Messages, Message
from notmuch.tag import Tags
@ -56,37 +58,48 @@ class Database(object):
"""notmuch_database_get_directory"""
_get_directory = nmlib.notmuch_database_get_directory
_get_directory.restype = c_void_p
_get_directory.argtypes = [NotmuchDatabaseP, c_char_p]
_get_directory.restype = NotmuchDirectoryP
"""notmuch_database_get_path"""
_get_path = nmlib.notmuch_database_get_path
_get_path.argtypes = [NotmuchDatabaseP]
_get_path.restype = c_char_p
"""notmuch_database_get_version"""
_get_version = nmlib.notmuch_database_get_version
_get_version.argtypes = [NotmuchDatabaseP]
_get_version.restype = c_uint
"""notmuch_database_open"""
_open = nmlib.notmuch_database_open
_open.restype = c_void_p
_open.argtypes = [c_char_p, c_uint]
_open.restype = NotmuchDatabaseP
"""notmuch_database_upgrade"""
_upgrade = nmlib.notmuch_database_upgrade
_upgrade.argtypes = [c_void_p, c_void_p, c_void_p]
_upgrade.argtypes = [NotmuchDatabaseP, c_void_p, c_void_p]
_upgrade.restype = c_uint
""" notmuch_database_find_message"""
_find_message = nmlib.notmuch_database_find_message
_find_message.argtypes = [NotmuchDatabaseP, c_char_p, POINTER(NotmuchMessageP)]
_find_message.restype = c_uint
"""notmuch_database_find_message_by_filename"""
_find_message_by_filename = nmlib.notmuch_database_find_message_by_filename
_find_message_by_filename.argtypes = [NotmuchDatabaseP, c_char_p, POINTER(NotmuchMessageP)]
_find_message_by_filename.restype = c_uint
"""notmuch_database_get_all_tags"""
_get_all_tags = nmlib.notmuch_database_get_all_tags
_get_all_tags.restype = c_void_p
_get_all_tags.argtypes = [NotmuchDatabaseP]
_get_all_tags.restype = NotmuchTagsP
"""notmuch_database_create"""
_create = nmlib.notmuch_database_create
_create.restype = c_void_p
_create.argtypes = [c_char_p]
_create.restype = NotmuchDatabaseP
def __init__(self, path=None, create=False, mode=0):
"""If *path* is `None`, we will try to read a users notmuch
@ -186,6 +199,10 @@ class Database(object):
self._assert_db_is_initialized()
return Database._get_version(self._db)
_needs_upgrade = nmlib.notmuch_database_needs_upgrade
_needs_upgrade.argtypes = [NotmuchDatabaseP]
_needs_upgrade.restype = bool
def needs_upgrade(self):
"""Does this database need to be upgraded before writing to it?
@ -197,7 +214,7 @@ class Database(object):
:returns: `True` or `False`
"""
self._assert_db_is_initialized()
return nmlib.notmuch_database_needs_upgrade(self._db)
return self._needs_upgrade(self._db)
def upgrade(self):
"""Upgrades the current database
@ -219,6 +236,10 @@ class Database(object):
#TODO: catch exceptions, document return values and etc
return status
_begin_atomic = nmlib.notmuch_database_begin_atomic
_begin_atomic.argtypes = [NotmuchDatabaseP]
_begin_atomic.restype = c_uint
def begin_atomic(self):
"""Begin an atomic database operation
@ -236,11 +257,15 @@ class Database(object):
*Added in notmuch 0.9*"""
self._assert_db_is_initialized()
status = nmlib.notmuch_database_begin_atomic(self._db)
status = self._begin_atomic(self._db)
if status != STATUS.SUCCESS:
raise NotmuchError(status)
return status
_end_atomic = nmlib.notmuch_database_end_atomic
_end_atomic.argtypes = [NotmuchDatabaseP]
_end_atomic.restype = c_uint
def end_atomic(self):
"""Indicate the end of an atomic database operation
@ -258,7 +283,7 @@ class Database(object):
*Added in notmuch 0.9*"""
self._assert_db_is_initialized()
status = nmlib.notmuch_database_end_atomic(self._db)
status = self._end_atomic(self._db)
if status != STATUS.SUCCESS:
raise NotmuchError(status)
return status
@ -299,6 +324,10 @@ class Database(object):
# return the Directory, init it with the absolute path
return Directory(_str(abs_dirpath), dir_p, self)
_add_message = nmlib.notmuch_database_add_message
_add_message.argtypes = [NotmuchDatabaseP, c_char_p, POINTER(NotmuchMessageP)]
_add_message.restype = c_uint
def add_message(self, filename, sync_maildir_flags=False):
"""Adds a new message to the database
@ -350,9 +379,7 @@ class Database(object):
"""
self._assert_db_is_initialized()
msg_p = c_void_p()
status = nmlib.notmuch_database_add_message(self._db,
_str(filename),
byref(msg_p))
status = self._add_message(self._db, _str(filename), byref(msg_p))
if not status in [STATUS.SUCCESS, STATUS.DUPLICATE_MESSAGE_ID]:
raise NotmuchError(status)
@ -364,6 +391,10 @@ class Database(object):
msg.maildir_flags_to_tags()
return (msg, status)
_remove_message = nmlib.notmuch_database_remove_message
_remove_message.argtypes = [NotmuchDatabaseP, c_char_p]
_remove_message.restype = c_uint
def remove_message(self, filename):
"""Removes a message (filename) from the given notmuch database
@ -392,8 +423,7 @@ class Database(object):
removed.
"""
self._assert_db_is_initialized()
return nmlib.notmuch_database_remove_message(self._db,
filename)
return self._remove_message(self._db, filename)
def find_message(self, msgid):
"""Returns a :class:`Message` as identified by its message ID
@ -491,10 +521,14 @@ class Database(object):
def __repr__(self):
return "'Notmuch DB " + self.get_path() + "'"
_close = nmlib.notmuch_database_close
_close.argtypes = [NotmuchDatabaseP]
_close.restype = None
def __del__(self):
"""Close and free the notmuch database if needed"""
if self._db is not None:
nmlib.notmuch_database_close(self._db)
self._close(self._db)
def _get_user_default_db(self):
""" Reads a user's notmuch config and returns his db location
@ -545,18 +579,22 @@ class Query(object):
"""notmuch_query_create"""
_create = nmlib.notmuch_query_create
_create.restype = c_void_p
_create.argtypes = [NotmuchDatabaseP, c_char_p]
_create.restype = NotmuchQueryP
"""notmuch_query_search_threads"""
_search_threads = nmlib.notmuch_query_search_threads
_search_threads.restype = c_void_p
_search_threads.argtypes = [NotmuchQueryP]
_search_threads.restype = NotmuchThreadsP
"""notmuch_query_search_messages"""
_search_messages = nmlib.notmuch_query_search_messages
_search_messages.restype = c_void_p
_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):
@ -602,6 +640,10 @@ class Query(object):
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
@ -609,7 +651,7 @@ class Query(object):
"""
self._assert_query_is_initialized()
self.sort = sort
nmlib.notmuch_query_set_sort(self._query, sort)
self._set_sort(self._query, sort)
def search_threads(self):
"""Execute a query for threads
@ -661,10 +703,14 @@ class Query(object):
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:
nmlib.notmuch_query_destroy(self._query)
self._destroy(self._query)
class Directory(object):
@ -683,19 +729,23 @@ class Directory(object):
"""notmuch_directory_get_mtime"""
_get_mtime = nmlib.notmuch_directory_get_mtime
_get_mtime.argtypes = [NotmuchDirectoryP]
_get_mtime.restype = c_long
"""notmuch_directory_set_mtime"""
_set_mtime = nmlib.notmuch_directory_set_mtime
_set_mtime.argtypes = [c_char_p, c_long]
_set_mtime.argtypes = [NotmuchDirectoryP, c_long]
_set_mtime.restype = c_uint
"""notmuch_directory_get_child_files"""
_get_child_files = nmlib.notmuch_directory_get_child_files
_get_child_files.restype = c_void_p
_get_child_files.argtypes = [NotmuchDirectoryP]
_get_child_files.restype = NotmuchFilenamesP
"""notmuch_directory_get_child_directories"""
_get_child_directories = nmlib.notmuch_directory_get_child_directories
_get_child_directories.restype = c_void_p
_get_child_directories.argtypes = [NotmuchDirectoryP]
_get_child_directories.restype = NotmuchFilenamesP
def _assert_dir_is_initialized(self):
"""Raises a NotmuchError(:attr:`STATUS`.NOT_INITIALIZED) if dir_p is None"""
@ -815,10 +865,14 @@ class Directory(object):
"""Object representation"""
return "<notmuch Directory object '%s'>" % self._path
_destroy = nmlib.notmuch_directory_destroy
_destroy.argtypes = [NotmuchDirectoryP]
_destroy.argtypes = None
def __del__(self):
"""Close and free the Directory"""
if self._dir_p is not None:
nmlib.notmuch_directory_destroy(self._dir_p)
self._destroy(self._dir_p)
class Filenames(object):
@ -826,6 +880,7 @@ class Filenames(object):
#notmuch_filenames_get
_get = nmlib.notmuch_filenames_get
_get.argtypes = [NotmuchFilenamesP]
_get.restype = c_char_p
def __init__(self, files_p, parent):
@ -844,16 +899,24 @@ class Filenames(object):
""" Make Filenames an iterator """
return self
_valid = nmlib.notmuch_filenames_valid
_valid.argtypes = [NotmuchFilenamesP]
_valid.restype = bool
_move_to_next = nmlib.notmuch_filenames_move_to_next
_move_to_next.argtypes = [NotmuchFilenamesP]
_move_to_next.restype = None
def next(self):
if self._files_p is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
if not nmlib.notmuch_filenames_valid(self._files_p):
if not self._valid(self._files_p):
self._files_p = None
raise StopIteration
file = Filenames._get(self._files_p)
nmlib.notmuch_filenames_move_to_next(self._files_p)
self._move_to_next(self._files_p)
return file
def __len__(self):
@ -872,13 +935,17 @@ class Filenames(object):
raise NotmuchError(STATUS.NOT_INITIALIZED)
i = 0
while nmlib.notmuch_filenames_valid(self._files_p):
nmlib.notmuch_filenames_move_to_next(self._files_p)
while self._valid(self._files_p):
self._move_to_next(self._files_p)
i += 1
self._files_p = None
return i
_destroy = nmlib.notmuch_filenames_destroy
_destroy.argtypes = [NotmuchFilenamesP]
_destroy.restype = None
def __del__(self):
"""Close and free Filenames"""
if self._files_p is not None:
nmlib.notmuch_filenames_destroy(self._files_p)
self._destroy(self._files_p)

View file

@ -17,7 +17,8 @@ along with notmuch. If not, see <http://www.gnu.org/licenses/>.
Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>'
"""
from ctypes import c_char_p
from notmuch.globals import nmlib, STATUS, NotmuchError
from notmuch.globals import (nmlib, STATUS, NotmuchError,
NotmuchFilenamesP, NotmuchMessagesP, NotmuchMessageP)
class Filenames(object):
@ -50,6 +51,7 @@ class Filenames(object):
#notmuch_filenames_get
_get = nmlib.notmuch_filenames_get
_get.argtypes = [NotmuchFilenamesP]
_get.restype = c_char_p
def __init__(self, files_p, parent):
@ -74,6 +76,14 @@ class Filenames(object):
#save reference to parent object so we keep it alive
self._parent = parent
_valid = nmlib.notmuch_filenames_valid
_valid.argtypes = [NotmuchFilenamesP]
_valid.restype = bool
_move_to_next = nmlib.notmuch_filenames_move_to_next
_move_to_next.argtypes = [NotmuchFilenamesP]
_move_to_next.restype = None
def as_generator(self):
"""Return generator of Filenames
@ -82,9 +92,9 @@ class Filenames(object):
if self._files is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
while nmlib.notmuch_filenames_valid(self._files):
while self._valid(self._files):
yield Filenames._get(self._files)
nmlib.notmuch_filenames_move_to_next(self._files)
self._move_to_next(self._files)
self._files = None
@ -101,7 +111,11 @@ class Filenames(object):
"""
return "\n".join(self)
_destroy = nmlib.notmuch_filenames_destroy
_destroy.argtypes = [NotmuchMessageP]
_destroy.restype = None
def __del__(self):
"""Close and free the notmuch filenames"""
if self._files is not None:
nmlib.notmuch_filenames_destroy(self._files)
self._destroy(self._files)

View file

@ -21,7 +21,8 @@ Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>'
from ctypes import c_char_p, c_void_p, c_long, c_uint, c_int
from datetime import date
from notmuch.globals import nmlib, STATUS, NotmuchError, Enum, _str
from notmuch.globals import (nmlib, STATUS, NotmuchError, Enum, _str,
NotmuchTagsP, NotmuchMessagesP, NotmuchMessageP, NotmuchFilenamesP)
from notmuch.tag import Tags
from notmuch.filename import Filenames
import sys
@ -92,10 +93,12 @@ class Messages(object):
#notmuch_messages_get
_get = nmlib.notmuch_messages_get
_get.restype = c_void_p
_get.argtypes = [NotmuchMessagesP]
_get.restype = NotmuchMessageP
_collect_tags = nmlib.notmuch_messages_collect_tags
_collect_tags.restype = c_void_p
_collect_tags.argtypes = [NotmuchMessagesP]
_collect_tags.restype = NotmuchTagsP
def __init__(self, msgs_p, parent=None):
"""
@ -146,16 +149,24 @@ class Messages(object):
""" Make Messages an iterator """
return self
_valid = nmlib.notmuch_messages_valid
_valid.argtypes = [NotmuchMessagesP]
_valid.restype = bool
_move_to_next = nmlib.notmuch_messages_move_to_next
_move_to_next.argtypes = [NotmuchMessagesP]
_move_to_next.restype = None
def next(self):
if self._msgs is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
if not nmlib.notmuch_messages_valid(self._msgs):
if not self._valid(self._msgs):
self._msgs = None
raise StopIteration
msg = Message(Messages._get(self._msgs), self)
nmlib.notmuch_messages_move_to_next(self._msgs)
self._move_to_next(self._msgs)
return msg
def __nonzero__(self):
@ -163,12 +174,16 @@ class Messages(object):
:return: True if there is at least one more thread in the
Iterator, False if not."""
return self._msgs is not None and \
nmlib.notmuch_messages_valid(self._msgs) > 0
self._valid(self._msgs) > 0
_destroy = nmlib.notmuch_messages_destroy
_destroy.argtypes = [NotmuchMessagesP]
_destroy.restype = None
def __del__(self):
"""Close and free the notmuch Messages"""
if self._msgs is not None:
nmlib.notmuch_messages_destroy(self._msgs)
self._destroy(self._msgs)
def print_messages(self, format, indent=0, entire_thread=False):
"""Outputs messages as needed for 'notmuch show' to sys.stdout
@ -235,44 +250,60 @@ class Message(object):
"""notmuch_message_get_filename (notmuch_message_t *message)"""
_get_filename = nmlib.notmuch_message_get_filename
_get_filename.argtypes = [NotmuchMessageP]
_get_filename.restype = c_char_p
"""return all filenames for a message"""
_get_filenames = nmlib.notmuch_message_get_filenames
_get_filenames.restype = c_void_p
_get_filenames.argtypes = [NotmuchMessageP]
_get_filenames.restype = NotmuchFilenamesP
"""notmuch_message_get_flag"""
_get_flag = nmlib.notmuch_message_get_flag
_get_flag.restype = c_uint
_get_flag.argtypes = [NotmuchMessageP, c_uint]
_get_flag.restype = bool
"""notmuch_message_set_flag"""
_set_flag = nmlib.notmuch_message_set_flag
_set_flag.argtypes = [NotmuchMessageP, c_uint, c_int]
_set_flag.restype = None
"""notmuch_message_get_message_id (notmuch_message_t *message)"""
_get_message_id = nmlib.notmuch_message_get_message_id
_get_message_id.argtypes = [NotmuchMessageP]
_get_message_id.restype = c_char_p
"""notmuch_message_get_thread_id"""
_get_thread_id = nmlib.notmuch_message_get_thread_id
_get_thread_id.argtypes = [NotmuchMessageP]
_get_thread_id.restype = c_char_p
"""notmuch_message_get_replies"""
_get_replies = nmlib.notmuch_message_get_replies
_get_replies.restype = c_void_p
_get_replies.argtypes = [NotmuchMessageP]
_get_replies.restype = NotmuchMessagesP
"""notmuch_message_get_tags (notmuch_message_t *message)"""
_get_tags = nmlib.notmuch_message_get_tags
_get_tags.restype = c_void_p
_get_tags.argtypes = [NotmuchMessageP]
_get_tags.restype = NotmuchTagsP
_get_date = nmlib.notmuch_message_get_date
_get_date.argtypes = [NotmuchMessageP]
_get_date.restype = c_long
_get_header = nmlib.notmuch_message_get_header
_get_header.argtypes = [NotmuchMessageP, c_char_p]
_get_header.restype = c_char_p
"""notmuch_status_t ..._maildir_flags_to_tags (notmuch_message_t *)"""
_tags_to_maildir_flags = nmlib.notmuch_message_tags_to_maildir_flags
_tags_to_maildir_flags.argtypes = [NotmuchMessageP]
_tags_to_maildir_flags.restype = c_int
"""notmuch_status_t ..._tags_to_maildir_flags (notmuch_message_t *)"""
_maildir_flags_to_tags = nmlib.notmuch_message_maildir_flags_to_tags
_maildir_flags_to_tags.argtypes = [NotmuchMessageP]
_maildir_flags_to_tags.restype = c_int
#Constants: Flags that can be set/get with set_flag
@ -450,7 +481,7 @@ class Message(object):
"""
if self._msg is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
nmlib.notmuch_message_set_flag(self._msg, flag, value)
self._set_flag(self._msg, flag, value)
def get_tags(self):
"""Returns the message tags
@ -470,6 +501,10 @@ class Message(object):
raise NotmuchError(STATUS.NULL_POINTER)
return Tags(tags_p, self)
_add_tag = nmlib.notmuch_message_add_tag
_add_tag.argtypes = [NotmuchMessageP, c_char_p]
_add_tag.restype = c_uint
def add_tag(self, tag, sync_maildir_flags=False):
"""Adds a tag to the given message
@ -504,7 +539,7 @@ class Message(object):
if self._msg is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
status = nmlib.notmuch_message_add_tag(self._msg, _str(tag))
status = self._add_tag(self._msg, _str(tag))
# bail out on failure
if status != STATUS.SUCCESS:
@ -514,6 +549,10 @@ class Message(object):
self.tags_to_maildir_flags()
return STATUS.SUCCESS
_remove_tag = nmlib.notmuch_message_remove_tag
_remove_tag.argtypes = [NotmuchMessageP, c_char_p]
_remove_tag.restype = c_uint
def remove_tag(self, tag, sync_maildir_flags=False):
"""Removes a tag from the given message
@ -548,7 +587,7 @@ class Message(object):
if self._msg is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
status = nmlib.notmuch_message_remove_tag(self._msg, _str(tag))
status = self._remove_tag(self._msg, _str(tag))
# bail out on error
if status != STATUS.SUCCESS:
raise NotmuchError(status)
@ -557,6 +596,10 @@ class Message(object):
self.tags_to_maildir_flags()
return STATUS.SUCCESS
_remove_all_tags = nmlib.notmuch_message_remove_all_tags
_remove_all_tags.argtypes = [NotmuchMessageP]
_remove_all_tags.restype = c_uint
def remove_all_tags(self, sync_maildir_flags=False):
"""Removes all tags from the given message.
@ -585,7 +628,7 @@ class Message(object):
if self._msg is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
status = nmlib.notmuch_message_remove_all_tags(self._msg)
status = self._remove_all_tags(self._msg)
# bail out on error
if status != STATUS.SUCCESS:
@ -595,6 +638,10 @@ class Message(object):
self.tags_to_maildir_flags()
return STATUS.SUCCESS
_freeze = nmlib.notmuch_message_freeze
_freeze.argtypes = [NotmuchMessageP]
_freeze.restype = c_uint
def freeze(self):
"""Freezes the current state of 'message' within the database
@ -639,7 +686,7 @@ class Message(object):
if self._msg is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
status = nmlib.notmuch_message_freeze(self._msg)
status = self._freeze(self._msg)
if STATUS.SUCCESS == status:
# return on success
@ -647,6 +694,10 @@ class Message(object):
raise NotmuchError(status)
_thaw = nmlib.notmuch_message_thaw
_thaw.argtypes = [NotmuchMessageP]
_thaw.restype = c_uint
def thaw(self):
"""Thaws the current 'message'
@ -674,7 +725,7 @@ class Message(object):
if self._msg is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
status = nmlib.notmuch_message_thaw(self._msg)
status = self._thaw(self._msg)
if STATUS.SUCCESS == status:
# return on success
@ -896,7 +947,11 @@ class Message(object):
res = cmp(list(self.get_filenames()), list(other.get_filenames()))
return res
_destroy = nmlib.notmuch_message_destroy
_destroy.argtypes = [NotmuchMessageP]
_destroy.restype = None
def __del__(self):
"""Close and free the notmuch Message"""
if self._msg is not None:
nmlib.notmuch_message_destroy(self._msg)
self._destroy(self._msg)

View file

@ -17,7 +17,7 @@ along with notmuch. If not, see <http://www.gnu.org/licenses/>.
Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>'
"""
from ctypes import c_char_p
from notmuch.globals import nmlib, STATUS, NotmuchError
from notmuch.globals import nmlib, STATUS, NotmuchError, NotmuchTagsP
class Tags(object):
@ -50,6 +50,7 @@ class Tags(object):
#notmuch_tags_get
_get = nmlib.notmuch_tags_get
_get.argtypes = [NotmuchTagsP]
_get.restype = c_char_p
def __init__(self, tags_p, parent=None):
@ -80,14 +81,22 @@ class Tags(object):
""" Make Tags an iterator """
return self
_valid = nmlib.notmuch_tags_valid
_valid.argtypes = [NotmuchTagsP]
_valid.restype = bool
_move_to_next = nmlib.notmuch_tags_move_to_next
_move_to_next.argtypes = [NotmuchTagsP]
_move_to_next.restype = None
def next(self):
if self._tags is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
if not nmlib.notmuch_tags_valid(self._tags):
if not self._valid(self._tags):
self._tags = None
raise StopIteration
tag = Tags._get(self._tags).decode('UTF-8')
nmlib.notmuch_tags_move_to_next(self._tags)
self._move_to_next(self._tags)
return tag
def __nonzero__(self):
@ -99,7 +108,7 @@ class Tags(object):
:returns: True if the Tags() iterator has at least one more Tag
left."""
return nmlib.notmuch_tags_valid(self._tags) > 0
return self._valid(self._tags) > 0
def __str__(self):
"""The str() representation of Tags() is a space separated list of tags
@ -112,7 +121,11 @@ class Tags(object):
"""
return " ".join(self)
_destroy = nmlib.notmuch_tags_destroy
_destroy.argtypes = [NotmuchTagsP]
_destroy.restype = None
def __del__(self):
"""Close and free the notmuch tags"""
if self._tags is not None:
nmlib.notmuch_tags_destroy(self._tags)
self._destroy(self._tags)

View file

@ -17,8 +17,10 @@ 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_void_p, c_long
from notmuch.globals import nmlib, STATUS, NotmuchError
from ctypes import c_char_p, c_void_p, c_long, c_int
from notmuch.globals import (nmlib, STATUS,
NotmuchError, NotmuchThreadP, NotmuchThreadsP, NotmuchMessagesP,
NotmuchTagsP,)
from notmuch.message import Messages
from notmuch.tag import Tags
from datetime import date
@ -75,7 +77,8 @@ class Threads(object):
#notmuch_threads_get
_get = nmlib.notmuch_threads_get
_get.restype = c_void_p
_get.argtypes = [NotmuchThreadsP]
_get.restype = NotmuchThreadP
def __init__(self, threads_p, parent=None):
"""
@ -105,16 +108,24 @@ class Threads(object):
""" Make Threads an iterator """
return self
_valid = nmlib.notmuch_threads_valid
_valid.argtypes = [NotmuchThreadsP]
_valid.restype = bool
_move_to_next = nmlib.notmuch_threads_move_to_next
_move_to_next.argtypes = [NotmuchThreadsP]
_move_to_next.restype = None
def next(self):
if self._threads is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
if not nmlib.notmuch_threads_valid(self._threads):
if not self._valid(self._threads):
self._threads = None
raise StopIteration
thread = Thread(Threads._get(self._threads), self)
nmlib.notmuch_threads_move_to_next(self._threads)
self._move_to_next(self._threads)
return thread
def __len__(self):
@ -134,8 +145,8 @@ class Threads(object):
i = 0
# returns 'bool'. On out-of-memory it returns None
while nmlib.notmuch_threads_valid(self._threads):
nmlib.notmuch_threads_move_to_next(self._threads)
while self._valid(self._threads):
self._move_to_next(self._threads)
i += 1
# reset self._threads to mark as "exhausted"
self._threads = None
@ -153,12 +164,16 @@ class Threads(object):
Iterator, False if not. None on a "Out-of-memory" error.
"""
return self._threads is not None and \
nmlib.notmuch_threads_valid(self._threads) > 0
self._valid(self._threads) > 0
_destroy = nmlib.notmuch_threads_destroy
_destroy.argtypes = [NotmuchThreadsP]
_destroy.argtypes = None
def __del__(self):
"""Close and free the notmuch Threads"""
if self._threads is not None:
nmlib.notmuch_messages_destroy(self._threads)
self._destroy(self._threads)
class Thread(object):
@ -166,29 +181,36 @@ class Thread(object):
"""notmuch_thread_get_thread_id"""
_get_thread_id = nmlib.notmuch_thread_get_thread_id
_get_thread_id.argtypes = [NotmuchThreadP]
_get_thread_id.restype = c_char_p
"""notmuch_thread_get_authors"""
_get_authors = nmlib.notmuch_thread_get_authors
_get_authors.argtypes = [NotmuchThreadP]
_get_authors.restype = c_char_p
"""notmuch_thread_get_subject"""
_get_subject = nmlib.notmuch_thread_get_subject
_get_subject.argtypes = [NotmuchThreadP]
_get_subject.restype = c_char_p
"""notmuch_thread_get_toplevel_messages"""
_get_toplevel_messages = nmlib.notmuch_thread_get_toplevel_messages
_get_toplevel_messages.restype = c_void_p
_get_toplevel_messages.argtypes = [NotmuchThreadP]
_get_toplevel_messages.restype = NotmuchMessagesP
_get_newest_date = nmlib.notmuch_thread_get_newest_date
_get_newest_date.argtypes = [NotmuchThreadP]
_get_newest_date.restype = c_long
_get_oldest_date = nmlib.notmuch_thread_get_oldest_date
_get_oldest_date.argtypes = [NotmuchThreadP]
_get_oldest_date.restype = c_long
"""notmuch_thread_get_tags"""
_get_tags = nmlib.notmuch_thread_get_tags
_get_tags.restype = c_void_p
_get_tags.argtypes = [NotmuchThreadP]
_get_tags.restype = NotmuchTagsP
def __init__(self, thread_p, parent=None):
"""
@ -225,6 +247,11 @@ class Thread(object):
raise NotmuchError(STATUS.NOT_INITIALIZED)
return Thread._get_thread_id(self._thread)
_get_total_messages = nmlib.notmuch_thread_get_total_messages
_get_total_messages.argtypes = [NotmuchThreadP]
_get_total_messages.restype = c_int
def get_total_messages(self):
"""Get the total number of messages in 'thread'
@ -236,7 +263,7 @@ class Thread(object):
"""
if self._thread is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
return nmlib.notmuch_thread_get_total_messages(self._thread)
return self._get_total_messages(self._thread)
def get_toplevel_messages(self):
"""Returns a :class:`Messages` iterator for the top-level messages in
@ -267,6 +294,10 @@ class Thread(object):
return Messages(msgs_p, self)
_get_matched_messages = nmlib.notmuch_thread_get_matched_messages
_get_matched_messages.argtypes = [NotmuchThreadP]
_get_matched_messages.restype = c_int
def get_matched_messages(self):
"""Returns the number of messages in 'thread' that matched the query
@ -278,7 +309,7 @@ class Thread(object):
"""
if self._thread is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
return nmlib.notmuch_thread_get_matched_messages(self._thread)
return self._get_matched_messages(self._thread)
def get_authors(self):
"""Returns the authors of 'thread'
@ -387,7 +418,11 @@ class Thread(object):
thread['subject'],
thread['tags'])
_destroy = nmlib.notmuch_thread_destroy
_destroy.argtypes = [NotmuchThreadP]
_destroy.restype = None
def __del__(self):
"""Close and free the notmuch Thread"""
if self._thread is not None:
nmlib.notmuch_thread_destroy(self._thread)
self._destroy(self._thread)