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
)]) + \
103 """Return a fast deterministic random generator with the given SEED."""
104 return C
.chacha8rand(C
.sha256().hash(bin(seed
)).done())
106 class GenericTestMixin (U
.TestCase
):
108 A mixin class to generate test-case functions for all similar things.
112 def generate_testcases(cls
, things
):
115 for k
, v
in iteritems(cls
.__dict__
):
116 if k
.startswith("_test_"): checkfns
.append((k
[6:], v
))
117 for name
, thing
in things
:
118 for test
, checkfn
in checkfns
:
119 testfn
= lambda me
, thing
= thing
: checkfn(me
, thing
)
120 doc
= getattr(checkfn
, "__doc__", None)
121 if doc
is not None: testfn
.__doc__
= doc % name
122 testfns
["test_%s%%%s" %
(test
, name
)] = testfn
123 tmpcls
= type("_tmp", (cls
,), testfns
)
124 for k
, v
in iteritems(tmpcls
.__dict__
):
125 if k
.startswith("test_"): setattr(cls
, k
, v
)
127 class ImmutableMappingTextMixin (U
.TestCase
):
130 def _mkkey(me
, i
): return "k#%d" % i
131 def _getkey(me
, k
): return int(k
[2:])
132 def _getvalue(me
, v
): return int(v
[2:])
133 def _getitem(me
, it
): k
, v
= it
; return me
._getkey(k
), me
._getvalue(v
)
135 def check_immutable_mapping(me
, map, model
):
140 me
.assertEqual(len(map), len(model
))
141 for k
, v
in iteritems(model
):
143 if k
>= limk
: limk
= k
+ 1
144 me
.assertTrue(me
._mkkey(k
) in map)
145 if PY2
: me
.assertTrue(map.has_key(me
._mkkey(k
)))
146 me
.assertEqual(me
._getvalue(map[me
._mkkey(k
)]), v
)
147 me
.assertEqual(me
._getvalue(map.get(me
._mkkey(k
))), v
)
148 if any
: me
.assertTrue(me
._mkkey(k
) in map)
149 if PY2
: me
.assertFalse(map.has_key(me
._mkkey(limk
)))
150 me
.assertRaises(KeyError, lambda: map[me
._mkkey(limk
)])
151 me
.assertEqual(map.get(me
._mkkey(limk
)), None)
156 for k
, v
in iteritems(map):
157 me
.assertTrue(k
in map.keys())
158 me
.assertTrue((k
, v
) in map.items())
159 me
.assertFalse(me
._mkkey(limk
) in map.keys())
161 for viewfn
, getfn
in [(lambda x
: x
.keys(), me
._getkey
),
162 (lambda x
: x
.items(), me
._getitem
)]:
163 rview
, rview2
, mview
= viewfn(map), viewfn(map), viewfn(model
)
164 me
.assertEqual(set(imap(getfn
, rview
)), set(mview
))
165 me
.assertEqual(rview
, rview2
)
166 me
.assertEqual(rview
, set(rview2
))
167 me
.assertEqual(rview | empty
, set(rview
))
168 me
.assertEqual(rview | rview2
, set(rview
))
169 me
.assertEqual(rview ^ empty
, set(rview
))
170 me
.assertEqual(rview ^ rview
, empty
)
171 me
.assertEqual(rview
& empty
, empty
)
172 me
.assertEqual(len(rview
), len(model
))
174 if any
: subset
= set(rview2
); subset
.pop()
175 superset
= set(rview2
); superset
.add(object())
177 me
.assertFalse(rview
< rview2
)
178 me
.assertTrue(rview
< superset
)
179 me
.assertFalse(superset
< rview
)
180 me
.assertFalse(rview
< empty
)
182 me
.assertTrue(empty
< rview
)
183 me
.assertTrue(subset
< rview
)
184 me
.assertFalse(rview
< subset
)
186 me
.assertTrue(rview
<= rview2
)
187 me
.assertTrue(rview
<= superset
)
188 me
.assertFalse(superset
<= rview
)
190 me
.assertTrue(empty
<= rview
)
191 me
.assertFalse(rview
<= empty
)
192 me
.assertTrue(subset
<= rview
)
193 me
.assertFalse(rview
<= subset
)
195 me
.assertTrue(rview
>= rview2
)
196 me
.assertTrue(superset
>= rview
)
197 me
.assertFalse(rview
>= superset
)
199 me
.assertTrue(rview
>= empty
)
200 me
.assertFalse(empty
>= rview
)
201 me
.assertTrue(rview
>= subset
)
202 me
.assertFalse(subset
>= rview
)
204 me
.assertFalse(rview
> rview2
)
205 me
.assertTrue(superset
> rview
)
206 me
.assertFalse(rview
> superset
)
207 me
.assertFalse(empty
> rview
)
209 me
.assertTrue(rview
> empty
)
210 me
.assertTrue(rview
> subset
)
211 me
.assertFalse(subset
> rview
)
214 for listfn
, getfn
in [(lambda x
: x
.keys(), me
._getkey
),
215 (lambda x
: x
.values(), me
._getvalue
),
216 (lambda x
: x
.items(), me
._getitem
)]:
217 rlist
, mlist
= listfn(map), listfn(model
)
218 me
.assertEqual(type(rlist
), list)
219 rlist
= B
.map(getfn
, rlist
)
220 rlist
.sort(); mlist
.sort(); me
.assertEqual(rlist
, mlist
)
221 for iterfn
, getfn
in [(lambda x
: x
.iterkeys(), me
._getkey
),
222 (lambda x
: x
.itervalues(), me
._getvalue
),
223 (lambda x
: x
.iteritems(), me
._getitem
)]:
224 me
.assertEqual(set(imap(getfn
, iterfn(map))), set(iterfn(model
)))
226 class MutableMappingTestMixin (ImmutableMappingTextMixin
):
229 def _mkvalue(me
, i
): return "v#%d" % i
231 def check_mapping(me
, emptymapfn
):
234 me
.assertEqual(len(map), 0)
238 me
.check_immutable_mapping(map, model
)
240 kview
, iview
, vview
= map.keys(), map.items(), map.values()
242 me
.check_immutable_mapping(map, model
)
243 me
.assertEqual(set(imap(me
._getkey
, kview
)), model
.keys())
244 me
.assertEqual(set(imap(me
._getitem
, iview
)), model
.items())
245 me
.assertEqual(set(imap(me
._getvalue
, vview
)), set(model
.values()))
247 model
= { 1: 101, 2: 202, 4: 404 }
248 for k
, v
in iteritems(model
): map[me
._mkkey(k
)] = me
._mkvalue(v
)
251 model
.update({ 2: 212, 6: 606, 7: 707 })
252 map.update({ me
._mkkey(2): me
._mkvalue(212),
253 me
._mkkey(6): me
._mkvalue(606) },
254 **{ me
._mkkey(7): me
._mkvalue(707) })
258 map[me
._mkkey(9)] = me
._mkvalue(909)
262 map[me
._mkkey(9)] = me
._mkvalue(919)
265 map.setdefault(me
._mkkey(9), me
._mkvalue(929))
269 map.setdefault(me
._mkkey(8), me
._mkvalue(808))
272 me
.assertRaises(KeyError, map.pop
, me
._mkkey(5))
274 me
.assertEqual(map.pop(me
._mkkey(5), obj
), obj
)
275 me
.assertEqual(me
._getvalue(map.pop(me
._mkkey(8))), 808)
280 del map[me
._mkkey(9)]
284 mk
, mv
= me
._getkey(k
), me
._getvalue(v
)
285 me
.assertEqual(model
[mk
], mv
)
293 class Explosion (Exception): pass
295 class EventRecorder (C
.PrimeGenEventHandler
):
296 def __init__(me
, parent
= None, explode_after
= None, *args
, **kw
):
297 super(EventRecorder
, me
).__init__(*args
, **kw
)
301 me
._countdown
= explode_after
303 if parent
is None: me
._buf
= StringIO()
304 else: me
._buf
= parent
._buf
305 def _event_common(me
, ev
):
306 if me
.rng
is None: me
.rng
= ev
.rng
307 if me
._countdown
is None: pass
308 elif me
._countdown
== 0: raise Explosion()
309 else: me
._countdown
-= 1
314 if me
._op
is not None: me
._buf
.write("%s%d/" %
(me
._op
, me
._streak
))
317 def pg_begin(me
, ev
):
319 me
._buf
.write("[%s:" % ev
.name
)
330 me
._put(None); me
._buf
.write("D]")
331 def pg_abort(me
, ev
):
333 me
._put(None); me
._buf
.write("A]")
336 return me
._buf
.getvalue()
338 ## Functions for operators.
341 add
= lambda x
, y
: x
+ y
342 sub
= lambda x
, y
: x
- y
343 mul
= lambda x
, y
: x
*y
344 div
= lambda x
, y
: x
/y
345 mod
= lambda x
, y
: x
%y
346 floordiv
= lambda x
, y
: x
//y
347 bitand
= lambda x
, y
: x
&y
348 bitor
= lambda x
, y
: x | y
349 bitxor
= lambda x
, y
: x ^ y
350 bitnot
= lambda x
: ~x
351 lsl
= lambda x
, y
: x
<< y
352 lsr
= lambda x
, y
: x
>> y
353 eq
= lambda x
, y
: x
== y
354 ne
= lambda x
, y
: x
!= y
355 lt
= lambda x
, y
: x
< y
356 le
= lambda x
, y
: x
<= y
357 ge
= lambda x
, y
: x
>= y
358 gt
= lambda x
, y
: x
> y
360 ###----- That's all, folks --------------------------------------------------