python: refactor the python bindings
authorJustus Winter <4winter@informatik.uni-hamburg.de>
Wed, 22 Feb 2012 20:55:59 +0000 (21:55 +0100)
committerJustus Winter <4winter@informatik.uni-hamburg.de>
Wed, 22 Feb 2012 21:06:45 +0000 (22:06 +0100)
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
bindings/python/notmuch/__init__.py
bindings/python/notmuch/database.py
bindings/python/notmuch/directory.py [new file with mode: 0644]
bindings/python/notmuch/filename.py

index 25eb146c23dc0f56ab837cc71653ebc6afde99b1..e87a865947bdf8720d4a4d7692ebfd295b2c47d7 100644 (file)
@@ -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 <status_and_errors.html>`_ contains information on possible Status and Error values.
index f3ff98745c07a9a69927702f13cbd7f261e59b22..8de73d58864c5cfbc16904a7ffe83020898fe1d1 100644 (file)
@@ -51,11 +51,14 @@ along with notmuch.  If not, see <http://www.gnu.org/licenses/>.
 
 Copyright 2010-2011 Sebastian Spaeth <Sebastian@SSpaeth.de>
 """
-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 <Sebastian@SSpaeth.de>'
index a054be76e1ba4a72ee8d678b3950fa659508cb01..800264b7bd157f0e890714e21fdf7741b63f33d5 100644 (file)
@@ -19,7 +19,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 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 "<notmuch Directory object '%s'>" % self._path
-
-    _destroy = nmlib.notmuch_directory_destroy
-    _destroy.argtypes = [NotmuchDirectoryP]
-    _destroy.argtypes = None
-
-    def __del__(self):
-        """Close and free the Directory"""
-        if self._dir_p is not None:
-            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 (file)
index 0000000..3e0763f
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+
+Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>'
+"""
+
+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 "<notmuch Directory object '%s'>" % self._path
+
+    _destroy = nmlib.notmuch_directory_destroy
+    _destroy.argtypes = [NotmuchDirectoryP]
+    _destroy.argtypes = None
+
+    def __del__(self):
+        """Close and free the Directory"""
+        if self._dir_p is not None:
+            self._destroy(self._dir_p)
index 353eb76e3e946f7b480eca634640fcbdf405bba9..232a9edae9e09cdc95148402b5fc67e1bcc41899 100644 (file)
@@ -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