Added clen attribute to imgstreams.
[automanga.git] / manga / lib.py
CommitLineData
f3ad0817 1class library(object):
6a1e046b
FT
2 """Class representing a single source of multiple mangas."""
3
4 def byname(self, prefix):
5 """Returns an iterable object of all mangas in this library
6 whose names (case-insensitively) begin with the given
7 prefix.
8
9 All libraries should implement this."""
10 raise NotImplementedError()
11
46b3b90e
FT
12 def byid(self, id):
13 """Returns a previously known manga by its string ID, or
14 raises KeyError if no such manga could be found.
15
16 All libraries should implement this."""
17 raise KeyError(id)
18
6a1e046b
FT
19 def __iter__(self):
20 """Return an iterator of all known mangas in this library.
21
22 Not all libraries need implement this."""
23 raise NotImplementedError("manga.lib.library iterator")
f3ad0817 24
3683ab38
FT
25class pagetree(object):
26 """Base class for objects in the tree of pages and pagelists.
27
46b3b90e
FT
28 All pagetree objects should contain an attribute `stack',
29 containing a list of pairs. The last pair in the list should be
30 the pagetree object which yielded this pagetree object, along with
31 the index which yielded it. Every non-last pair should be the same
3683ab38 32 information for the pair following it. The only objects with empty
46b3b90e
FT
33 `stack' lists should be `manga' objects.
34
35 All non-root pagetree objects should also contain an attribute
36 `id', which should be a string that can be passed to the `byid'
37 function of its parent node to recover the node. Such string ID
38 should be more persistent than the node's numeric index in the
39 parent."""
40
41 def idlist(self):
42 """Returns a list of the IDs necessary to resolve this node
43 from the root node."""
44 if len(self.stack) == 0:
45 raise Exception("Cannot get ID list on root node.")
46 return [n.id for n, i in self.stack[1:]] + [self.id]
47
48 def byidlist(self, idlist):
49 if len(idlist) == 0:
50 return self
51 return self.byid(idlist[0]).byidlist(idlist[1:])
3683ab38
FT
52
53class pagelist(pagetree):
6a1e046b
FT
54 """Class representing a list of either pages, or nested
55 pagelists. Might be, for instance, a volume or a chapter.
56
57 All pagelists should contain an attribute `name', containing some
58 human-readable Unicode representation of the pagelist."""
59
60 def __len__(self):
61 """Return the number of (direct) sub-nodes in this pagelist.
62
63 All pagelists need to implement this."""
64 raise NotImplementedError()
65
66 def __getitem__(self, idx):
67 """Return the direct sub-node of the given index in this
68 pagelist. Sub-node indexes are always zero-based and
69 contiguous, regardless of any gaps in the underlying medium,
70 which should be indicated instead by way of the `name'
71 attribute.
72
73 All pagelists need to implement this."""
74 raise NotImplementedError()
f3ad0817 75
46b3b90e
FT
76 def byid(self, id):
77 """Return the direct sub-node of this pagelist which has the
78 given string ID. If none is found, a KeyError is raised.
79
80 This default method iterates the children of this node, but
81 may be overridden by some more efficient implementation.
82 """
83 for ch in self:
84 if ch.id == id:
85 return ch
86 raise KeyError(id)
87
f3ad0817 88class manga(pagelist):
6a1e046b 89 """Class reprenting a single manga. Includes the pagelist class,
46b3b90e
FT
90 and all constraints valid for it.
91
92 A manga is a root pagetree node, but should also contain an `id'
93 attribute, which can be used to recover the manga from its
94 library's `byid' function."""
f3ad0817
FT
95 pass
96
3683ab38 97class page(pagetree):
6a1e046b
FT
98 """Class representing a single page of a manga. Pages make up the
99 leaf nodes of a pagelist tree.
100
101 All pages should contain an attribute `manga', referring back to
102 the containing manga instance."""
103
104 def open(self):
105 """Open a stream for the image this page represents. The
106 returned object should be an imgstream class.
107
108 All pages need to implement this."""
109 raise NotImplementedError()
110
111class imgstream(object):
112 """An open image I/O stream for a manga page. Generally, it should
113 be file-like. This base class implements the resource-manager
114 interface for use in `with' statements, calling close() on itself
115 when exiting the with-scope.
116
117 All imgstreams should contain an attribute `ctype', being the
9948db89
FT
118 Content-Type of the image being read by the stream, and `clen`,
119 being either an int describing the total number of bytes in the
120 stream, or None if the value is not known in advance."""
6a1e046b
FT
121
122 def __enter__(self):
123 return self
124
125 def __exit__(self, *exc_info):
126 self.close()
127
af730068
FT
128 def fileno(self):
129 """If reading the imgstream may block, fileno() should return
130 a file descriptor that can be polled. If fileno() returns
131 None, that should mean that reading will not block."""
132 return None
133
6a1e046b
FT
134 def close(self):
135 """Close this stream."""
136 raise NotImplementedError()
137
138 def read(self, sz = None):
139 """Read SZ bytes from the stream, or the entire rest of the
140 stream of SZ is not given."""
141 raise NotImplementedError()
07be272b 142
055ad3fd
FT
143class cursor(object):
144 def __init__(self, ob):
d8bea304
FT
145 if isinstance(ob, cursor):
146 self.cur = ob.cur
147 else:
148 self.cur = self.descend(ob)
055ad3fd
FT
149
150 def descend(self, ob):
151 while isinstance(ob, pagelist):
152 ob = ob[0]
153 if not isinstance(ob, page):
154 raise TypeError("object in page tree was unexpectedly not a pagetree")
155 return ob
07be272b
FT
156
157 def next(self):
055ad3fd
FT
158 for n, i in reversed(self.cur.stack):
159 if i < len(n) - 1:
160 self.cur = self.descend(n[i + 1])
161 return self.cur
162 raise StopIteration()
163
164 def prev(self):
165 for n, i in reversed(self.cur,stack):
166 if i > 0:
167 self.cur = self.descend(n[i - 1])
168 return self.cur
169 raise StopIteration()
07be272b
FT
170
171 def __iter__(self):
172 return self