]> git.notmuchmail.org Git - notmuch/blob - bindings/python/notmuch/tag.py
py3k: Rename .next() to __next__(), add python2.x compatibility alias
[notmuch] / bindings / python / notmuch / tag.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 nmlib, STATUS, NotmuchError, NotmuchTagsP
21
22
23 class Tags(object):
24     """Represents a list of notmuch tags
25
26     This object provides an iterator over a list of notmuch tags (which
27     are unicode instances).
28
29     Do note that the underlying library only provides a one-time
30     iterator (it cannot reset the iterator to the start). Thus iterating
31     over the function will "exhaust" the list of tags, and a subsequent
32     iteration attempt will raise a :exc:`NotmuchError`
33     STATUS.NOT_INITIALIZED. Also note, that any function that uses
34     iteration (nearly all) will also exhaust the tags. So both::
35
36       for tag in tags: print tag
37
38     as well as::
39
40        number_of_tags = len(tags)
41
42     and even a simple::
43
44        #str() iterates over all tags to construct a space separated list
45        print(str(tags))
46
47     will "exhaust" the Tags. If you need to re-iterate over a list of
48     tags you will need to retrieve a new :class:`Tags` object.
49     """
50
51     #notmuch_tags_get
52     _get = nmlib.notmuch_tags_get
53     _get.argtypes = [NotmuchTagsP]
54     _get.restype = c_char_p
55
56     def __init__(self, tags_p, parent=None):
57         """
58         :param tags_p: A pointer to an underlying *notmuch_tags_t*
59              structure. These are not publically exposed, so a user
60              will almost never instantiate a :class:`Tags` object
61              herself. They are usually handed back as a result,
62              e.g. in :meth:`Database.get_all_tags`.  *tags_p* must be
63              valid, we will raise an :exc:`NotmuchError`
64              (STATUS.NULL_POINTER) if it is `None`.
65         :type tags_p: :class:`ctypes.c_void_p`
66         :param parent: The parent object (ie :class:`Database` or
67              :class:`Message` these tags are derived from, and saves a
68              reference to it, so we can automatically delete the db object
69              once all derived objects are dead.
70         :TODO: Make the iterator optionally work more than once by
71                cache the tags in the Python object(?)
72         """
73         if tags_p is None:
74             raise NotmuchError(STATUS.NULL_POINTER)
75
76         self._tags = tags_p
77         #save reference to parent object so we keep it alive
78         self._parent = parent
79
80     def __iter__(self):
81         """ Make Tags an iterator """
82         return self
83
84     _valid = nmlib.notmuch_tags_valid
85     _valid.argtypes = [NotmuchTagsP]
86     _valid.restype = bool
87
88     _move_to_next = nmlib.notmuch_tags_move_to_next
89     _move_to_next.argtypes = [NotmuchTagsP]
90     _move_to_next.restype = None
91
92     def __next__(self):
93         if self._tags is None:
94             raise NotmuchError(STATUS.NOT_INITIALIZED)
95         if not self._valid(self._tags):
96             self._tags = None
97             raise StopIteration
98         tag = Tags._get(self._tags).decode('UTF-8')
99         self._move_to_next(self._tags)
100         return tag
101     next = __next__ # python2.x iterator protocol compatibility
102
103     def __nonzero__(self):
104         """Implement bool(Tags) check that can be repeatedly used
105
106         If __nonzero__ is not implemented, "if Tags()"
107         will implicitly call __len__, using up our iterator, so it is
108         important that this function is defined.
109
110         :returns: True if the Tags() iterator has at least one more Tag
111             left."""
112         return self._valid(self._tags) > 0
113
114     def __str__(self):
115         return unicode(self).encode('utf-8')
116
117     def __unicode__(self):
118         """string representation of :class:`Tags`: a space separated list of tags
119
120         .. note::
121
122             As this iterates over the tags, we will not be able to iterate over
123             them again (as in retrieve them)! If the tags have been exhausted
124             already, this will raise a :exc:`NotmuchError`
125             STATUS.NOT_INITIALIZED on subsequent attempts.
126         """
127         return " ".join(self)
128
129     _destroy = nmlib.notmuch_tags_destroy
130     _destroy.argtypes = [NotmuchTagsP]
131     _destroy.restype = None
132
133     def __del__(self):
134         """Close and free the notmuch tags"""
135         if self._tags is not None:
136             self._destroy(self._tags)