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 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, 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.thread import Threads
from notmuch.message import Messages, Message from notmuch.message import Messages, Message
from notmuch.tag import Tags from notmuch.tag import Tags
@ -56,37 +58,48 @@ class Database(object):
"""notmuch_database_get_directory""" """notmuch_database_get_directory"""
_get_directory = nmlib.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""" """notmuch_database_get_path"""
_get_path = nmlib.notmuch_database_get_path _get_path = nmlib.notmuch_database_get_path
_get_path.argtypes = [NotmuchDatabaseP]
_get_path.restype = c_char_p _get_path.restype = c_char_p
"""notmuch_database_get_version""" """notmuch_database_get_version"""
_get_version = nmlib.notmuch_database_get_version _get_version = nmlib.notmuch_database_get_version
_get_version.argtypes = [NotmuchDatabaseP]
_get_version.restype = c_uint _get_version.restype = c_uint
"""notmuch_database_open""" """notmuch_database_open"""
_open = nmlib.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""" """notmuch_database_upgrade"""
_upgrade = nmlib.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""" """ notmuch_database_find_message"""
_find_message = nmlib.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""" """notmuch_database_find_message_by_filename"""
_find_message_by_filename = nmlib.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""" """notmuch_database_get_all_tags"""
_get_all_tags = nmlib.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""" """notmuch_database_create"""
_create = nmlib.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): def __init__(self, path=None, create=False, mode=0):
"""If *path* is `None`, we will try to read a users notmuch """If *path* is `None`, we will try to read a users notmuch
@ -186,6 +199,10 @@ class Database(object):
self._assert_db_is_initialized() self._assert_db_is_initialized()
return Database._get_version(self._db) 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): def needs_upgrade(self):
"""Does this database need to be upgraded before writing to it? """Does this database need to be upgraded before writing to it?
@ -197,7 +214,7 @@ class Database(object):
:returns: `True` or `False` :returns: `True` or `False`
""" """
self._assert_db_is_initialized() self._assert_db_is_initialized()
return nmlib.notmuch_database_needs_upgrade(self._db) return self._needs_upgrade(self._db)
def upgrade(self): def upgrade(self):
"""Upgrades the current database """Upgrades the current database
@ -219,6 +236,10 @@ class Database(object):
#TODO: catch exceptions, document return values and etc #TODO: catch exceptions, document return values and etc
return status return status
_begin_atomic = nmlib.notmuch_database_begin_atomic
_begin_atomic.argtypes = [NotmuchDatabaseP]
_begin_atomic.restype = c_uint
def begin_atomic(self): def begin_atomic(self):
"""Begin an atomic database operation """Begin an atomic database operation
@ -236,11 +257,15 @@ class Database(object):
*Added in notmuch 0.9*""" *Added in notmuch 0.9*"""
self._assert_db_is_initialized() self._assert_db_is_initialized()
status = nmlib.notmuch_database_begin_atomic(self._db) status = self._begin_atomic(self._db)
if status != STATUS.SUCCESS: if status != STATUS.SUCCESS:
raise NotmuchError(status) raise NotmuchError(status)
return status return status
_end_atomic = nmlib.notmuch_database_end_atomic
_end_atomic.argtypes = [NotmuchDatabaseP]
_end_atomic.restype = c_uint
def end_atomic(self): def end_atomic(self):
"""Indicate the end of an atomic database operation """Indicate the end of an atomic database operation
@ -258,7 +283,7 @@ class Database(object):
*Added in notmuch 0.9*""" *Added in notmuch 0.9*"""
self._assert_db_is_initialized() self._assert_db_is_initialized()
status = nmlib.notmuch_database_end_atomic(self._db) status = self._end_atomic(self._db)
if status != STATUS.SUCCESS: if status != STATUS.SUCCESS:
raise NotmuchError(status) raise NotmuchError(status)
return status return status
@ -299,6 +324,10 @@ class Database(object):
# return the Directory, init it with the absolute path # return the Directory, init it with the absolute path
return Directory(_str(abs_dirpath), dir_p, self) 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): def add_message(self, filename, sync_maildir_flags=False):
"""Adds a new message to the database """Adds a new message to the database
@ -350,9 +379,7 @@ class Database(object):
""" """
self._assert_db_is_initialized() self._assert_db_is_initialized()
msg_p = c_void_p() msg_p = c_void_p()
status = nmlib.notmuch_database_add_message(self._db, status = self._add_message(self._db, _str(filename), byref(msg_p))
_str(filename),
byref(msg_p))
if not status in [STATUS.SUCCESS, STATUS.DUPLICATE_MESSAGE_ID]: if not status in [STATUS.SUCCESS, STATUS.DUPLICATE_MESSAGE_ID]:
raise NotmuchError(status) raise NotmuchError(status)
@ -364,6 +391,10 @@ class Database(object):
msg.maildir_flags_to_tags() msg.maildir_flags_to_tags()
return (msg, status) 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): def remove_message(self, filename):
"""Removes a message (filename) from the given notmuch database """Removes a message (filename) from the given notmuch database
@ -392,8 +423,7 @@ class Database(object):
removed. removed.
""" """
self._assert_db_is_initialized() self._assert_db_is_initialized()
return nmlib.notmuch_database_remove_message(self._db, return self._remove_message(self._db, filename)
filename)
def find_message(self, msgid): def find_message(self, msgid):
"""Returns a :class:`Message` as identified by its message ID """Returns a :class:`Message` as identified by its message ID
@ -491,10 +521,14 @@ class Database(object):
def __repr__(self): def __repr__(self):
return "'Notmuch DB " + self.get_path() + "'" return "'Notmuch DB " + self.get_path() + "'"
_close = nmlib.notmuch_database_close
_close.argtypes = [NotmuchDatabaseP]
_close.restype = None
def __del__(self): def __del__(self):
"""Close and free the notmuch database if needed""" """Close and free the notmuch database if needed"""
if self._db is not None: if self._db is not None:
nmlib.notmuch_database_close(self._db) self._close(self._db)
def _get_user_default_db(self): def _get_user_default_db(self):
""" Reads a user's notmuch config and returns his db location """ Reads a user's notmuch config and returns his db location
@ -545,18 +579,22 @@ class Query(object):
"""notmuch_query_create""" """notmuch_query_create"""
_create = nmlib.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""" """notmuch_query_search_threads"""
_search_threads = nmlib.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""" """notmuch_query_search_messages"""
_search_messages = nmlib.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""" """notmuch_query_count_messages"""
_count_messages = nmlib.notmuch_query_count_messages _count_messages = nmlib.notmuch_query_count_messages
_count_messages.argtypes = [NotmuchQueryP]
_count_messages.restype = c_uint _count_messages.restype = c_uint
def __init__(self, db, querystr): def __init__(self, db, querystr):
@ -602,6 +640,10 @@ class Query(object):
raise NullPointerError raise NullPointerError
self._query = query_p 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): def set_sort(self, sort):
"""Set the sort order future results will be delivered in """Set the sort order future results will be delivered in
@ -609,7 +651,7 @@ class Query(object):
""" """
self._assert_query_is_initialized() self._assert_query_is_initialized()
self.sort = sort self.sort = sort
nmlib.notmuch_query_set_sort(self._query, sort) self._set_sort(self._query, sort)
def search_threads(self): def search_threads(self):
"""Execute a query for threads """Execute a query for threads
@ -661,10 +703,14 @@ class Query(object):
self._assert_query_is_initialized() self._assert_query_is_initialized()
return Query._count_messages(self._query) return Query._count_messages(self._query)
_destroy = nmlib.notmuch_query_destroy
_destroy.argtypes = [NotmuchQueryP]
_destroy.restype = None
def __del__(self): def __del__(self):
"""Close and free the Query""" """Close and free the Query"""
if self._query is not None: if self._query is not None:
nmlib.notmuch_query_destroy(self._query) self._destroy(self._query)
class Directory(object): class Directory(object):
@ -683,19 +729,23 @@ class Directory(object):
"""notmuch_directory_get_mtime""" """notmuch_directory_get_mtime"""
_get_mtime = nmlib.notmuch_directory_get_mtime _get_mtime = nmlib.notmuch_directory_get_mtime
_get_mtime.argtypes = [NotmuchDirectoryP]
_get_mtime.restype = c_long _get_mtime.restype = c_long
"""notmuch_directory_set_mtime""" """notmuch_directory_set_mtime"""
_set_mtime = nmlib.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""" """notmuch_directory_get_child_files"""
_get_child_files = nmlib.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""" """notmuch_directory_get_child_directories"""
_get_child_directories = nmlib.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): def _assert_dir_is_initialized(self):
"""Raises a NotmuchError(:attr:`STATUS`.NOT_INITIALIZED) if dir_p is None""" """Raises a NotmuchError(:attr:`STATUS`.NOT_INITIALIZED) if dir_p is None"""
@ -815,10 +865,14 @@ class Directory(object):
"""Object representation""" """Object representation"""
return "<notmuch Directory object '%s'>" % self._path return "<notmuch Directory object '%s'>" % self._path
_destroy = nmlib.notmuch_directory_destroy
_destroy.argtypes = [NotmuchDirectoryP]
_destroy.argtypes = None
def __del__(self): def __del__(self):
"""Close and free the Directory""" """Close and free the Directory"""
if self._dir_p is not None: if self._dir_p is not None:
nmlib.notmuch_directory_destroy(self._dir_p) self._destroy(self._dir_p)
class Filenames(object): class Filenames(object):
@ -826,6 +880,7 @@ class Filenames(object):
#notmuch_filenames_get #notmuch_filenames_get
_get = nmlib.notmuch_filenames_get _get = nmlib.notmuch_filenames_get
_get.argtypes = [NotmuchFilenamesP]
_get.restype = c_char_p _get.restype = c_char_p
def __init__(self, files_p, parent): def __init__(self, files_p, parent):
@ -844,16 +899,24 @@ class Filenames(object):
""" Make Filenames an iterator """ """ Make Filenames an iterator """
return self 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): def next(self):
if self._files_p is None: if self._files_p is None:
raise NotmuchError(STATUS.NOT_INITIALIZED) 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 self._files_p = None
raise StopIteration raise StopIteration
file = Filenames._get(self._files_p) file = Filenames._get(self._files_p)
nmlib.notmuch_filenames_move_to_next(self._files_p) self._move_to_next(self._files_p)
return file return file
def __len__(self): def __len__(self):
@ -872,13 +935,17 @@ class Filenames(object):
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotmuchError(STATUS.NOT_INITIALIZED)
i = 0 i = 0
while nmlib.notmuch_filenames_valid(self._files_p): while self._valid(self._files_p):
nmlib.notmuch_filenames_move_to_next(self._files_p) self._move_to_next(self._files_p)
i += 1 i += 1
self._files_p = None self._files_p = None
return i return i
_destroy = nmlib.notmuch_filenames_destroy
_destroy.argtypes = [NotmuchFilenamesP]
_destroy.restype = None
def __del__(self): def __del__(self):
"""Close and free Filenames""" """Close and free Filenames"""
if self._files_p is not None: 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>' Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>'
""" """
from ctypes import c_char_p 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): class Filenames(object):
@ -50,6 +51,7 @@ class Filenames(object):
#notmuch_filenames_get #notmuch_filenames_get
_get = nmlib.notmuch_filenames_get _get = nmlib.notmuch_filenames_get
_get.argtypes = [NotmuchFilenamesP]
_get.restype = c_char_p _get.restype = c_char_p
def __init__(self, files_p, parent): def __init__(self, files_p, parent):
@ -74,6 +76,14 @@ class Filenames(object):
#save reference to parent object so we keep it alive #save reference to parent object so we keep it alive
self._parent = parent 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): def as_generator(self):
"""Return generator of Filenames """Return generator of Filenames
@ -82,9 +92,9 @@ class Filenames(object):
if self._files is None: if self._files is None:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotmuchError(STATUS.NOT_INITIALIZED)
while nmlib.notmuch_filenames_valid(self._files): while self._valid(self._files):
yield Filenames._get(self._files) yield Filenames._get(self._files)
nmlib.notmuch_filenames_move_to_next(self._files) self._move_to_next(self._files)
self._files = None self._files = None
@ -101,7 +111,11 @@ class Filenames(object):
""" """
return "\n".join(self) return "\n".join(self)
_destroy = nmlib.notmuch_filenames_destroy
_destroy.argtypes = [NotmuchMessageP]
_destroy.restype = None
def __del__(self): def __del__(self):
"""Close and free the notmuch filenames""" """Close and free the notmuch filenames"""
if self._files is not None: 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 ctypes import c_char_p, c_void_p, c_long, c_uint, c_int
from datetime import date 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.tag import Tags
from notmuch.filename import Filenames from notmuch.filename import Filenames
import sys import sys
@ -92,10 +93,12 @@ class Messages(object):
#notmuch_messages_get #notmuch_messages_get
_get = nmlib.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 = 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): def __init__(self, msgs_p, parent=None):
""" """
@ -146,16 +149,24 @@ class Messages(object):
""" Make Messages an iterator """ """ Make Messages an iterator """
return self 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): def next(self):
if self._msgs is None: if self._msgs is None:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotmuchError(STATUS.NOT_INITIALIZED)
if not nmlib.notmuch_messages_valid(self._msgs): if not self._valid(self._msgs):
self._msgs = None self._msgs = None
raise StopIteration raise StopIteration
msg = Message(Messages._get(self._msgs), self) msg = Message(Messages._get(self._msgs), self)
nmlib.notmuch_messages_move_to_next(self._msgs) self._move_to_next(self._msgs)
return msg return msg
def __nonzero__(self): def __nonzero__(self):
@ -163,12 +174,16 @@ class Messages(object):
:return: True if there is at least one more thread in the :return: True if there is at least one more thread in the
Iterator, False if not.""" Iterator, False if not."""
return self._msgs is not None and \ 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): def __del__(self):
"""Close and free the notmuch Messages""" """Close and free the notmuch Messages"""
if self._msgs is not None: 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): def print_messages(self, format, indent=0, entire_thread=False):
"""Outputs messages as needed for 'notmuch show' to sys.stdout """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)""" """notmuch_message_get_filename (notmuch_message_t *message)"""
_get_filename = nmlib.notmuch_message_get_filename _get_filename = nmlib.notmuch_message_get_filename
_get_filename.argtypes = [NotmuchMessageP]
_get_filename.restype = c_char_p _get_filename.restype = c_char_p
"""return all filenames for a message""" """return all filenames for a message"""
_get_filenames = nmlib.notmuch_message_get_filenames _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""" """notmuch_message_get_flag"""
_get_flag = nmlib.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)""" """notmuch_message_get_message_id (notmuch_message_t *message)"""
_get_message_id = nmlib.notmuch_message_get_message_id _get_message_id = nmlib.notmuch_message_get_message_id
_get_message_id.argtypes = [NotmuchMessageP]
_get_message_id.restype = c_char_p _get_message_id.restype = c_char_p
"""notmuch_message_get_thread_id""" """notmuch_message_get_thread_id"""
_get_thread_id = nmlib.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 _get_thread_id.restype = c_char_p
"""notmuch_message_get_replies""" """notmuch_message_get_replies"""
_get_replies = nmlib.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)""" """notmuch_message_get_tags (notmuch_message_t *message)"""
_get_tags = nmlib.notmuch_message_get_tags _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 = nmlib.notmuch_message_get_date
_get_date.argtypes = [NotmuchMessageP]
_get_date.restype = c_long _get_date.restype = c_long
_get_header = nmlib.notmuch_message_get_header _get_header = nmlib.notmuch_message_get_header
_get_header.argtypes = [NotmuchMessageP, c_char_p]
_get_header.restype = c_char_p _get_header.restype = c_char_p
"""notmuch_status_t ..._maildir_flags_to_tags (notmuch_message_t *)""" """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 = nmlib.notmuch_message_tags_to_maildir_flags
_tags_to_maildir_flags.argtypes = [NotmuchMessageP]
_tags_to_maildir_flags.restype = c_int _tags_to_maildir_flags.restype = c_int
"""notmuch_status_t ..._tags_to_maildir_flags (notmuch_message_t *)""" """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 = nmlib.notmuch_message_maildir_flags_to_tags
_maildir_flags_to_tags.argtypes = [NotmuchMessageP]
_maildir_flags_to_tags.restype = c_int _maildir_flags_to_tags.restype = c_int
#Constants: Flags that can be set/get with set_flag #Constants: Flags that can be set/get with set_flag
@ -450,7 +481,7 @@ class Message(object):
""" """
if self._msg is None: if self._msg is None:
raise NotmuchError(STATUS.NOT_INITIALIZED) 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): def get_tags(self):
"""Returns the message tags """Returns the message tags
@ -470,6 +501,10 @@ class Message(object):
raise NotmuchError(STATUS.NULL_POINTER) raise NotmuchError(STATUS.NULL_POINTER)
return Tags(tags_p, self) 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): def add_tag(self, tag, sync_maildir_flags=False):
"""Adds a tag to the given message """Adds a tag to the given message
@ -504,7 +539,7 @@ class Message(object):
if self._msg is None: if self._msg is None:
raise NotmuchError(STATUS.NOT_INITIALIZED) 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 # bail out on failure
if status != STATUS.SUCCESS: if status != STATUS.SUCCESS:
@ -514,6 +549,10 @@ class Message(object):
self.tags_to_maildir_flags() self.tags_to_maildir_flags()
return STATUS.SUCCESS 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): def remove_tag(self, tag, sync_maildir_flags=False):
"""Removes a tag from the given message """Removes a tag from the given message
@ -548,7 +587,7 @@ class Message(object):
if self._msg is None: if self._msg is None:
raise NotmuchError(STATUS.NOT_INITIALIZED) 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 # bail out on error
if status != STATUS.SUCCESS: if status != STATUS.SUCCESS:
raise NotmuchError(status) raise NotmuchError(status)
@ -557,6 +596,10 @@ class Message(object):
self.tags_to_maildir_flags() self.tags_to_maildir_flags()
return STATUS.SUCCESS 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): def remove_all_tags(self, sync_maildir_flags=False):
"""Removes all tags from the given message. """Removes all tags from the given message.
@ -585,7 +628,7 @@ class Message(object):
if self._msg is None: if self._msg is None:
raise NotmuchError(STATUS.NOT_INITIALIZED) 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 # bail out on error
if status != STATUS.SUCCESS: if status != STATUS.SUCCESS:
@ -595,6 +638,10 @@ class Message(object):
self.tags_to_maildir_flags() self.tags_to_maildir_flags()
return STATUS.SUCCESS return STATUS.SUCCESS
_freeze = nmlib.notmuch_message_freeze
_freeze.argtypes = [NotmuchMessageP]
_freeze.restype = c_uint
def freeze(self): def freeze(self):
"""Freezes the current state of 'message' within the database """Freezes the current state of 'message' within the database
@ -639,7 +686,7 @@ class Message(object):
if self._msg is None: if self._msg is None:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotmuchError(STATUS.NOT_INITIALIZED)
status = nmlib.notmuch_message_freeze(self._msg) status = self._freeze(self._msg)
if STATUS.SUCCESS == status: if STATUS.SUCCESS == status:
# return on success # return on success
@ -647,6 +694,10 @@ class Message(object):
raise NotmuchError(status) raise NotmuchError(status)
_thaw = nmlib.notmuch_message_thaw
_thaw.argtypes = [NotmuchMessageP]
_thaw.restype = c_uint
def thaw(self): def thaw(self):
"""Thaws the current 'message' """Thaws the current 'message'
@ -674,7 +725,7 @@ class Message(object):
if self._msg is None: if self._msg is None:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotmuchError(STATUS.NOT_INITIALIZED)
status = nmlib.notmuch_message_thaw(self._msg) status = self._thaw(self._msg)
if STATUS.SUCCESS == status: if STATUS.SUCCESS == status:
# return on success # return on success
@ -896,7 +947,11 @@ class Message(object):
res = cmp(list(self.get_filenames()), list(other.get_filenames())) res = cmp(list(self.get_filenames()), list(other.get_filenames()))
return res return res
_destroy = nmlib.notmuch_message_destroy
_destroy.argtypes = [NotmuchMessageP]
_destroy.restype = None
def __del__(self): def __del__(self):
"""Close and free the notmuch Message""" """Close and free the notmuch Message"""
if self._msg is not None: 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>' Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>'
""" """
from ctypes import c_char_p from ctypes import c_char_p
from notmuch.globals import nmlib, STATUS, NotmuchError from notmuch.globals import nmlib, STATUS, NotmuchError, NotmuchTagsP
class Tags(object): class Tags(object):
@ -50,6 +50,7 @@ class Tags(object):
#notmuch_tags_get #notmuch_tags_get
_get = nmlib.notmuch_tags_get _get = nmlib.notmuch_tags_get
_get.argtypes = [NotmuchTagsP]
_get.restype = c_char_p _get.restype = c_char_p
def __init__(self, tags_p, parent=None): def __init__(self, tags_p, parent=None):
@ -80,14 +81,22 @@ class Tags(object):
""" Make Tags an iterator """ """ Make Tags an iterator """
return self 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): def next(self):
if self._tags is None: if self._tags is None:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotmuchError(STATUS.NOT_INITIALIZED)
if not nmlib.notmuch_tags_valid(self._tags): if not self._valid(self._tags):
self._tags = None self._tags = None
raise StopIteration raise StopIteration
tag = Tags._get(self._tags).decode('UTF-8') tag = Tags._get(self._tags).decode('UTF-8')
nmlib.notmuch_tags_move_to_next(self._tags) self._move_to_next(self._tags)
return tag return tag
def __nonzero__(self): def __nonzero__(self):
@ -99,7 +108,7 @@ class Tags(object):
:returns: True if the Tags() iterator has at least one more Tag :returns: True if the Tags() iterator has at least one more Tag
left.""" left."""
return nmlib.notmuch_tags_valid(self._tags) > 0 return self._valid(self._tags) > 0
def __str__(self): def __str__(self):
"""The str() representation of Tags() is a space separated list of tags """The str() representation of Tags() is a space separated list of tags
@ -112,7 +121,11 @@ class Tags(object):
""" """
return " ".join(self) return " ".join(self)
_destroy = nmlib.notmuch_tags_destroy
_destroy.argtypes = [NotmuchTagsP]
_destroy.restype = None
def __del__(self): def __del__(self):
"""Close and free the notmuch tags""" """Close and free the notmuch tags"""
if self._tags is not None: 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>' Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>'
""" """
from ctypes import c_char_p, c_void_p, c_long from ctypes import c_char_p, c_void_p, c_long, c_int
from notmuch.globals import nmlib, STATUS, NotmuchError from notmuch.globals import (nmlib, STATUS,
NotmuchError, NotmuchThreadP, NotmuchThreadsP, NotmuchMessagesP,
NotmuchTagsP,)
from notmuch.message import Messages from notmuch.message import Messages
from notmuch.tag import Tags from notmuch.tag import Tags
from datetime import date from datetime import date
@ -75,7 +77,8 @@ class Threads(object):
#notmuch_threads_get #notmuch_threads_get
_get = nmlib.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): def __init__(self, threads_p, parent=None):
""" """
@ -105,16 +108,24 @@ class Threads(object):
""" Make Threads an iterator """ """ Make Threads an iterator """
return self 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): def next(self):
if self._threads is None: if self._threads is None:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotmuchError(STATUS.NOT_INITIALIZED)
if not nmlib.notmuch_threads_valid(self._threads): if not self._valid(self._threads):
self._threads = None self._threads = None
raise StopIteration raise StopIteration
thread = Thread(Threads._get(self._threads), self) thread = Thread(Threads._get(self._threads), self)
nmlib.notmuch_threads_move_to_next(self._threads) self._move_to_next(self._threads)
return thread return thread
def __len__(self): def __len__(self):
@ -134,8 +145,8 @@ class Threads(object):
i = 0 i = 0
# returns 'bool'. On out-of-memory it returns None # returns 'bool'. On out-of-memory it returns None
while nmlib.notmuch_threads_valid(self._threads): while self._valid(self._threads):
nmlib.notmuch_threads_move_to_next(self._threads) self._move_to_next(self._threads)
i += 1 i += 1
# reset self._threads to mark as "exhausted" # reset self._threads to mark as "exhausted"
self._threads = None self._threads = None
@ -153,12 +164,16 @@ class Threads(object):
Iterator, False if not. None on a "Out-of-memory" error. Iterator, False if not. None on a "Out-of-memory" error.
""" """
return self._threads is not None and \ 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): def __del__(self):
"""Close and free the notmuch Threads""" """Close and free the notmuch Threads"""
if self._threads is not None: if self._threads is not None:
nmlib.notmuch_messages_destroy(self._threads) self._destroy(self._threads)
class Thread(object): class Thread(object):
@ -166,29 +181,36 @@ class Thread(object):
"""notmuch_thread_get_thread_id""" """notmuch_thread_get_thread_id"""
_get_thread_id = nmlib.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 _get_thread_id.restype = c_char_p
"""notmuch_thread_get_authors""" """notmuch_thread_get_authors"""
_get_authors = nmlib.notmuch_thread_get_authors _get_authors = nmlib.notmuch_thread_get_authors
_get_authors.argtypes = [NotmuchThreadP]
_get_authors.restype = c_char_p _get_authors.restype = c_char_p
"""notmuch_thread_get_subject""" """notmuch_thread_get_subject"""
_get_subject = nmlib.notmuch_thread_get_subject _get_subject = nmlib.notmuch_thread_get_subject
_get_subject.argtypes = [NotmuchThreadP]
_get_subject.restype = c_char_p _get_subject.restype = c_char_p
"""notmuch_thread_get_toplevel_messages""" """notmuch_thread_get_toplevel_messages"""
_get_toplevel_messages = nmlib.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 = nmlib.notmuch_thread_get_newest_date
_get_newest_date.argtypes = [NotmuchThreadP]
_get_newest_date.restype = c_long _get_newest_date.restype = c_long
_get_oldest_date = nmlib.notmuch_thread_get_oldest_date _get_oldest_date = nmlib.notmuch_thread_get_oldest_date
_get_oldest_date.argtypes = [NotmuchThreadP]
_get_oldest_date.restype = c_long _get_oldest_date.restype = c_long
"""notmuch_thread_get_tags""" """notmuch_thread_get_tags"""
_get_tags = nmlib.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): def __init__(self, thread_p, parent=None):
""" """
@ -225,6 +247,11 @@ class Thread(object):
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotmuchError(STATUS.NOT_INITIALIZED)
return Thread._get_thread_id(self._thread) 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): def get_total_messages(self):
"""Get the total number of messages in 'thread' """Get the total number of messages in 'thread'
@ -236,7 +263,7 @@ class Thread(object):
""" """
if self._thread is None: if self._thread is None:
raise NotmuchError(STATUS.NOT_INITIALIZED) 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): def get_toplevel_messages(self):
"""Returns a :class:`Messages` iterator for the top-level messages in """Returns a :class:`Messages` iterator for the top-level messages in
@ -267,6 +294,10 @@ class Thread(object):
return Messages(msgs_p, self) 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): def get_matched_messages(self):
"""Returns the number of messages in 'thread' that matched the query """Returns the number of messages in 'thread' that matched the query
@ -278,7 +309,7 @@ class Thread(object):
""" """
if self._thread is None: if self._thread is None:
raise NotmuchError(STATUS.NOT_INITIALIZED) 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): def get_authors(self):
"""Returns the authors of 'thread' """Returns the authors of 'thread'
@ -387,7 +418,11 @@ class Thread(object):
thread['subject'], thread['subject'],
thread['tags']) thread['tags'])
_destroy = nmlib.notmuch_thread_destroy
_destroy.argtypes = [NotmuchThreadP]
_destroy.restype = None
def __del__(self): def __del__(self):
"""Close and free the notmuch Thread""" """Close and free the notmuch Thread"""
if self._thread is not None: if self._thread is not None:
nmlib.notmuch_thread_destroy(self._thread) self._destroy(self._thread)