rapydscript-ns 0.9.2 → 0.9.4

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.
Files changed (88) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/PYTHON_GAPS.md +352 -0
  3. package/README.md +176 -32
  4. package/TODO.md +1 -128
  5. package/bin/rapydscript +70 -70
  6. package/language-service/index.js +242 -11
  7. package/memory/project_string_impl.md +43 -0
  8. package/package.json +1 -1
  9. package/release/baselib-plain-pretty.js +248 -38
  10. package/release/baselib-plain-ugly.js +8 -8
  11. package/release/compiler.js +778 -277
  12. package/release/signatures.json +30 -30
  13. package/src/ast.pyj +10 -1
  14. package/src/baselib-builtins.pyj +56 -2
  15. package/src/baselib-containers.pyj +25 -1
  16. package/src/baselib-errors.pyj +7 -3
  17. package/src/baselib-internal.pyj +51 -6
  18. package/src/baselib-str.pyj +18 -5
  19. package/src/lib/asyncio.pyj +534 -0
  20. package/src/lib/base64.pyj +399 -0
  21. package/src/lib/bisect.pyj +73 -0
  22. package/src/lib/collections.pyj +228 -4
  23. package/src/lib/csv.pyj +494 -0
  24. package/src/lib/heapq.pyj +98 -0
  25. package/src/lib/html.pyj +382 -0
  26. package/src/lib/http/__init__.pyj +98 -0
  27. package/src/lib/http/client.pyj +304 -0
  28. package/src/lib/http/cookies.pyj +236 -0
  29. package/src/lib/logging.pyj +672 -0
  30. package/src/lib/pprint.pyj +455 -0
  31. package/src/lib/pythonize.pyj +20 -20
  32. package/src/lib/statistics.pyj +0 -0
  33. package/src/lib/string.pyj +357 -0
  34. package/src/lib/textwrap.pyj +329 -0
  35. package/src/lib/urllib/__init__.pyj +14 -0
  36. package/src/lib/urllib/error.pyj +66 -0
  37. package/src/lib/urllib/parse.pyj +475 -0
  38. package/src/lib/urllib/request.pyj +86 -0
  39. package/src/monaco-language-service/analyzer.js +5 -2
  40. package/src/monaco-language-service/completions.js +26 -0
  41. package/src/monaco-language-service/diagnostics.js +203 -4
  42. package/src/monaco-language-service/scope.js +1 -0
  43. package/src/output/codegen.pyj +4 -1
  44. package/src/output/functions.pyj +152 -6
  45. package/src/output/loops.pyj +17 -2
  46. package/src/output/modules.pyj +1 -1
  47. package/src/output/operators.pyj +15 -0
  48. package/src/output/stream.pyj +0 -1
  49. package/src/parse.pyj +108 -24
  50. package/src/tokenizer.pyj +19 -3
  51. package/test/async_generators.pyj +144 -0
  52. package/test/asyncio.pyj +307 -0
  53. package/test/base64.pyj +202 -0
  54. package/test/baselib.pyj +23 -0
  55. package/test/bisect.pyj +178 -0
  56. package/test/chainmap.pyj +185 -0
  57. package/test/csv.pyj +405 -0
  58. package/test/float_special.pyj +64 -0
  59. package/test/heapq.pyj +174 -0
  60. package/test/html.pyj +212 -0
  61. package/test/http.pyj +259 -0
  62. package/test/imports.pyj +79 -72
  63. package/test/logging.pyj +356 -0
  64. package/test/long.pyj +130 -0
  65. package/test/parenthesized_with.pyj +141 -0
  66. package/test/pprint.pyj +232 -0
  67. package/test/python_compat.pyj +3 -5
  68. package/test/python_modulo.pyj +76 -0
  69. package/test/python_modulo_off.pyj +21 -0
  70. package/test/statistics.pyj +224 -0
  71. package/test/str.pyj +14 -0
  72. package/test/string.pyj +245 -0
  73. package/test/textwrap.pyj +172 -0
  74. package/test/type_display.pyj +48 -0
  75. package/test/type_enforcement.pyj +164 -0
  76. package/test/unit/index.js +94 -6
  77. package/test/unit/language-service-completions.js +121 -0
  78. package/test/unit/language-service-scope.js +32 -0
  79. package/test/unit/language-service.js +190 -5
  80. package/test/unit/run-language-service.js +17 -3
  81. package/test/unit/web-repl.js +2401 -13
  82. package/test/urllib.pyj +193 -0
  83. package/tools/compile.js +1 -1
  84. package/tools/embedded_compiler.js +7 -7
  85. package/tools/export.js +4 -2
  86. package/web-repl/main.js +1 -1
  87. package/web-repl/rapydscript.js +7 -5
  88. package/test/omit_function_metadata.pyj +0 -20
package/test/csv.pyj ADDED
@@ -0,0 +1,405 @@
1
+ # globals: assrt
2
+ # vim:fileencoding=utf-8
3
+ #
4
+ # csv.pyj
5
+ # Tests for the csv standard library module.
6
+
7
+ from csv import reader, writer, DictReader, DictWriter
8
+ from csv import Dialect, excel, excel_tab, unix_dialect
9
+ from csv import register_dialect, unregister_dialect, get_dialect, list_dialects
10
+ from csv import field_size_limit, Error
11
+ from csv import QUOTE_MINIMAL, QUOTE_ALL, QUOTE_NONNUMERIC, QUOTE_NONE
12
+ from io import StringIO
13
+
14
+ ae = assrt.equal
15
+ ade = assrt.deepEqual
16
+ ok = assrt.ok
17
+
18
+ # ── 1. Constants ──────────────────────────────────────────────────────────────
19
+
20
+ ae(QUOTE_MINIMAL, 0)
21
+ ae(QUOTE_ALL, 1)
22
+ ae(QUOTE_NONNUMERIC, 2)
23
+ ae(QUOTE_NONE, 3)
24
+
25
+ # ── 2. Basic reader — list input ──────────────────────────────────────────────
26
+
27
+ _r1 = reader(['a,b,c', '1,2,3'])
28
+ _row = _r1.__next__()
29
+ ade(_row, ['a', 'b', 'c'])
30
+ _row = _r1.__next__()
31
+ ade(_row, ['1', '2', '3'])
32
+ _done = False
33
+ try:
34
+ _r1.__next__()
35
+ except StopIteration:
36
+ _done = True
37
+ ok(_done, 'reader raises StopIteration at end')
38
+
39
+ # ── 3. Reader via for loop ─────────────────────────────────────────────────
40
+
41
+ _rows = []
42
+ for _row in reader(['x,y', 'p,q']):
43
+ _rows.push(_row)
44
+ ae(_rows.length, 2)
45
+ ade(_rows[0], ['x', 'y'])
46
+ ade(_rows[1], ['p', 'q'])
47
+
48
+ # ── 4. Reader — quoted fields ─────────────────────────────────────────────────
49
+
50
+ _rows = []
51
+ for _row in reader(['"hello, world",foo', '"has ""quotes""",bar']):
52
+ _rows.push(_row)
53
+ ade(_rows[0], ['hello, world', 'foo'])
54
+ ade(_rows[1], ['has "quotes"', 'bar'])
55
+
56
+ # ── 5. Reader — custom delimiter ──────────────────────────────────────────────
57
+
58
+ _rows = []
59
+ for _row in reader(['a|b|c', '1|2|3'], delimiter='|'):
60
+ _rows.push(_row)
61
+ ade(_rows[0], ['a', 'b', 'c'])
62
+ ade(_rows[1], ['1', '2', '3'])
63
+
64
+ # ── 6. Reader — skipinitialspace ──────────────────────────────────────────────
65
+
66
+ _rows = []
67
+ for _row in reader(['a, b, c'], skipinitialspace=True):
68
+ _rows.push(_row)
69
+ ade(_rows[0], ['a', 'b', 'c'])
70
+
71
+ # ── 7. Reader — tab-delimited (excel-tab dialect) ────────────────────────────
72
+
73
+ _rows = []
74
+ for _row in reader(['a\tb\tc'], dialect='excel-tab'):
75
+ _rows.push(_row)
76
+ ade(_rows[0], ['a', 'b', 'c'])
77
+
78
+ # ── 8. Reader — QUOTE_NONNUMERIC converts unquoted fields to float ────────────
79
+
80
+ _rows = []
81
+ for _row in reader(['"text",3.14,42'], quoting=QUOTE_NONNUMERIC):
82
+ _rows.push(_row)
83
+ ae(_rows[0][0], 'text') # quoted → stays string
84
+ ae(_rows[0][1], 3.14) # unquoted numeric → float
85
+ ae(_rows[0][2], 42.0) # unquoted int → float
86
+
87
+ # ── 9. Reader — multi-line quoted field ───────────────────────────────────────
88
+
89
+ _rows = []
90
+ for _row in reader(['"line1\nline2",b']):
91
+ _rows.push(_row)
92
+ ae(_rows[0][0], 'line1\nline2')
93
+ ae(_rows[0][1], 'b')
94
+
95
+ # ── 10. Reader — empty fields and trailing delimiter ─────────────────────────
96
+
97
+ _rows = []
98
+ for _row in reader(['a,,c', ',,']):
99
+ _rows.push(_row)
100
+ ade(_rows[0], ['a', '', 'c'])
101
+ ade(_rows[1], ['', '', ''])
102
+
103
+ # ── 11. Reader — file-like input (StringIO) ───────────────────────────────────
104
+
105
+ _sio_in = StringIO('hello,world\nfoo,bar\n')
106
+ _rows = []
107
+ for _row in reader(_sio_in):
108
+ _rows.push(_row)
109
+ ae(_rows.length, 2)
110
+ ade(_rows[0], ['hello', 'world'])
111
+ ade(_rows[1], ['foo', 'bar'])
112
+
113
+ # ── 12. Reader — line_num ─────────────────────────────────────────────────────
114
+
115
+ _r2 = reader(['a,b', 'c,d', 'e,f'])
116
+ ae(_r2.line_num, 0)
117
+ _r2.__next__()
118
+ ae(_r2.line_num, 1)
119
+ _r2.__next__()
120
+ ae(_r2.line_num, 2)
121
+
122
+ # ── 13. Reader — list items with trailing newlines ───────────────────────────
123
+
124
+ _rows = []
125
+ for _row in reader(['a,b\n', 'c,d\n']):
126
+ _rows.push(_row)
127
+ ae(_rows.length, 2)
128
+ ade(_rows[0], ['a', 'b'])
129
+ ade(_rows[1], ['c', 'd'])
130
+
131
+ # ── 14. Reader — empty list ───────────────────────────────────────────────────
132
+
133
+ _rows = []
134
+ for _row in reader([]):
135
+ _rows.push(_row)
136
+ ae(_rows.length, 0)
137
+
138
+ # ── 15. Reader — single empty field ──────────────────────────────────────────
139
+
140
+ _rows = []
141
+ for _row in reader(['']):
142
+ _rows.push(_row)
143
+ ae(_rows.length, 1)
144
+ ade(_rows[0], [''])
145
+
146
+ # ── 16. Reader — dialect attribute ────────────────────────────────────────────
147
+
148
+ _r3 = reader(['a,b'])
149
+ ae(_r3.dialect.delimiter, ',')
150
+ ae(_r3.dialect.quotechar, '"')
151
+ ok(_r3.dialect.doublequote)
152
+
153
+ # ── 17. Writer — basic writerow ───────────────────────────────────────────────
154
+
155
+ _out = StringIO()
156
+ _w = writer(_out)
157
+ _w.writerow(['a', 'b', 'c'])
158
+ ae(_out.getvalue(), 'a,b,c\r\n')
159
+
160
+ # ── 18. Writer — writerow with field needing quoting ─────────────────────────
161
+
162
+ _out = StringIO()
163
+ _w = writer(_out)
164
+ _w.writerow(['hello, world', 'foo'])
165
+ ae(_out.getvalue(), '"hello, world",foo\r\n')
166
+
167
+ # ── 19. Writer — writerow quotes double-quote characters ─────────────────────
168
+
169
+ _out = StringIO()
170
+ _w = writer(_out)
171
+ _w.writerow(['say "hi"', 'ok'])
172
+ ae(_out.getvalue(), '"say ""hi""",ok\r\n')
173
+
174
+ # ── 20. Writer — QUOTE_ALL ────────────────────────────────────────────────────
175
+
176
+ _out = StringIO()
177
+ _w = writer(_out, quoting=QUOTE_ALL)
178
+ _w.writerow(['a', 'b', 'c'])
179
+ ae(_out.getvalue(), '"a","b","c"\r\n')
180
+
181
+ # ── 21. Writer — QUOTE_NONE with escapechar ───────────────────────────────────
182
+
183
+ _out = StringIO()
184
+ _w = writer(_out, quoting=QUOTE_NONE, escapechar='\\')
185
+ _w.writerow(['a,b', 'c'])
186
+ ae(_out.getvalue(), 'a\\,b,c\r\n')
187
+
188
+ # ── 22. Writer — custom delimiter ─────────────────────────────────────────────
189
+
190
+ _out = StringIO()
191
+ _w = writer(_out, delimiter='|')
192
+ _w.writerow(['x', 'y', 'z'])
193
+ ae(_out.getvalue(), 'x|y|z\r\n')
194
+
195
+ # ── 23. Writer — writerows ────────────────────────────────────────────────────
196
+
197
+ _out = StringIO()
198
+ _w = writer(_out)
199
+ _w.writerows([['1', '2'], ['3', '4']])
200
+ ae(_out.getvalue(), '1,2\r\n3,4\r\n')
201
+
202
+ # ── 24. Writer — unix dialect (lineterminator=\n) ────────────────────────────
203
+
204
+ _out = StringIO()
205
+ _w = writer(_out, dialect='unix')
206
+ _w.writerow(['a', 'b'])
207
+ ae(_out.getvalue(), '"a","b"\n')
208
+
209
+ # ── 25. Writer — numeric values ───────────────────────────────────────────────
210
+
211
+ _out = StringIO()
212
+ _w = writer(_out)
213
+ _w.writerow([1, 3.14, True])
214
+ ae(_out.getvalue(), '1,3.14,true\r\n')
215
+
216
+ # ── 26. DictReader — fieldnames from first row ───────────────────────────────
217
+
218
+ _dr = DictReader(['name,age', 'Alice,30', 'Bob,25'])
219
+ _rows = []
220
+ for _row in _dr:
221
+ _rows.push(_row)
222
+ ae(_rows.length, 2)
223
+ ae(_rows[0]['name'], 'Alice')
224
+ ae(_rows[0]['age'], '30')
225
+ ae(_rows[1]['name'], 'Bob')
226
+ ae(_rows[1]['age'], '25')
227
+
228
+ # ── 27. DictReader — provided fieldnames ─────────────────────────────────────
229
+
230
+ _dr2 = DictReader(['Alice,30', 'Bob,25'], fieldnames=['name', 'age'])
231
+ _rows = []
232
+ for _row in _dr2:
233
+ _rows.push(_row)
234
+ ae(_rows.length, 2)
235
+ ae(_rows[0]['name'], 'Alice')
236
+ ae(_rows[0]['age'], '30')
237
+
238
+ # ── 28. DictReader — restval for missing fields ───────────────────────────────
239
+
240
+ _dr3 = DictReader(['name,age,city', 'Alice,30'], restval='N/A')
241
+ _rows = []
242
+ for _row in _dr3:
243
+ _rows.push(_row)
244
+ ae(_rows[0]['city'], 'N/A')
245
+
246
+ # ── 29. DictReader — extra fields go to restkey ──────────────────────────────
247
+
248
+ _dr4 = DictReader(['name,age', 'Alice,30,extra1,extra2'])
249
+ _row = _dr4.__next__()
250
+ ok(_row['EXTRA'] is not None)
251
+ ae(_row['EXTRA'].length, 2)
252
+ ae(_row['EXTRA'][0], 'extra1')
253
+
254
+ # ── 30. DictReader — empty input ─────────────────────────────────────────────
255
+
256
+ _dr5 = DictReader([])
257
+ _empty = True
258
+ for _row in _dr5:
259
+ _empty = False
260
+ ok(_empty, 'DictReader on empty input produces no rows')
261
+
262
+ # ── 31. DictWriter — basic writerow ──────────────────────────────────────────
263
+
264
+ _out = StringIO()
265
+ _dw = DictWriter(_out, ['name', 'age'])
266
+ _dw.writerow({'name': 'Alice', 'age': '30'})
267
+ ae(_out.getvalue(), 'Alice,30\r\n')
268
+
269
+ # ── 32. DictWriter — writeheader ──────────────────────────────────────────────
270
+
271
+ _out = StringIO()
272
+ _dw = DictWriter(_out, ['name', 'age'])
273
+ _dw.writeheader()
274
+ ae(_out.getvalue(), 'name,age\r\n')
275
+
276
+ # ── 33. DictWriter — restval for missing fields ───────────────────────────────
277
+
278
+ _out = StringIO()
279
+ _dw = DictWriter(_out, ['name', 'age', 'city'], restval='')
280
+ _dw.writerow({'name': 'Alice', 'age': '30'})
281
+ ae(_out.getvalue(), 'Alice,30,\r\n')
282
+
283
+ # ── 34. DictWriter — writerows ────────────────────────────────────────────────
284
+
285
+ _out = StringIO()
286
+ _dw = DictWriter(_out, ['x', 'y'])
287
+ _dw.writerows([{'x': '1', 'y': '2'}, {'x': '3', 'y': '4'}])
288
+ ae(_out.getvalue(), '1,2\r\n3,4\r\n')
289
+
290
+ # ── 35. DictWriter — extrasaction='raise' detects unknown fields ──────────────
291
+
292
+ _out = StringIO()
293
+ _dw2 = DictWriter(_out, ['name'], extrasaction='raise')
294
+ _extra_raised = False
295
+ try:
296
+ _dw2.writerow({'name': 'Alice', 'foo': 'bar'})
297
+ except ValueError:
298
+ _extra_raised = True
299
+ ok(_extra_raised, "DictWriter extrasaction='raise' should raise ValueError for extra fields")
300
+
301
+ # ── 36. DictWriter — extrasaction='ignore' silently ignores extra fields ──────
302
+
303
+ _out = StringIO()
304
+ _dw3 = DictWriter(_out, ['name'], extrasaction='ignore')
305
+ _dw3.writerow({'name': 'Alice', 'extra': 'ignored'})
306
+ ae(_out.getvalue(), 'Alice\r\n')
307
+
308
+ # ── 37. register_dialect / list_dialects / get_dialect ───────────────────────
309
+
310
+ register_dialect('pipes', delimiter='|')
311
+ _dl = list_dialects()
312
+ ok(_dl.indexOf('pipes') >= 0, 'pipes dialect should be in list_dialects()')
313
+
314
+ _d = get_dialect('pipes')
315
+ ae(_d.delimiter, '|')
316
+
317
+ _out = StringIO()
318
+ _w = writer(_out, dialect='pipes')
319
+ _w.writerow(['a', 'b'])
320
+ ae(_out.getvalue(), 'a|b\r\n')
321
+
322
+ # ── 38. unregister_dialect ────────────────────────────────────────────────────
323
+
324
+ unregister_dialect('pipes')
325
+ _dl2 = list_dialects()
326
+ ok(_dl2.indexOf('pipes') < 0, 'pipes dialect removed after unregister_dialect')
327
+
328
+ _err_raised = False
329
+ try:
330
+ get_dialect('pipes')
331
+ except Error:
332
+ _err_raised = True
333
+ ok(_err_raised, 'get_dialect on unknown name should raise csv.Error')
334
+
335
+ # ── 39. unregister_dialect on built-ins raises Error ─────────────────────────
336
+
337
+ _unregister_bad = False
338
+ try:
339
+ unregister_dialect('no_such_dialect')
340
+ except Error:
341
+ _unregister_bad = True
342
+ ok(_unregister_bad, 'unregister_dialect on unknown name should raise csv.Error')
343
+
344
+ # ── 40. reader with unknown dialect raises Error ──────────────────────────────
345
+
346
+ _bad_dialect = False
347
+ try:
348
+ reader(['a,b'], dialect='nonexistent')
349
+ except Error:
350
+ _bad_dialect = True
351
+ ok(_bad_dialect, 'reader with unknown dialect should raise csv.Error')
352
+
353
+ # ── 41. field_size_limit ──────────────────────────────────────────────────────
354
+
355
+ ae(field_size_limit(), 131072)
356
+ _old = field_size_limit(65536)
357
+ ae(_old, 131072)
358
+ ae(field_size_limit(), 65536)
359
+ field_size_limit(131072) # restore
360
+
361
+ # ── 42. list_dialects includes defaults ───────────────────────────────────────
362
+
363
+ _default_dialects = list_dialects()
364
+ ok(_default_dialects.indexOf('excel') >= 0)
365
+ ok(_default_dialects.indexOf('excel-tab') >= 0)
366
+ ok(_default_dialects.indexOf('unix') >= 0)
367
+
368
+ # ── 43. Writer — dialect attribute ────────────────────────────────────────────
369
+
370
+ _out = StringIO()
371
+ _w4 = writer(_out, dialect='excel-tab')
372
+ ae(_w4.dialect.delimiter, '\t')
373
+
374
+ # ── 44. Round-trip: write then read ──────────────────────────────────────────
375
+
376
+ _buf = StringIO()
377
+ _wrt = writer(_buf)
378
+ _wrt.writerow(['name', 'age', 'city'])
379
+ _wrt.writerow(['Alice', '30', 'New York'])
380
+ _wrt.writerow(['Bob\'s Diner', '25', 'Los Angeles, CA'])
381
+ _buf.seek(0)
382
+ _result = []
383
+ for _r in reader(_buf):
384
+ _result.push(_r)
385
+ ae(_result.length, 3)
386
+ ade(_result[0], ['name', 'age', 'city'])
387
+ ade(_result[1], ['Alice', '30', 'New York'])
388
+ ade(_result[2], ["Bob's Diner", '25', 'Los Angeles, CA'])
389
+
390
+ # ── 45. Round-trip: DictWriter then DictReader ───────────────────────────────
391
+
392
+ _buf2 = StringIO()
393
+ _dw4 = DictWriter(_buf2, ['name', 'score'])
394
+ _dw4.writeheader()
395
+ _dw4.writerow({'name': 'Eve', 'score': '99'})
396
+ _dw4.writerow({'name': 'Frank', 'score': '88'})
397
+ _buf2.seek(0)
398
+ _result2 = []
399
+ for _r in DictReader(_buf2):
400
+ _result2.push(_r)
401
+ ae(_result2.length, 2)
402
+ ae(_result2[0]['name'], 'Eve')
403
+ ae(_result2[0]['score'], '99')
404
+ ae(_result2[1]['name'], 'Frank')
405
+ ae(_result2[1]['score'], '88')
@@ -0,0 +1,64 @@
1
+ # vim:fileencoding=utf-8
2
+ # globals: assrt
3
+
4
+ ae = assrt.equal
5
+ ok = assrt.ok
6
+
7
+ # ── float() — special string values ───────────────────────────────────────────
8
+
9
+ # 'inf' / '+inf'
10
+ ae(float('inf'), v'Infinity')
11
+ ae(float('+inf'), v'Infinity')
12
+ ae(float('INF'), v'Infinity')
13
+ ae(float('Inf'), v'Infinity')
14
+
15
+ # '-inf'
16
+ ae(float('-inf'), v'-Infinity')
17
+ ae(float('-INF'), v'-Infinity')
18
+ ae(float('-Inf'), v'-Infinity')
19
+
20
+ # 'infinity' variants
21
+ ae(float('infinity'), v'Infinity')
22
+ ae(float('+infinity'), v'Infinity')
23
+ ae(float('-infinity'), v'-Infinity')
24
+ ae(float('Infinity'), v'Infinity')
25
+ ae(float('+Infinity'), v'Infinity')
26
+ ae(float('-Infinity'), v'-Infinity')
27
+ ae(float('INFINITY'), v'Infinity')
28
+ ae(float('-INFINITY'), v'-Infinity')
29
+
30
+ # 'nan' variants
31
+ ok(isNaN(float('nan')))
32
+ ok(isNaN(float('NaN')))
33
+ ok(isNaN(float('NAN')))
34
+ ok(isNaN(float('+nan')))
35
+ ok(isNaN(float('-nan')))
36
+
37
+ # leading/trailing whitespace is accepted (matches Python)
38
+ ae(float(' inf '), v'Infinity')
39
+ ae(float(' -inf '), v'-Infinity')
40
+ ok(isNaN(float(' nan ')))
41
+
42
+ # numeric strings still work
43
+ ae(float('3.14'), 3.14)
44
+ ae(float('1e10'), 1e10)
45
+ ae(float('-2.5'), -2.5)
46
+
47
+ # passing a real Infinity/NaN JS value through float() still works
48
+ ae(float(v'Infinity'), v'Infinity')
49
+ ae(float(v'-Infinity'), v'-Infinity')
50
+
51
+ # ValueError is still raised for unrecognised strings
52
+ _err = False
53
+ try:
54
+ float('not_a_number')
55
+ except ValueError:
56
+ _err = True
57
+ ok(_err)
58
+
59
+ _err2 = False
60
+ try:
61
+ float('')
62
+ except ValueError:
63
+ _err2 = True
64
+ ok(_err2)
package/test/heapq.pyj ADDED
@@ -0,0 +1,174 @@
1
+ # globals: assrt
2
+ # vim:fileencoding=utf-8
3
+ #
4
+ # heapq.pyj
5
+ # Tests for the heapq standard library module.
6
+
7
+ from heapq import heappush, heappop, heapify, heapreplace, heappushpop, nlargest, nsmallest
8
+
9
+ ae = assrt.equal
10
+ ade = assrt.deepEqual
11
+ ok = assrt.ok
12
+
13
+ # ── 1. heappush / heappop — basic sort order ──────────────────────────────────
14
+
15
+ h = []
16
+ heappush(h, 3)
17
+ heappush(h, 1)
18
+ heappush(h, 4)
19
+ heappush(h, 1)
20
+ heappush(h, 5)
21
+ ae(heappop(h), 1)
22
+ ae(heappop(h), 1)
23
+ ae(heappop(h), 3)
24
+ ae(heappop(h), 4)
25
+ ae(heappop(h), 5)
26
+ ae(h.length, 0)
27
+
28
+ # ── 2. heapify — min at root ──────────────────────────────────────────────────
29
+
30
+ x = [5, 3, 8, 1, 2, 4]
31
+ heapify(x)
32
+ ae(x[0], 1)
33
+
34
+ # verify heap invariant: every parent <= its children
35
+ _hi = 0
36
+ while _hi < Math.floor(x.length / 2):
37
+ _left = 2 * _hi + 1
38
+ _right = 2 * _hi + 2
39
+ ok(x[_hi] <= x[_left], 'parent <= left child at ' + str(_hi))
40
+ if _right < x.length:
41
+ ok(x[_hi] <= x[_right], 'parent <= right child at ' + str(_hi))
42
+ _hi += 1
43
+
44
+ # ── 3. heapify + heappop produces sorted output ───────────────────────────────
45
+
46
+ _sort_data = [5, 3, 8, 1, 2, 4]
47
+ heapify(_sort_data)
48
+ _sort_result = []
49
+ while _sort_data.length > 0:
50
+ _sort_result.push(heappop(_sort_data))
51
+ ade(_sort_result, [1, 2, 3, 4, 5, 8])
52
+
53
+ # ── 4. heapreplace — returns old min, inserts new item ────────────────────────
54
+
55
+ _r = [1, 3, 5, 7, 9]
56
+ heapify(_r)
57
+ _old = heapreplace(_r, 4)
58
+ ae(_old, 1)
59
+ ae(_r[0], 3)
60
+
61
+ _r_sorted = []
62
+ while _r.length > 0:
63
+ _r_sorted.push(heappop(_r))
64
+ ade(_r_sorted, [3, 4, 5, 7, 9])
65
+
66
+ # ── 5. heappushpop ────────────────────────────────────────────────────────────
67
+
68
+ # item > heap[0]: swap root out, sift new item in
69
+ _pp = [1, 3, 5]
70
+ heapify(_pp)
71
+ _res_pp = heappushpop(_pp, 2)
72
+ ae(_res_pp, 1)
73
+ ae(_pp[0], 2)
74
+
75
+ # item <= heap[0]: return item unchanged, heap unmodified
76
+ _pp2 = [5, 7, 9]
77
+ heapify(_pp2)
78
+ _res_pp2 = heappushpop(_pp2, 4)
79
+ ae(_res_pp2, 4)
80
+ ae(_pp2[0], 5)
81
+
82
+ # ── 6. heappop raises IndexError on empty heap ────────────────────────────────
83
+
84
+ _pop_err = False
85
+ try:
86
+ heappop([])
87
+ except IndexError:
88
+ _pop_err = True
89
+ ok(_pop_err, 'heappop on empty heap raises IndexError')
90
+
91
+ # ── 7. heapreplace raises IndexError on empty heap ───────────────────────────
92
+
93
+ _rep_err = False
94
+ try:
95
+ heapreplace([], 1)
96
+ except IndexError:
97
+ _rep_err = True
98
+ ok(_rep_err, 'heapreplace on empty heap raises IndexError')
99
+
100
+ # ── 8. nsmallest — basic ─────────────────────────────────────────────────────
101
+
102
+ _ns_data = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3]
103
+ ade(nsmallest(3, _ns_data), [1, 1, 2])
104
+ ade(nsmallest(1, _ns_data), [1])
105
+ ade(nsmallest(0, _ns_data), [])
106
+ ade(nsmallest(100, _ns_data), [1, 1, 2, 3, 3, 4, 5, 5, 6, 9])
107
+
108
+ # ── 9. nlargest — basic ──────────────────────────────────────────────────────
109
+
110
+ ade(nlargest(3, _ns_data), [9, 6, 5])
111
+ ade(nlargest(1, _ns_data), [9])
112
+ ade(nlargest(0, _ns_data), [])
113
+ ade(nlargest(100, _ns_data), [9, 6, 5, 5, 4, 3, 3, 2, 1, 1])
114
+
115
+ # ── 10. nsmallest / nlargest with key ────────────────────────────────────────
116
+
117
+ _pairs = [[3, 'c'], [1, 'a'], [4, 'd'], [1, 'b'], [5, 'e']]
118
+ _kfn = def(p): return p[0];
119
+
120
+ _sm2 = nsmallest(2, _pairs, key=_kfn)
121
+ ae(_sm2.length, 2)
122
+ ae(_sm2[0][0], 1)
123
+ ae(_sm2[1][0], 1)
124
+
125
+ _lg2 = nlargest(2, _pairs, key=_kfn)
126
+ ae(_lg2.length, 2)
127
+ ae(_lg2[0][0], 5)
128
+ ae(_lg2[1][0], 4)
129
+
130
+ # ── 11. Single element ────────────────────────────────────────────────────────
131
+
132
+ _single = [42]
133
+ heapify(_single)
134
+ ae(_single[0], 42)
135
+ ae(heappop(_single), 42)
136
+ ae(_single.length, 0)
137
+
138
+ # ── 12. Negative numbers ─────────────────────────────────────────────────────
139
+
140
+ _neg = [-3, -1, -4, -1, -5]
141
+ heapify(_neg)
142
+ _neg_sorted = []
143
+ while _neg.length > 0:
144
+ _neg_sorted.push(heappop(_neg))
145
+ ade(_neg_sorted, [-5, -4, -3, -1, -1])
146
+
147
+ # ── 13. Duplicates ───────────────────────────────────────────────────────────
148
+
149
+ _dups = [2, 2, 2, 1, 1, 1, 3, 3, 3]
150
+ heapify(_dups)
151
+ _dups_sorted = []
152
+ while _dups.length > 0:
153
+ _dups_sorted.push(heappop(_dups))
154
+ ade(_dups_sorted, [1, 1, 1, 2, 2, 2, 3, 3, 3])
155
+
156
+ # ── 14. heappush onto pre-existing heap ──────────────────────────────────────
157
+
158
+ _pre = [2, 5, 8]
159
+ heapify(_pre)
160
+ heappush(_pre, 1)
161
+ ae(_pre[0], 1)
162
+ heappush(_pre, 3)
163
+ _pre_sorted = []
164
+ while _pre.length > 0:
165
+ _pre_sorted.push(heappop(_pre))
166
+ ade(_pre_sorted, [1, 2, 3, 5, 8])
167
+
168
+ # ── 15. nsmallest / nlargest do not mutate original ──────────────────────────
169
+
170
+ _orig = [5, 2, 8, 1, 9]
171
+ _orig_copy = _orig.slice()
172
+ nsmallest(3, _orig)
173
+ nlargest(3, _orig)
174
+ ade(_orig, _orig_copy)