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