rapydscript-ns 0.8.0

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 (144) hide show
  1. package/.agignore +1 -0
  2. package/.gitattributes +4 -0
  3. package/.github/workflows/ci.yml +38 -0
  4. package/.github/workflows/web-repl-page-deploy.yml +42 -0
  5. package/=template.pyj +5 -0
  6. package/CHANGELOG.md +456 -0
  7. package/CONTRIBUTORS +13 -0
  8. package/HACKING.md +103 -0
  9. package/LICENSE +24 -0
  10. package/README.md +2512 -0
  11. package/TODO.md +327 -0
  12. package/add-toc-to-readme +2 -0
  13. package/bin/export +75 -0
  14. package/bin/rapydscript +70 -0
  15. package/bin/web-repl-export +102 -0
  16. package/build +3 -0
  17. package/package.json +46 -0
  18. package/publish.py +37 -0
  19. package/release/baselib-plain-pretty.js +4370 -0
  20. package/release/baselib-plain-ugly.js +3 -0
  21. package/release/compiler.js +18394 -0
  22. package/release/signatures.json +31 -0
  23. package/session.vim +4 -0
  24. package/setup.cfg +2 -0
  25. package/src/ast.pyj +1356 -0
  26. package/src/baselib-builtins.pyj +279 -0
  27. package/src/baselib-containers.pyj +723 -0
  28. package/src/baselib-errors.pyj +37 -0
  29. package/src/baselib-internal.pyj +421 -0
  30. package/src/baselib-itertools.pyj +97 -0
  31. package/src/baselib-str.pyj +798 -0
  32. package/src/compiler.pyj +36 -0
  33. package/src/errors.pyj +30 -0
  34. package/src/lib/aes.pyj +646 -0
  35. package/src/lib/collections.pyj +695 -0
  36. package/src/lib/elementmaker.pyj +83 -0
  37. package/src/lib/encodings.pyj +126 -0
  38. package/src/lib/functools.pyj +148 -0
  39. package/src/lib/gettext.pyj +569 -0
  40. package/src/lib/itertools.pyj +580 -0
  41. package/src/lib/math.pyj +193 -0
  42. package/src/lib/numpy.pyj +2101 -0
  43. package/src/lib/operator.pyj +11 -0
  44. package/src/lib/pythonize.pyj +20 -0
  45. package/src/lib/random.pyj +118 -0
  46. package/src/lib/re.pyj +470 -0
  47. package/src/lib/traceback.pyj +63 -0
  48. package/src/lib/uuid.pyj +77 -0
  49. package/src/monaco-language-service/analyzer.js +526 -0
  50. package/src/monaco-language-service/builtins.js +543 -0
  51. package/src/monaco-language-service/completions.js +498 -0
  52. package/src/monaco-language-service/diagnostics.js +643 -0
  53. package/src/monaco-language-service/dts.js +550 -0
  54. package/src/monaco-language-service/hover.js +121 -0
  55. package/src/monaco-language-service/index.js +386 -0
  56. package/src/monaco-language-service/scope.js +162 -0
  57. package/src/monaco-language-service/signature.js +144 -0
  58. package/src/output/__init__.pyj +0 -0
  59. package/src/output/classes.pyj +296 -0
  60. package/src/output/codegen.pyj +492 -0
  61. package/src/output/comments.pyj +45 -0
  62. package/src/output/exceptions.pyj +105 -0
  63. package/src/output/functions.pyj +491 -0
  64. package/src/output/literals.pyj +109 -0
  65. package/src/output/loops.pyj +444 -0
  66. package/src/output/modules.pyj +329 -0
  67. package/src/output/operators.pyj +429 -0
  68. package/src/output/statements.pyj +463 -0
  69. package/src/output/stream.pyj +309 -0
  70. package/src/output/treeshake.pyj +182 -0
  71. package/src/output/utils.pyj +72 -0
  72. package/src/parse.pyj +3106 -0
  73. package/src/string_interpolation.pyj +72 -0
  74. package/src/tokenizer.pyj +702 -0
  75. package/src/unicode_aliases.pyj +576 -0
  76. package/src/utils.pyj +192 -0
  77. package/test/_import_one.pyj +37 -0
  78. package/test/_import_two/__init__.pyj +11 -0
  79. package/test/_import_two/level2/__init__.pyj +0 -0
  80. package/test/_import_two/level2/deep.pyj +4 -0
  81. package/test/_import_two/other.pyj +6 -0
  82. package/test/_import_two/sub.pyj +13 -0
  83. package/test/aes_vectors.pyj +421 -0
  84. package/test/annotations.pyj +80 -0
  85. package/test/baselib.pyj +319 -0
  86. package/test/classes.pyj +452 -0
  87. package/test/collections.pyj +152 -0
  88. package/test/decorators.pyj +77 -0
  89. package/test/dict_spread.pyj +76 -0
  90. package/test/docstrings.pyj +39 -0
  91. package/test/elementmaker_test.pyj +45 -0
  92. package/test/ellipsis.pyj +49 -0
  93. package/test/functions.pyj +151 -0
  94. package/test/generators.pyj +41 -0
  95. package/test/generic.pyj +370 -0
  96. package/test/imports.pyj +72 -0
  97. package/test/internationalization.pyj +73 -0
  98. package/test/lint.pyj +164 -0
  99. package/test/loops.pyj +85 -0
  100. package/test/numpy.pyj +734 -0
  101. package/test/omit_function_metadata.pyj +20 -0
  102. package/test/regexp.pyj +55 -0
  103. package/test/repl.pyj +121 -0
  104. package/test/scoped_flags.pyj +76 -0
  105. package/test/starargs.pyj +506 -0
  106. package/test/starred_assign.pyj +104 -0
  107. package/test/str.pyj +198 -0
  108. package/test/subscript_tuple.pyj +53 -0
  109. package/test/unit/fixtures/fibonacci_expected.js +46 -0
  110. package/test/unit/index.js +2989 -0
  111. package/test/unit/language-service-builtins.js +815 -0
  112. package/test/unit/language-service-completions.js +1067 -0
  113. package/test/unit/language-service-dts.js +543 -0
  114. package/test/unit/language-service-hover.js +455 -0
  115. package/test/unit/language-service-scope.js +833 -0
  116. package/test/unit/language-service-signature.js +458 -0
  117. package/test/unit/language-service.js +705 -0
  118. package/test/unit/run-language-service.js +41 -0
  119. package/test/unit/web-repl.js +484 -0
  120. package/tools/build-language-service.js +190 -0
  121. package/tools/cli.js +547 -0
  122. package/tools/compile.js +219 -0
  123. package/tools/compiler.js +108 -0
  124. package/tools/completer.js +131 -0
  125. package/tools/embedded_compiler.js +251 -0
  126. package/tools/export.js +316 -0
  127. package/tools/gettext.js +185 -0
  128. package/tools/ini.js +65 -0
  129. package/tools/lint.js +705 -0
  130. package/tools/msgfmt.js +187 -0
  131. package/tools/repl.js +223 -0
  132. package/tools/self.js +162 -0
  133. package/tools/test.js +118 -0
  134. package/tools/utils.js +128 -0
  135. package/tools/web_repl.js +95 -0
  136. package/try +41 -0
  137. package/web-repl/env.js +74 -0
  138. package/web-repl/index.html +163 -0
  139. package/web-repl/language-service.js +4084 -0
  140. package/web-repl/main.js +254 -0
  141. package/web-repl/prism.css +139 -0
  142. package/web-repl/prism.js +113 -0
  143. package/web-repl/rapydscript.js +435 -0
  144. package/web-repl/sha1.js +25 -0
@@ -0,0 +1,41 @@
1
+ /*
2
+ * test/unit/run-language-service.js
3
+ *
4
+ * Runs all language-service unit tests in sequence.
5
+ * Each test file runs in its own Node.js process so ES-module imports
6
+ * and process.exit() calls are fully isolated.
7
+ *
8
+ * Usage:
9
+ * node test/unit/run-language-service.js
10
+ * npm run test:ls
11
+ */
12
+ "use strict";
13
+
14
+ var path = require("path");
15
+ var spawn = require("child_process").spawnSync;
16
+ var utils = require("../../tools/utils");
17
+ var colored = utils.safe_colored;
18
+
19
+ var FILES = [
20
+ "language-service.js",
21
+ "language-service-scope.js",
22
+ "language-service-completions.js",
23
+ "language-service-signature.js",
24
+ "language-service-hover.js",
25
+ "language-service-dts.js",
26
+ "language-service-builtins.js",
27
+ "web-repl.js",
28
+ ];
29
+
30
+ var failed = false;
31
+
32
+ FILES.forEach(function (file) {
33
+ var result = spawn(
34
+ process.execPath,
35
+ [path.join(__dirname, file)],
36
+ { stdio: "inherit" }
37
+ );
38
+ if (result.status !== 0) failed = true;
39
+ });
40
+
41
+ process.exit(failed ? 1 : 0);
@@ -0,0 +1,484 @@
1
+ /*
2
+ * test/unit/web-repl.js
3
+ *
4
+ * Tests that exercise the web-repl bundle (web-repl/rapydscript.js) end-to-end:
5
+ * load the bundle into a Node.js vm sandbox, compile RapydScript via
6
+ * RapydScript.web_repl().compile(), run the output with Node's vm, and assert
7
+ * expected values.
8
+ *
9
+ * Usage:
10
+ * node test/unit/web-repl.js # run all tests
11
+ * node test/unit/web-repl.js <test-name> # run a specific test
12
+ */
13
+ "use strict";
14
+
15
+ var fs = require("fs");
16
+ var vm = require("vm");
17
+ var path = require("path");
18
+ var assert = require("assert");
19
+ var utils = require("../../tools/utils");
20
+ var colored = utils.safe_colored;
21
+
22
+ // ---------------------------------------------------------------------------
23
+ // Load the web-repl bundle into a Node.js vm sandbox
24
+ // ---------------------------------------------------------------------------
25
+
26
+ var BASE_PATH = path.resolve(__dirname, "../..");
27
+ var bundle_path = path.join(BASE_PATH, "web-repl", "rapydscript.js");
28
+
29
+ if (!utils.path_exists(bundle_path)) {
30
+ console.error("web-repl/rapydscript.js not found — run: node bin/web-repl-export web-repl");
31
+ process.exit(1);
32
+ }
33
+
34
+ // The bundle ends with `})(this)` so it assigns to `this` (the sandbox global).
35
+ // env.js inside the bundle uses `document.createElement('iframe')` to create
36
+ // an isolated evaluation context for the repl's runjs. We provide a minimal
37
+ // Node-compatible stub: each createContext() call allocates a real Node vm
38
+ // context; properties written to the fake contentWindow are mirrored into it
39
+ // so that setup code (baselib init, print replacement, etc.) takes effect.
40
+ var bundle_sandbox = vm.createContext({
41
+ console: console,
42
+ document: {
43
+ createElement: function () {
44
+ var repl_ctx = vm.createContext({});
45
+ // A plain object that delegates eval() into the real Node ctx, and
46
+ // proxies property writes so initialisation code (ctx.foo = bar)
47
+ // lands in the Node ctx too.
48
+ var win = {
49
+ eval: function (code) {
50
+ return vm.runInContext(
51
+ code.replace(/^export ((?:async )?function |let )/gm, "$1"),
52
+ repl_ctx
53
+ );
54
+ },
55
+ };
56
+ // Intercept property assignments the bundle makes via Object.keys copy
57
+ var orig_win = win;
58
+ win = new Proxy(win, {
59
+ set: function (target, prop, value) {
60
+ target[prop] = value;
61
+ try { repl_ctx[prop] = value; } catch (e) {}
62
+ return true;
63
+ },
64
+ get: function (target, prop) {
65
+ if (prop in target) return target[prop];
66
+ try { return repl_ctx[prop]; } catch (e) {}
67
+ },
68
+ });
69
+ return { style: { display: "" }, contentWindow: win };
70
+ },
71
+ body: { appendChild: function () {} },
72
+ },
73
+ });
74
+
75
+ vm.runInContext(fs.readFileSync(bundle_path, "utf-8"), bundle_sandbox);
76
+ var RS = bundle_sandbox.RapydScript;
77
+
78
+ if (!RS || typeof RS.web_repl !== "function") {
79
+ console.error("Failed to load RapydScript.web_repl from bundle");
80
+ process.exit(1);
81
+ }
82
+
83
+ // ---------------------------------------------------------------------------
84
+ // Helpers
85
+ // ---------------------------------------------------------------------------
86
+
87
+ // Patch assert.deepEqual to handle RapydScript list objects (extra properties)
88
+ var _deepEqual = assert.deepEqual;
89
+ assert.deepEqual = function (a, b, message) {
90
+ if (Array.isArray(a) && Array.isArray(b)) {
91
+ if (a === b) return;
92
+ if (a.length !== b.length)
93
+ throw new assert.AssertionError({ actual: a, expected: b, operator: "deepEqual", stackStartFunction: assert.deepEqual });
94
+ for (var i = 0; i < a.length; i++) assert.deepEqual(a[i], b[i], message);
95
+ } else if (a !== undefined && a !== null && typeof a.__eq__ === "function") {
96
+ if (!a.__eq__(b))
97
+ throw new assert.AssertionError({ actual: a, expected: b, operator: "deepEqual", stackStartFunction: assert.deepEqual });
98
+ } else {
99
+ return _deepEqual(a, b, message);
100
+ }
101
+ };
102
+
103
+ // Compile RapydScript using the web-repl's own compile() path (same as browser)
104
+ function bundle_compile(repl, src) {
105
+ return repl.compile(src, {
106
+ omit_function_metadata: false,
107
+ tree_shake: false,
108
+ keep_baselib: true,
109
+ });
110
+ }
111
+
112
+ // Run compiled JS in a fresh Node vm context with assrt available
113
+ function run_js(js) {
114
+ return vm.runInNewContext(js, {
115
+ __name__ : "<test>",
116
+ console : console,
117
+ assrt : assert,
118
+ ρσ_last_exception : undefined,
119
+ });
120
+ }
121
+
122
+ // ---------------------------------------------------------------------------
123
+ // Tests
124
+ // ---------------------------------------------------------------------------
125
+
126
+ var TESTS = [
127
+
128
+ {
129
+ name: "bundle_loads",
130
+ description: "web-repl bundle exports RapydScript with web_repl function",
131
+ run: function () {
132
+ assert.equal(typeof RS.compile, "function");
133
+ assert.equal(typeof RS.web_repl, "function");
134
+ assert.ok(RS.rs_version, "rs_version is set");
135
+ },
136
+ },
137
+
138
+ {
139
+ name: "bundle_dict_from_counter",
140
+ description: "dict(counter) works in the bundled baselib",
141
+ run: function () {
142
+ var repl = RS.web_repl();
143
+ var js = bundle_compile(repl, [
144
+ "from __python__ import overload_getitem",
145
+ "from collections import Counter",
146
+ "c = Counter('aab')",
147
+ "d = dict(c)",
148
+ "assrt.equal(d['a'], 2)",
149
+ "assrt.equal(d['b'], 1)",
150
+ ].join("\n"));
151
+ run_js(js);
152
+ },
153
+ },
154
+
155
+ {
156
+ name: "bundle_dict_from_ordered_dict",
157
+ description: "dict(ordered_dict) works in the bundled baselib",
158
+ run: function () {
159
+ var repl = RS.web_repl();
160
+ var js = bundle_compile(repl, [
161
+ "from __python__ import overload_getitem",
162
+ "from collections import OrderedDict",
163
+ "od = OrderedDict()",
164
+ "od['x'] = 10",
165
+ "od['y'] = 20",
166
+ "d = dict(od)",
167
+ "assrt.equal(d['x'], 10)",
168
+ "assrt.equal(d['y'], 20)",
169
+ ].join("\n"));
170
+ run_js(js);
171
+ },
172
+ },
173
+
174
+ {
175
+ name: "bundle_dict_from_defaultdict",
176
+ description: "dict(defaultdict) works — the original failing example",
177
+ run: function () {
178
+ var repl = RS.web_repl();
179
+ var js = bundle_compile(repl, [
180
+ "from __python__ import overload_getitem",
181
+ "from collections import defaultdict",
182
+ "groups = defaultdict(list)",
183
+ "for name, dept in [('Alice', 'eng'), ('Bob', 'eng'), ('Carol', 'hr')]:",
184
+ " groups[dept].append(name)",
185
+ "d = dict(groups)",
186
+ "assrt.deepEqual(d['eng'], ['Alice', 'Bob'])",
187
+ "assrt.deepEqual(d['hr'], ['Carol'])",
188
+ ].join("\n"));
189
+ run_js(js);
190
+ },
191
+ },
192
+
193
+ {
194
+ name: "bundle_namedtuple",
195
+ description: "namedtuple compiles and runs correctly via bundle",
196
+ run: function () {
197
+ var repl = RS.web_repl();
198
+ var js = bundle_compile(repl, [
199
+ "from collections import namedtuple",
200
+ "Point = namedtuple('Point', ['x', 'y'])",
201
+ "p = Point(3, 4)",
202
+ "assrt.equal(p.x, 3)",
203
+ "assrt.equal(p.y, 4)",
204
+ "assrt.deepEqual(list(p), [3, 4])",
205
+ ].join("\n"));
206
+ run_js(js);
207
+ },
208
+ },
209
+
210
+ {
211
+ name: "bundle_operator_overloading",
212
+ description: "overload_operators flag works in the web-repl bundle",
213
+ run: function () {
214
+ var repl = RS.web_repl();
215
+ var js = bundle_compile(repl, [
216
+ "from __python__ import overload_operators",
217
+ "class Vec:",
218
+ " def __init__(self, x, y):",
219
+ " self.x = x",
220
+ " self.y = y",
221
+ " def __add__(self, other):",
222
+ " return Vec(self.x + other.x, self.y + other.y)",
223
+ " def __neg__(self):",
224
+ " return Vec(-self.x, -self.y)",
225
+ "a = Vec(1, 2)",
226
+ "b = Vec(3, 4)",
227
+ "c = a + b",
228
+ "assrt.equal(c.x, 4)",
229
+ "assrt.equal(c.y, 6)",
230
+ "d = -a",
231
+ "assrt.equal(d.x, -1)",
232
+ "assrt.equal(d.y, -2)",
233
+ // native fallback still works
234
+ "assrt.equal(10 + 5, 15)",
235
+ ].join("\n"));
236
+ run_js(js);
237
+ },
238
+ },
239
+
240
+ {
241
+ name: "bundle_counter_operators",
242
+ description: "Counter +, -, |, & work via operator syntax in the web-repl bundle",
243
+ run: function () {
244
+ var repl = RS.web_repl();
245
+ var js = bundle_compile(repl, [
246
+ "from __python__ import overload_getitem, overload_operators",
247
+ "from collections import Counter",
248
+ "c1 = Counter('aab')",
249
+ "c2 = Counter('ab')",
250
+ "c3 = c1 + c2",
251
+ "assrt.equal(c3['a'], 3)",
252
+ "assrt.equal(c3['b'], 2)",
253
+ "c4 = c1 - c2",
254
+ "assrt.equal(c4['a'], 1)",
255
+ "assrt.equal(c4.get('b', 0), 0)",
256
+ ].join("\n"));
257
+ run_js(js);
258
+ },
259
+ },
260
+
261
+ // ── itertools ─────────────────────────────────────────────────────────
262
+
263
+ {
264
+ name: "itertools_chain_web_repl",
265
+ description: "itertools.chain works in the web-repl bundle",
266
+ run: function () {
267
+ var repl = RS.web_repl();
268
+ var js = bundle_compile(repl, [
269
+ "from itertools import chain, islice, count",
270
+ "assrt.deepEqual(list(chain([1,2],[3,4])), [1,2,3,4])",
271
+ "assrt.deepEqual(list(islice(count(5), 4)), [5,6,7,8])",
272
+ ].join("\n"));
273
+ run_js(js);
274
+ },
275
+ },
276
+
277
+ {
278
+ name: "itertools_combinatorics_web_repl",
279
+ description: "itertools combinatoric functions work in the web-repl bundle",
280
+ run: function () {
281
+ var repl = RS.web_repl();
282
+ var js = bundle_compile(repl, [
283
+ "from itertools import product, permutations, combinations, combinations_with_replacement",
284
+ "assrt.deepEqual(list(product([1,2],[3,4])), [[1,3],[1,4],[2,3],[2,4]])",
285
+ "assrt.deepEqual(list(permutations([1,2,3],2)), [[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]])",
286
+ "assrt.deepEqual(list(combinations([1,2,3],2)), [[1,2],[1,3],[2,3]])",
287
+ "assrt.deepEqual(list(combinations_with_replacement([1,2],2)), [[1,1],[1,2],[2,2]])",
288
+ ].join("\n"));
289
+ run_js(js);
290
+ },
291
+ },
292
+
293
+ {
294
+ name: "itertools_filtering_web_repl",
295
+ description: "itertools filtering/selecting functions work in the web-repl bundle",
296
+ run: function () {
297
+ var repl = RS.web_repl();
298
+ var js = bundle_compile(repl, [
299
+ "from itertools import compress, dropwhile, filterfalse, takewhile, zip_longest",
300
+ "assrt.deepEqual(list(compress([1,2,3,4,5],[1,0,1,0,1])), [1,3,5])",
301
+ "assrt.deepEqual(list(dropwhile(lambda x: x < 3, [1,2,3,4])), [3,4])",
302
+ "assrt.deepEqual(list(filterfalse(lambda x: x%2, [1,2,3,4])), [2,4])",
303
+ "assrt.deepEqual(list(takewhile(lambda x: x < 3, [1,2,3,4])), [1,2])",
304
+ "assrt.deepEqual(list(zip_longest([1,2],[3],fillvalue=0)), [[1,3],[2,0]])",
305
+ ].join("\n"));
306
+ run_js(js);
307
+ },
308
+ },
309
+
310
+ // ── nested comprehensions ─────────────────────────────────────────────
311
+
312
+ {
313
+ name: "nested_comprehension_web_repl",
314
+ description: "nested list/set/dict comprehensions work in the web-repl bundle",
315
+ run: function () {
316
+ var repl = RS.web_repl();
317
+ var js = bundle_compile(repl, [
318
+ "flat = [x for row in [[1,2],[3,4],[5,6]] for x in row]",
319
+ "assrt.deepEqual(flat, [1,2,3,4,5,6])",
320
+ "evens = [x for row in [[1,2,3],[4,5,6]] for x in row if x % 2 == 0]",
321
+ "assrt.deepEqual(evens, [2,4,6])",
322
+ "coords = [[i,j] for i in range(2) for j in range(2)]",
323
+ "assrt.deepEqual(coords, [[0,0],[0,1],[1,0],[1,1]])",
324
+ "s = sorted(list({x+y for x in range(3) for y in range(3)}))",
325
+ "assrt.deepEqual(s, [0,1,2,3,4])",
326
+ ].join("\n"));
327
+ run_js(js);
328
+ },
329
+ },
330
+
331
+ // ── python_flags via embedded compiler opts ────────────────────────────
332
+
333
+ {
334
+ name: "python_flags_overload_operators_web_repl",
335
+ description: "python_flags='overload_operators' in compile opts activates operator overloading",
336
+ run: function () {
337
+ var repl = RS.web_repl();
338
+ // Compile without inline import — flag passed via opts.python_flags
339
+ var js = repl.compile([
340
+ "class Vec:",
341
+ " def __init__(self, x):",
342
+ " self.x = x",
343
+ " def __add__(self, other):",
344
+ " return Vec(self.x + other.x)",
345
+ "a = Vec(3)",
346
+ "b = Vec(4)",
347
+ "c = a + b",
348
+ "assrt.equal(c.x, 7)",
349
+ ].join("\n"), {
350
+ keep_baselib: true,
351
+ python_flags: "overload_operators",
352
+ });
353
+ assert.ok(js.indexOf("ρσ_op_add") !== -1,
354
+ "Expected ρσ_op_add in output, got: " + js.slice(0, 500));
355
+ run_js(js);
356
+ },
357
+ },
358
+
359
+ {
360
+ name: "python_flags_overload_getitem_web_repl",
361
+ description: "python_flags='overload_getitem' in compile opts activates __getitem__",
362
+ run: function () {
363
+ var repl = RS.web_repl();
364
+ var js = repl.compile([
365
+ "class MyList:",
366
+ " def __init__(self):",
367
+ " self.data = [10, 20, 30]",
368
+ " def __getitem__(self, i):",
369
+ " return self.data[i]",
370
+ "ml = MyList()",
371
+ "assrt.equal(ml[1], 20)",
372
+ ].join("\n"), {
373
+ keep_baselib: true,
374
+ python_flags: "overload_getitem",
375
+ });
376
+ assert.ok(js.indexOf("__getitem__") !== -1,
377
+ "Expected __getitem__ in output");
378
+ run_js(js);
379
+ },
380
+ },
381
+
382
+ {
383
+ name: "python_flags_multiple_web_repl",
384
+ description: "python_flags with multiple comma-separated flags all activate",
385
+ run: function () {
386
+ var repl = RS.web_repl();
387
+ var js = repl.compile([
388
+ "class N:",
389
+ " def __init__(self, v):",
390
+ " self.v = v",
391
+ " def __add__(self, o):",
392
+ " return N(self.v + o.v)",
393
+ " def __getitem__(self, k):",
394
+ " return self.v * k",
395
+ "a = N(3)",
396
+ "b = N(4)",
397
+ "c = a + b",
398
+ "assrt.equal(c.v, 7)",
399
+ "assrt.equal(a[2], 6)",
400
+ ].join("\n"), {
401
+ keep_baselib: true,
402
+ python_flags: "overload_operators,overload_getitem",
403
+ });
404
+ assert.ok(js.indexOf("ρσ_op_add") !== -1, "Expected ρσ_op_add");
405
+ assert.ok(js.indexOf("__getitem__") !== -1, "Expected __getitem__");
406
+ run_js(js);
407
+ },
408
+ },
409
+
410
+ {
411
+ name: "bundle_dict_spread_js_object",
412
+ description: "Dict merge literal {**d1, **d2} works for plain JS-object dicts in the web-repl bundle",
413
+ run: function () {
414
+ var repl = RS.web_repl();
415
+ var js = bundle_compile(repl, [
416
+ "d1 = {'a': 1, 'b': 2}",
417
+ "d2 = {'c': 3, 'b': 99}",
418
+ "merged = {**d1, **d2}",
419
+ "assrt.equal(merged['a'], 1)",
420
+ "assrt.equal(merged['b'], 99)",
421
+ "assrt.equal(merged['c'], 3)",
422
+ "single = {**d1, 'extra': 42}",
423
+ "assrt.equal(single['a'], 1)",
424
+ "assrt.equal(single['extra'], 42)",
425
+ ].join("\n"));
426
+ run_js(js);
427
+ },
428
+ },
429
+
430
+ {
431
+ name: "bundle_dict_spread_pydict",
432
+ description: "Dict merge literal {**d1, **d2} works for Python dicts (dict_literals) in the web-repl bundle",
433
+ run: function () {
434
+ var repl = RS.web_repl();
435
+ var js = bundle_compile(repl, [
436
+ "from __python__ import dict_literals, overload_getitem",
437
+ "pd1 = {'x': 10, 'y': 20}",
438
+ "pd2 = {'y': 99, 'z': 30}",
439
+ "merged = {**pd1, **pd2}",
440
+ "assrt.equal(merged['x'], 10)",
441
+ "assrt.equal(merged['y'], 99)",
442
+ "assrt.equal(merged['z'], 30)",
443
+ ].join("\n"));
444
+ run_js(js);
445
+ },
446
+ },
447
+
448
+ ];
449
+
450
+ // ---------------------------------------------------------------------------
451
+ // Runner
452
+ // ---------------------------------------------------------------------------
453
+
454
+ function run_tests(filter) {
455
+ var tests = filter
456
+ ? TESTS.filter(function (t) { return t.name === filter; })
457
+ : TESTS;
458
+
459
+ if (tests.length === 0) {
460
+ console.error(colored("No test found: " + filter, "red"));
461
+ process.exit(1);
462
+ }
463
+
464
+ var failures = [];
465
+ tests.forEach(function (test) {
466
+ try {
467
+ test.run();
468
+ console.log(colored("PASS " + test.name, "green") + " – " + test.description);
469
+ } catch (e) {
470
+ failures.push(test.name);
471
+ console.log(colored("FAIL " + test.name, "red") + "\n " + (e.stack || String(e)) + "\n");
472
+ }
473
+ });
474
+
475
+ console.log("");
476
+ if (failures.length) {
477
+ console.log(colored(failures.length + " test(s) failed.", "red"));
478
+ } else {
479
+ console.log(colored("All " + tests.length + " web-repl tests passed!", "green"));
480
+ }
481
+ process.exit(failures.length ? 1 : 0);
482
+ }
483
+
484
+ run_tests(process.argv[2] || null);