python: rename filename.py into filenames.py
[notmuch] / bindings / python / notmuch / filenames.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 from ctypes import c_char_p
20 from notmuch.globals import (
21     nmlib,
22     NullPointerError,
23     NotInitializedError,
24     NotmuchMessageP,
25     NotmuchFilenamesP,
26     Python3StringMixIn,
27 )
28
29
30 class Filenames(Python3StringMixIn):
31     """Represents a list of filenames as returned by notmuch
32
33     This object contains the Filenames iterator. The main function is
34     as_generator() which will return a generator so we can do a Filenamesth an
35     iterator over a list of notmuch filenames. Do note that the underlying
36     library only provides a one-time iterator (it cannot reset the iterator to
37     the start). Thus iterating over the function will "exhaust" the list of
38     tags, and a subsequent iteration attempt will raise a
39     :exc:`NotInitializedError`. Also note, that any function that uses
40     iteration (nearly all) will also exhaust the tags. So both::
41
42       for name in filenames: print name
43
44     as well as::
45
46        number_of_names = len(names)
47
48     and even a simple::
49
50        #str() iterates over all tags to construct a space separated list
51        print(str(filenames))
52
53     will "exhaust" the Filenames. However, you can use
54     :meth:`Message.get_filenames` repeatedly to get fresh Filenames
55     objects to perform various actions on filenames.
56     """
57
58     #notmuch_filenames_get
59     _get = nmlib.notmuch_filenames_get
60     _get.argtypes = [NotmuchFilenamesP]
61     _get.restype = c_char_p
62
63     def __init__(self, files_p, parent):
64         """
65         :param files_p: A pointer to an underlying *notmuch_tags_t*
66              structure. These are not publically exposed, so a user
67              will almost never instantiate a :class:`Tags` object
68              herself. They are usually handed back as a result,
69              e.g. in :meth:`Database.get_all_tags`.  *tags_p* must be
70              valid, we will raise an :exc:`NullPointerError`
71              if it is `None`.
72         :type files_p: :class:`ctypes.c_void_p`
73         :param parent: The parent object (ie :class:`Message` these
74              filenames are derived from, and saves a
75              reference to it, so we can automatically delete the db object
76              once all derived objects are dead.
77         """
78         if not files_p:
79             raise NullPointerError()
80
81         self._files_p = files_p
82         #save reference to parent object so we keep it alive
83         self._parent = parent
84
85     def __iter__(self):
86         """ Make Filenames an iterator """
87         return self
88
89     _valid = nmlib.notmuch_filenames_valid
90     _valid.argtypes = [NotmuchFilenamesP]
91     _valid.restype = bool
92
93     _move_to_next = nmlib.notmuch_filenames_move_to_next
94     _move_to_next.argtypes = [NotmuchFilenamesP]
95     _move_to_next.restype = None
96
97     def __next__(self):
98         if not self._files_p:
99             raise NotInitializedError()
100
101         if not self._valid(self._files_p):
102             self._files_p = None
103             raise StopIteration
104
105         file_ = Filenames._get(self._files_p)
106         self._move_to_next(self._files_p)
107         return file_.decode('utf-8', 'ignore')
108     next = __next__ # python2.x iterator protocol compatibility
109
110     def as_generator(self):
111         """Return generator of Filenames
112
113         This is the main function that will usually be used by the
114         user.
115
116         .. deprecated:: 0.12
117                         :class:`Filenames` objects implement the
118                         iterator protocol.
119         """
120         return self
121
122     def __unicode__(self):
123         """Represent Filenames() as newline-separated list of full paths
124
125         .. note:: As this iterates over the filenames, we will not be
126                able to iterate over them again (as in retrieve them)! If
127                the tags have been exhausted already, this will raise a
128                :exc:`NotInitializedError` on subsequent
129                attempts. However, you can use
130                :meth:`Message.get_filenames` repeatedly to perform
131                various actions on filenames.
132         """
133         return "\n".join(self)
134
135     _destroy = nmlib.notmuch_filenames_destroy
136     _destroy.argtypes = [NotmuchMessageP]
137     _destroy.restype = None
138
139     def __del__(self):
140         """Close and free the notmuch filenames"""
141         if self._files_p is not None:
142             self._destroy(self._files_p)
143
144     def __len__(self):
145         """len(:class:`Filenames`) returns the number of contained files
146
147         .. note::
148
149             As this iterates over the files, we will not be able to
150             iterate over them again! So this will fail::
151
152                  #THIS FAILS
153                  files = Database().get_directory('').get_child_files()
154                  if len(files) > 0:  # this 'exhausts' msgs
155                      # next line raises
156                      # NotmuchError(:attr:`STATUS`.NOT_INITIALIZED)
157                      for file in files: print file
158         """
159         if not self._files_p:
160             raise NotInitializedError()
161
162         i = 0
163         while self._valid(self._files_p):
164             self._move_to_next(self._files_p)
165             i += 1
166         self._files_p = None
167         return i