1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 import types
19 import cPickle
20 import struct
21 import qm.fields
22 from qm.test.file_result_stream import FileResultStream
23 from qm.test.file_result_reader import FileResultReader
24
25
26
27
28
29
30
31
32
33 _annotation_sentinel = None
34 """The sentinel value that marks the beginning of an annotation."""
35
36
37 _int_format = "!I"
38 _int_size = struct.calcsize(_int_format)
39
40
41
42
43
45 """A 'PickleResultStream' writes out results as Python pickles.
46
47 See also 'PickleResultReader', which does the reverse."""
48
49 _max_pinned_results = 1000
50 """A limit on how many `Result's to pin in memory at once.
51
52 Pickling an object normally pins it in memory; this is necessary
53 to ensure correct behaviour when pickling multiple references to
54 the same object. We know that `Result's can't refer to each
55 other, so this pinning is useless overhead; however, clearing the
56 cache at every call to `WriteResult' slows down both pickling and
57 unpickling by about a factor of two. As a solution, any given
58 `PickleResultStream', will clear its cache after
59 `_max_pinned_results' calls to WriteResult. This cache-clearing
60 technique causes a very minor slowdown on small result streams,
61 and a substantial speedup on large result streams."""
62
63 _format_version = 1
64 """The version number of the format we write.
65
66 This is bumped every time the format is changed, to make sure that
67 we can retain backwards compatibility.
68
69 "Version 0" contains no version number, and is simply a bunch
70 of 'Result's pickled one after another.
71
72 "Version 1", and all later versions, contain a pickled version
73 number as the first thing in the file. In version 1, this is
74 followed by a 4-byte unsigned integer in network byte order giving
75 the address of the first annotation, followed by the file proper.
76 The file proper is composed of a bunch of pickled 'Result's,
77 followed by a pickled sentinel value (None), followed by a 4-byte
78 unsigned integer in network-byte order, followed by the beginning of
79 a new pickle whose first item is a annotation tuple, and following
80 items are more 'Result's, and then another sentinel value, and so
81 on. An annotation tuple is a tuple of n items, the first of which
82 is a string tagging the type of annotation, and the rest of which
83 have an interpretation that depends on the tag found. The only tag
84 currently defined is "annotation", which is followed by two string
85 elements giving respectively the key and the value. The 4-byte
86 integers always point to the file address of the next such integer,
87 except for the last, which has a value of 0; they are used to
88 quickly find all annotations."""
89
90 arguments = [
91 qm.fields.IntegerField(
92 name = "protocol_version",
93 description = """The pickle protocol version to use.
94
95 There are multiple versions of the pickle protocol; in
96 general, higher numbers correspond to faster operation and
97 more compact files, but may produce files that cannot be
98 understood by older versions of Python.
99
100 As of 2003-06-20, the defined protocol versions are:
101 0: Traditional ASCII-only format.
102 1: Traditional binary format.
103 2: New binary format.
104 -1: Equivalent to the highest version supported by your
105 Python release.
106 Pickle versions 0 and 1 can be understood by any version
107 of Python; version 2 pickles can only be created or
108 understood by Python 2.3 and newer. (See PEP 307 for
109 details.)
110
111 Currently the default version is 1.
112
113 """,
114 default_value = 1,
115 ),
116 ]
117
118 _is_binary_file = 1
119
135
136
140
141
143
144 new_annotation = self.file.tell()
145 if self.__last_annotation is not None:
146 self.file.seek(self.__last_annotation)
147 self.file.write(struct.pack(_int_format, new_annotation))
148 self.file.seek(new_annotation)
149 self.file.write(struct.pack(_int_format, 0))
150 self.__last_annotation = new_annotation
151 self._ResetPickler()
152
153
161
162
164
165 self.__pickler.dump(result)
166 self.__processed += 1
167
168
169 if not self.__processed % self._max_pinned_results:
170 self.__pickler.clear_memo()
171
172
173
175 """A 'PickleResultReader' reads in results from pickle files.
176
177 See also 'PickleResultStream', which does the reverse."""
178
203
204
206
207 self.__unpickler = cPickle.Unpickler(self.file)
208
209
214
215
245
246
248
249 return self._annotations
250
251
253
254 while 1:
255 try:
256 thing = self.__unpickler.load()
257 except (EOFError, cPickle.UnpicklingError):
258
259
260
261
262 return None
263 else:
264 if thing is _annotation_sentinel:
265
266
267
268 self.file.seek(_int_size, 1)
269 self._ResetUnpickler()
270
271 self.__unpickler.noload()
272
273 else:
274
275 return thing
276
277
278
279
280
281
282
283