]> git.notmuchmail.org Git - notmuch/blobdiff - bindings/python/notmuch/database.py
python: implement the context manager protocol for database objects
[notmuch] / bindings / python / notmuch / database.py
index 2eae69ed72ae6aebde5671a55df97500899b4161..533948c4491407597b8b0371eccbbb038300615c 100644 (file)
@@ -18,6 +18,7 @@ Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>'
 """
 
 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,
@@ -40,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
@@ -141,6 +146,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:
@@ -168,7 +176,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,10 +196,32 @@ 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
 
+    _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 __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()
@@ -530,15 +560,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
 
@@ -553,11 +574,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):
@@ -651,7 +672,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 +706,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 +720,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 +786,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 +947,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):
@@ -935,7 +956,7 @@ class Filenames(object):
 
         file_ = Filenames._get(self._files_p)
         self._move_to_next(self._files_p)
-        return file_.decode('utf-8', errors='ignore')
+        return file_.decode('utf-8', 'ignore')
     next = __next__ # python2.x iterator protocol compatibility
 
     def __len__(self):
@@ -953,7 +974,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