rapydscript-ns 0.9.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/test/io.pyj ADDED
@@ -0,0 +1,316 @@
1
+ # globals: assrt
2
+ # vim:fileencoding=utf-8
3
+ #
4
+ # io.pyj
5
+ # Tests for the io standard library module (StringIO and BytesIO).
6
+
7
+ from io import StringIO, BytesIO, UnsupportedOperation
8
+
9
+ ae = assrt.equal
10
+ ade = assrt.deepEqual
11
+ ok = assrt.ok
12
+ throws = assrt.throws
13
+
14
+ # ── 1. StringIO — construction ────────────────────────────────────────────────
15
+
16
+ _sio = StringIO()
17
+ ae(_sio.getvalue(), '')
18
+ ae(_sio.tell(), 0)
19
+ ok(not _sio.closed)
20
+
21
+ _sio2 = StringIO('hello')
22
+ ae(_sio2.getvalue(), 'hello')
23
+ ae(_sio2.tell(), 0)
24
+
25
+ # ── 2. StringIO — read ────────────────────────────────────────────────────────
26
+
27
+ _s = StringIO('hello world')
28
+ ae(_s.read(5), 'hello')
29
+ ae(_s.read(1), ' ')
30
+ ae(_s.read(), 'world')
31
+ ae(_s.read(), '') # at end
32
+
33
+ _s.seek(0)
34
+ ae(_s.read(), 'hello world')
35
+
36
+ # read with size=0
37
+ _s.seek(0)
38
+ ae(_s.read(0), '')
39
+ ae(_s.tell(), 0)
40
+
41
+ # ── 3. StringIO — readline ────────────────────────────────────────────────────
42
+
43
+ _ml = StringIO('line1\nline2\nline3')
44
+ ae(_ml.readline(), 'line1\n')
45
+ ae(_ml.readline(), 'line2\n')
46
+ ae(_ml.readline(), 'line3')
47
+ ae(_ml.readline(), '') # past end
48
+
49
+ # readline with size limit
50
+ _ml2 = StringIO('abcdef\nghijkl')
51
+ ae(_ml2.readline(3), 'abc')
52
+ ae(_ml2.readline(100), 'def\n')
53
+
54
+ # ── 4. StringIO — readlines ───────────────────────────────────────────────────
55
+
56
+ _rls = StringIO('a\nb\nc\n')
57
+ ade(_rls.readlines(), ['a\n', 'b\n', 'c\n'])
58
+
59
+ _rls2 = StringIO('x\ny\nz')
60
+ ade(_rls2.readlines(), ['x\n', 'y\n', 'z'])
61
+
62
+ # hint stops early (total >= hint)
63
+ _hint = StringIO('aa\nbb\ncc\n')
64
+ _hlines = _hint.readlines(hint=4)
65
+ ok(_hlines.length >= 1)
66
+ ok(_hlines.length <= 3)
67
+
68
+ # ── 5. StringIO — write ───────────────────────────────────────────────────────
69
+
70
+ _w = StringIO()
71
+ _w.write('hello')
72
+ _w.write(' ')
73
+ _w.write('world')
74
+ ae(_w.getvalue(), 'hello world')
75
+ ae(_w.tell(), 11)
76
+
77
+ # write returns the number of chars written
78
+ _w2 = StringIO()
79
+ ae(_w2.write('abc'), 3)
80
+ ae(_w2.write(''), 0)
81
+
82
+ # overwrite in the middle
83
+ _ow = StringIO('hello world')
84
+ _ow.seek(6)
85
+ _ow.write('Python')
86
+ ae(_ow.getvalue(), 'hello Python')
87
+
88
+ # ── 6. StringIO — writelines ──────────────────────────────────────────────────
89
+
90
+ _wl = StringIO()
91
+ _wl.writelines(['one', ' ', 'two'])
92
+ ae(_wl.getvalue(), 'one two')
93
+
94
+ # ── 7. StringIO — seek / tell ─────────────────────────────────────────────────
95
+
96
+ _sk = StringIO('abcde')
97
+ ae(_sk.seek(2), 2)
98
+ ae(_sk.tell(), 2)
99
+ ae(_sk.read(2), 'cd')
100
+
101
+ # whence=1 (relative)
102
+ _sk.seek(0)
103
+ _sk.seek(2, 1)
104
+ ae(_sk.tell(), 2)
105
+
106
+ # whence=2 (from end)
107
+ _sk.seek(-2, 2)
108
+ ae(_sk.tell(), 3)
109
+ ae(_sk.read(), 'de')
110
+
111
+ # seek before start clamps to 0
112
+ _sk.seek(-99)
113
+ ae(_sk.tell(), 0)
114
+
115
+ # ── 8. StringIO — truncate ────────────────────────────────────────────────────
116
+
117
+ _tr = StringIO('hello world')
118
+ _tr.seek(5)
119
+ ae(_tr.truncate(), 5)
120
+ ae(_tr.getvalue(), 'hello')
121
+
122
+ _tr2 = StringIO('hello world')
123
+ ae(_tr2.truncate(3), 3)
124
+ ae(_tr2.getvalue(), 'hel')
125
+
126
+ # ── 9. StringIO — closed / context manager ────────────────────────────────────
127
+
128
+ _cm = StringIO('text')
129
+ with _cm as _f:
130
+ ae(_f.read(), 'text')
131
+ ok(_cm.closed)
132
+
133
+ _err_raised = False
134
+ try:
135
+ _cm.read()
136
+ except ValueError:
137
+ _err_raised = True
138
+ ok(_err_raised, 'read on closed StringIO should raise ValueError')
139
+
140
+ # ── 10. StringIO — readable / writable / seekable ────────────────────────────
141
+
142
+ _rws = StringIO()
143
+ ok(_rws.readable())
144
+ ok(_rws.writable())
145
+ ok(_rws.seekable())
146
+
147
+ # ── 11. StringIO — iteration ──────────────────────────────────────────────────
148
+
149
+ _it = StringIO('line1\nline2\nline3\n')
150
+ _lines = list(_it)
151
+ ade(_lines, ['line1\n', 'line2\n', 'line3\n'])
152
+
153
+ # ── 12. BytesIO — construction ────────────────────────────────────────────────
154
+
155
+ _bio = BytesIO()
156
+ ae(len(_bio.getvalue()), 0)
157
+ ae(_bio.tell(), 0)
158
+ ok(not _bio.closed)
159
+
160
+ _bio2 = BytesIO(bytes([72, 101, 108, 108, 111]))
161
+ ae(len(_bio2.getvalue()), 5)
162
+ ae(_bio2.getvalue()[0], 72)
163
+ ae(_bio2.getvalue()[4], 111)
164
+
165
+ # init from bytearray
166
+ _bio3 = BytesIO(bytearray([1, 2, 3]))
167
+ ae(len(_bio3.getvalue()), 3)
168
+
169
+ # ── 13. BytesIO — read ────────────────────────────────────────────────────────
170
+
171
+ _br = BytesIO(bytes([10, 20, 30, 40, 50]))
172
+ ae(_br.read(2)[0], 10)
173
+ ae(_br.read(2)[0], 30)
174
+ ae(len(_br.read()), 1)
175
+ ae(len(_br.read()), 0) # at end
176
+
177
+ _br.seek(0)
178
+ ae(len(_br.read()), 5)
179
+
180
+ # ── 14. BytesIO — readline ────────────────────────────────────────────────────
181
+
182
+ # b'line1\nline2' (0x0a is newline)
183
+ _brl = BytesIO(bytes([108, 49, 10, 108, 50])) # 'l1\nl2'
184
+ _first = _brl.readline()
185
+ ae(len(_first), 3)
186
+ ae(_first[2], 10) # includes the newline byte
187
+
188
+ _second = _brl.readline()
189
+ ae(len(_second), 2)
190
+
191
+ ae(len(_brl.readline()), 0) # at end
192
+
193
+ # ── 15. BytesIO — write ───────────────────────────────────────────────────────
194
+
195
+ _bw = BytesIO()
196
+ ae(_bw.write(bytes([1, 2, 3])), 3)
197
+ ae(_bw.write(bytes([4, 5])), 2)
198
+ _bw.seek(0)
199
+ ae(len(_bw.read()), 5)
200
+ ae(_bw.getvalue()[0], 1)
201
+ ae(_bw.getvalue()[4], 5)
202
+
203
+ # write returns byte count
204
+ _bw2 = BytesIO()
205
+ ae(_bw2.write(bytes()), 0)
206
+
207
+ # overwrite in the middle
208
+ _bo = BytesIO(bytes([1, 2, 3, 4, 5]))
209
+ _bo.seek(1)
210
+ _bo.write(bytes([20, 30]))
211
+ _bo.seek(0)
212
+ _boval = _bo.read()
213
+ ae(_boval[0], 1)
214
+ ae(_boval[1], 20)
215
+ ae(_boval[2], 30)
216
+ ae(_boval[3], 4)
217
+ ae(_boval[4], 5)
218
+
219
+ # write past end extends with zeros
220
+ _bext = BytesIO(bytes([1, 2]))
221
+ _bext.seek(4)
222
+ _bext.write(bytes([99]))
223
+ _bextval = _bext.getvalue()
224
+ ae(len(_bextval), 5)
225
+ ae(_bextval[2], 0)
226
+ ae(_bextval[3], 0)
227
+ ae(_bextval[4], 99)
228
+
229
+ # ── 16. BytesIO — writelines ─────────────────────────────────────────────────
230
+
231
+ _bwl = BytesIO()
232
+ _bwl.writelines([bytes([1, 2]), bytes([3, 4])])
233
+ ae(len(_bwl.getvalue()), 4)
234
+
235
+ # ── 17. BytesIO — seek / tell ─────────────────────────────────────────────────
236
+
237
+ _bsk = BytesIO(bytes([10, 20, 30, 40, 50]))
238
+ ae(_bsk.seek(3), 3)
239
+ ae(_bsk.tell(), 3)
240
+ ae(_bsk.read(1)[0], 40)
241
+
242
+ # whence=1
243
+ _bsk.seek(0)
244
+ _bsk.seek(2, 1)
245
+ ae(_bsk.tell(), 2)
246
+
247
+ # whence=2
248
+ _bsk.seek(-1, 2)
249
+ ae(_bsk.tell(), 4)
250
+ ae(_bsk.read(1)[0], 50)
251
+
252
+ # clamp below 0
253
+ _bsk.seek(-999)
254
+ ae(_bsk.tell(), 0)
255
+
256
+ # ── 18. BytesIO — truncate ────────────────────────────────────────────────────
257
+
258
+ _btr = BytesIO(bytes([1, 2, 3, 4, 5]))
259
+ _btr.seek(3)
260
+ ae(_btr.truncate(), 3)
261
+ ae(len(_btr.getvalue()), 3)
262
+
263
+ _btr2 = BytesIO(bytes([10, 20, 30, 40]))
264
+ ae(_btr2.truncate(2), 2)
265
+ ae(len(_btr2.getvalue()), 2)
266
+ ae(_btr2.getvalue()[1], 20)
267
+
268
+ # ── 19. BytesIO — closed / context manager ───────────────────────────────────
269
+
270
+ _bcm = BytesIO(bytes([7, 8, 9]))
271
+ with _bcm as _bf:
272
+ _bf.seek(0)
273
+ ae(_bf.read()[0], 7)
274
+ ok(_bcm.closed)
275
+
276
+ _berr_raised = False
277
+ try:
278
+ _bcm.read()
279
+ except ValueError:
280
+ _berr_raised = True
281
+ ok(_berr_raised, 'read on closed BytesIO should raise ValueError')
282
+
283
+ # ── 20. BytesIO — readable / writable / seekable ─────────────────────────────
284
+
285
+ _brws = BytesIO()
286
+ ok(_brws.readable())
287
+ ok(_brws.writable())
288
+ ok(_brws.seekable())
289
+
290
+ # ── 21. BytesIO — iteration ───────────────────────────────────────────────────
291
+
292
+ _bit = BytesIO(bytes([65, 66, 10, 67, 68, 10])) # 'AB\nCD\n'
293
+ _bitlines = list(_bit)
294
+ ae(_bitlines.length, 2)
295
+ ae(_bitlines[0][0], 65) # 'A'
296
+ ae(_bitlines[0][2], 10) # '\n'
297
+
298
+ # ── 22. getvalue() does not depend on position ────────────────────────────────
299
+
300
+ _gv = StringIO('hello')
301
+ _gv.seek(3)
302
+ ae(_gv.getvalue(), 'hello') # full buffer regardless of pos
303
+
304
+ _bgv = BytesIO(bytes([1, 2, 3]))
305
+ _bgv.seek(2)
306
+ ae(len(_bgv.getvalue()), 3)
307
+
308
+ # ── 23. round-trip with json.dump / json.load ────────────────────────────────
309
+
310
+ from json import dump, load
311
+ _jsio = StringIO()
312
+ dump({'key': 'value', 'n': 42}, _jsio)
313
+ _jsio.seek(0)
314
+ _result = load(_jsio)
315
+ ae(_result.key, 'value')
316
+ ae(_result.n, 42)
package/test/json.pyj ADDED
@@ -0,0 +1,196 @@
1
+ # globals: assrt
2
+ # vim:fileencoding=utf-8
3
+ #
4
+ # json.pyj
5
+ # Tests for the json standard library module.
6
+
7
+ from json import dumps, loads, dump, load, JSONDecodeError
8
+
9
+ ae = assrt.equal
10
+ ade = assrt.deepEqual
11
+ ok = assrt.ok
12
+ throws = assrt.throws
13
+
14
+ # ── 1. dumps — basic types ────────────────────────────────────────────────────
15
+
16
+ ae(dumps(None), 'null')
17
+ ae(dumps(True), 'true')
18
+ ae(dumps(False), 'false')
19
+ ae(dumps(42), '42')
20
+ ae(dumps(3.14), '3.14')
21
+ ae(dumps('hello'), '"hello"')
22
+ ae(dumps('with "quotes"'), '"with \\"quotes\\""')
23
+
24
+ # ── 2. dumps — collections ────────────────────────────────────────────────────
25
+
26
+ ae(dumps([]), '[]')
27
+ ae(dumps([1, 2, 3]), '[1,2,3]')
28
+ ae(dumps({}), '{}')
29
+
30
+ # Object with single key (no ordering ambiguity)
31
+ _d1 = dumps({'a': 1})
32
+ ae(_d1, '{"a":1}')
33
+
34
+ # ── 3. dumps — indent ─────────────────────────────────────────────────────────
35
+
36
+ _pretty = dumps([1, 2], indent=2)
37
+ ok(_pretty.indexOf('\n') >= 0, 'indented output should contain newlines')
38
+ ok(_pretty.indexOf(' ') >= 0, 'indented output should contain spaces')
39
+
40
+ # ── 4. dumps — sort_keys ──────────────────────────────────────────────────────
41
+
42
+ _sk = dumps({'b': 2, 'a': 1}, sort_keys=True)
43
+ ae(_sk, '{"a":1,"b":2}')
44
+
45
+ # Nested sort_keys
46
+ _sk2 = dumps({'z': {'y': 3, 'x': 4}, 'a': 1}, sort_keys=True)
47
+ ae(_sk2, '{"a":1,"z":{"x":4,"y":3}}')
48
+
49
+ # ── 5. dumps — separators ─────────────────────────────────────────────────────
50
+
51
+ # Compact form used in Python: separators=(',', ':')
52
+ _compact = dumps({'a': 1, 'b': 2}, sort_keys=True, separators=(',', ':'))
53
+ ae(_compact, '{"a":1,"b":2}')
54
+
55
+ # ── 6. dumps — default callback ───────────────────────────────────────────────
56
+
57
+ class _Point:
58
+ def __init__(self, x, y):
59
+ self.x = x
60
+ self.y = y
61
+
62
+ def _point_default(obj):
63
+ if isinstance(obj, _Point):
64
+ return {'x': obj.x, 'y': obj.y}
65
+ raise TypeError('not serializable')
66
+
67
+ _pt_json = dumps(_Point(3, 4), dflt=_point_default)
68
+ _pt_obj = loads(_pt_json)
69
+ ae(_pt_obj.x, 3)
70
+ ae(_pt_obj.y, 4)
71
+
72
+ # ── 7. dumps — allow_nan ──────────────────────────────────────────────────────
73
+
74
+ _nan_raised = False
75
+ try:
76
+ dumps(v'NaN')
77
+ except ValueError:
78
+ _nan_raised = True
79
+ ok(_nan_raised, 'dumps(NaN) should raise ValueError by default')
80
+
81
+ _inf_raised = False
82
+ try:
83
+ dumps(v'Infinity')
84
+ except ValueError:
85
+ _inf_raised = True
86
+ ok(_inf_raised, 'dumps(Infinity) should raise ValueError by default')
87
+
88
+ # allow_nan=True should not raise
89
+ _nan_str = dumps(v'NaN', allow_nan=True)
90
+ ok(_nan_str.length > 0, 'allow_nan=True should return a string')
91
+
92
+ # ── 8. loads — basic types ────────────────────────────────────────────────────
93
+
94
+ ae(loads('null'), None)
95
+ ae(loads('true'), True)
96
+ ae(loads('false'), False)
97
+ ae(loads('42'), 42)
98
+ ae(loads('3.14'), 3.14)
99
+ ae(loads('"hello"'), 'hello')
100
+
101
+ # ── 9. loads — collections ────────────────────────────────────────────────────
102
+
103
+ _arr = loads('[1, 2, 3]')
104
+ ade(_arr, [1, 2, 3])
105
+
106
+ _obj = loads('{"a": 1, "b": 2}')
107
+ ae(_obj.a, 1)
108
+ ae(_obj.b, 2)
109
+
110
+ # ── 10. loads — nested structures ─────────────────────────────────────────────
111
+
112
+ _nested = loads('{"users": [{"name": "Alice", "age": 30}, {"name": "Bob", "age": 25}]}')
113
+ ae(_nested.users.length, 2)
114
+ ae(_nested.users[0].name, 'Alice')
115
+ ae(_nested.users[1].age, 25)
116
+
117
+ # ── 11. loads — object_hook ───────────────────────────────────────────────────
118
+
119
+ class _NameAge:
120
+ def __init__(self, d):
121
+ self.name = d.name if d.name else ''
122
+ self.age = d.age if d.age else 0
123
+
124
+ def _obj_hook(d):
125
+ if d.name is not None:
126
+ return _NameAge(d)
127
+ return d
128
+
129
+ _h = loads('{"name": "Alice", "age": 30}', object_hook=_obj_hook)
130
+ ok(isinstance(_h, _NameAge), 'object_hook should produce a _NameAge instance')
131
+ ae(_h.name, 'Alice')
132
+ ae(_h.age, 30)
133
+
134
+ # ── 12. loads — object_pairs_hook ─────────────────────────────────────────────
135
+
136
+ _pairs_result = []
137
+ def _pairs_hook(pairs):
138
+ _pairs_result.push(pairs.length)
139
+ return pairs
140
+
141
+ loads('{"x": 1, "y": 2}', object_pairs_hook=_pairs_hook)
142
+ ae(_pairs_result[0], 2, 'object_pairs_hook should receive 2 pairs')
143
+
144
+ # ── 13. loads — parse_float / parse_int ───────────────────────────────────────
145
+
146
+ _floats = []
147
+ def _capture_float(s):
148
+ _floats.push(s)
149
+ return float(s)
150
+
151
+ loads('3.14', parse_float=_capture_float)
152
+ ae(_floats.length, 1)
153
+ ae(_floats[0], '3.14')
154
+
155
+ _ints = []
156
+ def _capture_int(s):
157
+ _ints.push(s)
158
+ return int(s)
159
+
160
+ loads('42', parse_int=_capture_int)
161
+ ae(_ints.length, 1)
162
+ ae(_ints[0], '42')
163
+
164
+ # ── 14. loads — JSONDecodeError ───────────────────────────────────────────────
165
+
166
+ _err_raised = False
167
+ try:
168
+ loads('{invalid json}')
169
+ except JSONDecodeError as e:
170
+ _err_raised = True
171
+ ok(isinstance(e, JSONDecodeError), 'should be JSONDecodeError')
172
+ ok(isinstance(e, ValueError), 'JSONDecodeError should be a ValueError')
173
+ ok(_err_raised, 'invalid JSON should raise JSONDecodeError')
174
+
175
+ # ── 15. round-trip ────────────────────────────────────────────────────────────
176
+
177
+ _original = {'name': 'test', 'values': [1, 2, 3], 'nested': {'ok': True}}
178
+ _rt = loads(dumps(_original))
179
+ ae(_rt.name, _original.name)
180
+ ade(_rt.values, _original.values)
181
+ ae(_rt.nested.ok, _original.nested.ok)
182
+
183
+ # ── 16. dump / load via file-like object ──────────────────────────────────────
184
+
185
+ class _StringIO:
186
+ def __init__(self):
187
+ self._buf = ''
188
+ def write(self, s):
189
+ self._buf += s
190
+ def read(self):
191
+ return self._buf
192
+
193
+ _sio = _StringIO()
194
+ dump({'key': 'value'}, _sio)
195
+ _loaded = load(_sio)
196
+ ae(_loaded.key, 'value')