1 ### -*- mode: python, coding: utf-8 -*-
5 ### (c) 2019 Straylight/Edgeware
8 ###----- Licensing notice ---------------------------------------------------
10 ### This file is part of the Python interface to Catacomb.
12 ### Catacomb/Python is free software: you can redistribute it and/or
13 ### modify it under the terms of the GNU General Public License as
14 ### published by the Free Software Foundation; either version 2 of the
15 ### License, or (at your option) any later version.
17 ### Catacomb/Python is distributed in the hope that it will be useful, but
18 ### WITHOUT ANY WARRANTY; without even the implied warranty of
19 ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 ### General Public License for more details.
22 ### You should have received a copy of the GNU General Public License
23 ### along with Catacomb/Python. If not, write to the Free Software
24 ### Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
27 ###--------------------------------------------------------------------------
32 if SYS
.version_info
>= (3,): import builtins
as B
33 else: import __builtin__
as B
36 ###--------------------------------------------------------------------------
39 ## Some compatibility hacks.
40 if SYS
.version_info
>= (3,):
41 PY2
, PY3
= False, True
42 def bin(x
): return x
.encode('iso8859-1')
43 def py23(x
, y
): return y
48 def iterkeys(m
): return m
.keys()
49 def itervalues(m
): return m
.values()
50 def iteritems(m
): return m
.items()
51 from io
import StringIO
52 MAXFIXNUM
= SYS
.maxsize
55 PY2
, PY3
= True, False
57 def py23(x
, y
): return x
61 def byteseq(seq
): return "".join(map(chr, seq
))
62 def iterkeys(m
): return m
.iterkeys()
63 def itervalues(m
): return m
.itervalues()
64 def iteritems(m
): return m
.iteritems()
65 from cStringIO
import StringIO
66 MAXFIXNUM
= SYS
.maxint
68 DEBUGP
= hasattr(SYS
, "gettotalrefcount")
70 FULLSPAN
= byteseq(range(256))
72 """A string `00 01 .. NN'."""
73 return (n
>> 8)*FULLSPAN
+ FULLSPAN
[:n
&255]
75 def bytes_as_int(w
, bigendp
):
76 """Convert the byte-sequence `01 02 ... WW' to an integer."""
79 for i
in range(w
): x
= x
<< 8 | i
+ 1
81 for i
in range(w
): x |
= i
+ 1 << 8*i
84 def prep_lenseq(w
, n
, bigendp
, goodp
):
86 Return a reference buffer containing `00 LL .. LL 00 01 02 .. NN ff'.
88 Here, LL .. LL is the length of following sequence, not including the final
89 `ff', as a W-byte integer. If GOODP is false, then the most significant
90 bit of LL .. LL is set, to provoke an overflow.
93 else: l
= n
+ (1 << 8*w
- 1)
95 and (lambda i
: (l
>> 8*(w
- i
- 1))&0xff) \
96 or (lambda i
: (l
>> 8*i
)&0xff)
97 return byteseq([0x00]) + \
98 byteseq([lenbyte(i
) for i
in range(w
)]) + \
102 Z64
= C
.ByteString
.zero(8)
104 """Return a fast deterministic random generator with the given SEED."""
105 return C
.chacha8rand(C
.sha256().hash(bin(seed
)).done(), Z64
)
107 class GenericTestMixin (U
.TestCase
):
109 A mixin class to generate test-case functions for all similar things.
113 def generate_testcases(cls
, things
):
116 for k
, v
in iteritems(cls
.__dict__
):
117 if k
.startswith("_test_"): checkfns
.append((k
[6:], v
))
118 for name
, thing
in things
:
119 for test
, checkfn
in checkfns
:
120 testfn
= lambda me
, thing
= thing
: checkfn(me
, thing
)
121 doc
= getattr(checkfn
, "__doc__", None)
122 if doc
is not None: testfn
.__doc__
= doc % name
123 testfns
["test_%s%%%s" %
(test
, name
)] = testfn
124 tmpcls
= type("_tmp", (cls
,), testfns
)
125 for k
, v
in iteritems(tmpcls
.__dict__
):
126 if k
.startswith("test_"): setattr(cls
, k
, v
)
128 class ImmutableMappingTextMixin (U
.TestCase
):
131 def _mkkey(me
, i
): return "k#%d" % i
132 def _getkey(me
, k
): return int(k
[2:])
133 def _getvalue(me
, v
): return int(v
[2:])
134 def _getitem(me
, it
): k
, v
= it
; return me
._getkey(k
), me
._getvalue(v
)
136 def check_immutable_mapping(me
, map, model
):
141 me
.assertEqual(len(map), len(model
))
142 for k
, v
in iteritems(model
):
144 if k
>= limk
: limk
= k
+ 1
145 me
.assertTrue(me
._mkkey(k
) in map)
146 if PY2
: me
.assertTrue(map.has_key(me
._mkkey(k
)))
147 me
.assertEqual(me
._getvalue(map[me
._mkkey(k
)]), v
)
148 me
.assertEqual(me
._getvalue(map.get(me
._mkkey(k
))), v
)
149 if any
: me
.assertTrue(me
._mkkey(k
) in map)
150 if PY2
: me
.assertFalse(map.has_key(me
._mkkey(limk
)))
151 me
.assertRaises(KeyError, lambda: map[me
._mkkey(limk
)])
152 me
.assertEqual(map.get(me
._mkkey(limk
)), None)
157 for k
, v
in iteritems(map):
158 me
.assertTrue(k
in map.keys())
159 me
.assertTrue((k
, v
) in map.items())
160 me
.assertFalse(me
._mkkey(limk
) in map.keys())
162 for viewfn
, getfn
in [(lambda x
: x
.keys(), me
._getkey
),
163 (lambda x
: x
.items(), me
._getitem
)]:
164 rview
, rview2
, mview
= viewfn(map), viewfn(map), viewfn(model
)
165 me
.assertEqual(set(imap(getfn
, rview
)), set(mview
))
166 me
.assertEqual(rview
, rview2
)
167 me
.assertEqual(rview
, set(rview2
))
168 me
.assertEqual(rview | empty
, set(rview
))
169 me
.assertEqual(rview | rview2
, set(rview
))
170 me
.assertEqual(rview ^ empty
, set(rview
))
171 me
.assertEqual(rview ^ rview
, empty
)
172 me
.assertEqual(rview
& empty
, empty
)
173 me
.assertEqual(len(rview
), len(model
))
175 if any
: subset
= set(rview2
); subset
.pop()
176 superset
= set(rview2
); superset
.add(object())
178 me
.assertFalse(rview
< rview2
)
179 me
.assertTrue(rview
< superset
)
180 me
.assertFalse(superset
< rview
)
181 me
.assertFalse(rview
< empty
)
183 me
.assertTrue(empty
< rview
)
184 me
.assertTrue(subset
< rview
)
185 me
.assertFalse(rview
< subset
)
187 me
.assertTrue(rview
<= rview2
)
188 me
.assertTrue(rview
<= superset
)
189 me
.assertFalse(superset
<= rview
)
191 me
.assertTrue(empty
<= rview
)
192 me
.assertFalse(rview
<= empty
)
193 me
.assertTrue(subset
<= rview
)
194 me
.assertFalse(rview
<= subset
)
196 me
.assertTrue(rview
>= rview2
)
197 me
.assertTrue(superset
>= rview
)
198 me
.assertFalse(rview
>= superset
)
200 me
.assertTrue(rview
>= empty
)
201 me
.assertFalse(empty
>= rview
)
202 me
.assertTrue(rview
>= subset
)
203 me
.assertFalse(subset
>= rview
)
205 me
.assertFalse(rview
> rview2
)
206 me
.assertTrue(superset
> rview
)
207 me
.assertFalse(rview
> superset
)
208 me
.assertFalse(empty
> rview
)
210 me
.assertTrue(rview
> empty
)
211 me
.assertTrue(rview
> subset
)
212 me
.assertFalse(subset
> rview
)
215 for listfn
, getfn
in [(lambda x
: x
.keys(), me
._getkey
),
216 (lambda x
: x
.values(), me
._getvalue
),
217 (lambda x
: x
.items(), me
._getitem
)]:
218 rlist
, mlist
= listfn(map), listfn(model
)
219 me
.assertEqual(type(rlist
), list)
220 rlist
= B
.map(getfn
, rlist
)
221 rlist
.sort(); mlist
.sort(); me
.assertEqual(rlist
, mlist
)
222 for iterfn
, getfn
in [(lambda x
: x
.iterkeys(), me
._getkey
),
223 (lambda x
: x
.itervalues(), me
._getvalue
),
224 (lambda x
: x
.iteritems(), me
._getitem
)]:
225 me
.assertEqual(set(imap(getfn
, iterfn(map))), set(iterfn(model
)))
227 class MutableMappingTestMixin (ImmutableMappingTextMixin
):
230 def _mkvalue(me
, i
): return "v#%d" % i
232 def check_mapping(me
, emptymapfn
):
235 me
.assertEqual(len(map), 0)
239 me
.check_immutable_mapping(map, model
)
241 kview
, iview
, vview
= map.keys(), map.items(), map.values()
243 me
.check_immutable_mapping(map, model
)
244 me
.assertEqual(set(imap(me
._getkey
, kview
)), model
.keys())
245 me
.assertEqual(set(imap(me
._getitem
, iview
)), model
.items())
246 me
.assertEqual(set(imap(me
._getvalue
, vview
)), set(model
.values()))
248 model
= { 1: 101, 2: 202, 4: 404 }
249 for k
, v
in iteritems(model
): map[me
._mkkey(k
)] = me
._mkvalue(v
)
252 model
.update({ 2: 212, 6: 606, 7: 707 })
253 map.update({ me
._mkkey(2): me
._mkvalue(212),
254 me
._mkkey(6): me
._mkvalue(606) },
255 **{ me
._mkkey(7): me
._mkvalue(707) })
259 map[me
._mkkey(9)] = me
._mkvalue(909)
263 map[me
._mkkey(9)] = me
._mkvalue(919)
266 map.setdefault(me
._mkkey(9), me
._mkvalue(929))
270 map.setdefault(me
._mkkey(8), me
._mkvalue(808))
273 me
.assertRaises(KeyError, map.pop
, me
._mkkey(5))
275 me
.assertEqual(map.pop(me
._mkkey(5), obj
), obj
)
276 me
.assertEqual(me
._getvalue(map.pop(me
._mkkey(8))), 808)
281 del map[me
._mkkey(9)]
285 mk
, mv
= me
._getkey(k
), me
._getvalue(v
)
286 me
.assertEqual(model
[mk
], mv
)
294 class Explosion (Exception): pass
296 class EventRecorder (C
.PrimeGenEventHandler
):
297 def __init__(me
, parent
= None, explode_after
= None, *args
, **kw
):
298 super(EventRecorder
, me
).__init__(*args
, **kw
)
302 me
._countdown
= explode_after
304 if parent
is None: me
._buf
= StringIO()
305 else: me
._buf
= parent
._buf
306 def _event_common(me
, ev
):
307 if me
.rng
is None: me
.rng
= ev
.rng
308 if me
._countdown
is None: pass
309 elif me
._countdown
== 0: raise Explosion()
310 else: me
._countdown
-= 1
315 if me
._op
is not None: me
._buf
.write("%s%d/" %
(me
._op
, me
._streak
))
318 def pg_begin(me
, ev
):
320 me
._buf
.write("[%s:" % ev
.name
)
331 me
._put(None); me
._buf
.write("D]")
332 def pg_abort(me
, ev
):
334 me
._put(None); me
._buf
.write("A]")
337 return me
._buf
.getvalue()
339 ## Functions for operators.
342 add
= lambda x
, y
: x
+ y
343 sub
= lambda x
, y
: x
- y
344 mul
= lambda x
, y
: x
*y
345 div
= lambda x
, y
: x
/y
346 mod
= lambda x
, y
: x
%y
347 floordiv
= lambda x
, y
: x
//y
348 bitand
= lambda x
, y
: x
&y
349 bitor
= lambda x
, y
: x | y
350 bitxor
= lambda x
, y
: x ^ y
351 bitnot
= lambda x
: ~x
352 lsl
= lambda x
, y
: x
<< y
353 lsr
= lambda x
, y
: x
>> y
354 eq
= lambda x
, y
: x
== y
355 ne
= lambda x
, y
: x
!= y
356 lt
= lambda x
, y
: x
< y
357 le
= lambda x
, y
: x
<= y
358 ge
= lambda x
, y
: x
>= y
359 gt
= lambda x
, y
: x
> y
361 ###----- That's all, folks --------------------------------------------------