3e0763f256660f06a7550ffd3de656b458ab9f65
[notmuch] / bindings / python / notmuch / directory.py
1 """
2 This file is part of notmuch.
3
4 Notmuch is free software: you can redistribute it and/or modify it
5 under the terms of the GNU General Public License as published by the
6 Free Software Foundation, either version 3 of the License, or (at your
7 option) any later version.
8
9 Notmuch is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with notmuch.  If not, see <http://www.gnu.org/licenses/>.
16
17 Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>'
18 """
19
20 from ctypes import c_uint, c_long
21 from notmuch.globals import (
22     nmlib,
23     STATUS,
24     NotmuchError,
25     NotInitializedError,
26     NotmuchDirectoryP,
27     NotmuchFilenamesP
28 )
29 from .filename import Filenames
30
31 class Directory(object):
32     """Represents a directory entry in the notmuch directory
33
34     Modifying attributes of this object will modify the
35     database, not the real directory attributes.
36
37     The Directory object is usually derived from another object
38     e.g. via :meth:`Database.get_directory`, and will automatically be
39     become invalid whenever that parent is deleted. You should
40     therefore initialized this object handing it a reference to the
41     parent, preventing the parent from automatically being garbage
42     collected.
43     """
44
45     """notmuch_directory_get_mtime"""
46     _get_mtime = nmlib.notmuch_directory_get_mtime
47     _get_mtime.argtypes = [NotmuchDirectoryP]
48     _get_mtime.restype = c_long
49
50     """notmuch_directory_set_mtime"""
51     _set_mtime = nmlib.notmuch_directory_set_mtime
52     _set_mtime.argtypes = [NotmuchDirectoryP, c_long]
53     _set_mtime.restype = c_uint
54
55     """notmuch_directory_get_child_files"""
56     _get_child_files = nmlib.notmuch_directory_get_child_files
57     _get_child_files.argtypes = [NotmuchDirectoryP]
58     _get_child_files.restype = NotmuchFilenamesP
59
60     """notmuch_directory_get_child_directories"""
61     _get_child_directories = nmlib.notmuch_directory_get_child_directories
62     _get_child_directories.argtypes = [NotmuchDirectoryP]
63     _get_child_directories.restype = NotmuchFilenamesP
64
65     def _assert_dir_is_initialized(self):
66         """Raises a NotmuchError(:attr:`STATUS`.NOT_INITIALIZED)
67         if dir_p is None"""
68         if not self._dir_p:
69             raise NotInitializedError()
70
71     def __init__(self, path, dir_p, parent):
72         """
73         :param path:   The absolute path of the directory object.
74         :param dir_p:  The pointer to an internal notmuch_directory_t object.
75         :param parent: The object this Directory is derived from
76                        (usually a :class:`Database`). We do not directly use
77                        this, but store a reference to it as long as
78                        this Directory object lives. This keeps the
79                        parent object alive.
80         """
81         self._path = path
82         self._dir_p = dir_p
83         self._parent = parent
84
85     def set_mtime(self, mtime):
86         """Sets the mtime value of this directory in the database
87
88         The intention is for the caller to use the mtime to allow efficient
89         identification of new messages to be added to the database. The
90         recommended usage is as follows:
91
92         * Read the mtime of a directory from the filesystem
93
94         * Call :meth:`Database.add_message` for all mail files in
95           the directory
96
97         * Call notmuch_directory_set_mtime with the mtime read from the
98           filesystem.  Then, when wanting to check for updates to the
99           directory in the future, the client can call :meth:`get_mtime`
100           and know that it only needs to add files if the mtime of the
101           directory and files are newer than the stored timestamp.
102
103           .. note::
104
105                 :meth:`get_mtime` function does not allow the caller to
106                 distinguish a timestamp of 0 from a non-existent timestamp. So
107                 don't store a timestamp of 0 unless you are comfortable with
108                 that.
109
110         :param mtime: A (time_t) timestamp
111         :raises: :exc:`XapianError` a Xapian exception occurred, mtime
112                  not stored
113         :raises: :exc:`ReadOnlyDatabaseError` the database was opened
114                  in read-only mode so directory mtime cannot be modified
115         :raises: :exc:`NotInitializedError` the directory object has not
116                  been initialized
117         """
118         self._assert_dir_is_initialized()
119         status = Directory._set_mtime(self._dir_p, mtime)
120
121         if status != STATUS.SUCCESS:
122             raise NotmuchError(status)
123
124     def get_mtime(self):
125         """Gets the mtime value of this directory in the database
126
127         Retrieves a previously stored mtime for this directory.
128
129         :param mtime: A (time_t) timestamp
130         :raises: :exc:`NotmuchError`:
131
132                         :attr:`STATUS`.NOT_INITIALIZED
133                           The directory has not been initialized
134         """
135         self._assert_dir_is_initialized()
136         return Directory._get_mtime(self._dir_p)
137
138     # Make mtime attribute a property of Directory()
139     mtime = property(get_mtime, set_mtime, doc="""Property that allows getting
140                      and setting of the Directory *mtime* (read-write)
141
142                      See :meth:`get_mtime` and :meth:`set_mtime` for usage and
143                      possible exceptions.""")
144
145     def get_child_files(self):
146         """Gets a Filenames iterator listing all the filenames of
147         messages in the database within the given directory.
148
149         The returned filenames will be the basename-entries only (not
150         complete paths.
151         """
152         self._assert_dir_is_initialized()
153         files_p = Directory._get_child_files(self._dir_p)
154         return Filenames(files_p, self)
155
156     def get_child_directories(self):
157         """Gets a :class:`Filenames` iterator listing all the filenames of
158         sub-directories in the database within the given directory
159
160         The returned filenames will be the basename-entries only (not
161         complete paths.
162         """
163         self._assert_dir_is_initialized()
164         files_p = Directory._get_child_directories(self._dir_p)
165         return Filenames(files_p, self)
166
167     @property
168     def path(self):
169         """Returns the absolute path of this Directory (read-only)"""
170         return self._path
171
172     def __repr__(self):
173         """Object representation"""
174         return "<notmuch Directory object '%s'>" % self._path
175
176     _destroy = nmlib.notmuch_directory_destroy
177     _destroy.argtypes = [NotmuchDirectoryP]
178     _destroy.argtypes = None
179
180     def __del__(self):
181         """Close and free the Directory"""
182         if self._dir_p is not None:
183             self._destroy(self._dir_p)