2 """Class representing a single source of multiple mangas."""
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
9 All libraries should implement this."""
10 raise NotImplementedError()
13 """Returns a previously known manga by its string ID, or
14 raises KeyError if no such manga could be found.
16 All libraries should implement this."""
20 """Return an iterator of all known mangas in this library.
22 Not all libraries need implement this."""
23 raise NotImplementedError("manga.lib.library iterator")
25 class pagetree(object):
26 """Base class for objects in the tree of pages and pagelists.
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
32 information for the pair following it. The only objects with empty
33 `stack' lists should be `manga' objects.
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
41 All pagetree objects should contain an attribute `name',
42 containing some human-readable Unicode representation of the
46 """Returns a list of the IDs necessary to resolve this node
47 from the root node."""
48 if len(self.stack) == 0:
49 raise Exception("Cannot get ID list on root node.")
50 return [n.id for n, i in self.stack[1:]] + [self.id]
52 def byidlist(self, idlist):
55 return self.byid(idlist[0]).byidlist(idlist[1:])
57 class pagelist(pagetree):
58 """Class representing a list of either pages, or nested
59 pagelists. Might be, for instance, a volume or a chapter."""
62 """Return the number of (direct) sub-nodes in this pagelist.
64 All pagelists need to implement this."""
65 raise NotImplementedError()
67 def __getitem__(self, idx):
68 """Return the direct sub-node of the given index in this
69 pagelist. Sub-node indexes are always zero-based and
70 contiguous, regardless of any gaps in the underlying medium,
71 which should be indicated instead by way of the `name'
74 All pagelists need to implement this."""
75 raise NotImplementedError()
78 """Return the direct sub-node of this pagelist which has the
79 given string ID. If none is found, a KeyError is raised.
81 This default method iterates the children of this node, but
82 may be overridden by some more efficient implementation.
89 class manga(pagelist):
90 """Class reprenting a single manga. Includes the pagelist class,
91 and all constraints valid for it.
93 A manga is a root pagetree node, but should also contain an `id'
94 attribute, which can be used to recover the manga from its
95 library's `byid' function."""
99 """Class representing a single page of a manga. Pages make up the
100 leaf nodes of a pagelist tree.
102 All pages should contain an attribute `manga', referring back to
103 the containing manga instance."""
106 """Open a stream for the image this page represents. The
107 returned object should be an imgstream class.
109 All pages need to implement this."""
110 raise NotImplementedError()
112 class imgstream(object):
113 """An open image I/O stream for a manga page. Generally, it should
114 be file-like. This base class implements the resource-manager
115 interface for use in `with' statements, calling close() on itself
116 when exiting the with-scope.
118 All imgstreams should contain an attribute `ctype', being the
119 Content-Type of the image being read by the stream, and `clen`,
120 being either an int describing the total number of bytes in the
121 stream, or None if the value is not known in advance."""
126 def __exit__(self, *exc_info):
130 """If reading the imgstream may block, fileno() should return
131 a file descriptor that can be polled. If fileno() returns
132 None, that should mean that reading will not block."""
136 """Close this stream."""
137 raise NotImplementedError()
139 def read(self, sz = None):
140 """Read SZ bytes from the stream, or the entire rest of the
141 stream of SZ is not given."""
142 raise NotImplementedError()
144 class cursor(object):
145 def __init__(self, ob):
146 if isinstance(ob, cursor):
149 self.cur = self.descend(ob)
151 def descend(self, ob):
152 while isinstance(ob, pagelist):
154 if not isinstance(ob, page):
155 raise TypeError("object in page tree was unexpectedly not a pagetree")
159 for n, i in reversed(self.cur.stack):
161 self.cur = self.descend(n[i + 1])
163 raise StopIteration()
166 for n, i in reversed(self.cur.stack):
168 self.cur = self.descend(n[i - 1])
170 raise StopIteration()