From 8015cbff263606f009b5750d23b28ee332c25db8 Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Sun, 22 Jan 2012 06:14:57 +0100 Subject: python: fix error handling Before 3434d1940 the return values of libnotmuch functions were declared as c_void_p and the code checking for errors compared the returned value to None, which is the ctypes equivalent of a NULL pointer. But said commit wrapped all the data types in python classes and the semantic changed in a subtle way. If a function returns NULL, the wrapped python value is falsish, but no longer equal to None. --- bindings/python/notmuch/database.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'bindings/python/notmuch/database.py') diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py index 24da8e9..6238b28 100644 --- a/bindings/python/notmuch/database.py +++ b/bindings/python/notmuch/database.py @@ -168,7 +168,7 @@ class Database(object): res = Database._create(_str(path), Database.MODE.READ_WRITE) - if res is None: + if not res: raise NotmuchError( message="Could not create the specified database") self._db = res @@ -188,7 +188,7 @@ class Database(object): """ res = Database._open(_str(path), mode) - if res is None: + if not res: raise NotmuchError(message="Could not open the specified database") self._db = res @@ -651,7 +651,7 @@ class Query(object): self._db = db # create query, return None if too little mem available query_p = Query._create(db.db_p, _str(querystr)) - if query_p is None: + if not query_p: raise NullPointerError self._query = query_p @@ -685,7 +685,7 @@ class Query(object): self._assert_query_is_initialized() threads_p = Query._search_threads(self._query) - if threads_p is None: + if not threads_p: raise NullPointerError return Threads(threads_p, self) @@ -699,7 +699,7 @@ class Query(object): self._assert_query_is_initialized() msgs_p = Query._search_messages(self._query) - if msgs_p is None: + if not msgs_p: raise NullPointerError return Messages(msgs_p, self) @@ -765,7 +765,7 @@ class Directory(object): def _assert_dir_is_initialized(self): """Raises a NotmuchError(:attr:`STATUS`.NOT_INITIALIZED) if dir_p is None""" - if self._dir_p is None: + if not self._dir_p: raise NotmuchError(STATUS.NOT_INITIALIZED) def __init__(self, path, dir_p, parent): @@ -926,7 +926,7 @@ class Filenames(object): _move_to_next.restype = None def __next__(self): - if self._files_p is None: + if not self._files_p: raise NotmuchError(STATUS.NOT_INITIALIZED) if not self._valid(self._files_p): @@ -953,7 +953,7 @@ class Filenames(object): # NotmuchError(:attr:`STATUS`.NOT_INITIALIZED) for file in files: print file """ - if self._files_p is None: + if not self._files_p: raise NotmuchError(STATUS.NOT_INITIALIZED) i = 0 -- cgit v1.2.3 From bb514d7862aa38f6628ef1c8cb69ed5ec72098fd Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Fri, 10 Feb 2012 22:34:47 +0100 Subject: py3k: Fix decoding of default database name in Database._get_user_default_db Signed-off-by: Justus Winter <4winter@informatik.uni-hamburg.de> --- bindings/python/notmuch/database.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'bindings/python/notmuch/database.py') diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py index 6238b28..36b65ec 100644 --- a/bindings/python/notmuch/database.py +++ b/bindings/python/notmuch/database.py @@ -18,6 +18,7 @@ Copyright 2010 Sebastian Spaeth ' """ import os +import codecs from ctypes import c_char_p, c_void_p, c_uint, c_long, byref, POINTER from notmuch.globals import (nmlib, STATUS, NotmuchError, NotInitializedError, NullPointerError, Enum, _str, @@ -553,11 +554,11 @@ class Database(object): config = SafeConfigParser() conf_f = os.getenv('NOTMUCH_CONFIG', os.path.expanduser('~/.notmuch-config')) - config.read(conf_f) + config.readfp(codecs.open(conf_f, 'r', 'utf-8')) if not config.has_option('database', 'path'): raise NotmuchError(message="No DB path specified" " and no user default found") - return config.get('database', 'path').decode('utf-8') + return config.get('database', 'path') @property def db_p(self): -- cgit v1.2.3 From b2734519db78fdec76eeafc5fe8f5631a6436cf6 Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Wed, 15 Feb 2012 22:25:13 +0100 Subject: python: provide a Database.close function Rename Database.__del__ to Database.close, move it just below the open function and call close() in a newly created destructor just below the constructor. Signed-off-by: Justus Winter <4winter@informatik.uni-hamburg.de> --- bindings/python/notmuch/database.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) (limited to 'bindings/python/notmuch/database.py') diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py index 36b65ec..d0e38dd 100644 --- a/bindings/python/notmuch/database.py +++ b/bindings/python/notmuch/database.py @@ -142,6 +142,9 @@ class Database(object): else: self.create(path) + def __del__(self): + self.close() + def _assert_db_is_initialized(self): """Raises :exc:`NotInitializedError` if self._db is `None`""" if self._db is None: @@ -193,6 +196,16 @@ class Database(object): raise NotmuchError(message="Could not open the specified database") self._db = res + _close = nmlib.notmuch_database_close + _close.argtypes = [NotmuchDatabaseP] + _close.restype = None + + def close(self): + """Close and free the notmuch database if needed""" + if self._db is not None: + self._close(self._db) + self._db = None + def get_path(self): """Returns the file path of an open database""" self._assert_db_is_initialized() @@ -531,15 +544,6 @@ 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: - self._close(self._db) - def _get_user_default_db(self): """ Reads a user's notmuch config and returns his db location -- cgit v1.2.3 From 36ce7e3c989f6f66a4b250483de3b45e902d68d1 Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Wed, 15 Feb 2012 22:41:16 +0100 Subject: python: implement the context manager protocol for database objects Signed-off-by: Justus Winter <4winter@informatik.uni-hamburg.de> --- bindings/python/notmuch/database.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'bindings/python/notmuch/database.py') diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py index d0e38dd..533948c 100644 --- a/bindings/python/notmuch/database.py +++ b/bindings/python/notmuch/database.py @@ -41,6 +41,10 @@ class Database(object): :exc:`XapianError` as the underlying database has been modified. Close and reopen the database to continue working with it. + :class:`Database` objects implement the context manager protocol + so you can use the :keyword:`with` statement to ensure that the + database is properly closed. + .. note:: Any function in this class can and will throw an @@ -206,6 +210,18 @@ class Database(object): self._close(self._db) self._db = None + def __enter__(self): + ''' + Implements the context manager protocol. + ''' + return self + + def __exit__(self, exc_type, exc_value, traceback): + ''' + Implements the context manager protocol. + ''' + self.close() + def get_path(self): """Returns the file path of an open database""" self._assert_db_is_initialized() -- cgit v1.2.3 From 5d69d272c3025f537de0e9995dee394c75ed1005 Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Sat, 18 Feb 2012 01:10:45 +0100 Subject: python: move Query class to its own file Signed-off-by: Justus Winter <4winter@informatik.uni-hamburg.de> --- bindings/python/notmuch/database.py | 181 +++------------------------------ bindings/python/notmuch/query.py | 192 ++++++++++++++++++++++++++++++++++++ 2 files changed, 207 insertions(+), 166 deletions(-) create mode 100644 bindings/python/notmuch/query.py (limited to 'bindings/python/notmuch/database.py') diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py index 533948c..0958ce0 100644 --- a/bindings/python/notmuch/database.py +++ b/bindings/python/notmuch/database.py @@ -20,14 +20,22 @@ Copyright 2010 Sebastian Spaeth ' import os import codecs from ctypes import c_char_p, c_void_p, c_uint, c_long, byref, POINTER -from notmuch.globals import (nmlib, STATUS, NotmuchError, NotInitializedError, - NullPointerError, Enum, _str, - NotmuchDatabaseP, NotmuchDirectoryP, NotmuchMessageP, NotmuchTagsP, - NotmuchQueryP, NotmuchMessagesP, NotmuchThreadsP, NotmuchFilenamesP) -from notmuch.thread import Threads -from notmuch.message import Messages, Message +from notmuch.globals import ( + nmlib, + STATUS, + NotmuchError, + NotInitializedError, + Enum, + _str, + NotmuchDatabaseP, + NotmuchDirectoryP, + NotmuchMessageP, + NotmuchTagsP, + NotmuchFilenamesP +) +from notmuch.message import Message from notmuch.tag import Tags - +from .query import Query class Database(object): """The :class:`Database` is the highest-level object that notmuch @@ -590,165 +598,6 @@ class Database(object): return self._db -class Query(object): - """Represents a search query on an opened :class:`Database`. - - A query selects and filters a subset of messages from the notmuch - database we derive from. - - :class:`Query` provides an instance attribute :attr:`sort`, which - contains the sort order (if specified via :meth:`set_sort`) or - `None`. - - Any function in this class may throw an :exc:`NotInitializedError` - in case the underlying query object was not set up correctly. - - .. note:: Do remember that as soon as we tear down this object, - all underlying derived objects such as 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. - """ - # constants - SORT = Enum(['OLDEST_FIRST', 'NEWEST_FIRST', 'MESSAGE_ID', 'UNSORTED']) - """Constants: Sort order in which to return results""" - - """notmuch_query_create""" - _create = nmlib.notmuch_query_create - _create.argtypes = [NotmuchDatabaseP, c_char_p] - _create.restype = NotmuchQueryP - - """notmuch_query_search_threads""" - _search_threads = nmlib.notmuch_query_search_threads - _search_threads.argtypes = [NotmuchQueryP] - _search_threads.restype = NotmuchThreadsP - - """notmuch_query_search_messages""" - _search_messages = nmlib.notmuch_query_search_messages - _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): - """ - :param db: An open database which we derive the Query from. - :type db: :class:`Database` - :param querystr: The query string for the message. - :type querystr: utf-8 encoded str or unicode - """ - self._db = None - self._query = None - self.sort = None - self.create(db, querystr) - - def _assert_query_is_initialized(self): - """Raises :exc:`NotInitializedError` if self._query is `None`""" - if self._query is None: - raise NotInitializedError() - - def create(self, db, querystr): - """Creates a new query derived from a Database - - This function is utilized by __init__() and usually does not need to - be called directly. - - :param db: Database to create the query from. - :type db: :class:`Database` - :param querystr: The query string - :type querystr: utf-8 encoded str or unicode - :returns: Nothing - :exception: - :exc:`NullPointerError` if the query creation failed - (e.g. too little memory). - :exc:`NotInitializedError` if the underlying db was not - intitialized. - """ - db._assert_db_is_initialized() - # create reference to parent db to keep it alive - self._db = db - # create query, return None if too little mem available - query_p = Query._create(db.db_p, _str(querystr)) - if not query_p: - 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 - - :param sort: Sort order (see :attr:`Query.SORT`) - """ - self._assert_query_is_initialized() - self.sort = sort - self._set_sort(self._query, sort) - - def search_threads(self): - """Execute a query for threads - - Execute a query for threads, returning a :class:`Threads` iterator. - The returned threads are owned by the query and as such, will only be - valid until the Query is deleted. - - The method sets :attr:`Message.FLAG`\.MATCH for those messages that - match the query. The method :meth:`Message.get_flag` allows us - to get the value of this flag. - - :returns: :class:`Threads` - :exception: :exc:`NullPointerError` if search_threads failed - """ - self._assert_query_is_initialized() - threads_p = Query._search_threads(self._query) - - if not threads_p: - raise NullPointerError - return Threads(threads_p, self) - - def search_messages(self): - """Filter messages according to the query and return - :class:`Messages` in the defined sort order - - :returns: :class:`Messages` - :exception: :exc:`NullPointerError` if search_messages failed - """ - self._assert_query_is_initialized() - msgs_p = Query._search_messages(self._query) - - if not msgs_p: - raise NullPointerError - return Messages(msgs_p, self) - - def count_messages(self): - """Estimate the number of messages matching the query - - This function performs a search and returns Xapian's best - guess as to the number of matching messages. It is much faster - than performing :meth:`search_messages` and counting the - result with `len()` (although it always returned the same - result in my tests). Technically, it wraps the underlying - *notmuch_query_count_messages* function. - - :returns: :class:`Messages` - """ - 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: - self._destroy(self._query) - - class Directory(object): """Represents a directory entry in the notmuch directory diff --git a/bindings/python/notmuch/query.py b/bindings/python/notmuch/query.py new file mode 100644 index 0000000..bab5c3e --- /dev/null +++ b/bindings/python/notmuch/query.py @@ -0,0 +1,192 @@ +""" +This file is part of notmuch. + +Notmuch is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation, either version 3 of the License, or (at your +option) any later version. + +Notmuch is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with notmuch. If not, see . + +Copyright 2010 Sebastian Spaeth ' +""" + +from ctypes import c_char_p, c_uint +from notmuch.globals import ( + nmlib, + Enum, + _str, + NotmuchQueryP, + NotmuchThreadsP, + NotmuchDatabaseP, + NotmuchMessagesP, + NullPointerError, + NotInitializedError, +) +from notmuch.thread import Threads +from notmuch.message import Messages + + +class Query(object): + """Represents a search query on an opened :class:`Database`. + + A query selects and filters a subset of messages from the notmuch + database we derive from. + + :class:`Query` provides an instance attribute :attr:`sort`, which + contains the sort order (if specified via :meth:`set_sort`) or + `None`. + + Any function in this class may throw an :exc:`NotInitializedError` + in case the underlying query object was not set up correctly. + + .. note:: Do remember that as soon as we tear down this object, + all underlying derived objects such as 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. + """ + # constants + SORT = Enum(['OLDEST_FIRST', 'NEWEST_FIRST', 'MESSAGE_ID', 'UNSORTED']) + """Constants: Sort order in which to return results""" + + """notmuch_query_create""" + _create = nmlib.notmuch_query_create + _create.argtypes = [NotmuchDatabaseP, c_char_p] + _create.restype = NotmuchQueryP + + """notmuch_query_search_threads""" + _search_threads = nmlib.notmuch_query_search_threads + _search_threads.argtypes = [NotmuchQueryP] + _search_threads.restype = NotmuchThreadsP + + """notmuch_query_search_messages""" + _search_messages = nmlib.notmuch_query_search_messages + _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): + """ + :param db: An open database which we derive the Query from. + :type db: :class:`Database` + :param querystr: The query string for the message. + :type querystr: utf-8 encoded str or unicode + """ + self._db = None + self._query = None + self.sort = None + self.create(db, querystr) + + def _assert_query_is_initialized(self): + """Raises :exc:`NotInitializedError` if self._query is `None`""" + if self._query is None: + raise NotInitializedError() + + def create(self, db, querystr): + """Creates a new query derived from a Database + + This function is utilized by __init__() and usually does not need to + be called directly. + + :param db: Database to create the query from. + :type db: :class:`Database` + :param querystr: The query string + :type querystr: utf-8 encoded str or unicode + :returns: Nothing + :exception: + :exc:`NullPointerError` if the query creation failed + (e.g. too little memory). + :exc:`NotInitializedError` if the underlying db was not + intitialized. + """ + db._assert_db_is_initialized() + # create reference to parent db to keep it alive + self._db = db + # create query, return None if too little mem available + query_p = Query._create(db.db_p, _str(querystr)) + if not query_p: + 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 + + :param sort: Sort order (see :attr:`Query.SORT`) + """ + self._assert_query_is_initialized() + self.sort = sort + self._set_sort(self._query, sort) + + def search_threads(self): + """Execute a query for threads + + Execute a query for threads, returning a :class:`Threads` iterator. + The returned threads are owned by the query and as such, will only be + valid until the Query is deleted. + + The method sets :attr:`Message.FLAG`\.MATCH for those messages that + match the query. The method :meth:`Message.get_flag` allows us + to get the value of this flag. + + :returns: :class:`Threads` + :exception: :exc:`NullPointerError` if search_threads failed + """ + self._assert_query_is_initialized() + threads_p = Query._search_threads(self._query) + + if not threads_p: + raise NullPointerError + return Threads(threads_p, self) + + def search_messages(self): + """Filter messages according to the query and return + :class:`Messages` in the defined sort order + + :returns: :class:`Messages` + :exception: :exc:`NullPointerError` if search_messages failed + """ + self._assert_query_is_initialized() + msgs_p = Query._search_messages(self._query) + + if not msgs_p: + raise NullPointerError + return Messages(msgs_p, self) + + def count_messages(self): + """Estimate the number of messages matching the query + + This function performs a search and returns Xapian's best + guess as to the number of matching messages. It is much faster + than performing :meth:`search_messages` and counting the + result with `len()` (although it always returned the same + result in my tests). Technically, it wraps the underlying + *notmuch_query_count_messages* function. + + :returns: :class:`Messages` + """ + 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: + self._destroy(self._query) -- cgit v1.2.3 From be851ad39de11f38e1cd4f7f15f1fa952232efe2 Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Sun, 19 Feb 2012 00:36:15 +0100 Subject: python: more error handling fixes This is a follow up commit to 221c7e0b38177f5f1dbf0561580c15e8aaa49004 fixing more NULL pointer checks. Signed-off-by: Justus Winter <4winter@informatik.uni-hamburg.de> --- bindings/python/notmuch/database.py | 2 +- bindings/python/notmuch/filename.py | 2 +- bindings/python/notmuch/message.py | 40 ++++++++++++++++++------------------- bindings/python/notmuch/query.py | 2 +- bindings/python/notmuch/tag.py | 2 +- bindings/python/notmuch/thread.py | 26 ++++++++++++------------ 6 files changed, 37 insertions(+), 37 deletions(-) (limited to 'bindings/python/notmuch/database.py') diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py index 0958ce0..6edb18b 100644 --- a/bindings/python/notmuch/database.py +++ b/bindings/python/notmuch/database.py @@ -159,7 +159,7 @@ class Database(object): def _assert_db_is_initialized(self): """Raises :exc:`NotInitializedError` if self._db is `None`""" - if self._db is None: + if not self._db: raise NotInitializedError() def create(self, path): diff --git a/bindings/python/notmuch/filename.py b/bindings/python/notmuch/filename.py index 469b6a5..322e6bf 100644 --- a/bindings/python/notmuch/filename.py +++ b/bindings/python/notmuch/filename.py @@ -89,7 +89,7 @@ class Filenames(Python3StringMixIn): This is the main function that will usually be used by the user.""" - if self._files is None: + if not self._files: raise NotmuchError(STATUS.NOT_INITIALIZED) while self._valid(self._files): diff --git a/bindings/python/notmuch/message.py b/bindings/python/notmuch/message.py index 883ed23..28723c1 100644 --- a/bindings/python/notmuch/message.py +++ b/bindings/python/notmuch/message.py @@ -135,7 +135,7 @@ class Messages(object): :meth:`collect_tags` will iterate over the messages and therefore will not allow further iterations. """ - if self._msgs is None: + if not self._msgs: raise NotmuchError(STATUS.NOT_INITIALIZED) # collect all tags (returns NULL on error) @@ -160,7 +160,7 @@ class Messages(object): _move_to_next.restype = None def __next__(self): - if self._msgs is None: + if not self._msgs: raise NotmuchError(STATUS.NOT_INITIALIZED) if not self._valid(self._msgs): @@ -362,7 +362,7 @@ class Message(Python3StringMixIn): :exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the message is not initialized. """ - if self._msg is None: + if not self._msg: raise NotmuchError(STATUS.NOT_INITIALIZED) return Message._get_message_id(self._msg).decode('utf-8', 'ignore') @@ -379,7 +379,7 @@ class Message(Python3StringMixIn): :exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the message is not initialized. """ - if self._msg is None: + if not self._msg: raise NotmuchError(STATUS.NOT_INITIALIZED) return Message._get_thread_id(self._msg).decode('utf-8', 'ignore') @@ -402,7 +402,7 @@ class Message(Python3StringMixIn): :exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the message is not initialized. """ - if self._msg is None: + if not self._msg: raise NotmuchError(STATUS.NOT_INITIALIZED) msgs_p = Message._get_replies(self._msg) @@ -424,7 +424,7 @@ class Message(Python3StringMixIn): :exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the message is not initialized. """ - if self._msg is None: + if not self._msg: raise NotmuchError(STATUS.NOT_INITIALIZED) return Message._get_date(self._msg) @@ -447,7 +447,7 @@ class Message(Python3StringMixIn): is not initialized. * STATUS.NULL_POINTER if any error occured. """ - if self._msg is None: + if not self._msg: raise NotmuchError(STATUS.NOT_INITIALIZED) #Returns NULL if any error occurs. @@ -463,7 +463,7 @@ class Message(Python3StringMixIn): :exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the message is not initialized. """ - if self._msg is None: + if not self._msg: raise NotmuchError(STATUS.NOT_INITIALIZED) return Message._get_filename(self._msg).decode('utf-8', 'ignore') @@ -473,7 +473,7 @@ class Message(Python3StringMixIn): Returns a Filenames() generator with all absolute filepaths for messages recorded to have the same Message-ID. These files must not necessarily have identical content.""" - if self._msg is None: + if not self._msg: raise NotmuchError(STATUS.NOT_INITIALIZED) files_p = Message._get_filenames(self._msg) @@ -493,7 +493,7 @@ class Message(Python3StringMixIn): :exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the message is not initialized. """ - if self._msg is None: + if not self._msg: raise NotmuchError(STATUS.NOT_INITIALIZED) return Message._get_flag(self._msg, flag) @@ -508,7 +508,7 @@ class Message(Python3StringMixIn): :exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the message is not initialized. """ - if self._msg is None: + if not self._msg: raise NotmuchError(STATUS.NOT_INITIALIZED) self._set_flag(self._msg, flag, value) @@ -522,7 +522,7 @@ class Message(Python3StringMixIn): is not initialized. * STATUS.NULL_POINTER, on error """ - if self._msg is None: + if not self._msg: raise NotmuchError(STATUS.NOT_INITIALIZED) tags_p = Message._get_tags(self._msg) @@ -565,7 +565,7 @@ class Message(Python3StringMixIn): STATUS.NOT_INITIALIZED The message has not been initialized. """ - if self._msg is None: + if not self._msg: raise NotmuchError(STATUS.NOT_INITIALIZED) status = self._add_tag(self._msg, _str(tag)) @@ -613,7 +613,7 @@ class Message(Python3StringMixIn): STATUS.NOT_INITIALIZED The message has not been initialized. """ - if self._msg is None: + if not self._msg: raise NotmuchError(STATUS.NOT_INITIALIZED) status = self._remove_tag(self._msg, _str(tag)) @@ -654,7 +654,7 @@ class Message(Python3StringMixIn): STATUS.NOT_INITIALIZED The message has not been initialized. """ - if self._msg is None: + if not self._msg: raise NotmuchError(STATUS.NOT_INITIALIZED) status = self._remove_all_tags(self._msg) @@ -712,7 +712,7 @@ class Message(Python3StringMixIn): STATUS.NOT_INITIALIZED The message has not been initialized. """ - if self._msg is None: + if not self._msg: raise NotmuchError(STATUS.NOT_INITIALIZED) status = self._freeze(self._msg) @@ -751,7 +751,7 @@ class Message(Python3StringMixIn): STATUS.NOT_INITIALIZED The message has not been initialized. """ - if self._msg is None: + if not self._msg: raise NotmuchError(STATUS.NOT_INITIALIZED) status = self._thaw(self._msg) @@ -787,7 +787,7 @@ class Message(Python3StringMixIn): :returns: a :class:`STATUS` value. In short, you want to see notmuch.STATUS.SUCCESS here. See there for details.""" - if self._msg is None: + if not self._msg: raise NotmuchError(STATUS.NOT_INITIALIZED) return Message._tags_to_maildir_flags(self._msg) @@ -814,7 +814,7 @@ class Message(Python3StringMixIn): :returns: a :class:`STATUS`. In short, you want to see notmuch.STATUS.SUCCESS here. See there for details.""" - if self._msg is None: + if not self._msg: raise NotmuchError(STATUS.NOT_INITIALIZED) return Message._tags_to_maildir_flags(self._msg) @@ -957,7 +957,7 @@ class Message(Python3StringMixIn): def __hash__(self): """Implement hash(), so we can use Message() sets""" file = self.get_filename() - if file is None: + if not file: return None return hash(file) diff --git a/bindings/python/notmuch/query.py b/bindings/python/notmuch/query.py index 0c08aa9..6132ca0 100644 --- a/bindings/python/notmuch/query.py +++ b/bindings/python/notmuch/query.py @@ -70,7 +70,7 @@ class Query(object): def _assert_query_is_initialized(self): """Raises :exc:`NotInitializedError` if self._query is `None`""" - if self._query is None: + if not self._query: raise NotInitializedError() """notmuch_query_create""" diff --git a/bindings/python/notmuch/tag.py b/bindings/python/notmuch/tag.py index d2dc498..d0f7bb4 100644 --- a/bindings/python/notmuch/tag.py +++ b/bindings/python/notmuch/tag.py @@ -90,7 +90,7 @@ class Tags(Python3StringMixIn): _move_to_next.restype = None def __next__(self): - if self._tags is None: + if not self._tags: raise NotmuchError(STATUS.NOT_INITIALIZED) if not self._valid(self._tags): self._tags = None diff --git a/bindings/python/notmuch/thread.py b/bindings/python/notmuch/thread.py index 104710c..c2347fe 100644 --- a/bindings/python/notmuch/thread.py +++ b/bindings/python/notmuch/thread.py @@ -117,7 +117,7 @@ class Threads(Python3StringMixIn): _move_to_next.restype = None def __next__(self): - if self._threads is None: + if not self._threads: raise NotmuchError(STATUS.NOT_INITIALIZED) if not self._valid(self._threads): @@ -141,7 +141,7 @@ class Threads(Python3StringMixIn): # next line raises NotmuchError(STATUS.NOT_INITIALIZED)!!! for thread in threads: print thread """ - if self._threads is None: + if not self._threads: raise NotmuchError(STATUS.NOT_INITIALIZED) i = 0 @@ -244,7 +244,7 @@ class Thread(object): :exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the thread is not initialized. """ - if self._thread is None: + if not self._thread: raise NotmuchError(STATUS.NOT_INITIALIZED) return Thread._get_thread_id(self._thread).decode('utf-8', 'ignore') @@ -261,7 +261,7 @@ class Thread(object): :exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the thread is not initialized. """ - if self._thread is None: + if not self._thread: raise NotmuchError(STATUS.NOT_INITIALIZED) return self._get_total_messages(self._thread) @@ -284,7 +284,7 @@ class Thread(object): * STATUS.NOT_INITIALIZED if query is not inited * STATUS.NULL_POINTER if search_messages failed """ - if self._thread is None: + if not self._thread: raise NotmuchError(STATUS.NOT_INITIALIZED) msgs_p = Thread._get_toplevel_messages(self._thread) @@ -307,7 +307,7 @@ class Thread(object): :exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the thread is not initialized. """ - if self._thread is None: + if not self._thread: raise NotmuchError(STATUS.NOT_INITIALIZED) return self._get_matched_messages(self._thread) @@ -321,10 +321,10 @@ class Thread(object): The returned string belongs to 'thread' and will only be valid for as long as this Thread() is not deleted. """ - if self._thread is None: + if not self._thread: raise NotmuchError(STATUS.NOT_INITIALIZED) authors = Thread._get_authors(self._thread) - if authors is None: + if not authors: return None return authors.decode('UTF-8', 'ignore') @@ -334,10 +334,10 @@ class Thread(object): The returned string belongs to 'thread' and will only be valid for as long as this Thread() is not deleted. """ - if self._thread is None: + if not self._thread: raise NotmuchError(STATUS.NOT_INITIALIZED) subject = Thread._get_subject(self._thread) - if subject is None: + if not subject: return None return subject.decode('UTF-8', 'ignore') @@ -349,7 +349,7 @@ class Thread(object): :exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the message is not initialized. """ - if self._thread is None: + if not self._thread: raise NotmuchError(STATUS.NOT_INITIALIZED) return Thread._get_newest_date(self._thread) @@ -361,7 +361,7 @@ class Thread(object): :exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the message is not initialized. """ - if self._thread is None: + if not self._thread: raise NotmuchError(STATUS.NOT_INITIALIZED) return Thread._get_oldest_date(self._thread) @@ -384,7 +384,7 @@ class Thread(object): is not initialized. * STATUS.NULL_POINTER, on error """ - if self._thread is None: + if not self._thread: raise NotmuchError(STATUS.NOT_INITIALIZED) tags_p = Thread._get_tags(self._thread) -- cgit v1.2.3 From a1442952d4d7fad8b7612502802ee346ac8fd349 Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Mon, 20 Feb 2012 23:49:07 +0100 Subject: python: refactor the error handling machinery Raise specific error classes instead of a generic NotmuchError with an magic status value (e.g. NotmuchError(STATUS.NULL_POINTER) -> NullPointerError()), update the documentation accordingly. Signed-off-by: Justus Winter <4winter@informatik.uni-hamburg.de> --- bindings/python/notmuch/database.py | 15 +-- bindings/python/notmuch/filename.py | 26 ++++-- bindings/python/notmuch/message.py | 181 +++++++++++++++++------------------- bindings/python/notmuch/tag.py | 26 ++++-- bindings/python/notmuch/thread.py | 78 ++++++++-------- 5 files changed, 165 insertions(+), 161 deletions(-) (limited to 'bindings/python/notmuch/database.py') diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py index 6edb18b..3de0f2b 100644 --- a/bindings/python/notmuch/database.py +++ b/bindings/python/notmuch/database.py @@ -23,7 +23,9 @@ from ctypes import c_char_p, c_void_p, c_uint, c_long, byref, POINTER from notmuch.globals import ( nmlib, STATUS, + FileError, NotmuchError, + NullPointerError, NotInitializedError, Enum, _str, @@ -355,9 +357,8 @@ class Database(object): # we got an absolute path if not path.startswith(self.get_path()): # but its initial components are not equal to the db path - raise NotmuchError(STATUS.FILE_ERROR, - message="Database().get_directory() called " - "with a wrong absolute path.") + raise FileError('Database().get_directory() called ' + 'with a wrong absolute path') abs_dirpath = path else: #we got a relative path, make it absolute @@ -542,7 +543,7 @@ class Database(object): self._assert_db_is_initialized() tags_p = Database._get_all_tags(self._db) if tags_p == None: - raise NotmuchError(STATUS.NULL_POINTER) + raise NullPointerError() return Tags(tags_p, self) def create_query(self, querystring): @@ -636,7 +637,7 @@ class Directory(object): """Raises a NotmuchError(:attr:`STATUS`.NOT_INITIALIZED) if dir_p is None""" if not self._dir_p: - raise NotmuchError(STATUS.NOT_INITIALIZED) + raise NotInitializedError() def __init__(self, path, dir_p, parent): """ @@ -797,7 +798,7 @@ class Filenames(object): def __next__(self): if not self._files_p: - raise NotmuchError(STATUS.NOT_INITIALIZED) + raise NotInitializedError() if not self._valid(self._files_p): self._files_p = None @@ -824,7 +825,7 @@ class Filenames(object): for file in files: print file """ if not self._files_p: - raise NotmuchError(STATUS.NOT_INITIALIZED) + raise NotInitializedError() i = 0 while self._valid(self._files_p): diff --git a/bindings/python/notmuch/filename.py b/bindings/python/notmuch/filename.py index 322e6bf..353eb76 100644 --- a/bindings/python/notmuch/filename.py +++ b/bindings/python/notmuch/filename.py @@ -17,8 +17,14 @@ along with notmuch. If not, see . Copyright 2010 Sebastian Spaeth ' """ from ctypes import c_char_p -from notmuch.globals import (nmlib, STATUS, NotmuchError, - NotmuchFilenamesP, NotmuchMessageP, Python3StringMixIn) +from notmuch.globals import ( + nmlib, + NullPointerError, + NotInitializedError, + NotmuchMessageP, + NotmuchFilenamesP, + Python3StringMixIn, +) class Filenames(Python3StringMixIn): @@ -29,9 +35,9 @@ class Filenames(Python3StringMixIn): iterator over a list of notmuch filenames. Do note that the underlying library only provides a one-time iterator (it cannot reset the iterator to the start). Thus iterating over the function will "exhaust" the list of - tags, and a subsequent iteration attempt will raise a :exc:`NotmuchError` - STATUS.NOT_INITIALIZED. Also note, that any function that uses iteration - (nearly all) will also exhaust the tags. So both:: + tags, and a subsequent iteration attempt will raise a + :exc:`NotInitializedError`. Also note, that any function that uses + iteration (nearly all) will also exhaust the tags. So both:: for name in filenames: print name @@ -61,8 +67,8 @@ class Filenames(Python3StringMixIn): will almost never instantiate a :class:`Tags` object herself. They are usually handed back as a result, e.g. in :meth:`Database.get_all_tags`. *tags_p* must be - valid, we will raise an :exc:`NotmuchError` - (STATUS.NULL_POINTER) if it is `None`. + valid, we will raise an :exc:`NullPointerError` + if it is `None`. :type files_p: :class:`ctypes.c_void_p` :param parent: The parent object (ie :class:`Message` these filenames are derived from, and saves a @@ -70,7 +76,7 @@ class Filenames(Python3StringMixIn): once all derived objects are dead. """ if not files_p: - raise NotmuchError(STATUS.NULL_POINTER) + raise NullPointerError() self._files = files_p #save reference to parent object so we keep it alive @@ -90,7 +96,7 @@ class Filenames(Python3StringMixIn): This is the main function that will usually be used by the user.""" if not self._files: - raise NotmuchError(STATUS.NOT_INITIALIZED) + raise NotInitializedError() while self._valid(self._files): yield Filenames._get(self._files).decode('utf-8', 'ignore') @@ -104,7 +110,7 @@ class Filenames(Python3StringMixIn): .. note:: As this iterates over the filenames, 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 + :exc:`NotInitializedError` on subsequent attempts. However, you can use :meth:`Message.get_filenames` repeatedly to perform various actions on filenames. diff --git a/bindings/python/notmuch/message.py b/bindings/python/notmuch/message.py index 28723c1..b291f9f 100644 --- a/bindings/python/notmuch/message.py +++ b/bindings/python/notmuch/message.py @@ -22,8 +22,19 @@ Copyright 2010 Sebastian Spaeth ' 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, Python3StringMixIn, - NotmuchTagsP, NotmuchMessagesP, NotmuchMessageP, NotmuchFilenamesP) + nmlib, + Enum, + _str, + Python3StringMixIn, + STATUS, + NotmuchError, + NullPointerError, + NotInitializedError, + NotmuchTagsP, + NotmuchMessageP, + NotmuchMessagesP, + NotmuchFilenamesP, +) from notmuch.tag import Tags from notmuch.filename import Filenames import sys @@ -43,7 +54,7 @@ class Messages(object): only provides a one-time iterator (it cannot reset the iterator to the start). Thus iterating over the function will "exhaust" the list of messages, and a subsequent iteration attempt will raise a - :exc:`NotmuchError` STATUS.NOT_INITIALIZED. If you need to + :exc:`NotInitializedError`. If you need to re-iterate over a list of messages you will need to retrieve a new :class:`Messages` object or cache your :class:`Message`\s in a list via:: @@ -107,8 +118,8 @@ class Messages(object): will almost never instantiate a :class:`Messages` object herself. They are usually handed back as a result, e.g. in :meth:`Query.search_messages`. *msgs_p* must be - valid, we will raise an :exc:`NotmuchError` - (STATUS.NULL_POINTER) if it is `None`. + valid, we will raise an :exc:`NullPointerError` if it is + `None`. :type msgs_p: :class:`ctypes.c_void_p` :param parent: The parent object (ie :class:`Query`) these tags are derived from. It saves @@ -118,7 +129,7 @@ class Messages(object): the Python object.(?) """ if not msgs_p: - raise NotmuchError(STATUS.NULL_POINTER) + raise NullPointerError() self._msgs = msgs_p #store parent, so we keep them alive as long as self is alive @@ -128,7 +139,7 @@ class Messages(object): """Return the unique :class:`Tags` in the contained messages :returns: :class:`Tags` - :exceptions: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if not init'ed + :exceptions: :exc:`NotInitializedError` if not init'ed .. note:: @@ -136,7 +147,7 @@ class Messages(object): will not allow further iterations. """ if not self._msgs: - raise NotmuchError(STATUS.NOT_INITIALIZED) + raise NotInitializedError() # collect all tags (returns NULL on error) tags_p = Messages._collect_tags(self._msgs) @@ -144,7 +155,7 @@ class Messages(object): self._msgs = None if tags_p == None: - raise NotmuchError(STATUS.NULL_POINTER) + raise NullPointerError() return Tags(tags_p, self) def __iter__(self): @@ -161,7 +172,7 @@ class Messages(object): def __next__(self): if not self._msgs: - raise NotmuchError(STATUS.NOT_INITIALIZED) + raise NotInitializedError() if not self._valid(self._msgs): self._msgs = None @@ -341,8 +352,8 @@ class Message(Python3StringMixIn): def __init__(self, msg_p, parent=None): """ :param msg_p: A pointer to an internal notmuch_message_t - Structure. If it is `None`, we will raise an :exc:`NotmuchError` - STATUS.NULL_POINTER. + Structure. If it is `None`, we will raise an + :exc:`NullPointerError`. :param parent: A 'parent' object is passed which this message is derived from. We save a reference to it, so we can @@ -350,7 +361,7 @@ class Message(Python3StringMixIn): objects are dead. """ if not msg_p: - raise NotmuchError(STATUS.NULL_POINTER) + raise NullPointerError() self._msg = msg_p #keep reference to parent, so we keep it alive self._parent = parent @@ -359,11 +370,11 @@ class Message(Python3StringMixIn): """Returns the message ID :returns: String with a message ID - :exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the message + :exception: :exc:`NotInitializedError` if the message is not initialized. """ if not self._msg: - raise NotmuchError(STATUS.NOT_INITIALIZED) + raise NotInitializedError() return Message._get_message_id(self._msg).decode('utf-8', 'ignore') def get_thread_id(self): @@ -376,11 +387,11 @@ class Message(Python3StringMixIn): message belongs to a single thread. :returns: String with a thread ID - :exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the message + :exception: :exc:`NotInitializedError` if the message is not initialized. """ if not self._msg: - raise NotmuchError(STATUS.NOT_INITIALIZED) + raise NotInitializedError() return Message._get_thread_id(self._msg).decode('utf-8', 'ignore') @@ -399,11 +410,11 @@ class Message(Python3StringMixIn): an empty Messages iterator. :returns: :class:`Messages`. - :exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the message + :exception: :exc:`NotInitializedError` if the message is not initialized. """ if not self._msg: - raise NotmuchError(STATUS.NOT_INITIALIZED) + raise NotInitializedError() msgs_p = Message._get_replies(self._msg) @@ -421,11 +432,11 @@ class Message(Python3StringMixIn): :returns: A time_t timestamp. :rtype: c_unit64 - :exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the message + :exception: :exc:`NotInitializedError` if the message is not initialized. """ if not self._msg: - raise NotmuchError(STATUS.NOT_INITIALIZED) + raise NotInitializedError() return Message._get_date(self._msg) def get_header(self, header): @@ -441,30 +452,28 @@ class Message(Python3StringMixIn): It is not case-sensitive. :type header: str :returns: The header value as string - :exception: :exc:`NotmuchError` - - * STATUS.NOT_INITIALIZED if the message - is not initialized. - * STATUS.NULL_POINTER if any error occured. + :raises: :exc:`NotInitializedError` if the message is not + initialized + :raises: :exc:`NullPointerError` if any error occured """ if not self._msg: - raise NotmuchError(STATUS.NOT_INITIALIZED) + raise NotInitializedError() #Returns NULL if any error occurs. header = Message._get_header(self._msg, _str(header)) if header == None: - raise NotmuchError(STATUS.NULL_POINTER) + raise NullPointerError() return header.decode('UTF-8', 'ignore') def get_filename(self): """Returns the file path of the message file :returns: Absolute file path & name of the message file - :exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the message + :exception: :exc:`NotInitializedError` if the message is not initialized. """ if not self._msg: - raise NotmuchError(STATUS.NOT_INITIALIZED) + raise NotInitializedError() return Message._get_filename(self._msg).decode('utf-8', 'ignore') def get_filenames(self): @@ -474,7 +483,7 @@ class Message(Python3StringMixIn): messages recorded to have the same Message-ID. These files must not necessarily have identical content.""" if not self._msg: - raise NotmuchError(STATUS.NOT_INITIALIZED) + raise NotInitializedError() files_p = Message._get_filenames(self._msg) @@ -490,11 +499,11 @@ class Message(Python3StringMixIn): :param flag: One of the :attr:`Message.FLAG` values (currently only *Message.FLAG.MATCH* :returns: An unsigned int (0/1), indicating whether the flag is set. - :exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the message + :exception: :exc:`NotInitializedError` if the message is not initialized. """ if not self._msg: - raise NotmuchError(STATUS.NOT_INITIALIZED) + raise NotInitializedError() return Message._get_flag(self._msg, flag) def set_flag(self, flag, value): @@ -505,29 +514,27 @@ class Message(Python3StringMixIn): :param value: A bool indicating whether to set or unset the flag. :returns: Nothing - :exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the message + :exception: :exc:`NotInitializedError` if the message is not initialized. """ if not self._msg: - raise NotmuchError(STATUS.NOT_INITIALIZED) + raise NotInitializedError() self._set_flag(self._msg, flag, value) def get_tags(self): """Returns the message tags :returns: A :class:`Tags` iterator. - :exception: :exc:`NotmuchError` - - * STATUS.NOT_INITIALIZED if the message - is not initialized. - * STATUS.NULL_POINTER, on error + :raises: :exc:`NotInitializedError` if the message is not + initialized + :raises: :exc:`NullPointerError` if any error occured """ if not self._msg: - raise NotmuchError(STATUS.NOT_INITIALIZED) + raise NotInitializedError() tags_p = Message._get_tags(self._msg) if tags_p == None: - raise NotmuchError(STATUS.NULL_POINTER) + raise NullPointerError() return Tags(tags_p, self) _add_tag = nmlib.notmuch_message_add_tag @@ -552,21 +559,16 @@ class Message(Python3StringMixIn): :returns: STATUS.SUCCESS if the tag was successfully added. Raises an exception otherwise. - :exception: :exc:`NotmuchError`. They have the following meaning: - - STATUS.NULL_POINTER - The 'tag' argument is NULL - STATUS.TAG_TOO_LONG - The length of 'tag' is too long - (exceeds Message.NOTMUCH_TAG_MAX) - STATUS.READ_ONLY_DATABASE - Database was opened in read-only mode so message cannot be - modified. - STATUS.NOT_INITIALIZED - The message has not been initialized. - """ + :raises: :exc:`NullPointerError` if the `tag` argument is NULL + :raises: :exc:`TagTooLongError` if the length of `tag` exceeds + Message.NOTMUCH_TAG_MAX) + :raises: :exc:`ReadOnlyDatabaseError` if the database was opened + in read-only mode so message cannot be modified + :raises: :exc:`NotInitializedError` if message has not been + initialized + """ if not self._msg: - raise NotmuchError(STATUS.NOT_INITIALIZED) + raise NotInitializedError() status = self._add_tag(self._msg, _str(tag)) @@ -600,21 +602,16 @@ class Message(Python3StringMixIn): :returns: STATUS.SUCCESS if the tag was successfully removed or if the message had no such tag. Raises an exception otherwise. - :exception: :exc:`NotmuchError`. They have the following meaning: - - STATUS.NULL_POINTER - The 'tag' argument is NULL - STATUS.TAG_TOO_LONG - The length of 'tag' is too long - (exceeds NOTMUCH_TAG_MAX) - STATUS.READ_ONLY_DATABASE - Database was opened in read-only mode so message cannot - be modified. - STATUS.NOT_INITIALIZED - The message has not been initialized. + :raises: :exc:`NullPointerError` if the `tag` argument is NULL + :raises: :exc:`TagTooLongError` if the length of `tag` exceeds + Message.NOTMUCH_TAG_MAX) + :raises: :exc:`ReadOnlyDatabaseError` if the database was opened + in read-only mode so message cannot be modified + :raises: :exc:`NotInitializedError` if message has not been + initialized """ if not self._msg: - raise NotmuchError(STATUS.NOT_INITIALIZED) + raise NotInitializedError() status = self._remove_tag(self._msg, _str(tag)) # bail out on error @@ -646,16 +643,13 @@ class Message(Python3StringMixIn): :returns: STATUS.SUCCESS if the tags were successfully removed. Raises an exception otherwise. - :exception: :exc:`NotmuchError`. They have the following meaning: - - STATUS.READ_ONLY_DATABASE - Database was opened in read-only mode so message cannot - be modified. - STATUS.NOT_INITIALIZED - The message has not been initialized. + :raises: :exc:`ReadOnlyDatabaseError` if the database was opened + in read-only mode so message cannot be modified + :raises: :exc:`NotInitializedError` if message has not been + initialized """ if not self._msg: - raise NotmuchError(STATUS.NOT_INITIALIZED) + raise NotInitializedError() status = self._remove_all_tags(self._msg) @@ -704,16 +698,13 @@ class Message(Python3StringMixIn): :returns: STATUS.SUCCESS if the message was successfully frozen. Raises an exception otherwise. - :exception: :exc:`NotmuchError`. They have the following meaning: - - STATUS.READ_ONLY_DATABASE - Database was opened in read-only mode so message cannot - be modified. - STATUS.NOT_INITIALIZED - The message has not been initialized. + :raises: :exc:`ReadOnlyDatabaseError` if the database was opened + in read-only mode so message cannot be modified + :raises: :exc:`NotInitializedError` if message has not been + initialized """ if not self._msg: - raise NotmuchError(STATUS.NOT_INITIALIZED) + raise NotInitializedError() status = self._freeze(self._msg) @@ -742,17 +733,15 @@ class Message(Python3StringMixIn): :returns: STATUS.SUCCESS if the message was successfully frozen. Raises an exception otherwise. - :exception: :exc:`NotmuchError`. They have the following meaning: - - STATUS.UNBALANCED_FREEZE_THAW - An attempt was made to thaw an unfrozen message. - That is, there have been an unbalanced number of calls - to :meth:`freeze` and :meth:`thaw`. - STATUS.NOT_INITIALIZED - The message has not been initialized. + :raises: :exc:`UnbalancedFreezeThawError` if an attempt was made + to thaw an unfrozen message. That is, there have been + an unbalanced number of calls to :meth:`freeze` and + :meth:`thaw`. + :raises: :exc:`NotInitializedError` if message has not been + initialized """ if not self._msg: - raise NotmuchError(STATUS.NOT_INITIALIZED) + raise NotInitializedError() status = self._thaw(self._msg) @@ -788,7 +777,7 @@ class Message(Python3StringMixIn): :returns: a :class:`STATUS` value. In short, you want to see notmuch.STATUS.SUCCESS here. See there for details.""" if not self._msg: - raise NotmuchError(STATUS.NOT_INITIALIZED) + raise NotInitializedError() return Message._tags_to_maildir_flags(self._msg) def maildir_flags_to_tags(self): @@ -815,7 +804,7 @@ class Message(Python3StringMixIn): :returns: a :class:`STATUS`. In short, you want to see notmuch.STATUS.SUCCESS here. See there for details.""" if not self._msg: - raise NotmuchError(STATUS.NOT_INITIALIZED) + raise NotInitializedError() return Message._tags_to_maildir_flags(self._msg) def __repr__(self): diff --git a/bindings/python/notmuch/tag.py b/bindings/python/notmuch/tag.py index d0f7bb4..526e51c 100644 --- a/bindings/python/notmuch/tag.py +++ b/bindings/python/notmuch/tag.py @@ -17,7 +17,13 @@ along with notmuch. If not, see . Copyright 2010 Sebastian Spaeth ' """ from ctypes import c_char_p -from notmuch.globals import nmlib, STATUS, NotmuchError, NotmuchTagsP, Python3StringMixIn +from notmuch.globals import ( + nmlib, + Python3StringMixIn, + NullPointerError, + NotInitializedError, + NotmuchTagsP, +) class Tags(Python3StringMixIn): @@ -29,9 +35,9 @@ class Tags(Python3StringMixIn): Do note that the underlying library only provides a one-time iterator (it cannot reset the iterator to the start). Thus iterating over the function will "exhaust" the list of tags, and a subsequent - iteration attempt will raise a :exc:`NotmuchError` - STATUS.NOT_INITIALIZED. Also note, that any function that uses - iteration (nearly all) will also exhaust the tags. So both:: + iteration attempt will raise a :exc:`NotInitializedError`. + Also note, that any function that uses iteration (nearly all) will + also exhaust the tags. So both:: for tag in tags: print tag @@ -60,8 +66,8 @@ class Tags(Python3StringMixIn): will almost never instantiate a :class:`Tags` object herself. They are usually handed back as a result, e.g. in :meth:`Database.get_all_tags`. *tags_p* must be - valid, we will raise an :exc:`NotmuchError` - (STATUS.NULL_POINTER) if it is `None`. + valid, we will raise an :exc:`NullPointerError` if it is + `None`. :type tags_p: :class:`ctypes.c_void_p` :param parent: The parent object (ie :class:`Database` or :class:`Message` these tags are derived from, and saves a @@ -71,7 +77,7 @@ class Tags(Python3StringMixIn): cache the tags in the Python object(?) """ if not tags_p: - raise NotmuchError(STATUS.NULL_POINTER) + raise NullPointerError() self._tags = tags_p #save reference to parent object so we keep it alive @@ -91,7 +97,7 @@ class Tags(Python3StringMixIn): def __next__(self): if not self._tags: - raise NotmuchError(STATUS.NOT_INITIALIZED) + raise NotInitializedError() if not self._valid(self._tags): self._tags = None raise StopIteration @@ -118,8 +124,8 @@ class Tags(Python3StringMixIn): 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. + already, this will raise a :exc:`NotInitializedError`on subsequent + attempts. """ return " ".join(self) diff --git a/bindings/python/notmuch/thread.py b/bindings/python/notmuch/thread.py index c2347fe..5c58028 100644 --- a/bindings/python/notmuch/thread.py +++ b/bindings/python/notmuch/thread.py @@ -18,9 +18,16 @@ Copyright 2010 Sebastian Spaeth ' """ from ctypes import c_char_p, c_long, c_int -from notmuch.globals import (nmlib, STATUS, - NotmuchError, NotmuchThreadP, NotmuchThreadsP, NotmuchMessagesP, - NotmuchTagsP, Python3StringMixIn) +from notmuch.globals import ( + nmlib, + Python3StringMixIn, + NullPointerError, + NotInitializedError, + NotmuchThreadP, + NotmuchThreadsP, + NotmuchMessagesP, + NotmuchTagsP, +) from notmuch.message import Messages from notmuch.tag import Tags from datetime import date @@ -35,7 +42,7 @@ class Threads(Python3StringMixIn): library only provides a one-time iterator (it cannot reset the iterator to the start). Thus iterating over the function will "exhaust" the list of threads, and a subsequent iteration attempt - will raise a :exc:`NotmuchError` STATUS.NOT_INITIALIZED. Also + will raise a :exc:`NotInitializedError`. Also note, that any function that uses iteration will also exhaust the messages. So both:: @@ -87,8 +94,8 @@ class Threads(Python3StringMixIn): will almost never instantiate a :class:`Threads` object herself. They are usually handed back as a result, e.g. in :meth:`Query.search_threads`. *threads_p* must be - valid, we will raise an :exc:`NotmuchError` - (STATUS.NULL_POINTER) if it is `None`. + valid, we will raise an :exc:`NullPointerError` if it is + `None`. :type threads_p: :class:`ctypes.c_void_p` :param parent: The parent object (ie :class:`Query`) these tags are derived from. It saves @@ -98,7 +105,7 @@ class Threads(Python3StringMixIn): the Python object.(?) """ if not threads_p: - raise NotmuchError(STATUS.NULL_POINTER) + raise NullPointerError() self._threads = threads_p #store parent, so we keep them alive as long as self is alive @@ -118,7 +125,7 @@ class Threads(Python3StringMixIn): def __next__(self): if not self._threads: - raise NotmuchError(STATUS.NOT_INITIALIZED) + raise NotInitializedError() if not self._valid(self._threads): self._threads = None @@ -138,11 +145,11 @@ class Threads(Python3StringMixIn): #THIS FAILS threads = Database().create_query('').search_threads() if len(threads) > 0: #this 'exhausts' threads - # next line raises NotmuchError(STATUS.NOT_INITIALIZED)!!! + # next line raises :exc:`NotInitializedError`!!! for thread in threads: print thread """ if not self._threads: - raise NotmuchError(STATUS.NOT_INITIALIZED) + raise NotInitializedError() i = 0 # returns 'bool'. On out-of-memory it returns None @@ -220,8 +227,8 @@ class Thread(object): will almost never instantiate a :class:`Thread` object herself. They are usually handed back as a result, e.g. when iterating through :class:`Threads`. *thread_p* - must be valid, we will raise an :exc:`NotmuchError` - (STATUS.NULL_POINTER) if it is `None`. + must be valid, we will raise an :exc:`NullPointerError` + if it is `None`. :param parent: A 'parent' object is passed which this message is derived from. We save a reference to it, so we can @@ -229,7 +236,7 @@ class Thread(object): objects are dead. """ if not thread_p: - raise NotmuchError(STATUS.NULL_POINTER) + raise NullPointerError() self._thread = thread_p #keep reference to parent, so we keep it alive self._parent = parent @@ -241,11 +248,11 @@ class Thread(object): for as long as the thread is valid. :returns: String with a message ID - :exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the thread + :exception: :exc:`NotInitializedError` if the thread is not initialized. """ if not self._thread: - raise NotmuchError(STATUS.NOT_INITIALIZED) + raise NotInitializedError() return Thread._get_thread_id(self._thread).decode('utf-8', 'ignore') _get_total_messages = nmlib.notmuch_thread_get_total_messages @@ -258,11 +265,11 @@ class Thread(object): :returns: The number of all messages in the database belonging to this thread. Contrast with :meth:`get_matched_messages`. - :exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the thread + :exception: :exc:`NotInitializedError` if the thread is not initialized. """ if not self._thread: - raise NotmuchError(STATUS.NOT_INITIALIZED) + raise NotInitializedError() return self._get_total_messages(self._thread) def get_toplevel_messages(self): @@ -279,18 +286,16 @@ class Thread(object): messages, etc.). :returns: :class:`Messages` - :exception: :exc:`NotmuchError` - - * STATUS.NOT_INITIALIZED if query is not inited - * STATUS.NULL_POINTER if search_messages failed + :raises: :exc:`NotInitializedError` if query is not initialized + :raises: :exc:`NullPointerError` if search_messages failed """ if not self._thread: - raise NotmuchError(STATUS.NOT_INITIALIZED) + raise NotInitializedError() msgs_p = Thread._get_toplevel_messages(self._thread) if not msgs_p: - raise NotmuchError(STATUS.NULL_POINTER) + raise NullPointerError() return Messages(msgs_p, self) @@ -304,11 +309,11 @@ class Thread(object): :returns: The number of all messages belonging to this thread that matched the :class:`Query`from which this thread was created. Contrast with :meth:`get_total_messages`. - :exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the thread + :exception: :exc:`NotInitializedError` if the thread is not initialized. """ if not self._thread: - raise NotmuchError(STATUS.NOT_INITIALIZED) + raise NotInitializedError() return self._get_matched_messages(self._thread) def get_authors(self): @@ -322,7 +327,7 @@ class Thread(object): as long as this Thread() is not deleted. """ if not self._thread: - raise NotmuchError(STATUS.NOT_INITIALIZED) + raise NotInitializedError() authors = Thread._get_authors(self._thread) if not authors: return None @@ -335,7 +340,7 @@ class Thread(object): as long as this Thread() is not deleted. """ if not self._thread: - raise NotmuchError(STATUS.NOT_INITIALIZED) + raise NotInitializedError() subject = Thread._get_subject(self._thread) if not subject: return None @@ -346,11 +351,11 @@ class Thread(object): :returns: A time_t timestamp. :rtype: c_unit64 - :exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the message + :exception: :exc:`NotInitializedError` if the message is not initialized. """ if not self._thread: - raise NotmuchError(STATUS.NOT_INITIALIZED) + raise NotInitializedError() return Thread._get_newest_date(self._thread) def get_oldest_date(self): @@ -358,11 +363,11 @@ class Thread(object): :returns: A time_t timestamp. :rtype: c_unit64 - :exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the message + :exception: :exc:`NotInitializedError` if the message is not initialized. """ if not self._thread: - raise NotmuchError(STATUS.NOT_INITIALIZED) + raise NotInitializedError() return Thread._get_oldest_date(self._thread) def get_tags(self): @@ -378,18 +383,15 @@ class Thread(object): query from which it derived is explicitely deleted). :returns: A :class:`Tags` iterator. - :exception: :exc:`NotmuchError` - - * STATUS.NOT_INITIALIZED if the thread - is not initialized. - * STATUS.NULL_POINTER, on error + :raises: :exc:`NotInitializedError` if query is not initialized + :raises: :exc:`NullPointerError` if search_messages failed """ if not self._thread: - raise NotmuchError(STATUS.NOT_INITIALIZED) + raise NotInitializedError() tags_p = Thread._get_tags(self._thread) if tags_p == None: - raise NotmuchError(STATUS.NULL_POINTER) + raise NullPointerError() return Tags(tags_p, self) def __unicode__(self): -- cgit v1.2.3 From 798b74e859734d12c953390bca0753f8e5e1d67c Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Tue, 21 Feb 2012 00:01:23 +0100 Subject: python: harmonize the sphinx keyword for exceptions Signed-off-by: Justus Winter <4winter@informatik.uni-hamburg.de> --- bindings/python/notmuch/database.py | 24 ++++++++++++------------ bindings/python/notmuch/message.py | 14 +++++++------- bindings/python/notmuch/query.py | 6 +++--- bindings/python/notmuch/thread.py | 10 +++++----- 4 files changed, 27 insertions(+), 27 deletions(-) (limited to 'bindings/python/notmuch/database.py') diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py index 3de0f2b..42a4442 100644 --- a/bindings/python/notmuch/database.py +++ b/bindings/python/notmuch/database.py @@ -140,7 +140,7 @@ class Database(object): :param mode: Mode to open a database in. Is always :attr:`MODE`.READ_WRITE when creating a new one. :type mode: :attr:`MODE` - :exception: :exc:`NotmuchError` or derived exception in case of + :raises: :exc:`NotmuchError` or derived exception in case of failure. """ self._db = None @@ -177,7 +177,7 @@ class Database(object): :param path: A directory in which we should create the database. :type path: str :returns: Nothing - :exception: :exc:`NotmuchError` in case of any failure + :raises: :exc:`NotmuchError` in case of any failure (possibly after printing an error message on stderr). """ if self._db is not None: @@ -201,7 +201,7 @@ 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 + :raises: Raises :exc:`NotmuchError` in case of any failure (possibly after printing an error message on stderr). """ res = Database._open(_str(path), mode) @@ -296,7 +296,7 @@ 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 + :raises: :exc:`NotmuchError`: :attr:`STATUS`.XAPIAN_EXCEPTION Xapian exception occurred; atomic section not entered. *Added in notmuch 0.9*""" @@ -317,7 +317,7 @@ class Database(object): :returns: :attr:`STATUS`.SUCCESS or raises - :exception: + :raises: :exc:`NotmuchError`: :attr:`STATUS`.XAPIAN_EXCEPTION A Xapian exception occurred; atomic section not @@ -346,7 +346,7 @@ class Database(object): 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: + :raises: :exc:`NotmuchError` with :attr:`STATUS`.FILE_ERROR If path is not relative database or absolute with initial components same as database. @@ -410,7 +410,7 @@ class Database(object): :rtype: 2-tuple(:class:`Message`, :attr:`STATUS`) - :exception: Raises a :exc:`NotmuchError` with the following meaning. + :raises: Raises a :exc:`NotmuchError` with the following meaning. If such an exception occurs, nothing was added to the database. :attr:`STATUS`.FILE_ERROR @@ -460,7 +460,7 @@ class Database(object): This filename was removed but the message persists in the database with at least one other filename. - :exception: Raises a :exc:`NotmuchError` with the following meaning. + :raises: Raises a :exc:`NotmuchError` with the following meaning. If such an exception occurs, nothing was removed from the database. @@ -479,7 +479,7 @@ class Database(object): :param msgid: The message ID :type msgid: unicode or str :returns: :class:`Message` or `None` if no message is found. - :exception: + :raises: :exc:`OutOfMemoryError` If an Out-of-memory occured while constructing the message. :exc:`XapianError` @@ -512,7 +512,7 @@ class Database(object): function returns None if no message is found with the given filename. - :exception: + :raises: :exc:`OutOfMemoryError` If an Out-of-memory occured while constructing the message. :exc:`XapianError` @@ -681,7 +681,7 @@ class Directory(object): :param mtime: A (time_t) timestamp :returns: Nothing on success, raising an exception on failure. - :exception: :exc:`NotmuchError`: + :raises: :exc:`NotmuchError`: :attr:`STATUS`.XAPIAN_EXCEPTION A Xapian exception occurred, mtime not stored. @@ -708,7 +708,7 @@ class Directory(object): :param mtime: A (time_t) timestamp :returns: Nothing on success, raising an exception on failure. - :exception: :exc:`NotmuchError`: + :raises: :exc:`NotmuchError`: :attr:`STATUS`.NOT_INITIALIZED The directory has not been initialized diff --git a/bindings/python/notmuch/message.py b/bindings/python/notmuch/message.py index b291f9f..ce7cb88 100644 --- a/bindings/python/notmuch/message.py +++ b/bindings/python/notmuch/message.py @@ -370,7 +370,7 @@ class Message(Python3StringMixIn): """Returns the message ID :returns: String with a message ID - :exception: :exc:`NotInitializedError` if the message + :raises: :exc:`NotInitializedError` if the message is not initialized. """ if not self._msg: @@ -387,7 +387,7 @@ class Message(Python3StringMixIn): message belongs to a single thread. :returns: String with a thread ID - :exception: :exc:`NotInitializedError` if the message + :raises: :exc:`NotInitializedError` if the message is not initialized. """ if not self._msg: @@ -410,7 +410,7 @@ class Message(Python3StringMixIn): an empty Messages iterator. :returns: :class:`Messages`. - :exception: :exc:`NotInitializedError` if the message + :raises: :exc:`NotInitializedError` if the message is not initialized. """ if not self._msg: @@ -432,7 +432,7 @@ class Message(Python3StringMixIn): :returns: A time_t timestamp. :rtype: c_unit64 - :exception: :exc:`NotInitializedError` if the message + :raises: :exc:`NotInitializedError` if the message is not initialized. """ if not self._msg: @@ -469,7 +469,7 @@ class Message(Python3StringMixIn): """Returns the file path of the message file :returns: Absolute file path & name of the message file - :exception: :exc:`NotInitializedError` if the message + :raises: :exc:`NotInitializedError` if the message is not initialized. """ if not self._msg: @@ -499,7 +499,7 @@ class Message(Python3StringMixIn): :param flag: One of the :attr:`Message.FLAG` values (currently only *Message.FLAG.MATCH* :returns: An unsigned int (0/1), indicating whether the flag is set. - :exception: :exc:`NotInitializedError` if the message + :raises: :exc:`NotInitializedError` if the message is not initialized. """ if not self._msg: @@ -514,7 +514,7 @@ class Message(Python3StringMixIn): :param value: A bool indicating whether to set or unset the flag. :returns: Nothing - :exception: :exc:`NotInitializedError` if the message + :raises: :exc:`NotInitializedError` if the message is not initialized. """ if not self._msg: diff --git a/bindings/python/notmuch/query.py b/bindings/python/notmuch/query.py index 6132ca0..25b9e78 100644 --- a/bindings/python/notmuch/query.py +++ b/bindings/python/notmuch/query.py @@ -89,7 +89,7 @@ class Query(object): :param querystr: The query string :type querystr: utf-8 encoded str or unicode :returns: Nothing - :exception: + :raises: :exc:`NullPointerError` if the query creation failed (e.g. too little memory). :exc:`NotInitializedError` if the underlying db was not @@ -134,7 +134,7 @@ class Query(object): to get the value of this flag. :returns: :class:`Threads` - :exception: :exc:`NullPointerError` if search_threads failed + :raises: :exc:`NullPointerError` if search_threads failed """ self._assert_query_is_initialized() threads_p = Query._search_threads(self._query) @@ -153,7 +153,7 @@ class Query(object): :class:`Messages` in the defined sort order :returns: :class:`Messages` - :exception: :exc:`NullPointerError` if search_messages failed + :raises: :exc:`NullPointerError` if search_messages failed """ self._assert_query_is_initialized() msgs_p = Query._search_messages(self._query) diff --git a/bindings/python/notmuch/thread.py b/bindings/python/notmuch/thread.py index 5c58028..d1ba3e5 100644 --- a/bindings/python/notmuch/thread.py +++ b/bindings/python/notmuch/thread.py @@ -248,7 +248,7 @@ class Thread(object): for as long as the thread is valid. :returns: String with a message ID - :exception: :exc:`NotInitializedError` if the thread + :raises: :exc:`NotInitializedError` if the thread is not initialized. """ if not self._thread: @@ -265,7 +265,7 @@ class Thread(object): :returns: The number of all messages in the database belonging to this thread. Contrast with :meth:`get_matched_messages`. - :exception: :exc:`NotInitializedError` if the thread + :raises: :exc:`NotInitializedError` if the thread is not initialized. """ if not self._thread: @@ -309,7 +309,7 @@ class Thread(object): :returns: The number of all messages belonging to this thread that matched the :class:`Query`from which this thread was created. Contrast with :meth:`get_total_messages`. - :exception: :exc:`NotInitializedError` if the thread + :raises: :exc:`NotInitializedError` if the thread is not initialized. """ if not self._thread: @@ -351,7 +351,7 @@ class Thread(object): :returns: A time_t timestamp. :rtype: c_unit64 - :exception: :exc:`NotInitializedError` if the message + :raises: :exc:`NotInitializedError` if the message is not initialized. """ if not self._thread: @@ -363,7 +363,7 @@ class Thread(object): :returns: A time_t timestamp. :rtype: c_unit64 - :exception: :exc:`NotInitializedError` if the message + :raises: :exc:`NotInitializedError` if the message is not initialized. """ if not self._thread: -- cgit v1.2.3 From 1737ff5290a8ce1f66b4a7aeb6a16146dbc3517c Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Tue, 21 Feb 2012 00:13:20 +0100 Subject: python: rework Directory.set_mtime Fix the indentation within the docstring, remove useless remarks, do some trivial refactoring. Signed-off-by: Justus Winter <4winter@informatik.uni-hamburg.de> --- bindings/python/notmuch/database.py | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) (limited to 'bindings/python/notmuch/database.py') diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py index 42a4442..9f6b380 100644 --- a/bindings/python/notmuch/database.py +++ b/bindings/python/notmuch/database.py @@ -679,27 +679,19 @@ class Directory(object): 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. - :raises: :exc:`NotmuchError`: - - :attr:`STATUS`.XAPIAN_EXCEPTION - A Xapian exception occurred, mtime not stored. - :attr:`STATUS`.READ_ONLY_DATABASE - Database was opened in read-only mode so directory - mtime cannot be modified. - :attr:`STATUS`.NOT_INITIALIZED - The directory has not been initialized + :param mtime: A (time_t) timestamp + :raises: :exc:`XapianError` a Xapian exception occurred, mtime + not stored + :raises: :exc:`ReadOnlyDatabaseError` the database was opened + in read-only mode so directory mtime cannot be modified + :raises: :exc:`NotInitializedError` the directory object has not + been initialized """ self._assert_dir_is_initialized() - #TODO: make sure, we convert the mtime parameter to a 'c_long' status = Directory._set_mtime(self._dir_p, mtime) - #return on success - if status == STATUS.SUCCESS: - return - #fail with Exception otherwise - raise NotmuchError(status) + if status != STATUS.SUCCESS: + raise NotmuchError(status) def get_mtime(self): """Gets the mtime value of this directory in the database -- cgit v1.2.3 From 786f9882e8b408e6ad4c6b7abfef1ac54144be15 Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Tue, 21 Feb 2012 00:15:59 +0100 Subject: python: remove :returns: keywords from functions returning nothing Signed-off-by: Justus Winter <4winter@informatik.uni-hamburg.de> --- bindings/python/notmuch/database.py | 3 --- bindings/python/notmuch/message.py | 1 - bindings/python/notmuch/query.py | 1 - 3 files changed, 5 deletions(-) (limited to 'bindings/python/notmuch/database.py') diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py index 9f6b380..d841367 100644 --- a/bindings/python/notmuch/database.py +++ b/bindings/python/notmuch/database.py @@ -176,7 +176,6 @@ class Database(object): :param path: A directory in which we should create the database. :type path: str - :returns: Nothing :raises: :exc:`NotmuchError` in case of any failure (possibly after printing an error message on stderr). """ @@ -200,7 +199,6 @@ class Database(object): :param status: Open the database in read-only or read-write mode :type status: :attr:`MODE` - :returns: Nothing :raises: Raises :exc:`NotmuchError` in case of any failure (possibly after printing an error message on stderr). """ @@ -699,7 +697,6 @@ class Directory(object): Retrieves a previously stored mtime for this directory. :param mtime: A (time_t) timestamp - :returns: Nothing on success, raising an exception on failure. :raises: :exc:`NotmuchError`: :attr:`STATUS`.NOT_INITIALIZED diff --git a/bindings/python/notmuch/message.py b/bindings/python/notmuch/message.py index ce7cb88..9ebb53d 100644 --- a/bindings/python/notmuch/message.py +++ b/bindings/python/notmuch/message.py @@ -513,7 +513,6 @@ class Message(Python3StringMixIn): *Message.FLAG.MATCH* :param value: A bool indicating whether to set or unset the flag. - :returns: Nothing :raises: :exc:`NotInitializedError` if the message is not initialized. """ diff --git a/bindings/python/notmuch/query.py b/bindings/python/notmuch/query.py index 25b9e78..15ed153 100644 --- a/bindings/python/notmuch/query.py +++ b/bindings/python/notmuch/query.py @@ -88,7 +88,6 @@ class Query(object): :type db: :class:`Database` :param querystr: The query string :type querystr: utf-8 encoded str or unicode - :returns: Nothing :raises: :exc:`NullPointerError` if the query creation failed (e.g. too little memory). -- cgit v1.2.3 From c1094bc2d767159b5478cd0399d37a4813d0de05 Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Tue, 21 Feb 2012 00:56:07 +0100 Subject: python: allow an empty path as parameter to Database.get_directory Signed-off-by: Justus Winter <4winter@informatik.uni-hamburg.de> --- bindings/python/notmuch/database.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bindings/python/notmuch/database.py') diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py index d841367..ab3cdec 100644 --- a/bindings/python/notmuch/database.py +++ b/bindings/python/notmuch/database.py @@ -351,7 +351,7 @@ class Database(object): """ self._assert_db_is_initialized() # sanity checking if path is valid, and make path absolute - if path[0] == os.sep: + if path and path[0] == os.sep: # we got an absolute path if not path.startswith(self.get_path()): # but its initial components are not equal to the db path -- cgit v1.2.3 From 0b2ff308ece7e45a32a9e5a98d400b268278071a Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Tue, 21 Feb 2012 01:06:15 +0100 Subject: python: fix the type nonsense of the first parameter of class Directory Signed-off-by: Justus Winter <4winter@informatik.uni-hamburg.de> --- bindings/python/notmuch/database.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'bindings/python/notmuch/database.py') diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py index ab3cdec..504bb88 100644 --- a/bindings/python/notmuch/database.py +++ b/bindings/python/notmuch/database.py @@ -365,7 +365,7 @@ class Database(object): dir_p = Database._get_directory(self._db, _str(path)) # return the Directory, init it with the absolute path - return Directory(_str(abs_dirpath), dir_p, self) + return Directory(abs_dirpath, dir_p, self) _add_message = nmlib.notmuch_database_add_message _add_message.argtypes = [NotmuchDatabaseP, c_char_p, @@ -639,7 +639,7 @@ class Directory(object): def __init__(self, path, dir_p, parent): """ - :param path: The absolute path of the directory object as unicode. + :param path: The absolute path of the directory object. :param dir_p: The pointer to an internal notmuch_directory_t object. :param parent: The object this Directory is derived from (usually a :class:`Database`). We do not directly use @@ -647,7 +647,6 @@ class Directory(object): this Directory object lives. This keeps the parent object alive. """ - assert isinstance(path, unicode), "Path needs to be an UNICODE object" self._path = path self._dir_p = dir_p self._parent = parent -- cgit v1.2.3 From 92983dd14e3c9d767bb8ef6e9b85ef76b293cdd4 Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Wed, 22 Feb 2012 21:01:24 +0100 Subject: python: avoid using a magic value for database mode in Database.__init__ Signed-off-by: Justus Winter <4winter@informatik.uni-hamburg.de> --- bindings/python/notmuch/database.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'bindings/python/notmuch/database.py') diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py index 504bb88..c905395 100644 --- a/bindings/python/notmuch/database.py +++ b/bindings/python/notmuch/database.py @@ -122,7 +122,8 @@ class Database(object): _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 = MODE.READ_ONLY): """If *path* is `None`, we will try to read a users notmuch configuration and use his configured database. The location of the configuration file can be specified through the environment variable -- cgit v1.2.3 From 35ceaf496fe2d2a1324a8462e629e618ed20037a Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Wed, 22 Feb 2012 20:46:42 +0100 Subject: python: Improve the docstring of Database.get_directory Signed-off-by: Justus Winter <4winter@informatik.uni-hamburg.de> --- bindings/python/notmuch/database.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'bindings/python/notmuch/database.py') diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py index c905395..bef9720 100644 --- a/bindings/python/notmuch/database.py +++ b/bindings/python/notmuch/database.py @@ -345,10 +345,8 @@ class Database(object): 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. - :raises: - :exc:`NotmuchError` with :attr:`STATUS`.FILE_ERROR - If path is not relative database or absolute with initial - components same as database. + :raises: :exc:`FileError` if path is not relative database or absolute + with initial components same as database. """ self._assert_db_is_initialized() # sanity checking if path is valid, and make path absolute -- cgit v1.2.3 From fcf19ad029913e88558a21135feb0b5e1b33cef3 Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Wed, 22 Feb 2012 20:58:44 +0100 Subject: python: work around libnotmuch calling exit(3) in Database.get_directory Signed-off-by: Justus Winter <4winter@informatik.uni-hamburg.de> --- bindings/python/notmuch/database.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'bindings/python/notmuch/database.py') diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py index bef9720..82cb803 100644 --- a/bindings/python/notmuch/database.py +++ b/bindings/python/notmuch/database.py @@ -27,6 +27,7 @@ from notmuch.globals import ( NotmuchError, NullPointerError, NotInitializedError, + ReadOnlyDatabaseError, Enum, _str, NotmuchDatabaseP, @@ -145,6 +146,7 @@ class Database(object): failure. """ self._db = None + self.mode = mode if path is None: # no path specified. use a user's default database if Database._std_db_path is None: @@ -335,20 +337,24 @@ 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! - :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'. :returns: :class:`Directory` or raises an exception. :raises: :exc:`FileError` if path is not relative database or absolute with initial components same as database. + :raises: :exc:`ReadOnlyDatabaseError` if the database has not been + opened in read-write mode """ self._assert_db_is_initialized() + + # work around libnotmuch calling exit(3), see + # id:20120221002921.8534.57091@thinkbox.jade-hamburg.de + # TODO: remove once this issue is resolved + if self.mode != Database.MODE.READ_WRITE: + raise ReadOnlyDatabaseError('The database has to be opened in ' + 'read-write mode for get_directory') + # sanity checking if path is valid, and make path absolute if path and path[0] == os.sep: # we got an absolute path -- cgit v1.2.3 From 05cdb3d7b7c007364fe9fa38ff36488feaf698b7 Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Wed, 22 Feb 2012 21:07:18 +0100 Subject: python: improve the docstring of Database.find_message_by_filename Signed-off-by: Justus Winter <4winter@informatik.uni-hamburg.de> --- bindings/python/notmuch/database.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) (limited to 'bindings/python/notmuch/database.py') diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py index 82cb803..ebb8f20 100644 --- a/bindings/python/notmuch/database.py +++ b/bindings/python/notmuch/database.py @@ -515,17 +515,16 @@ class Database(object): function returns None if no message is found with the given filename. - :raises: - :exc:`OutOfMemoryError` - If an Out-of-memory occured while constructing the message. - :exc:`XapianError` - In case of a Xapian Exception. These exceptions - include "Database modified" situations, e.g. when the - notmuch database has been modified by another program - in the meantime. In this case, you should close and - reopen the database and retry. - :exc:`NotInitializedError` if - the database was not intitialized. + :raises: :exc:`OutOfMemoryError` if an Out-of-memory occured while + constructing the message. + :raises: :exc:`XapianError` in case of a Xapian Exception. + These exceptions include "Database modified" + situations, e.g. when the notmuch database has been + modified by another program in the meantime. In this + case, you should close and reopen the database and + retry. + :raises: :exc:`NotInitializedError` if the database was not + intitialized. *Added in notmuch 0.9*""" self._assert_db_is_initialized() -- cgit v1.2.3 From 1736488ecfd9b18a380ce04ac2df0303c0ea3c80 Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Wed, 22 Feb 2012 21:14:13 +0100 Subject: python: work around libnotmuch calling exit(3) in Database.find_message_by_filename Signed-off-by: Justus Winter <4winter@informatik.uni-hamburg.de> --- bindings/python/notmuch/database.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'bindings/python/notmuch/database.py') diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py index ebb8f20..a054be7 100644 --- a/bindings/python/notmuch/database.py +++ b/bindings/python/notmuch/database.py @@ -504,12 +504,6 @@ 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! - :returns: If the database contains a message with the given filename, then a class:`Message:` is returned. This function returns None if no message is found with the given @@ -525,9 +519,19 @@ class Database(object): retry. :raises: :exc:`NotInitializedError` if the database was not intitialized. + :raises: :exc:`ReadOnlyDatabaseError` if the database has not been + opened in read-write mode *Added in notmuch 0.9*""" self._assert_db_is_initialized() + + # work around libnotmuch calling exit(3), see + # id:20120221002921.8534.57091@thinkbox.jade-hamburg.de + # TODO: remove once this issue is resolved + if self.mode != Database.MODE.READ_WRITE: + raise ReadOnlyDatabaseError('The database has to be opened in ' + 'read-write mode for get_directory') + msg_p = NotmuchMessageP() status = Database._find_message_by_filename(self._db, _str(filename), byref(msg_p)) -- cgit v1.2.3 From ba95980cf1a5e2b32104611ccdf2e9c43bf3305a Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Wed, 22 Feb 2012 21:55:59 +0100 Subject: python: refactor the python bindings Move the Directory class into its own file, merge the two Filenames classes into one, deprecate Filenames.as_iterator, update the documentation accordingly. Signed-off-by: Justus Winter <4winter@informatik.uni-hamburg.de> --- bindings/python/docs/source/index.rst | 20 +-- bindings/python/notmuch/__init__.py | 15 ++- bindings/python/notmuch/database.py | 239 +--------------------------------- bindings/python/notmuch/directory.py | 183 ++++++++++++++++++++++++++ bindings/python/notmuch/filename.py | 62 +++++++-- 5 files changed, 256 insertions(+), 263 deletions(-) create mode 100644 bindings/python/notmuch/directory.py (limited to 'bindings/python/notmuch/database.py') diff --git a/bindings/python/docs/source/index.rst b/bindings/python/docs/source/index.rst index 25eb146..e87a865 100644 --- a/bindings/python/docs/source/index.rst +++ b/bindings/python/docs/source/index.rst @@ -254,26 +254,28 @@ More information on specific topics can be found on the following pages: :class:`Filenames` -- An iterator over filenames ------------------------------------------------ -.. autoclass:: notmuch.database.Filenames +.. autoclass:: notmuch.Filenames - .. automethod:: notmuch.database.Filenames.__len__ + .. automethod:: notmuch.Filenames.__len__ + + .. automethod:: notmuch.Filenames.as_generator :class:`notmuch.database.Directoy` -- A directory entry in the database ------------------------------------------------------------------------ -.. autoclass:: notmuch.database.Directory +.. autoclass:: notmuch.Directory - .. automethod:: notmuch.database.Directory.get_child_files + .. automethod:: notmuch.Directory.get_child_files - .. automethod:: notmuch.database.Directory.get_child_directories + .. automethod:: notmuch.Directory.get_child_directories - .. automethod:: notmuch.database.Directory.get_mtime + .. automethod:: notmuch.Directory.get_mtime - .. automethod:: notmuch.database.Directory.set_mtime + .. automethod:: notmuch.Directory.set_mtime - .. autoattribute:: notmuch.database.Directory.mtime + .. autoattribute:: notmuch.Directory.mtime - .. autoattribute:: notmuch.database.Directory.path + .. autoattribute:: notmuch.Directory.path The `next page `_ contains information on possible Status and Error values. diff --git a/bindings/python/notmuch/__init__.py b/bindings/python/notmuch/__init__.py index f3ff987..8de73d5 100644 --- a/bindings/python/notmuch/__init__.py +++ b/bindings/python/notmuch/__init__.py @@ -51,11 +51,14 @@ along with notmuch. If not, see . Copyright 2010-2011 Sebastian Spaeth """ -from notmuch.database import Database, Query -from notmuch.message import Messages, Message -from notmuch.thread import Threads, Thread -from notmuch.tag import Tags -from notmuch.globals import ( +from .database import Database +from .directory import Directory +from .filename import Filenames +from .message import Messages, Message +from .query import Query +from .tag import Tags +from .thread import Threads, Thread +from .globals import ( nmlib, STATUS, NotmuchError, @@ -71,6 +74,6 @@ from notmuch.globals import ( UnbalancedAtomicError, NotInitializedError, ) -from notmuch.version import __VERSION__ +from .version import __VERSION__ __LICENSE__ = "GPL v3+" __AUTHOR__ = 'Sebastian Spaeth ' diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py index a054be7..800264b 100644 --- a/bindings/python/notmuch/database.py +++ b/bindings/python/notmuch/database.py @@ -19,7 +19,7 @@ Copyright 2010 Sebastian Spaeth ' import os import codecs -from ctypes import c_char_p, c_void_p, c_uint, c_long, byref, POINTER +from ctypes import c_char_p, c_void_p, c_uint, byref, POINTER from notmuch.globals import ( nmlib, STATUS, @@ -34,11 +34,11 @@ from notmuch.globals import ( NotmuchDirectoryP, NotmuchMessageP, NotmuchTagsP, - NotmuchFilenamesP ) from notmuch.message import Message from notmuch.tag import Tags from .query import Query +from .directory import Directory class Database(object): """The :class:`Database` is the highest-level object that notmuch @@ -603,238 +603,3 @@ class Database(object): guaranteed to remain stable in future versions). """ return self._db - - -class Directory(object): - """Represents a directory entry in the notmuch directory - - Modifying attributes of this object will modify the - database, not the real directory attributes. - - The Directory object is usually derived from another object - e.g. via :meth:`Database.get_directory`, and will automatically be - become invalid whenever that parent is deleted. You should - therefore initialized this object handing it a reference to the - parent, preventing the parent from automatically being garbage - collected. - """ - - """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 = [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.argtypes = [NotmuchDirectoryP] - _get_child_files.restype = NotmuchFilenamesP - - """notmuch_directory_get_child_directories""" - _get_child_directories = nmlib.notmuch_directory_get_child_directories - _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""" - if not self._dir_p: - raise NotInitializedError() - - def __init__(self, path, dir_p, parent): - """ - :param path: The absolute path of the directory object. - :param dir_p: The pointer to an internal notmuch_directory_t object. - :param parent: The object this Directory is derived from - (usually a :class:`Database`). We do not directly use - this, but store a reference to it as long as - this Directory object lives. This keeps the - parent object alive. - """ - self._path = path - self._dir_p = dir_p - self._parent = parent - - def set_mtime(self, mtime): - """Sets the mtime value of this directory in the database - - The intention is for the caller to use the mtime to allow efficient - identification of new messages to be added to the database. The - recommended usage is as follows: - - * Read the mtime of a directory from the filesystem - - * Call :meth:`Database.add_message` for all mail files in - the directory - - * Call notmuch_directory_set_mtime with the mtime read from the - filesystem. Then, when wanting to check for updates to the - directory in the future, the client can call :meth:`get_mtime` - 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. - - :param mtime: A (time_t) timestamp - :raises: :exc:`XapianError` a Xapian exception occurred, mtime - not stored - :raises: :exc:`ReadOnlyDatabaseError` the database was opened - in read-only mode so directory mtime cannot be modified - :raises: :exc:`NotInitializedError` the directory object has not - been initialized - """ - self._assert_dir_is_initialized() - status = Directory._set_mtime(self._dir_p, mtime) - - if status != STATUS.SUCCESS: - raise NotmuchError(status) - - def get_mtime(self): - """Gets the mtime value of this directory in the database - - Retrieves a previously stored mtime for this directory. - - :param mtime: A (time_t) timestamp - :raises: :exc:`NotmuchError`: - - :attr:`STATUS`.NOT_INITIALIZED - The directory has not been initialized - """ - self._assert_dir_is_initialized() - return Directory._get_mtime(self._dir_p) - - # Make mtime attribute a property of Directory() - mtime = property(get_mtime, set_mtime, doc="""Property that allows getting - and setting of the Directory *mtime* (read-write) - - See :meth:`get_mtime` and :meth:`set_mtime` for usage and - possible exceptions.""") - - def get_child_files(self): - """Gets a Filenames iterator listing all the filenames of - messages in the database within the given directory. - - The returned filenames will be the basename-entries only (not - complete paths. - """ - self._assert_dir_is_initialized() - files_p = Directory._get_child_files(self._dir_p) - return Filenames(files_p, self) - - def get_child_directories(self): - """Gets a :class:`Filenames` iterator listing all the filenames of - sub-directories in the database within the given directory - - The returned filenames will be the basename-entries only (not - complete paths. - """ - self._assert_dir_is_initialized() - files_p = Directory._get_child_directories(self._dir_p) - return Filenames(files_p, self) - - @property - def path(self): - """Returns the absolute path of this Directory (read-only)""" - return self._path - - def __repr__(self): - """Object representation""" - return "" % 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: - self._destroy(self._dir_p) - - -class Filenames(object): - """An iterator over File- or Directory names stored in the database""" - - #notmuch_filenames_get - _get = nmlib.notmuch_filenames_get - _get.argtypes = [NotmuchFilenamesP] - _get.restype = c_char_p - - def __init__(self, files_p, parent): - """ - :param files_p: The pointer to an internal notmuch_filenames_t object. - :param parent: The object this Directory is derived from - (usually a Directory()). We do not directly use - this, but store a reference to it as long as - this Directory object lives. This keeps the - parent object alive. - """ - self._files_p = files_p - self._parent = parent - - def __iter__(self): - """ 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 not self._files_p: - raise NotInitializedError() - - if not self._valid(self._files_p): - self._files_p = None - raise StopIteration - - file_ = Filenames._get(self._files_p) - self._move_to_next(self._files_p) - return file_.decode('utf-8', 'ignore') - next = __next__ # python2.x iterator protocol compatibility - - 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:: - - #THIS FAILS - files = Database().get_directory('').get_child_files() - if len(files) > 0: # this 'exhausts' msgs - # next line raises - # NotmuchError(:attr:`STATUS`.NOT_INITIALIZED) - for file in files: print file - """ - if not self._files_p: - raise NotInitializedError() - - i = 0 - 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: - self._destroy(self._files_p) diff --git a/bindings/python/notmuch/directory.py b/bindings/python/notmuch/directory.py new file mode 100644 index 0000000..3e0763f --- /dev/null +++ b/bindings/python/notmuch/directory.py @@ -0,0 +1,183 @@ +""" +This file is part of notmuch. + +Notmuch is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation, either version 3 of the License, or (at your +option) any later version. + +Notmuch is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with notmuch. If not, see . + +Copyright 2010 Sebastian Spaeth ' +""" + +from ctypes import c_uint, c_long +from notmuch.globals import ( + nmlib, + STATUS, + NotmuchError, + NotInitializedError, + NotmuchDirectoryP, + NotmuchFilenamesP +) +from .filename import Filenames + +class Directory(object): + """Represents a directory entry in the notmuch directory + + Modifying attributes of this object will modify the + database, not the real directory attributes. + + The Directory object is usually derived from another object + e.g. via :meth:`Database.get_directory`, and will automatically be + become invalid whenever that parent is deleted. You should + therefore initialized this object handing it a reference to the + parent, preventing the parent from automatically being garbage + collected. + """ + + """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 = [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.argtypes = [NotmuchDirectoryP] + _get_child_files.restype = NotmuchFilenamesP + + """notmuch_directory_get_child_directories""" + _get_child_directories = nmlib.notmuch_directory_get_child_directories + _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""" + if not self._dir_p: + raise NotInitializedError() + + def __init__(self, path, dir_p, parent): + """ + :param path: The absolute path of the directory object. + :param dir_p: The pointer to an internal notmuch_directory_t object. + :param parent: The object this Directory is derived from + (usually a :class:`Database`). We do not directly use + this, but store a reference to it as long as + this Directory object lives. This keeps the + parent object alive. + """ + self._path = path + self._dir_p = dir_p + self._parent = parent + + def set_mtime(self, mtime): + """Sets the mtime value of this directory in the database + + The intention is for the caller to use the mtime to allow efficient + identification of new messages to be added to the database. The + recommended usage is as follows: + + * Read the mtime of a directory from the filesystem + + * Call :meth:`Database.add_message` for all mail files in + the directory + + * Call notmuch_directory_set_mtime with the mtime read from the + filesystem. Then, when wanting to check for updates to the + directory in the future, the client can call :meth:`get_mtime` + 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. + + :param mtime: A (time_t) timestamp + :raises: :exc:`XapianError` a Xapian exception occurred, mtime + not stored + :raises: :exc:`ReadOnlyDatabaseError` the database was opened + in read-only mode so directory mtime cannot be modified + :raises: :exc:`NotInitializedError` the directory object has not + been initialized + """ + self._assert_dir_is_initialized() + status = Directory._set_mtime(self._dir_p, mtime) + + if status != STATUS.SUCCESS: + raise NotmuchError(status) + + def get_mtime(self): + """Gets the mtime value of this directory in the database + + Retrieves a previously stored mtime for this directory. + + :param mtime: A (time_t) timestamp + :raises: :exc:`NotmuchError`: + + :attr:`STATUS`.NOT_INITIALIZED + The directory has not been initialized + """ + self._assert_dir_is_initialized() + return Directory._get_mtime(self._dir_p) + + # Make mtime attribute a property of Directory() + mtime = property(get_mtime, set_mtime, doc="""Property that allows getting + and setting of the Directory *mtime* (read-write) + + See :meth:`get_mtime` and :meth:`set_mtime` for usage and + possible exceptions.""") + + def get_child_files(self): + """Gets a Filenames iterator listing all the filenames of + messages in the database within the given directory. + + The returned filenames will be the basename-entries only (not + complete paths. + """ + self._assert_dir_is_initialized() + files_p = Directory._get_child_files(self._dir_p) + return Filenames(files_p, self) + + def get_child_directories(self): + """Gets a :class:`Filenames` iterator listing all the filenames of + sub-directories in the database within the given directory + + The returned filenames will be the basename-entries only (not + complete paths. + """ + self._assert_dir_is_initialized() + files_p = Directory._get_child_directories(self._dir_p) + return Filenames(files_p, self) + + @property + def path(self): + """Returns the absolute path of this Directory (read-only)""" + return self._path + + def __repr__(self): + """Object representation""" + return "" % 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: + self._destroy(self._dir_p) diff --git a/bindings/python/notmuch/filename.py b/bindings/python/notmuch/filename.py index 353eb76..232a9ed 100644 --- a/bindings/python/notmuch/filename.py +++ b/bindings/python/notmuch/filename.py @@ -78,10 +78,14 @@ class Filenames(Python3StringMixIn): if not files_p: raise NullPointerError() - self._files = files_p + self._files_p = files_p #save reference to parent object so we keep it alive self._parent = parent + def __iter__(self): + """ Make Filenames an iterator """ + return self + _valid = nmlib.notmuch_filenames_valid _valid.argtypes = [NotmuchFilenamesP] _valid.restype = bool @@ -90,19 +94,30 @@ class Filenames(Python3StringMixIn): _move_to_next.argtypes = [NotmuchFilenamesP] _move_to_next.restype = None + def __next__(self): + if not self._files_p: + raise NotInitializedError() + + if not self._valid(self._files_p): + self._files_p = None + raise StopIteration + + file_ = Filenames._get(self._files_p) + self._move_to_next(self._files_p) + return file_.decode('utf-8', 'ignore') + next = __next__ # python2.x iterator protocol compatibility + def as_generator(self): """Return generator of Filenames This is the main function that will usually be used by the - user.""" - if not self._files: - raise NotInitializedError() + user. - while self._valid(self._files): - yield Filenames._get(self._files).decode('utf-8', 'ignore') - self._move_to_next(self._files) - - self._files = None + .. deprecated:: 0.12 + :class:`Filenames` objects implement the + iterator protocol. + """ + return self def __unicode__(self): """Represent Filenames() as newline-separated list of full paths @@ -123,5 +138,30 @@ class Filenames(Python3StringMixIn): def __del__(self): """Close and free the notmuch filenames""" - if self._files is not None: - self._destroy(self._files) + if self._files_p is not None: + self._destroy(self._files_p) + + 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:: + + #THIS FAILS + files = Database().get_directory('').get_child_files() + if len(files) > 0: # this 'exhausts' msgs + # next line raises + # NotmuchError(:attr:`STATUS`.NOT_INITIALIZED) + for file in files: print file + """ + if not self._files_p: + raise NotInitializedError() + + i = 0 + while self._valid(self._files_p): + self._move_to_next(self._files_p) + i += 1 + self._files_p = None + return i -- cgit v1.2.3 From a7561cc20b17669784c3259afcbcef98029f93e9 Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Thu, 23 Feb 2012 00:11:22 +0100 Subject: python: move the exception classes into error.py Signed-off-by: Justus Winter <4winter@informatik.uni-hamburg.de> --- bindings/python/notmuch/__init__.py | 4 +- bindings/python/notmuch/database.py | 14 +-- bindings/python/notmuch/directory.py | 6 +- bindings/python/notmuch/errors.py | 183 +++++++++++++++++++++++++++++++++++ bindings/python/notmuch/filenames.py | 6 +- bindings/python/notmuch/globals.py | 160 +----------------------------- bindings/python/notmuch/message.py | 10 +- bindings/python/notmuch/messages.py | 6 +- bindings/python/notmuch/query.py | 2 + bindings/python/notmuch/tag.py | 4 +- bindings/python/notmuch/thread.py | 6 +- bindings/python/notmuch/threads.py | 6 +- 12 files changed, 225 insertions(+), 182 deletions(-) create mode 100644 bindings/python/notmuch/errors.py (limited to 'bindings/python/notmuch/database.py') diff --git a/bindings/python/notmuch/__init__.py b/bindings/python/notmuch/__init__.py index fddc492..5561624 100644 --- a/bindings/python/notmuch/__init__.py +++ b/bindings/python/notmuch/__init__.py @@ -60,8 +60,8 @@ from .query import Query from .tag import Tags from .thread import Thread from .threads import Threads -from .globals import ( - nmlib, +from .globals import nmlib +from .errors import ( STATUS, NotmuchError, OutOfMemoryError, diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py index 800264b..44d40fd 100644 --- a/bindings/python/notmuch/database.py +++ b/bindings/python/notmuch/database.py @@ -22,12 +22,6 @@ import codecs from ctypes import c_char_p, c_void_p, c_uint, byref, POINTER from notmuch.globals import ( nmlib, - STATUS, - FileError, - NotmuchError, - NullPointerError, - NotInitializedError, - ReadOnlyDatabaseError, Enum, _str, NotmuchDatabaseP, @@ -35,6 +29,14 @@ from notmuch.globals import ( NotmuchMessageP, NotmuchTagsP, ) +from .errors import ( + STATUS, + FileError, + NotmuchError, + NullPointerError, + NotInitializedError, + ReadOnlyDatabaseError, +) from notmuch.message import Message from notmuch.tag import Tags from .query import Query diff --git a/bindings/python/notmuch/directory.py b/bindings/python/notmuch/directory.py index b679aa2..0c5e015 100644 --- a/bindings/python/notmuch/directory.py +++ b/bindings/python/notmuch/directory.py @@ -20,11 +20,13 @@ Copyright 2010 Sebastian Spaeth ' from ctypes import c_uint, c_long from notmuch.globals import ( nmlib, + NotmuchDirectoryP, + NotmuchFilenamesP +) +from .errors import ( STATUS, NotmuchError, NotInitializedError, - NotmuchDirectoryP, - NotmuchFilenamesP ) from .filenames import Filenames diff --git a/bindings/python/notmuch/errors.py b/bindings/python/notmuch/errors.py new file mode 100644 index 0000000..f153a9c --- /dev/null +++ b/bindings/python/notmuch/errors.py @@ -0,0 +1,183 @@ +""" +This file is part of notmuch. + +Notmuch is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation, either version 3 of the License, or (at your +option) any later version. + +Notmuch is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with notmuch. If not, see . + +Copyright 2010 Sebastian Spaeth +""" + +from ctypes import c_char_p, c_int + +from .globals import ( + nmlib, + Enum, + Python3StringMixIn, +) + +class Status(Enum): + """Enum with a string representation of a notmuch_status_t value.""" + _status2str = nmlib.notmuch_status_to_string + _status2str.restype = c_char_p + _status2str.argtypes = [c_int] + + def __init__(self, statuslist): + """It is initialized with a list of strings that are available as + Status().string1 - Status().stringn attributes. + """ + super(Status, self).__init__(statuslist) + + @classmethod + def status2str(self, status): + """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 unicode(Status._status2str(status)) + +STATUS = Status(['SUCCESS', + 'OUT_OF_MEMORY', + 'READ_ONLY_DATABASE', + 'XAPIAN_EXCEPTION', + 'FILE_ERROR', + 'FILE_NOT_EMAIL', + 'DUPLICATE_MESSAGE_ID', + 'NULL_POINTER', + 'TAG_TOO_LONG', + 'UNBALANCED_FREEZE_THAW', + 'UNBALANCED_ATOMIC', + 'NOT_INITIALIZED']) +"""STATUS is a class, whose attributes provide constants that serve as return +indicators for notmuch functions. Currently the following ones are defined. For +possible return values and specific meaning for each method, see the method +description. + + * SUCCESS + * OUT_OF_MEMORY + * READ_ONLY_DATABASE + * XAPIAN_EXCEPTION + * FILE_ERROR + * FILE_NOT_EMAIL + * DUPLICATE_MESSAGE_ID + * NULL_POINTER + * TAG_TOO_LONG + * UNBALANCED_FREEZE_THAW + * UNBALANCED_ATOMIC + * NOT_INITIALIZED + +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, Python3StringMixIn): + """Is initiated with a (notmuch.STATUS[, message=None]). It will not + return an instance of the class NotmuchError, but a derived instance + of a more specific Error Message, e.g. OutOfMemoryError. Each status + but SUCCESS has a corresponding subclassed Exception.""" + + @classmethod + def get_exc_subclass(cls, status): + """Returns a fine grained Exception() type, + detailing the error status""" + subclasses = { + STATUS.OUT_OF_MEMORY: OutOfMemoryError, + STATUS.READ_ONLY_DATABASE: ReadOnlyDatabaseError, + STATUS.XAPIAN_EXCEPTION: XapianError, + STATUS.FILE_ERROR: FileError, + STATUS.FILE_NOT_EMAIL: FileNotEmailError, + STATUS.DUPLICATE_MESSAGE_ID: DuplicateMessageIdError, + STATUS.NULL_POINTER: NullPointerError, + STATUS.TAG_TOO_LONG: TagTooLongError, + STATUS.UNBALANCED_FREEZE_THAW: UnbalancedFreezeThawError, + STATUS.UNBALANCED_ATOMIC: UnbalancedAtomicError, + STATUS.NOT_INITIALIZED: NotInitializedError, + } + assert 0 < status <= len(subclasses) + return subclasses[status] + + def __new__(cls, *args, **kwargs): + """Return a correct subclass of NotmuchError if needed + + We return a NotmuchError instance if status is None (or 0) and a + subclass that inherits from NotmuchError depending on the + 'status' parameter otherwise.""" + # get 'status'. Passed in as arg or kwarg? + status = args[0] if len(args) else kwargs.get('status', None) + # 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? + return subclass.__new__(subclass, *args, **kwargs) + + def __init__(self, status=None, message=None): + self.status = status + self.message = message + + 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' + + +# 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 + been exhausted. You can test for NotmuchError with .status = + STATUS.NOT_INITIALIZED""" + status = STATUS.NOT_INITIALIZED diff --git a/bindings/python/notmuch/filenames.py b/bindings/python/notmuch/filenames.py index 232a9ed..12050df 100644 --- a/bindings/python/notmuch/filenames.py +++ b/bindings/python/notmuch/filenames.py @@ -19,12 +19,14 @@ Copyright 2010 Sebastian Spaeth ' from ctypes import c_char_p from notmuch.globals import ( nmlib, - NullPointerError, - NotInitializedError, NotmuchMessageP, NotmuchFilenamesP, Python3StringMixIn, ) +from .errors import ( + NullPointerError, + NotInitializedError, +) class Filenames(Python3StringMixIn): diff --git a/bindings/python/notmuch/globals.py b/bindings/python/notmuch/globals.py index 4138460..442f3e3 100644 --- a/bindings/python/notmuch/globals.py +++ b/bindings/python/notmuch/globals.py @@ -17,7 +17,7 @@ along with notmuch. If not, see . Copyright 2010 Sebastian Spaeth ' """ import sys -from ctypes import CDLL, c_char_p, c_int, Structure, POINTER +from ctypes import CDLL, Structure, POINTER #----------------------------------------------------------------------------- #package-global instance of the notmuch library @@ -66,164 +66,6 @@ class Enum(object): setattr(self, name, number) -class Status(Enum): - """Enum with a string representation of a notmuch_status_t value.""" - _status2str = nmlib.notmuch_status_to_string - _status2str.restype = c_char_p - _status2str.argtypes = [c_int] - - def __init__(self, statuslist): - """It is initialized with a list of strings that are available as - Status().string1 - Status().stringn attributes. - """ - super(Status, self).__init__(statuslist) - - @classmethod - def status2str(self, status): - """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 unicode(Status._status2str(status)) - -STATUS = Status(['SUCCESS', - 'OUT_OF_MEMORY', - 'READ_ONLY_DATABASE', - 'XAPIAN_EXCEPTION', - 'FILE_ERROR', - 'FILE_NOT_EMAIL', - 'DUPLICATE_MESSAGE_ID', - 'NULL_POINTER', - 'TAG_TOO_LONG', - 'UNBALANCED_FREEZE_THAW', - 'UNBALANCED_ATOMIC', - 'NOT_INITIALIZED']) -"""STATUS is a class, whose attributes provide constants that serve as return -indicators for notmuch functions. Currently the following ones are defined. For -possible return values and specific meaning for each method, see the method -description. - - * SUCCESS - * OUT_OF_MEMORY - * READ_ONLY_DATABASE - * XAPIAN_EXCEPTION - * FILE_ERROR - * FILE_NOT_EMAIL - * DUPLICATE_MESSAGE_ID - * NULL_POINTER - * TAG_TOO_LONG - * UNBALANCED_FREEZE_THAW - * UNBALANCED_ATOMIC - * NOT_INITIALIZED - -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, Python3StringMixIn): - """Is initiated with a (notmuch.STATUS[, message=None]). It will not - return an instance of the class NotmuchError, but a derived instance - of a more specific Error Message, e.g. OutOfMemoryError. Each status - but SUCCESS has a corresponding subclassed Exception.""" - - @classmethod - def get_exc_subclass(cls, status): - """Returns a fine grained Exception() type, - detailing the error status""" - subclasses = { - STATUS.OUT_OF_MEMORY: OutOfMemoryError, - STATUS.READ_ONLY_DATABASE: ReadOnlyDatabaseError, - STATUS.XAPIAN_EXCEPTION: XapianError, - STATUS.FILE_ERROR: FileError, - STATUS.FILE_NOT_EMAIL: FileNotEmailError, - STATUS.DUPLICATE_MESSAGE_ID: DuplicateMessageIdError, - STATUS.NULL_POINTER: NullPointerError, - STATUS.TAG_TOO_LONG: TagTooLongError, - STATUS.UNBALANCED_FREEZE_THAW: UnbalancedFreezeThawError, - STATUS.UNBALANCED_ATOMIC: UnbalancedAtomicError, - STATUS.NOT_INITIALIZED: NotInitializedError, - } - assert 0 < status <= len(subclasses) - return subclasses[status] - - def __new__(cls, *args, **kwargs): - """Return a correct subclass of NotmuchError if needed - - We return a NotmuchError instance if status is None (or 0) and a - subclass that inherits from NotmuchError depending on the - 'status' parameter otherwise.""" - # get 'status'. Passed in as arg or kwarg? - status = args[0] if len(args) else kwargs.get('status', None) - # 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? - return subclass.__new__(subclass, *args, **kwargs) - - def __init__(self, status=None, message=None): - self.status = status - self.message = message - - 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' - - -# 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 - been exhausted. You can test for NotmuchError with .status = - STATUS.NOT_INITIALIZED""" - status = STATUS.NOT_INITIALIZED - - class NotmuchDatabaseS(Structure): pass NotmuchDatabaseP = POINTER(NotmuchDatabaseS) diff --git a/bindings/python/notmuch/message.py b/bindings/python/notmuch/message.py index d17b9bc..9eb4fee 100644 --- a/bindings/python/notmuch/message.py +++ b/bindings/python/notmuch/message.py @@ -26,15 +26,17 @@ from .globals import ( Enum, _str, Python3StringMixIn, - STATUS, - NotmuchError, - NullPointerError, - NotInitializedError, NotmuchTagsP, NotmuchMessageP, NotmuchMessagesP, NotmuchFilenamesP, ) +from .errors import ( + STATUS, + NotmuchError, + NullPointerError, + NotInitializedError, +) from .tag import Tags from .filenames import Filenames diff --git a/bindings/python/notmuch/messages.py b/bindings/python/notmuch/messages.py index 20d3632..d94f91b 100644 --- a/bindings/python/notmuch/messages.py +++ b/bindings/python/notmuch/messages.py @@ -20,12 +20,14 @@ Copyright 2010 Sebastian Spaeth ' from .globals import ( nmlib, - NullPointerError, - NotInitializedError, NotmuchTagsP, NotmuchMessageP, NotmuchMessagesP, ) +from .errors import ( + NullPointerError, + NotInitializedError, +) from .tag import Tags from .message import Message diff --git a/bindings/python/notmuch/query.py b/bindings/python/notmuch/query.py index fcd67e5..ddaf8e0 100644 --- a/bindings/python/notmuch/query.py +++ b/bindings/python/notmuch/query.py @@ -26,6 +26,8 @@ from notmuch.globals import ( NotmuchThreadsP, NotmuchDatabaseP, NotmuchMessagesP, +) +from .errors import ( NullPointerError, NotInitializedError, ) diff --git a/bindings/python/notmuch/tag.py b/bindings/python/notmuch/tag.py index 526e51c..711bf53 100644 --- a/bindings/python/notmuch/tag.py +++ b/bindings/python/notmuch/tag.py @@ -20,9 +20,11 @@ from ctypes import c_char_p from notmuch.globals import ( nmlib, Python3StringMixIn, + NotmuchTagsP, +) +from .errors import ( NullPointerError, NotInitializedError, - NotmuchTagsP, ) diff --git a/bindings/python/notmuch/thread.py b/bindings/python/notmuch/thread.py index 0dac522..a759c90 100644 --- a/bindings/python/notmuch/thread.py +++ b/bindings/python/notmuch/thread.py @@ -20,12 +20,14 @@ Copyright 2010 Sebastian Spaeth ' from ctypes import c_char_p, c_long, c_int from notmuch.globals import ( nmlib, - NullPointerError, - NotInitializedError, NotmuchThreadP, NotmuchMessagesP, NotmuchTagsP, ) +from .errors import ( + NullPointerError, + NotInitializedError, +) from .messages import Messages from notmuch.tag import Tags from datetime import date diff --git a/bindings/python/notmuch/threads.py b/bindings/python/notmuch/threads.py index 9d305e2..690206e 100644 --- a/bindings/python/notmuch/threads.py +++ b/bindings/python/notmuch/threads.py @@ -20,11 +20,13 @@ Copyright 2010 Sebastian Spaeth ' from notmuch.globals import ( nmlib, Python3StringMixIn, - NullPointerError, - NotInitializedError, NotmuchThreadP, NotmuchThreadsP, ) +from .errors import ( + NullPointerError, + NotInitializedError, +) from .thread import Thread class Threads(Python3StringMixIn): -- cgit v1.2.3 From 7bfc4bf501af2207123135065b08bd4874446de8 Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Sun, 22 Apr 2012 14:07:57 +0200 Subject: python: wrap and use notmuch_database_destroy as destructor Adapt the python bindings to the notmuch_database_close split. Signed-off-by: Justus Winter <4winter@informatik.uni-hamburg.de> --- bindings/python/notmuch/database.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'bindings/python/notmuch/database.py') diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py index 44d40fd..268e952 100644 --- a/bindings/python/notmuch/database.py +++ b/bindings/python/notmuch/database.py @@ -161,8 +161,13 @@ class Database(object): else: self.create(path) + _destroy = nmlib.notmuch_database_destroy + _destroy.argtypes = [NotmuchDatabaseP] + _destroy.restype = None + def __del__(self): - self.close() + if self._db: + self._destroy(self._db) def _assert_db_is_initialized(self): """Raises :exc:`NotInitializedError` if self._db is `None`""" @@ -218,10 +223,11 @@ class Database(object): _close.restype = None def close(self): - """Close and free the notmuch database if needed""" - if self._db is not None: + ''' + Closes the notmuch database. + ''' + if self._db: self._close(self._db) - self._db = None def __enter__(self): ''' -- cgit v1.2.3 From 1f08664a6b8f7cba63f63855833f877b66bbbe05 Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Sun, 29 Apr 2012 16:33:06 +0200 Subject: python: strip superfluous single quote from copyright notices Signed-off-by: Justus Winter <4winter@informatik.uni-hamburg.de> --- bindings/python/notmuch/database.py | 2 +- bindings/python/notmuch/directory.py | 2 +- bindings/python/notmuch/filenames.py | 2 +- bindings/python/notmuch/globals.py | 2 +- bindings/python/notmuch/message.py | 2 +- bindings/python/notmuch/messages.py | 2 +- bindings/python/notmuch/query.py | 2 +- bindings/python/notmuch/tag.py | 2 +- bindings/python/notmuch/thread.py | 2 +- bindings/python/notmuch/threads.py | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) (limited to 'bindings/python/notmuch/database.py') diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py index 268e952..0ac8b06 100644 --- a/bindings/python/notmuch/database.py +++ b/bindings/python/notmuch/database.py @@ -14,7 +14,7 @@ for more details. You should have received a copy of the GNU General Public License along with notmuch. If not, see . -Copyright 2010 Sebastian Spaeth ' +Copyright 2010 Sebastian Spaeth """ import os diff --git a/bindings/python/notmuch/directory.py b/bindings/python/notmuch/directory.py index 284cbdc..667d3a4 100644 --- a/bindings/python/notmuch/directory.py +++ b/bindings/python/notmuch/directory.py @@ -14,7 +14,7 @@ for more details. You should have received a copy of the GNU General Public License along with notmuch. If not, see . -Copyright 2010 Sebastian Spaeth ' +Copyright 2010 Sebastian Spaeth """ from ctypes import c_uint, c_long diff --git a/bindings/python/notmuch/filenames.py b/bindings/python/notmuch/filenames.py index 12050df..f3d761d 100644 --- a/bindings/python/notmuch/filenames.py +++ b/bindings/python/notmuch/filenames.py @@ -14,7 +14,7 @@ for more details. You should have received a copy of the GNU General Public License along with notmuch. If not, see . -Copyright 2010 Sebastian Spaeth ' +Copyright 2010 Sebastian Spaeth """ from ctypes import c_char_p from notmuch.globals import ( diff --git a/bindings/python/notmuch/globals.py b/bindings/python/notmuch/globals.py index 442f3e3..823c3e2 100644 --- a/bindings/python/notmuch/globals.py +++ b/bindings/python/notmuch/globals.py @@ -14,7 +14,7 @@ for more details. You should have received a copy of the GNU General Public License along with notmuch. If not, see . -Copyright 2010 Sebastian Spaeth ' +Copyright 2010 Sebastian Spaeth """ import sys from ctypes import CDLL, Structure, POINTER diff --git a/bindings/python/notmuch/message.py b/bindings/python/notmuch/message.py index 9eb4fee..f1faf1d 100644 --- a/bindings/python/notmuch/message.py +++ b/bindings/python/notmuch/message.py @@ -14,7 +14,7 @@ for more details. You should have received a copy of the GNU General Public License along with notmuch. If not, see . -Copyright 2010 Sebastian Spaeth ' +Copyright 2010 Sebastian Spaeth Jesse Rosenthal """ diff --git a/bindings/python/notmuch/messages.py b/bindings/python/notmuch/messages.py index d94f91b..6b024cb 100644 --- a/bindings/python/notmuch/messages.py +++ b/bindings/python/notmuch/messages.py @@ -14,7 +14,7 @@ for more details. You should have received a copy of the GNU General Public License along with notmuch. If not, see . -Copyright 2010 Sebastian Spaeth ' +Copyright 2010 Sebastian Spaeth Jesse Rosenthal """ diff --git a/bindings/python/notmuch/query.py b/bindings/python/notmuch/query.py index ddaf8e0..27bc4df 100644 --- a/bindings/python/notmuch/query.py +++ b/bindings/python/notmuch/query.py @@ -14,7 +14,7 @@ for more details. You should have received a copy of the GNU General Public License along with notmuch. If not, see . -Copyright 2010 Sebastian Spaeth ' +Copyright 2010 Sebastian Spaeth """ from ctypes import c_char_p, c_uint diff --git a/bindings/python/notmuch/tag.py b/bindings/python/notmuch/tag.py index 711bf53..9b18160 100644 --- a/bindings/python/notmuch/tag.py +++ b/bindings/python/notmuch/tag.py @@ -14,7 +14,7 @@ for more details. You should have received a copy of the GNU General Public License along with notmuch. If not, see . -Copyright 2010 Sebastian Spaeth ' +Copyright 2010 Sebastian Spaeth """ from ctypes import c_char_p from notmuch.globals import ( diff --git a/bindings/python/notmuch/thread.py b/bindings/python/notmuch/thread.py index a759c90..a47b209 100644 --- a/bindings/python/notmuch/thread.py +++ b/bindings/python/notmuch/thread.py @@ -14,7 +14,7 @@ for more details. You should have received a copy of the GNU General Public License along with notmuch. If not, see . -Copyright 2010 Sebastian Spaeth ' +Copyright 2010 Sebastian Spaeth """ from ctypes import c_char_p, c_long, c_int diff --git a/bindings/python/notmuch/threads.py b/bindings/python/notmuch/threads.py index 225f524..fb053f9 100644 --- a/bindings/python/notmuch/threads.py +++ b/bindings/python/notmuch/threads.py @@ -14,7 +14,7 @@ for more details. You should have received a copy of the GNU General Public License along with notmuch. If not, see . -Copyright 2010 Sebastian Spaeth ' +Copyright 2010 Sebastian Spaeth """ from notmuch.globals import ( -- cgit v1.2.3 From 162687a99e412098729d639ed7bc27f01372cb84 Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Mon, 30 Apr 2012 18:52:35 +0200 Subject: python: fix NULL pointer tests Fix the NULL pointer tests in the destructors of all classes and Database.create. Signed-off-by: Justus Winter <4winter@informatik.uni-hamburg.de> --- bindings/python/notmuch/database.py | 2 +- bindings/python/notmuch/directory.py | 2 +- bindings/python/notmuch/filenames.py | 2 +- bindings/python/notmuch/message.py | 2 +- bindings/python/notmuch/messages.py | 2 +- bindings/python/notmuch/query.py | 2 +- bindings/python/notmuch/tag.py | 2 +- bindings/python/notmuch/thread.py | 2 +- bindings/python/notmuch/threads.py | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) (limited to 'bindings/python/notmuch/database.py') diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py index 0ac8b06..525f7c9 100644 --- a/bindings/python/notmuch/database.py +++ b/bindings/python/notmuch/database.py @@ -189,7 +189,7 @@ class Database(object): :raises: :exc:`NotmuchError` in case of any failure (possibly after printing an error message on stderr). """ - if self._db is not None: + if self._db: raise NotmuchError(message="Cannot create db, this Database() " "already has an open one.") diff --git a/bindings/python/notmuch/directory.py b/bindings/python/notmuch/directory.py index 667d3a4..ae115f8 100644 --- a/bindings/python/notmuch/directory.py +++ b/bindings/python/notmuch/directory.py @@ -181,5 +181,5 @@ class Directory(object): def __del__(self): """Close and free the Directory""" - if self._dir_p is not None: + if self._dir_p: self._destroy(self._dir_p) diff --git a/bindings/python/notmuch/filenames.py b/bindings/python/notmuch/filenames.py index d201ae2..a0b2956 100644 --- a/bindings/python/notmuch/filenames.py +++ b/bindings/python/notmuch/filenames.py @@ -128,7 +128,7 @@ class Filenames(Python3StringMixIn): def __del__(self): """Close and free the notmuch filenames""" - if self._files_p is not None: + if self._files_p: self._destroy(self._files_p) def __len__(self): diff --git a/bindings/python/notmuch/message.py b/bindings/python/notmuch/message.py index 54c4e4b..4ec5147 100644 --- a/bindings/python/notmuch/message.py +++ b/bindings/python/notmuch/message.py @@ -741,5 +741,5 @@ class Message(Python3StringMixIn): def __del__(self): """Close and free the notmuch Message""" - if self._msg is not None: + if self._msg: self._destroy(self._msg) diff --git a/bindings/python/notmuch/messages.py b/bindings/python/notmuch/messages.py index 6b024cb..251fa3a 100644 --- a/bindings/python/notmuch/messages.py +++ b/bindings/python/notmuch/messages.py @@ -184,7 +184,7 @@ class Messages(object): def __del__(self): """Close and free the notmuch Messages""" - if self._msgs is not None: + if self._msgs: self._destroy(self._msgs) def format_messages(self, format, indent=0, entire_thread=False): diff --git a/bindings/python/notmuch/query.py b/bindings/python/notmuch/query.py index 27bc4df..756e63b 100644 --- a/bindings/python/notmuch/query.py +++ b/bindings/python/notmuch/query.py @@ -203,5 +203,5 @@ class Query(object): def __del__(self): """Close and free the Query""" - if self._query is not None: + if self._query: self._destroy(self._query) diff --git a/bindings/python/notmuch/tag.py b/bindings/python/notmuch/tag.py index 9b18160..e059813 100644 --- a/bindings/python/notmuch/tag.py +++ b/bindings/python/notmuch/tag.py @@ -137,5 +137,5 @@ class Tags(Python3StringMixIn): def __del__(self): """Close and free the notmuch tags""" - if self._tags is not None: + if self._tags: self._destroy(self._tags) diff --git a/bindings/python/notmuch/thread.py b/bindings/python/notmuch/thread.py index a47b209..2f60d49 100644 --- a/bindings/python/notmuch/thread.py +++ b/bindings/python/notmuch/thread.py @@ -260,5 +260,5 @@ class Thread(object): def __del__(self): """Close and free the notmuch Thread""" - if self._thread is not None: + if self._thread: self._destroy(self._thread) diff --git a/bindings/python/notmuch/threads.py b/bindings/python/notmuch/threads.py index fb053f9..a644164 100644 --- a/bindings/python/notmuch/threads.py +++ b/bindings/python/notmuch/threads.py @@ -176,5 +176,5 @@ class Threads(Python3StringMixIn): def __del__(self): """Close and free the notmuch Threads""" - if self._threads is not None: + if self._threads: self._destroy(self._threads) -- cgit v1.2.3 From ca1e232e5fa35c2fabe0ef4fcb78911bcffe67b8 Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Mon, 30 Apr 2012 19:48:45 +0200 Subject: python: document the Database.close function Put a prominent warning into the docstring of Database.close, make the function show up in the sphinx doc and refer to the warning in the paragraph mentioning the context manager protocol. Signed-off-by: Justus Winter <4winter@informatik.uni-hamburg.de> --- bindings/python/docs/source/database.rst | 2 ++ bindings/python/notmuch/database.py | 10 +++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'bindings/python/notmuch/database.py') diff --git a/bindings/python/docs/source/database.rst b/bindings/python/docs/source/database.rst index ee71085..2464bff 100644 --- a/bindings/python/docs/source/database.rst +++ b/bindings/python/docs/source/database.rst @@ -9,6 +9,8 @@ .. automethod:: open(path, status=MODE.READ_ONLY) + .. automethod:: close + .. automethod:: get_path .. automethod:: get_version diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py index 525f7c9..5c62d45 100644 --- a/bindings/python/notmuch/database.py +++ b/bindings/python/notmuch/database.py @@ -56,7 +56,8 @@ class Database(object): :class:`Database` objects implement the context manager protocol so you can use the :keyword:`with` statement to ensure that the - database is properly closed. + database is properly closed. See :meth:`close` for more + information. .. note:: @@ -225,6 +226,13 @@ class Database(object): def close(self): ''' Closes the notmuch database. + + .. warning:: + + This function closes the notmuch database. From that point + on every method invoked on any object ever derived from + the closed database may cease to function and raise a + NotmuchError. ''' if self._db: self._close(self._db) -- cgit v1.2.3 From fcfb619b44b0011ac6bc557e6cbe2d516bc58f8a Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Mon, 30 Apr 2012 19:51:16 +0200 Subject: python: remove a note stating wrong things about the memory management Signed-off-by: Justus Winter <4winter@informatik.uni-hamburg.de> --- bindings/python/notmuch/database.py | 8 -------- 1 file changed, 8 deletions(-) (limited to 'bindings/python/notmuch/database.py') diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py index 5c62d45..1db332f 100644 --- a/bindings/python/notmuch/database.py +++ b/bindings/python/notmuch/database.py @@ -64,14 +64,6 @@ class Database(object): Any function in this class can and will throw an :exc:`NotInitializedError` if the database was not intitialized properly. - - .. 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. """ _std_db_path = None """Class attribute to cache user's default database""" -- cgit v1.2.3 From 0a357fe4105b3ea936b41584cb40312e84a1e67a Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Mon, 30 Apr 2012 12:25:36 -0400 Subject: python: Update Python bindings for new notmuch_database_{open, create} signatures --- bindings/python/notmuch/database.py | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) (limited to 'bindings/python/notmuch/database.py') diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py index 1db332f..1b1ddc3 100644 --- a/bindings/python/notmuch/database.py +++ b/bindings/python/notmuch/database.py @@ -88,8 +88,8 @@ class Database(object): """notmuch_database_open""" _open = nmlib.notmuch_database_open - _open.argtypes = [c_char_p, c_uint] - _open.restype = NotmuchDatabaseP + _open.argtypes = [c_char_p, c_uint, POINTER(NotmuchDatabaseP)] + _open.restype = c_uint """notmuch_database_upgrade""" _upgrade = nmlib.notmuch_database_upgrade @@ -115,8 +115,8 @@ class Database(object): """notmuch_database_create""" _create = nmlib.notmuch_database_create - _create.argtypes = [c_char_p] - _create.restype = NotmuchDatabaseP + _create.argtypes = [c_char_p, POINTER(NotmuchDatabaseP)] + _create.restype = c_uint def __init__(self, path = None, create = False, mode = MODE.READ_ONLY): @@ -186,12 +186,13 @@ class Database(object): raise NotmuchError(message="Cannot create db, this Database() " "already has an open one.") - res = Database._create(_str(path), Database.MODE.READ_WRITE) + db = NotmuchDatabaseP() + status = Database._create(_str(path), Database.MODE.READ_WRITE, byref(db)) - if not res: - raise NotmuchError( - message="Could not create the specified database") - self._db = res + if status != STATUS.SUCCESS: + raise NotmuchError(status) + self._db = db + return status def open(self, path, mode=0): """Opens an existing database @@ -205,11 +206,13 @@ class Database(object): :raises: Raises :exc:`NotmuchError` in case of any failure (possibly after printing an error message on stderr). """ - res = Database._open(_str(path), mode) + db = NotmuchDatabaseP() + status = Database._open(_str(path), mode, byref(db)) - if not res: - raise NotmuchError(message="Could not open the specified database") - self._db = res + if status != STATUS.SUCCESS: + raise NotmuchError(status) + self._db = db + return status _close = nmlib.notmuch_database_close _close.argtypes = [NotmuchDatabaseP] -- cgit v1.2.3 From ed4f73a080b08c304bd4603d8b6313c45e7c40d3 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Sun, 13 May 2012 19:36:11 -0400 Subject: python: Update for changes to notmuch_database_get_directory notmuch_database_get_directory now returns NOTMUCH_STATUS_READ_ONLY_DATABASE on its own (rather than crashing) so the workaround in Database.get_directory is no longer necessary. --- bindings/python/notmuch/database.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'bindings/python/notmuch/database.py') diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py index 1b1ddc3..797554d 100644 --- a/bindings/python/notmuch/database.py +++ b/bindings/python/notmuch/database.py @@ -73,8 +73,8 @@ class Database(object): """notmuch_database_get_directory""" _get_directory = nmlib.notmuch_database_get_directory - _get_directory.argtypes = [NotmuchDatabaseP, c_char_p] - _get_directory.restype = NotmuchDirectoryP + _get_directory.argtypes = [NotmuchDatabaseP, c_char_p, POINTER(NotmuchDirectoryP)] + _get_directory.restype = c_uint """notmuch_database_get_path""" _get_path = nmlib.notmuch_database_get_path @@ -359,13 +359,6 @@ class Database(object): """ self._assert_db_is_initialized() - # work around libnotmuch calling exit(3), see - # id:20120221002921.8534.57091@thinkbox.jade-hamburg.de - # TODO: remove once this issue is resolved - if self.mode != Database.MODE.READ_WRITE: - raise ReadOnlyDatabaseError('The database has to be opened in ' - 'read-write mode for get_directory') - # sanity checking if path is valid, and make path absolute if path and path[0] == os.sep: # we got an absolute path @@ -378,7 +371,13 @@ class Database(object): #we got a relative path, make it absolute abs_dirpath = os.path.abspath(os.path.join(self.get_path(), path)) - dir_p = Database._get_directory(self._db, _str(path)) + dir_p = NotmuchDirectoryP() + status = Database._get_directory(self._db, _str(path), byref(dir_p)) + + if status != STATUS.SUCCESS: + raise NotmuchError(status) + if not dir_p: + return None # return the Directory, init it with the absolute path return Directory(abs_dirpath, dir_p, self) -- cgit v1.2.3 From 05c3e83bd272635ecc5e86d767250de1eb680a09 Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Thu, 17 May 2012 16:58:53 +0200 Subject: python: use relative imports Signed-off-by: Justus Winter <4winter@informatik.uni-hamburg.de> --- bindings/python/notmuch/database.py | 6 +++--- bindings/python/notmuch/directory.py | 2 +- bindings/python/notmuch/filenames.py | 2 +- bindings/python/notmuch/query.py | 2 +- bindings/python/notmuch/tag.py | 2 +- bindings/python/notmuch/thread.py | 4 ++-- bindings/python/notmuch/threads.py | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) (limited to 'bindings/python/notmuch/database.py') diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py index 797554d..fb4c929 100644 --- a/bindings/python/notmuch/database.py +++ b/bindings/python/notmuch/database.py @@ -20,7 +20,7 @@ Copyright 2010 Sebastian Spaeth import os import codecs from ctypes import c_char_p, c_void_p, c_uint, byref, POINTER -from notmuch.globals import ( +from .globals import ( nmlib, Enum, _str, @@ -37,8 +37,8 @@ from .errors import ( NotInitializedError, ReadOnlyDatabaseError, ) -from notmuch.message import Message -from notmuch.tag import Tags +from .message import Message +from .tag import Tags from .query import Query from .directory import Directory diff --git a/bindings/python/notmuch/directory.py b/bindings/python/notmuch/directory.py index ae115f8..3b0a525 100644 --- a/bindings/python/notmuch/directory.py +++ b/bindings/python/notmuch/directory.py @@ -18,7 +18,7 @@ Copyright 2010 Sebastian Spaeth """ from ctypes import c_uint, c_long -from notmuch.globals import ( +from .globals import ( nmlib, NotmuchDirectoryP, NotmuchFilenamesP diff --git a/bindings/python/notmuch/filenames.py b/bindings/python/notmuch/filenames.py index a0b2956..229f414 100644 --- a/bindings/python/notmuch/filenames.py +++ b/bindings/python/notmuch/filenames.py @@ -17,7 +17,7 @@ along with notmuch. If not, see . Copyright 2010 Sebastian Spaeth """ from ctypes import c_char_p -from notmuch.globals import ( +from .globals import ( nmlib, NotmuchMessageP, NotmuchFilenamesP, diff --git a/bindings/python/notmuch/query.py b/bindings/python/notmuch/query.py index 756e63b..4abba5b 100644 --- a/bindings/python/notmuch/query.py +++ b/bindings/python/notmuch/query.py @@ -18,7 +18,7 @@ Copyright 2010 Sebastian Spaeth """ from ctypes import c_char_p, c_uint -from notmuch.globals import ( +from .globals import ( nmlib, Enum, _str, diff --git a/bindings/python/notmuch/tag.py b/bindings/python/notmuch/tag.py index 363c348..1d52345 100644 --- a/bindings/python/notmuch/tag.py +++ b/bindings/python/notmuch/tag.py @@ -17,7 +17,7 @@ along with notmuch. If not, see . Copyright 2010 Sebastian Spaeth """ from ctypes import c_char_p -from notmuch.globals import ( +from .globals import ( nmlib, Python3StringMixIn, NotmuchTagsP, diff --git a/bindings/python/notmuch/thread.py b/bindings/python/notmuch/thread.py index 2f60d49..789f9a0 100644 --- a/bindings/python/notmuch/thread.py +++ b/bindings/python/notmuch/thread.py @@ -18,7 +18,7 @@ Copyright 2010 Sebastian Spaeth """ from ctypes import c_char_p, c_long, c_int -from notmuch.globals import ( +from .globals import ( nmlib, NotmuchThreadP, NotmuchMessagesP, @@ -29,7 +29,7 @@ from .errors import ( NotInitializedError, ) from .messages import Messages -from notmuch.tag import Tags +from .tag import Tags from datetime import date class Thread(object): diff --git a/bindings/python/notmuch/threads.py b/bindings/python/notmuch/threads.py index d2e0a91..f8ca34a 100644 --- a/bindings/python/notmuch/threads.py +++ b/bindings/python/notmuch/threads.py @@ -17,7 +17,7 @@ along with notmuch. If not, see . Copyright 2010 Sebastian Spaeth """ -from notmuch.globals import ( +from .globals import ( nmlib, Python3StringMixIn, NotmuchThreadP, -- cgit v1.2.3 From 8dc8495010057202b725ac029831c03f4e3ab6bd Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Thu, 17 May 2012 17:15:24 +0200 Subject: python: Fix the remaining broken NULL pointer tests Signed-off-by: Justus Winter <4winter@informatik.uni-hamburg.de> --- bindings/python/notmuch/database.py | 2 +- bindings/python/notmuch/message.py | 4 ++-- bindings/python/notmuch/messages.py | 2 +- bindings/python/notmuch/thread.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) (limited to 'bindings/python/notmuch/database.py') diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py index fb4c929..8f1b37f 100644 --- a/bindings/python/notmuch/database.py +++ b/bindings/python/notmuch/database.py @@ -558,7 +558,7 @@ class Database(object): """ self._assert_db_is_initialized() tags_p = Database._get_all_tags(self._db) - if tags_p == None: + if not tags_p: raise NullPointerError() return Tags(tags_p, self) diff --git a/bindings/python/notmuch/message.py b/bindings/python/notmuch/message.py index bdbe501..d7f029f 100644 --- a/bindings/python/notmuch/message.py +++ b/bindings/python/notmuch/message.py @@ -229,7 +229,7 @@ class Message(Python3StringMixIn): #Returns NULL if any error occurs. header = Message._get_header(self._msg, _str(header)) - if header == None: + if not header: raise NullPointerError() return header.decode('UTF-8', 'ignore') @@ -300,7 +300,7 @@ class Message(Python3StringMixIn): raise NotInitializedError() tags_p = Message._get_tags(self._msg) - if tags_p == None: + if not tags_p: raise NullPointerError() return Tags(tags_p, self) diff --git a/bindings/python/notmuch/messages.py b/bindings/python/notmuch/messages.py index 59ef40a..aee4a77 100644 --- a/bindings/python/notmuch/messages.py +++ b/bindings/python/notmuch/messages.py @@ -142,7 +142,7 @@ class Messages(object): #reset _msgs as we iterated over it and can do so only once self._msgs = None - if tags_p == None: + if not tags_p: raise NullPointerError() return Tags(tags_p, self) diff --git a/bindings/python/notmuch/thread.py b/bindings/python/notmuch/thread.py index 789f9a0..009cb2b 100644 --- a/bindings/python/notmuch/thread.py +++ b/bindings/python/notmuch/thread.py @@ -238,7 +238,7 @@ class Thread(object): raise NotInitializedError() tags_p = Thread._get_tags(self._thread) - if tags_p == None: + if not tags_p: raise NullPointerError() return Tags(tags_p, self) -- cgit v1.2.3 From 8e3faa7f18d9ca87a77834d76f4b8db95669252b Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Thu, 17 May 2012 18:13:55 +0200 Subject: python: add a file abstracting away differences between python 2 and 3 Signed-off-by: Justus Winter <4winter@informatik.uni-hamburg.de> --- bindings/python/notmuch/compat.py | 67 +++++++++++++++++++++++++++++++++++++ bindings/python/notmuch/database.py | 8 +---- bindings/python/notmuch/globals.py | 35 ++----------------- 3 files changed, 70 insertions(+), 40 deletions(-) create mode 100644 bindings/python/notmuch/compat.py (limited to 'bindings/python/notmuch/database.py') diff --git a/bindings/python/notmuch/compat.py b/bindings/python/notmuch/compat.py new file mode 100644 index 0000000..adc8d24 --- /dev/null +++ b/bindings/python/notmuch/compat.py @@ -0,0 +1,67 @@ +''' +This file is part of notmuch. + +This module handles differences between python2.x and python3.x and +allows the notmuch bindings to support both version families with one +source tree. + +Notmuch is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation, either version 3 of the License, or (at your +option) any later version. + +Notmuch is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with notmuch. If not, see . + +Copyright 2010 Sebastian Spaeth +Copyright 2012 Justus Winter <4winter@informatik.uni-hamburg.de> +''' + +import sys + +if sys.version_info[0] == 2: + from ConfigParser import SafeConfigParser + + class Python3StringMixIn(object): + def __str__(self): + return unicode(self).encode('utf-8') + + def encode_utf8(value): + ''' + Ensure a nicely utf-8 encoded string to pass to wrapped + libnotmuch functions. + + C++ code expects strings to be well formatted and unicode + strings to have no null bytes. + ''' + if not isinstance(value, basestring): + raise TypeError('Expected str or unicode, got %s' % type(value)) + + if isinstance(value, unicode): + return value.encode('utf-8', 'replace') + + return value +else: + from configparser import SafeConfigParser + + class Python3StringMixIn(object): + def __str__(self): + return self.__unicode__() + + def encode_utf8(value): + ''' + Ensure a nicely utf-8 encoded string to pass to wrapped + libnotmuch functions. + + C++ code expects strings to be well formatted and unicode + strings to have no null bytes. + ''' + if not isinstance(value, str): + raise TypeError('Expected str, got %s' % type(value)) + + return value.encode('utf-8', 'replace') diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py index 8f1b37f..6dab932 100644 --- a/bindings/python/notmuch/database.py +++ b/bindings/python/notmuch/database.py @@ -20,6 +20,7 @@ Copyright 2010 Sebastian Spaeth import os import codecs from ctypes import c_char_p, c_void_p, c_uint, byref, POINTER +from .compat import SafeConfigParser from .globals import ( nmlib, Enum, @@ -589,13 +590,6 @@ class Database(object): """ Reads a user's notmuch config and returns his db location Throws a NotmuchError if it cannot find it""" - try: - # python3.x - from configparser import SafeConfigParser - except ImportError: - # python2.x - from ConfigParser import SafeConfigParser - config = SafeConfigParser() conf_f = os.getenv('NOTMUCH_CONFIG', os.path.expanduser('~/.notmuch-config')) diff --git a/bindings/python/notmuch/globals.py b/bindings/python/notmuch/globals.py index f5fad72..c7632c3 100644 --- a/bindings/python/notmuch/globals.py +++ b/bindings/python/notmuch/globals.py @@ -16,7 +16,7 @@ along with notmuch. If not, see . Copyright 2010 Sebastian Spaeth """ -import sys + from ctypes import CDLL, Structure, POINTER #----------------------------------------------------------------------------- @@ -26,38 +26,7 @@ try: except: raise ImportError("Could not find shared 'notmuch' library.") - -if sys.version_info[0] == 2: - class Python3StringMixIn(object): - def __str__(self): - return unicode(self).encode('utf-8') - - - def _str(value): - """Ensure a nicely utf-8 encoded string to pass to libnotmuch - - C++ code expects strings to be well formatted and - unicode strings to have no null bytes.""" - if not isinstance(value, basestring): - raise TypeError("Expected str or unicode, got %s" % type(value)) - if isinstance(value, unicode): - return value.encode('UTF-8') - return value -else: - class Python3StringMixIn(object): - def __str__(self): - return self.__unicode__() - - - def _str(value): - """Ensure a nicely utf-8 encoded string to pass to libnotmuch - - C++ code expects strings to be well formatted and - unicode strings to have no null bytes.""" - if not isinstance(value, str): - raise TypeError("Expected str, got %s" % type(value)) - return value.encode('UTF-8') - +from .compat import Python3StringMixIn, encode_utf8 as _str class Enum(object): """Provides ENUMS as "code=Enum(['a','b','c'])" where code.a=0 etc...""" -- cgit v1.2.3 From 892bb1ee6d52556eaf88cc35b61048a4de862352 Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Thu, 17 May 2012 19:05:53 +0200 Subject: python: deprecate Database.db_p Signed-off-by: Justus Winter <4winter@informatik.uni-hamburg.de> --- bindings/python/notmuch/database.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'bindings/python/notmuch/database.py') diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py index 6dab932..ee0366c 100644 --- a/bindings/python/notmuch/database.py +++ b/bindings/python/notmuch/database.py @@ -603,7 +603,9 @@ class Database(object): def db_p(self): """Property returning a pointer to `notmuch_database_t` or `None` - This should normally not be needed by a user (and is not yet - guaranteed to remain stable in future versions). + .. deprecated:: 0.14 + If you really need a pointer to the notmuch + database object use the `_pointer` field. This + alias will be removed in notmuch 0.15. """ return self._db -- cgit v1.2.3 From f1f1e3963910a845e2bc0ebe6b9b5c852b4564eb Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Fri, 18 May 2012 00:13:39 -0400 Subject: python: Update Database.get_directory documentation notmuch_database_get_directory no longer returns an error for read-only databases, so remove ReadOnlyDatabaseError from the list of get_directory exceptions. --- bindings/python/notmuch/database.py | 3 --- 1 file changed, 3 deletions(-) (limited to 'bindings/python/notmuch/database.py') diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py index 797554d..ff89818 100644 --- a/bindings/python/notmuch/database.py +++ b/bindings/python/notmuch/database.py @@ -346,7 +346,6 @@ class Database(object): def get_directory(self, path): """Returns a :class:`Directory` of path, - (creating it if it does not exist(?)) :param path: An unicode string containing the path relative to the path of database (see :meth:`get_path`), or else should be an absolute @@ -354,8 +353,6 @@ class Database(object): :returns: :class:`Directory` or raises an exception. :raises: :exc:`FileError` if path is not relative database or absolute with initial components same as database. - :raises: :exc:`ReadOnlyDatabaseError` if the database has not been - opened in read-write mode """ self._assert_db_is_initialized() -- cgit v1.2.3 From 54508eb78d1c5c1c67c8b220cf2bd826af1203a9 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Fri, 18 May 2012 00:13:41 -0400 Subject: python: Remove find_message_by_filename workaround Now that notmuch_database_find_message_by_filename works on read-only databases, remove the workaround that disabled it on read-write databases. This also adds a regression test for find_message_by_filename. --- bindings/python/notmuch/database.py | 9 --------- test/python | 8 ++++++++ 2 files changed, 8 insertions(+), 9 deletions(-) (limited to 'bindings/python/notmuch/database.py') diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py index ff89818..e5c74cf 100644 --- a/bindings/python/notmuch/database.py +++ b/bindings/python/notmuch/database.py @@ -526,19 +526,10 @@ class Database(object): retry. :raises: :exc:`NotInitializedError` if the database was not intitialized. - :raises: :exc:`ReadOnlyDatabaseError` if the database has not been - opened in read-write mode *Added in notmuch 0.9*""" self._assert_db_is_initialized() - # work around libnotmuch calling exit(3), see - # id:20120221002921.8534.57091@thinkbox.jade-hamburg.de - # TODO: remove once this issue is resolved - if self.mode != Database.MODE.READ_WRITE: - raise ReadOnlyDatabaseError('The database has to be opened in ' - 'read-write mode for get_directory') - msg_p = NotmuchMessageP() status = Database._find_message_by_filename(self._db, _str(filename), byref(msg_p)) diff --git a/test/python b/test/python index 6018c2d..3f03a2e 100755 --- a/test/python +++ b/test/python @@ -28,4 +28,12 @@ EOF notmuch search --sort=oldest-first --output=messages tag:inbox | sed s/^id:// > EXPECTED test_expect_equal_file OUTPUT EXPECTED +test_begin_subtest "get non-existent file" +test_python <