ยปCore Development>Code coverage>Lib/chunk.py

Python code coverage for Lib/chunk.py

#countcontent
1n/a"""Simple class to read IFF chunks.
2n/a
3n/aAn IFF chunk (used in formats such as AIFF, TIFF, RMFF (RealMedia File
4n/aFormat)) has the following structure:
5n/a
6n/a+----------------+
7n/a| ID (4 bytes) |
8n/a+----------------+
9n/a| size (4 bytes) |
10n/a+----------------+
11n/a| data |
12n/a| ... |
13n/a+----------------+
14n/a
15n/aThe ID is a 4-byte string which identifies the type of chunk.
16n/a
17n/aThe size field (a 32-bit value, encoded using big-endian byte order)
18n/agives the size of the whole chunk, including the 8-byte header.
19n/a
20n/aUsually an IFF-type file consists of one or more chunks. The proposed
21n/ausage of the Chunk class defined here is to instantiate an instance at
22n/athe start of each chunk and read from the instance until it reaches
23n/athe end, after which a new instance can be instantiated. At the end
24n/aof the file, creating a new instance will fail with an EOFError
25n/aexception.
26n/a
27n/aUsage:
28n/awhile True:
29n/a try:
30n/a chunk = Chunk(file)
31n/a except EOFError:
32n/a break
33n/a chunktype = chunk.getname()
34n/a while True:
35n/a data = chunk.read(nbytes)
36n/a if not data:
37n/a pass
38n/a # do something with data
39n/a
40n/aThe interface is file-like. The implemented methods are:
41n/aread, close, seek, tell, isatty.
42n/aExtra methods are: skip() (called by close, skips to the end of the chunk),
43n/agetname() (returns the name (ID) of the chunk)
44n/a
45n/aThe __init__ method has one required argument, a file-like object
46n/a(including a chunk instance), and one optional argument, a flag which
47n/aspecifies whether or not chunks are aligned on 2-byte boundaries. The
48n/adefault is 1, i.e. aligned.
49n/a"""
50n/a
51n/aclass Chunk:
52n/a def __init__(self, file, align=True, bigendian=True, inclheader=False):
53n/a import struct
54n/a self.closed = False
55n/a self.align = align # whether to align to word (2-byte) boundaries
56n/a if bigendian:
57n/a strflag = '>'
58n/a else:
59n/a strflag = '<'
60n/a self.file = file
61n/a self.chunkname = file.read(4)
62n/a if len(self.chunkname) < 4:
63n/a raise EOFError
64n/a try:
65n/a self.chunksize = struct.unpack_from(strflag+'L', file.read(4))[0]
66n/a except struct.error:
67n/a raise EOFError
68n/a if inclheader:
69n/a self.chunksize = self.chunksize - 8 # subtract header
70n/a self.size_read = 0
71n/a try:
72n/a self.offset = self.file.tell()
73n/a except (AttributeError, OSError):
74n/a self.seekable = False
75n/a else:
76n/a self.seekable = True
77n/a
78n/a def getname(self):
79n/a """Return the name (ID) of the current chunk."""
80n/a return self.chunkname
81n/a
82n/a def getsize(self):
83n/a """Return the size of the current chunk."""
84n/a return self.chunksize
85n/a
86n/a def close(self):
87n/a if not self.closed:
88n/a try:
89n/a self.skip()
90n/a finally:
91n/a self.closed = True
92n/a
93n/a def isatty(self):
94n/a if self.closed:
95n/a raise ValueError("I/O operation on closed file")
96n/a return False
97n/a
98n/a def seek(self, pos, whence=0):
99n/a """Seek to specified position into the chunk.
100n/a Default position is 0 (start of chunk).
101n/a If the file is not seekable, this will result in an error.
102n/a """
103n/a
104n/a if self.closed:
105n/a raise ValueError("I/O operation on closed file")
106n/a if not self.seekable:
107n/a raise OSError("cannot seek")
108n/a if whence == 1:
109n/a pos = pos + self.size_read
110n/a elif whence == 2:
111n/a pos = pos + self.chunksize
112n/a if pos < 0 or pos > self.chunksize:
113n/a raise RuntimeError
114n/a self.file.seek(self.offset + pos, 0)
115n/a self.size_read = pos
116n/a
117n/a def tell(self):
118n/a if self.closed:
119n/a raise ValueError("I/O operation on closed file")
120n/a return self.size_read
121n/a
122n/a def read(self, size=-1):
123n/a """Read at most size bytes from the chunk.
124n/a If size is omitted or negative, read until the end
125n/a of the chunk.
126n/a """
127n/a
128n/a if self.closed:
129n/a raise ValueError("I/O operation on closed file")
130n/a if self.size_read >= self.chunksize:
131n/a return b''
132n/a if size < 0:
133n/a size = self.chunksize - self.size_read
134n/a if size > self.chunksize - self.size_read:
135n/a size = self.chunksize - self.size_read
136n/a data = self.file.read(size)
137n/a self.size_read = self.size_read + len(data)
138n/a if self.size_read == self.chunksize and \
139n/a self.align and \
140n/a (self.chunksize & 1):
141n/a dummy = self.file.read(1)
142n/a self.size_read = self.size_read + len(dummy)
143n/a return data
144n/a
145n/a def skip(self):
146n/a """Skip the rest of the chunk.
147n/a If you are not interested in the contents of the chunk,
148n/a this method should be called so that the file points to
149n/a the start of the next chunk.
150n/a """
151n/a
152n/a if self.closed:
153n/a raise ValueError("I/O operation on closed file")
154n/a if self.seekable:
155n/a try:
156n/a n = self.chunksize - self.size_read
157n/a # maybe fix alignment
158n/a if self.align and (self.chunksize & 1):
159n/a n = n + 1
160n/a self.file.seek(n, 1)
161n/a self.size_read = self.size_read + n
162n/a return
163n/a except OSError:
164n/a pass
165n/a while self.size_read < self.chunksize:
166n/a n = min(8192, self.chunksize - self.size_read)
167n/a dummy = self.read(n)
168n/a if not dummy:
169n/a raise EOFError