aboutsummaryrefslogtreecommitdiff
path: root/bindings/python
diff options
context:
space:
mode:
authorDavid Bremner <bremner@debian.org>2012-01-27 18:04:44 -0400
committerDavid Bremner <bremner@debian.org>2012-01-27 18:04:44 -0400
commitbcd44490684965082e5921d0fc71064af3170987 (patch)
tree12506f79f6792236b95ef0d6a19ab5113bb4587d /bindings/python
parentbfb7feb1f51ae348503046823d72348621e34d08 (diff)
parentffce9b7c25b9b44ec026b67d96e44cae09c99efe (diff)
Merge commit 'debian/0.11-1' into squeeze-backports
Conflicts: debian/changelog
Diffstat (limited to 'bindings/python')
-rw-r--r--bindings/python/docs/source/index.rst17
-rw-r--r--bindings/python/notmuch/__init__.py2
-rw-r--r--bindings/python/notmuch/database.py216
-rw-r--r--bindings/python/notmuch/filename.py25
-rw-r--r--bindings/python/notmuch/globals.py88
-rw-r--r--bindings/python/notmuch/message.py143
-rw-r--r--bindings/python/notmuch/tag.py39
-rw-r--r--bindings/python/notmuch/thread.py99
-rw-r--r--bindings/python/notmuch/version.py2
9 files changed, 449 insertions, 182 deletions
diff --git a/bindings/python/docs/source/index.rst b/bindings/python/docs/source/index.rst
index 73d2a3b0..f7d3d605 100644
--- a/bindings/python/docs/source/index.rst
+++ b/bindings/python/docs/source/index.rst
@@ -138,10 +138,10 @@ More information on specific topics can be found on the following pages:
.. method:: __len__()
- .. warning:: :meth:`__len__` was removed in version 0.6 as it exhausted
- the iterator and broke list(Messages()). Use the
- :meth:`Query.count_messages` function or use
- `len(list(msgs))`.
+ .. warning::
+
+ :meth:`__len__` was removed in version 0.6 as it exhausted the iterator and broke
+ list(Messages()). Use the :meth:`Query.count_messages` function or use `len(list(msgs))`.
:class:`Message` -- A single message
----------------------------------------
@@ -205,10 +205,11 @@ More information on specific topics can be found on the following pages:
.. method:: __len__
- .. warning:: :meth:`__len__` was removed in version 0.6 as it
- exhausted the iterator and broke list(Tags()). Use
- :meth:`len(list(msgs))` instead if you need to know the
- number of tags.
+ .. warning::
+
+ :meth:`__len__` was removed in version 0.6 as it exhausted the iterator and broke
+ list(Tags()). Use :meth:`len(list(msgs))` instead if you need to know the number of
+ tags.
.. automethod:: __str__
diff --git a/bindings/python/notmuch/__init__.py b/bindings/python/notmuch/__init__.py
index 539afedf..f3ff9874 100644
--- a/bindings/python/notmuch/__init__.py
+++ b/bindings/python/notmuch/__init__.py
@@ -69,7 +69,7 @@ from notmuch.globals import (
TagTooLongError,
UnbalancedFreezeThawError,
UnbalancedAtomicError,
- NotInitializedError
+ NotInitializedError,
)
from notmuch.version import __VERSION__
__LICENSE__ = "GPL v3+"
diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py
index f4bc53e0..7923f768 100644
--- a/bindings/python/notmuch/database.py
+++ b/bindings/python/notmuch/database.py
@@ -18,13 +18,16 @@ 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_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, 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
+
class Database(object):
"""The :class:`Database` is the highest-level object that notmuch
provides. It references a notmuch database, and can be opened in
@@ -37,16 +40,19 @@ class Database(object):
:exc:`XapianError` as the underlying database has been
modified. Close and reopen the database to continue working with it.
- .. note:: Any function in this class can and will throw an
- :exc:`NotInitializedError` if the database was not
- intitialized properly.
+ .. note::
+
+ Any function in this class can and will throw an
+ :exc:`NotInitializedError` if the database was not intitialized
+ properly.
+
+ .. note::
- .. note:: Do remember that as soon as we tear down (e.g. via `del
- db`) this object, all underlying derived objects such as
- queries, threads, messages, tags etc will be freed by the
- underlying library as well. Accessing these objects will lead
- to segfaults and other unexpected behavior. See above for
- more details.
+ Do remember that as soon as we tear down (e.g. via `del db`) this
+ object, all underlying derived objects such as queries, threads,
+ messages, tags etc will be freed by the underlying library as well.
+ Accessing these objects will lead to segfaults and other unexpected
+ behavior. See above for more details.
"""
_std_db_path = None
"""Class attribute to cache user's default database"""
@@ -56,37 +62,50 @@ 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
@@ -164,8 +183,8 @@ class Database(object):
:param status: Open the database in read-only or read-write mode
:type status: :attr:`MODE`
:returns: Nothing
- :exception: Raises :exc:`NotmuchError` in case
- of any failure (possibly after printing an error message on stderr).
+ :exception: Raises :exc:`NotmuchError` in case of any failure
+ (possibly after printing an error message on stderr).
"""
res = Database._open(_str(path), mode)
@@ -186,6 +205,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 +220,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 +242,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
@@ -229,18 +256,20 @@ class Database(object):
neither begin nor end necessarily flush modifications to disk.
:returns: :attr:`STATUS`.SUCCESS or raises
-
- :exception: :exc:`NotmuchError`:
- :attr:`STATUS`.XAPIAN_EXCEPTION
- Xapian exception occurred; atomic section not entered.
+ :exception: :exc:`NotmuchError`: :attr:`STATUS`.XAPIAN_EXCEPTION
+ Xapian exception occurred; atomic section not entered.
*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 +287,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
@@ -267,13 +296,15 @@ class Database(object):
"""Returns a :class:`Directory` of path,
(creating it if it does not exist(?))
- .. warning:: This call needs a writeable database in
- :attr:`Database.MODE`.READ_WRITE mode. The underlying library will exit the
- program if this method is used on a read-only database!
+ .. warning::
+
+ This call needs a writeable database in
+ :attr:`Database.MODE`.READ_WRITE mode. The underlying library will
+ exit the program if this method is used on a read-only database!
:param path: An unicode string containing the path relative to the path
- of database (see :meth:`get_path`), or else should be an absolute path
- with initial components that match the path of 'database'.
+ of database (see :meth:`get_path`), or else should be an absolute
+ path with initial components that match the path of 'database'.
:returns: :class:`Directory` or raises an exception.
:exception:
:exc:`NotmuchError` with :attr:`STATUS`.FILE_ERROR
@@ -299,6 +330,11 @@ 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
@@ -349,10 +385,8 @@ class Database(object):
be added.
"""
self._assert_db_is_initialized()
- msg_p = c_void_p()
- status = nmlib.notmuch_database_add_message(self._db,
- _str(filename),
- byref(msg_p))
+ msg_p = NotmuchMessageP()
+ 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 +398,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 +430,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
@@ -416,7 +453,7 @@ class Database(object):
the database was not intitialized.
"""
self._assert_db_is_initialized()
- msg_p = c_void_p()
+ msg_p = NotmuchMessageP()
status = Database._find_message(self._db, _str(msgid), byref(msg_p))
if status != STATUS.SUCCESS:
raise NotmuchError(status)
@@ -425,10 +462,11 @@ class Database(object):
def find_message_by_filename(self, filename):
"""Find a message with the given filename
- .. warning:: This call needs a writeable database in
- :attr:`Database.MODE`.READ_WRITE mode. The underlying library will
- exit the program if this method is used on a read-only
- database!
+ .. warning::
+
+ This call needs a writeable database in
+ :attr:`Database.MODE`.READ_WRITE mode. The underlying library will
+ exit the program if this method is used on a read-only database!
:returns: If the database contains a message with the given
filename, then a class:`Message:` is returned. This
@@ -449,7 +487,7 @@ class Database(object):
*Added in notmuch 0.9*"""
self._assert_db_is_initialized()
- msg_p = c_void_p()
+ msg_p = NotmuchMessageP()
status = Database._find_message_by_filename(self._db, _str(filename),
byref(msg_p))
if status != STATUS.SUCCESS:
@@ -460,7 +498,8 @@ class Database(object):
"""Returns :class:`Tags` with a list of all tags found in the database
:returns: :class:`Tags`
- :execption: :exc:`NotmuchError` with :attr:`STATUS`.NULL_POINTER on error
+ :execption: :exc:`NotmuchError` with :attr:`STATUS`.NULL_POINTER
+ on error
"""
self._assert_db_is_initialized()
tags_p = Database._get_all_tags(self._db)
@@ -491,10 +530,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 +588,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 +649,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 +660,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 +712,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,22 +738,27 @@ 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"""
+ """Raises a NotmuchError(:attr:`STATUS`.NOT_INITIALIZED)
+ if dir_p is None"""
if self._dir_p is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
@@ -735,10 +795,12 @@ class Directory(object):
and know that it only needs to add files if the mtime of the
directory and files are newer than the stored timestamp.
- .. note:: :meth:`get_mtime` function does not allow the caller
- to distinguish a timestamp of 0 from a non-existent
- timestamp. So don't store a timestamp of 0 unless you are
- comfortable with that.
+ .. note::
+
+ :meth:`get_mtime` function does not allow the caller to
+ distinguish a timestamp of 0 from a non-existent timestamp. So
+ don't store a timestamp of 0 unless you are comfortable with
+ that.
:param mtime: A (time_t) timestamp
:returns: Nothing on success, raising an exception on failure.
@@ -815,10 +877,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 +892,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,41 +911,56 @@ 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):
"""len(:class:`Filenames`) returns the number of contained files
- .. note:: As this iterates over the files, we will not be able to
- iterate over them again! So this will fail::
+ .. note::
+
+ As this iterates over the files, we will not be able to
+ iterate over them again! So this will fail::
#THIS FAILS
files = Database().get_directory('').get_child_files()
- if len(files) > 0: #this 'exhausts' msgs
- # next line raises NotmuchError(:attr:`STATUS`.NOT_INITIALIZED)!!!
+ if len(files) > 0: # this 'exhausts' msgs
+ # next line raises
+ # NotmuchError(:attr:`STATUS`.NOT_INITIALIZED)
for file in files: print file
"""
if self._files_p is None:
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)
diff --git a/bindings/python/notmuch/filename.py b/bindings/python/notmuch/filename.py
index de4d785a..a7cd7e63 100644
--- a/bindings/python/notmuch/filename.py
+++ b/bindings/python/notmuch/filename.py
@@ -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, 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,13 +92,16 @@ 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
def __str__(self):
+ return unicode(self).encode('utf-8')
+
+ def __unicode__(self):
"""Represent Filenames() as newline-separated list of full paths
.. note:: As this iterates over the filenames, we will not be
@@ -101,7 +114,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)
diff --git a/bindings/python/notmuch/globals.py b/bindings/python/notmuch/globals.py
index de1db161..54a49b2d 100644
--- a/bindings/python/notmuch/globals.py
+++ b/bindings/python/notmuch/globals.py
@@ -17,8 +17,7 @@ along with notmuch. If not, see <http://www.gnu.org/licenses/>.
Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>'
"""
-from ctypes import CDLL, c_char_p, c_int
-from ctypes.util import find_library
+from ctypes import CDLL, c_char_p, c_int, Structure, POINTER
#-----------------------------------------------------------------------------
#package-global instance of the notmuch library
@@ -49,11 +48,11 @@ class Status(Enum):
@classmethod
def status2str(self, status):
- """Get a string representation of a notmuch_status_t value."""
+ """Get a (unicode) string representation of a notmuch_status_t value."""
# define strings for custom error messages
if status == STATUS.NOT_INITIALIZED:
- return "Operation on uninitialized object impossible."
- return str(Status._status2str(status))
+ return u"Operation on uninitialized object impossible."
+ return unicode(Status._status2str(status))
STATUS = Status(['SUCCESS',
'OUT_OF_MEMORY',
@@ -89,6 +88,7 @@ Invoke the class method `notmuch.STATUS.status2str` with a status value as
argument to receive a human readable string"""
STATUS.__name__ = 'STATUS'
+
class NotmuchError(Exception):
"""Is initiated with a (notmuch.STATUS[, message=None]). It will not
return an instance of the class NotmuchError, but a derived instance
@@ -97,7 +97,8 @@ class NotmuchError(Exception):
@classmethod
def get_exc_subclass(cls, status):
- """Returns a fine grained Exception() type,detailing the error status"""
+ """Returns a fine grained Exception() type,
+ detailing the error status"""
subclasses = {
STATUS.OUT_OF_MEMORY: OutOfMemoryError,
STATUS.READ_ONLY_DATABASE: ReadOnlyDatabaseError,
@@ -109,7 +110,7 @@ class NotmuchError(Exception):
STATUS.TAG_TOO_LONG: TagTooLongError,
STATUS.UNBALANCED_FREEZE_THAW: UnbalancedFreezeThawError,
STATUS.UNBALANCED_ATOMIC: UnbalancedAtomicError,
- STATUS.NOT_INITIALIZED: NotInitializedError
+ STATUS.NOT_INITIALIZED: NotInitializedError,
}
assert 0 < status <= len(subclasses)
return subclasses[status]
@@ -125,7 +126,7 @@ class NotmuchError(Exception):
# no 'status' or cls is subclass already, return 'cls' instance
if not status or cls != NotmuchError:
return super(NotmuchError, cls).__new__(cls)
- subclass = cls.get_exc_subclass(status) # which class to use?
+ subclass = cls.get_exc_subclass(status) # which class to use?
return subclass.__new__(subclass, *args, **kwargs)
def __init__(self, status=None, message=None):
@@ -133,35 +134,59 @@ class NotmuchError(Exception):
self.message = message
def __str__(self):
+ return unicode(self).encode('utf-8')
+
+ def __unicode__(self):
if self.message is not None:
return self.message
elif self.status is not None:
return STATUS.status2str(self.status)
else:
- return 'Unknown error'
+ return u'Unknown error'
+
# List of Subclassed exceptions that correspond to STATUS values and are
# subclasses of NotmuchError.
class OutOfMemoryError(NotmuchError):
status = STATUS.OUT_OF_MEMORY
+
+
class ReadOnlyDatabaseError(NotmuchError):
status = STATUS.READ_ONLY_DATABASE
+
+
class XapianError(NotmuchError):
status = STATUS.XAPIAN_EXCEPTION
+
+
class FileError(NotmuchError):
status = STATUS.FILE_ERROR
+
+
class FileNotEmailError(NotmuchError):
status = STATUS.FILE_NOT_EMAIL
+
+
class DuplicateMessageIdError(NotmuchError):
status = STATUS.DUPLICATE_MESSAGE_ID
+
+
class NullPointerError(NotmuchError):
status = STATUS.NULL_POINTER
+
+
class TagTooLongError(NotmuchError):
status = STATUS.TAG_TOO_LONG
+
+
class UnbalancedFreezeThawError(NotmuchError):
status = STATUS.UNBALANCED_FREEZE_THAW
+
+
class UnbalancedAtomicError(NotmuchError):
status = STATUS.UNBALANCED_ATOMIC
+
+
class NotInitializedError(NotmuchError):
"""Derived from NotmuchError, this occurs if the underlying data
structure (e.g. database is not initialized (yet) or an iterator has
@@ -170,7 +195,6 @@ class NotInitializedError(NotmuchError):
status = STATUS.NOT_INITIALIZED
-
def _str(value):
"""Ensure a nicely utf-8 encoded string to pass to libnotmuch
@@ -182,3 +206,47 @@ def _str(value):
return value.encode('UTF-8')
return value
+
+class NotmuchDatabaseS(Structure):
+ pass
+NotmuchDatabaseP = POINTER(NotmuchDatabaseS)
+
+
+class NotmuchQueryS(Structure):
+ pass
+NotmuchQueryP = POINTER(NotmuchQueryS)
+
+
+class NotmuchThreadsS(Structure):
+ pass
+NotmuchThreadsP = POINTER(NotmuchThreadsS)
+
+
+class NotmuchThreadS(Structure):
+ pass
+NotmuchThreadP = POINTER(NotmuchThreadS)
+
+
+class NotmuchMessagesS(Structure):
+ pass
+NotmuchMessagesP = POINTER(NotmuchMessagesS)
+
+
+class NotmuchMessageS(Structure):
+ pass
+NotmuchMessageP = POINTER(NotmuchMessageS)
+
+
+class NotmuchTagsS(Structure):
+ pass
+NotmuchTagsP = POINTER(NotmuchTagsS)
+
+
+class NotmuchDirectoryS(Structure):
+ pass
+NotmuchDirectoryP = POINTER(NotmuchDirectoryS)
+
+
+class NotmuchFilenamesS(Structure):
+ pass
+NotmuchFilenamesP = POINTER(NotmuchFilenamesS)
diff --git a/bindings/python/notmuch/message.py b/bindings/python/notmuch/message.py
index 4bf90c22..ce8e7181 100644
--- a/bindings/python/notmuch/message.py
+++ b/bindings/python/notmuch/message.py
@@ -19,14 +19,14 @@ 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_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
import email
-import types
try:
import simplejson as json
except ImportError:
@@ -92,10 +92,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):
"""
@@ -125,10 +127,12 @@ class Messages(object):
"""Return the unique :class:`Tags` in the contained messages
:returns: :class:`Tags`
- :exceptions: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if not inited
+ :exceptions: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if not init'ed
- .. note:: :meth:`collect_tags` will iterate over the messages and
- therefore will not allow further iterations.
+ .. note::
+
+ :meth:`collect_tags` will iterate over the messages and therefore
+ will not allow further iterations.
"""
if self._msgs is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
@@ -146,16 +150,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 +175,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 +251,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
@@ -328,14 +360,15 @@ class Message(object):
"""Gets all direct replies to this message as :class:`Messages`
iterator
- .. note:: This call only makes sense if 'message' was
- ultimately obtained from a :class:`Thread` object, (such as
- by coming directly from the result of calling
- :meth:`Thread.get_toplevel_messages` or by any number of
- subsequent calls to :meth:`get_replies`). If this message was
- obtained through some non-thread means, (such as by a call
- to :meth:`Query.search_messages`), then this function will
- return `None`.
+ .. note::
+
+ This call only makes sense if 'message' was ultimately obtained from
+ a :class:`Thread` object, (such as by coming directly from the
+ result of calling :meth:`Thread.get_toplevel_messages` or by any
+ number of subsequent calls to :meth:`get_replies`). If this message
+ was obtained through some non-thread means, (such as by a call to
+ :meth:`Query.search_messages`), then this function will return
+ `None`.
:returns: :class:`Messages` or `None` if there are no replies to
this message.
@@ -394,7 +427,7 @@ class Message(object):
header = Message._get_header(self._msg, header)
if header == None:
raise NotmuchError(STATUS.NULL_POINTER)
- return header.decode('UTF-8')
+ return header.decode('UTF-8', errors='ignore')
def get_filename(self):
"""Returns the file path of the message file
@@ -450,7 +483,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 +503,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 +541,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 +551,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 +589,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 +598,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 +630,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,12 +640,16 @@ 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
This means that changes to the message state, (via :meth:`add_tag`,
:meth:`remove_tag`, and :meth:`remove_all_tags`), will not be
- committed to the database until the message is :meth:`thaw`ed.
+ committed to the database until the message is :meth:`thaw` ed.
Multiple calls to freeze/thaw are valid and these calls will
"stack". That is there must be as many calls to thaw as to freeze
@@ -639,7 +688,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 +696,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 +727,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
@@ -705,11 +758,11 @@ class Message(object):
not work yet, as the modified tags have not been committed yet
to the database.
- :returns: a :class:`STATUS`. In short, you want to see
+ :returns: a :class:`STATUS` value. In short, you want to see
notmuch.STATUS.SUCCESS here. See there for details."""
if self._msg is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
- status = Message._tags_to_maildir_flags(self._msg)
+ return Message._tags_to_maildir_flags(self._msg)
def maildir_flags_to_tags(self):
"""Synchronize file Maildir flags to notmuch tags
@@ -736,19 +789,21 @@ class Message(object):
notmuch.STATUS.SUCCESS here. See there for details."""
if self._msg is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
- status = Message._tags_to_maildir_flags(self._msg)
+ return Message._tags_to_maildir_flags(self._msg)
def __repr__(self):
"""Represent a Message() object by str()"""
return self.__str__()
def __str__(self):
- """A message() is represented by a 1-line summary"""
- msg = {}
- msg['from'] = self.get_header('from')
- msg['tags'] = self.get_tags()
- msg['date'] = date.fromtimestamp(self.get_date())
- return "%(from)s (%(date)s) (%(tags)s)" % (msg)
+ return unicode(self).encode('utf-8')
+
+ def __unicode__(self):
+ format = "%s (%s) (%s)"
+ return format % (self.get_header('from'),
+ self.get_tags(),
+ date.fromtimestamp(self.get_date()),
+ )
def get_message_parts(self):
"""Output like notmuch show"""
@@ -896,7 +951,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)
diff --git a/bindings/python/notmuch/tag.py b/bindings/python/notmuch/tag.py
index 50e3686b..2fb7d328 100644
--- a/bindings/python/notmuch/tag.py
+++ b/bindings/python/notmuch/tag.py
@@ -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,20 +108,28 @@ 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
+ return unicode(self).encode('utf-8')
+
+ def __unicode__(self):
+ """string representation of :class:`Tags`: a space separated list of tags
- .. note:: As this iterates over the tags, we will not be able
- to iterate over them again (as in retrieve them)! If
- the tags have been exhausted already, this will raise a
- :exc:`NotmuchError` STATUS.NOT_INITIALIZED on
- subsequent attempts.
+ .. note::
+
+ As this iterates over the tags, we will not be able to iterate over
+ them again (as in retrieve them)! If the tags have been exhausted
+ already, this will raise a :exc:`NotmuchError`
+ STATUS.NOT_INITIALIZED on subsequent attempts.
"""
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)
diff --git a/bindings/python/notmuch/thread.py b/bindings/python/notmuch/thread.py
index 5e08eb31..5058846d 100644
--- a/bindings/python/notmuch/thread.py
+++ b/bindings/python/notmuch/thread.py
@@ -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_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,10 @@ 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 +262,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 +293,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 +308,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'
@@ -295,7 +325,7 @@ class Thread(object):
authors = Thread._get_authors(self._thread)
if authors is None:
return None
- return authors.decode('UTF-8')
+ return authors.decode('UTF-8', errors='ignore')
def get_subject(self):
"""Returns the Subject of 'thread'
@@ -308,7 +338,7 @@ class Thread(object):
subject = Thread._get_subject(self._thread)
if subject is None:
return None
- return subject.decode('UTF-8')
+ return subject.decode('UTF-8', errors='ignore')
def get_newest_date(self):
"""Returns time_t of the newest message date
@@ -362,32 +392,25 @@ class Thread(object):
return Tags(tags_p, self)
def __str__(self):
- """A str(Thread()) is represented by a 1-line summary"""
- thread = {}
- thread['id'] = self.get_thread_id()
+ return unicode(self).encode('utf-8')
+
+ def __unicode__(self):
+ frm = "thread:%s %12s [%d/%d] %s; %s (%s)"
- ###TODO: How do we find out the current sort order of Threads?
- ###Add a "sort" attribute to the Threads() object?
- #if (sort == NOTMUCH_SORT_OLDEST_FIRST)
- # date = notmuch_thread_get_oldest_date (thread);
- #else
- # date = notmuch_thread_get_newest_date (thread);
- thread['date'] = date.fromtimestamp(self.get_newest_date())
- thread['matched'] = self.get_matched_messages()
- thread['total'] = self.get_total_messages()
- thread['authors'] = self.get_authors()
- thread['subject'] = self.get_subject()
- thread['tags'] = self.get_tags()
+ return frm % (self.get_thread_id(),
+ date.fromtimestamp(self.get_newest_date()),
+ self.get_matched_messages(),
+ self.get_total_messages(),
+ self.get_authors(),
+ self.get_subject(),
+ self.get_tags(),
+ )
- return "thread:%s %12s [%d/%d] %s; %s (%s)" % (thread['id'],
- thread['date'],
- thread['matched'],
- thread['total'],
- thread['authors'],
- 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)
diff --git a/bindings/python/notmuch/version.py b/bindings/python/notmuch/version.py
index fd414b0a..59c396fe 100644
--- a/bindings/python/notmuch/version.py
+++ b/bindings/python/notmuch/version.py
@@ -1,2 +1,2 @@
# this file should be kept in sync with ../../../version
-__VERSION__ = '0.10.2'
+__VERSION__ = '0.11'