rapydscript-ns 0.9.3 → 0.9.5

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 (111) hide show
  1. package/.agignore +1 -1
  2. package/.github/workflows/ci.yml +38 -38
  3. package/=template.pyj +5 -5
  4. package/CHANGELOG.md +18 -0
  5. package/HACKING.md +103 -103
  6. package/LICENSE +24 -24
  7. package/PYTHON_GAPS.md +52 -142
  8. package/README.md +51 -21
  9. package/TODO.md +1 -26
  10. package/add-toc-to-readme +2 -2
  11. package/bin/export +75 -75
  12. package/bin/rapydscript +0 -0
  13. package/bin/web-repl-export +102 -102
  14. package/build +2 -2
  15. package/language-service/index.js +88 -36
  16. package/package.json +1 -1
  17. package/publish.py +37 -37
  18. package/release/baselib-plain-pretty.js +157 -31
  19. package/release/baselib-plain-ugly.js +5 -5
  20. package/release/compiler.js +724 -426
  21. package/release/signatures.json +29 -29
  22. package/session.vim +4 -4
  23. package/setup.cfg +2 -2
  24. package/src/ast.pyj +7 -0
  25. package/src/baselib-containers.pyj +41 -4
  26. package/src/baselib-errors.pyj +4 -3
  27. package/src/baselib-internal.pyj +47 -18
  28. package/src/baselib-str.pyj +16 -3
  29. package/src/compiler.pyj +36 -36
  30. package/src/errors.pyj +30 -30
  31. package/src/lib/aes.pyj +646 -646
  32. package/src/lib/collections.pyj +227 -3
  33. package/src/lib/copy.pyj +120 -120
  34. package/src/lib/elementmaker.pyj +83 -83
  35. package/src/lib/encodings.pyj +126 -126
  36. package/src/lib/gettext.pyj +569 -569
  37. package/src/lib/itertools.pyj +580 -580
  38. package/src/lib/math.pyj +193 -193
  39. package/src/lib/operator.pyj +11 -11
  40. package/src/lib/pprint.pyj +455 -0
  41. package/src/lib/random.pyj +118 -118
  42. package/src/lib/react.pyj +74 -74
  43. package/src/lib/statistics.pyj +0 -0
  44. package/src/lib/traceback.pyj +63 -63
  45. package/src/lib/uuid.pyj +77 -77
  46. package/src/monaco-language-service/completions.js +21 -14
  47. package/src/monaco-language-service/diagnostics.js +2 -2
  48. package/src/monaco-language-service/dts.js +58 -15
  49. package/src/monaco-language-service/package.json +3 -0
  50. package/src/output/classes.pyj +25 -2
  51. package/src/output/codegen.pyj +4 -1
  52. package/src/output/comments.pyj +45 -45
  53. package/src/output/exceptions.pyj +201 -201
  54. package/src/output/jsx.pyj +164 -164
  55. package/src/output/treeshake.pyj +182 -182
  56. package/src/output/utils.pyj +72 -72
  57. package/src/parse.pyj +42 -7
  58. package/src/string_interpolation.pyj +72 -72
  59. package/src/tokenizer.pyj +18 -2
  60. package/src/unicode_aliases.pyj +576 -576
  61. package/src/utils.pyj +192 -192
  62. package/test/_import_one.pyj +37 -37
  63. package/test/_import_two/__init__.pyj +11 -11
  64. package/test/_import_two/level2/deep.pyj +4 -4
  65. package/test/_import_two/other.pyj +6 -6
  66. package/test/_import_two/sub.pyj +13 -13
  67. package/test/aes_vectors.pyj +421 -421
  68. package/test/annotations.pyj +80 -80
  69. package/test/baselib.pyj +23 -0
  70. package/test/chainmap.pyj +185 -0
  71. package/test/dataclasses.pyj +3 -4
  72. package/test/decorators.pyj +77 -77
  73. package/test/docstrings.pyj +39 -39
  74. package/test/elementmaker_test.pyj +45 -45
  75. package/test/enum.pyj +1 -1
  76. package/test/functions.pyj +151 -151
  77. package/test/generators.pyj +41 -41
  78. package/test/generic.pyj +370 -370
  79. package/test/internationalization.pyj +73 -73
  80. package/test/lint.pyj +164 -164
  81. package/test/loops.pyj +85 -85
  82. package/test/numpy.pyj +734 -734
  83. package/test/pprint.pyj +232 -0
  84. package/test/python_features.pyj +1 -1
  85. package/test/repl.pyj +121 -121
  86. package/test/scoped_flags.pyj +76 -76
  87. package/test/statistics.pyj +224 -0
  88. package/test/str.pyj +4 -4
  89. package/test/unit/index.js +455 -0
  90. package/test/unit/language-service-completions.js +2 -0
  91. package/test/unit/language-service-dts.js +113 -0
  92. package/test/unit/language-service-hover.js +455 -455
  93. package/test/unit/language-service.js +135 -2
  94. package/test/unit/web-repl.js +349 -1
  95. package/tools/compiler.d.ts +367 -367
  96. package/tools/completer.js +131 -131
  97. package/tools/export.js +4 -2
  98. package/tools/gettext.js +185 -185
  99. package/tools/ini.js +65 -65
  100. package/tools/msgfmt.js +187 -187
  101. package/tools/repl.js +223 -223
  102. package/tools/test.js +118 -118
  103. package/tools/utils.js +141 -128
  104. package/tools/web_repl.js +95 -95
  105. package/try +41 -41
  106. package/web-repl/env.js +196 -196
  107. package/web-repl/index.html +163 -163
  108. package/web-repl/prism.css +139 -139
  109. package/web-repl/prism.js +113 -113
  110. package/web-repl/rapydscript.js +228 -226
  111. package/web-repl/sha1.js +25 -25
@@ -900,13 +900,14 @@ function make_tests(Diagnostics, RS, STDLIB_MODULES) {
900
900
  description: "Importing all collections classes produces no errors",
901
901
  run: function () {
902
902
  var markers = d().check([
903
- "from collections import defaultdict, Counter, OrderedDict, deque, namedtuple",
903
+ "from collections import defaultdict, Counter, OrderedDict, deque, namedtuple, ChainMap",
904
904
  "d = defaultdict(list)",
905
905
  "c = Counter([1, 2, 2, 3])",
906
906
  "od = OrderedDict()",
907
907
  "dq = deque([1, 2, 3])",
908
908
  "Point = namedtuple('Point', 'x y')",
909
- "print(d, c, od, dq, Point)",
909
+ "cm = ChainMap({'a': 1}, {'b': 2})",
910
+ "print(d, c, od, dq, Point, cm)",
910
911
  ].join("\n"),
911
912
  { virtualFiles: { mymod: "def foo(): pass" } }
912
913
  );
@@ -930,6 +931,36 @@ function make_tests(Diagnostics, RS, STDLIB_MODULES) {
930
931
  },
931
932
  },
932
933
 
934
+ {
935
+ name: "stdlib_statistics_no_bad_import",
936
+ description: "Importing and using statistics functions produces no errors",
937
+ run: function () {
938
+ var markers = d().check([
939
+ "from statistics import mean, median, mode, stdev, variance",
940
+ "from statistics import quantiles, correlation, linear_regression",
941
+ "from statistics import NormalDist, StatisticsError",
942
+ "data = [1, 2, 3, 4, 5]",
943
+ "m = mean(data)",
944
+ "md = median(data)",
945
+ "mo = mode(data)",
946
+ "sd = stdev(data)",
947
+ "v = variance(data)",
948
+ "q = quantiles(data)",
949
+ "c = correlation(data, data)",
950
+ "lr = linear_regression(data, data)",
951
+ "nd = NormalDist(0, 1)",
952
+ "print(m, md, mo, sd, v, q, c, lr.slope, lr.intercept)",
953
+ "print(nd.mean, nd.stdev, nd.cdf(0), nd.pdf(0), StatisticsError)",
954
+ ].join("\n"),
955
+ { virtualFiles: { sentinel: "x = 1" } }
956
+ );
957
+ var errors = markers.filter(function (m) { return m.severity === SEV_ERROR; });
958
+ assert.deepStrictEqual(errors, [],
959
+ "Expected no errors for statistics imports and usage, got: " +
960
+ JSON.stringify(errors.map(function (m) { return m.message; })));
961
+ },
962
+ },
963
+
933
964
  {
934
965
  name: "copy_no_bad_import",
935
966
  description: "from copy import copy, deepcopy produces no bad-import error",
@@ -1591,6 +1622,108 @@ function make_tests(Diagnostics, RS, STDLIB_MODULES) {
1591
1622
  },
1592
1623
  },
1593
1624
 
1625
+ // ── BigInt literals ──────────────────────────────────────────────
1626
+
1627
+ {
1628
+ name: "bigint_literal_no_errors",
1629
+ description: "BigInt literals (42n, 0xFFn, 0b1010n, 0o77n) produce no markers",
1630
+ run: function () {
1631
+ var markers = d().check([
1632
+ "x = 42n",
1633
+ "y = 0xFFn",
1634
+ "z = 0b1010n",
1635
+ "w = 0o77n",
1636
+ ].join("\n"));
1637
+ assert.deepStrictEqual(markers, [],
1638
+ "Expected no markers for bigint literals, got: " + JSON.stringify(markers));
1639
+ },
1640
+ },
1641
+
1642
+ {
1643
+ name: "bigint_literal_assignment_no_errors",
1644
+ description: "BigInt literal assignment and arithmetic produce no markers",
1645
+ run: function () {
1646
+ var markers = d().check([
1647
+ "x = 42n",
1648
+ "y = x + 1n",
1649
+ ].join("\n"));
1650
+ assert.deepStrictEqual(markers, [],
1651
+ "Expected no markers for bigint assignment, got: " + JSON.stringify(markers));
1652
+ },
1653
+ },
1654
+
1655
+ // ── Exception.args ───────────────────────────────────────────────
1656
+ {
1657
+ name: "exception_args_no_errors",
1658
+ description: "Accessing .args on caught exception produces no diagnostics",
1659
+ run: function () {
1660
+ var markers = d().check([
1661
+ "try:",
1662
+ " raise ValueError('oops', 123)",
1663
+ "except ValueError as e:",
1664
+ " x = e.args",
1665
+ " y = e.args[0]",
1666
+ " z = len(e.args)",
1667
+ ].join("\n"));
1668
+ assert.deepStrictEqual(markers, [],
1669
+ "Expected no markers for exception .args access, got: " + JSON.stringify(markers));
1670
+ },
1671
+ },
1672
+ {
1673
+ name: "exception_args_custom_class_no_errors",
1674
+ description: "Custom exception class with variadic __init__ produces no diagnostics",
1675
+ run: function () {
1676
+ var markers = d().check([
1677
+ "class MyError(Exception):",
1678
+ " def __init__(self, code, detail):",
1679
+ " Exception.__init__(self, code, detail)",
1680
+ " self.code = code",
1681
+ "e = MyError(404, 'not found')",
1682
+ "x = e.args",
1683
+ ].join("\n"));
1684
+ assert.deepStrictEqual(markers, [],
1685
+ "Expected no markers for custom exception .args, got: " + JSON.stringify(markers));
1686
+ },
1687
+ },
1688
+
1689
+ // ── __slots__ ──────────────────────────────────────────────────────
1690
+ {
1691
+ name: "slots_no_errors",
1692
+ description: "Class with __slots__ produces no linter errors",
1693
+ run: function () {
1694
+ var markers = d().check([
1695
+ "class Point:",
1696
+ " __slots__ = ['x', 'y']",
1697
+ " def __init__(self, x, y):",
1698
+ " self.x = x",
1699
+ " self.y = y",
1700
+ "p = Point(1, 2)",
1701
+ ].join("\n"));
1702
+ assert.deepStrictEqual(markers, [],
1703
+ "Expected no markers for __slots__ class, got: " + JSON.stringify(markers));
1704
+ },
1705
+ },
1706
+ {
1707
+ name: "slots_subclass_no_errors",
1708
+ description: "Subclass with __slots__ produces no linter errors",
1709
+ run: function () {
1710
+ var markers = d().check([
1711
+ "class Base:",
1712
+ " __slots__ = ['x']",
1713
+ " def __init__(self):",
1714
+ " self.x = 1",
1715
+ "class Child(Base):",
1716
+ " __slots__ = ['y']",
1717
+ " def __init__(self):",
1718
+ " Base.__init__(self)",
1719
+ " self.y = 2",
1720
+ "c = Child()",
1721
+ ].join("\n"));
1722
+ assert.deepStrictEqual(markers, [],
1723
+ "Expected no markers for __slots__ subclass, got: " + JSON.stringify(markers));
1724
+ },
1725
+ },
1726
+
1594
1727
  ];
1595
1728
 
1596
1729
  return TESTS;
@@ -270,6 +270,95 @@ var TESTS = [
270
270
  },
271
271
  },
272
272
 
273
+ {
274
+ name: "bundle_chainmap_basic",
275
+ description: "ChainMap construction and first-map-wins lookup work in the web-repl bundle",
276
+ run: function () {
277
+ var repl = RS.web_repl();
278
+ var js = bundle_compile(repl, [
279
+ "from collections import ChainMap",
280
+ "defaults = {'color': 'red', 'user': 'guest'}",
281
+ "overrides = {'user': 'admin'}",
282
+ "cm = ChainMap(overrides, defaults)",
283
+ "assrt.equal(cm['user'], 'admin')",
284
+ "assrt.equal(cm['color'], 'red')",
285
+ "assrt.equal(len(cm), 2)",
286
+ "assrt.ok('color' in cm)",
287
+ "assrt.ok('missing' not in cm)",
288
+ "assrt.equal(cm.get('missing', 'fallback'), 'fallback')",
289
+ ].join("\n"));
290
+ run_js(js);
291
+ },
292
+ },
293
+
294
+ {
295
+ name: "bundle_chainmap_writes",
296
+ description: "ChainMap writes, deletes and updates affect only the first map in the bundle",
297
+ run: function () {
298
+ var repl = RS.web_repl();
299
+ var js = bundle_compile(repl, [
300
+ "from collections import ChainMap",
301
+ "defaults = {'depth': 1}",
302
+ "cm = ChainMap({}, defaults)",
303
+ "cm['depth'] = 99",
304
+ "assrt.equal(cm['depth'], 99)",
305
+ "assrt.equal(defaults['depth'], 1)",
306
+ "del cm['depth']",
307
+ "assrt.equal(cm['depth'], 1)",
308
+ "cm.update({'a': 1}, b=2)",
309
+ "assrt.equal(cm['a'], 1)",
310
+ "assrt.equal(cm['b'], 2)",
311
+ "assrt.equal(cm.pop('a'), 1)",
312
+ "assrt.equal(cm.pop('depth', 'dflt'), 'dflt')",
313
+ "assrt.equal(cm.setdefault('new', 7), 7)",
314
+ "assrt.equal(cm['new'], 7)",
315
+ ].join("\n"));
316
+ run_js(js);
317
+ },
318
+ },
319
+
320
+ {
321
+ name: "bundle_chainmap_new_child",
322
+ description: "ChainMap new_child and parents work in the web-repl bundle",
323
+ run: function () {
324
+ var repl = RS.web_repl();
325
+ var js = bundle_compile(repl, [
326
+ "from collections import ChainMap",
327
+ "base = ChainMap({'x': 1})",
328
+ "child = base.new_child({'x': 2, 'y': 3})",
329
+ "assrt.equal(child['x'], 2)",
330
+ "assrt.equal(child['y'], 3)",
331
+ "assrt.equal(base['x'], 1)",
332
+ "assrt.equal(child.maps.length, 2)",
333
+ "parents = child.parents",
334
+ "assrt.equal(parents['x'], 1)",
335
+ "assrt.equal(parents.maps.length, 1)",
336
+ ].join("\n"));
337
+ run_js(js);
338
+ },
339
+ },
340
+
341
+ {
342
+ name: "bundle_chainmap_iteration",
343
+ description: "ChainMap keys/values/items, iteration order and copy work in the bundle",
344
+ run: function () {
345
+ var repl = RS.web_repl();
346
+ var js = bundle_compile(repl, [
347
+ "from collections import ChainMap",
348
+ "cm = ChainMap({'a': 1, 'b': 2}, {'c': 3})",
349
+ "assrt.deepEqual(list(cm), ['c', 'a', 'b'])",
350
+ "assrt.deepEqual(cm.keys(), ['c', 'a', 'b'])",
351
+ "assrt.deepEqual(cm.values(), [3, 1, 2])",
352
+ "dup = cm.copy()",
353
+ "dup['a'] = 100",
354
+ "assrt.equal(cm['a'], 1)",
355
+ "assrt.equal(dup['a'], 100)",
356
+ "assrt.equal(dup['c'], 3)",
357
+ ].join("\n"));
358
+ run_js(js);
359
+ },
360
+ },
361
+
273
362
  {
274
363
  name: "bundle_operator_overloading",
275
364
  description: "overload_operators flag works in the web-repl bundle",
@@ -1290,7 +1379,7 @@ var TESTS = [
1290
1379
  var js = bundle_compile(repl, [
1291
1380
  "msg = 'hello'",
1292
1381
  "nums = [1, 2, 3]",
1293
- "assrt.equal(f'{msg=!r}', 'msg=\"hello\"')",
1382
+ "assrt.equal(f'{msg=!r}', \"msg='hello'\")",
1294
1383
  "assrt.equal(f'{nums=!r}', 'nums=[1, 2, 3]')",
1295
1384
  ].join("\n"));
1296
1385
  run_js(js);
@@ -3914,6 +4003,121 @@ var TESTS = [
3914
4003
  },
3915
4004
  },
3916
4005
 
4006
+ // ── pprint stdlib ────────────────────────────────────────────────────────
4007
+
4008
+ {
4009
+ name: "bundle_pprint_basic",
4010
+ description: "pprint stdlib: pformat of atoms and short containers in the web-repl bundle",
4011
+ run: function () {
4012
+ var repl = RS.web_repl();
4013
+ var js = bundle_compile(repl, [
4014
+ "from pprint import pformat",
4015
+ // atoms -> repr()
4016
+ "assrt.equal(pformat(None), 'None')",
4017
+ "assrt.equal(pformat(True), 'True')",
4018
+ "assrt.equal(pformat(42), '42')",
4019
+ "assrt.equal(pformat('hi'), \"'hi'\")",
4020
+ // empty containers
4021
+ "assrt.equal(pformat([]), '[]')",
4022
+ "assrt.equal(pformat({}), '{}')",
4023
+ "assrt.equal(pformat(set()), 'set()')",
4024
+ // short containers fit on one line
4025
+ "assrt.equal(pformat([1, 2, 3]), '[1, 2, 3]')",
4026
+ "assrt.equal(pformat({'a': 1, 'b': 2}), \"{'a': 1, 'b': 2}\")",
4027
+ "assrt.equal(pformat(frozenset([1])), 'frozenset({1})')",
4028
+ ].join("\n"));
4029
+ run_js(js);
4030
+ },
4031
+ },
4032
+
4033
+ {
4034
+ name: "bundle_pprint_wrap",
4035
+ description: "pprint stdlib: wide containers break across lines in the web-repl bundle",
4036
+ run: function () {
4037
+ var repl = RS.web_repl();
4038
+ var js = bundle_compile(repl, [
4039
+ "from pprint import pformat",
4040
+ // wide list breaks one element per line
4041
+ "assrt.equal(pformat([1, 2, 3], width=5), '[1,\\n 2,\\n 3]')",
4042
+ // wide dict breaks, keys sorted by default
4043
+ "assrt.equal(pformat({'name': 'Al', 'age': 3}, width=12), \"{'age': 3,\\n 'name': 'Al'}\")",
4044
+ // indent parameter widens inner indentation
4045
+ "assrt.equal(pformat([1, 2, 3], width=5, indent=4), '[ 1,\\n 2,\\n 3]')",
4046
+ // single-element containers never break
4047
+ "assrt.equal(pformat([42], width=1), '[42]')",
4048
+ ].join("\n"));
4049
+ run_js(js);
4050
+ },
4051
+ },
4052
+
4053
+ {
4054
+ name: "bundle_pprint_features",
4055
+ description: "pprint stdlib: depth, compact, sort_dicts, and pp() in the web-repl bundle",
4056
+ run: function () {
4057
+ var repl = RS.web_repl();
4058
+ var js = bundle_compile(repl, [
4059
+ "from pprint import pformat, pp",
4060
+ // depth limits expansion
4061
+ "assrt.equal(pformat([1, [2, [3]]], depth=1), '[1, [...]]')",
4062
+ "assrt.equal(pformat([1, [2, [3]]], depth=2), '[1, [2, [...]]]')",
4063
+ // sort_dicts=False keeps insertion order (ρσ_dict preserves it)
4064
+ "d = dict()",
4065
+ "d.set('c', 1)",
4066
+ "d.set('a', 2)",
4067
+ "assrt.equal(pformat(d, sort_dicts=False), \"{'c': 1, 'a': 2}\")",
4068
+ "assrt.equal(pformat(d, sort_dicts=True), \"{'a': 2, 'c': 1}\")",
4069
+ // compact packs multiple items per line
4070
+ "compact_out = pformat([1, 2, 3, 4, 5, 6, 7, 8], compact=True, width=15)",
4071
+ "assrt.equal(compact_out, '[1, 2, 3, 4, 5,\\n 6, 7, 8]')",
4072
+ // pp() writes to a custom stream and defaults sort_dicts=False
4073
+ "class _C:",
4074
+ " def __init__(self):",
4075
+ " self.parts = []",
4076
+ " def write(self, s):",
4077
+ " self.parts.push(s)",
4078
+ "col = _C()",
4079
+ "pp(d, stream=col)",
4080
+ "assrt.equal(col.parts.join(''), \"{'c': 1, 'a': 2}\\n\")",
4081
+ ].join("\n"));
4082
+ run_js(js);
4083
+ },
4084
+ },
4085
+
4086
+ {
4087
+ name: "bundle_pprint_safe",
4088
+ description: "pprint stdlib: saferepr, isreadable, isrecursive, PrettyPrinter in the web-repl bundle",
4089
+ run: function () {
4090
+ var repl = RS.web_repl();
4091
+ var js = bundle_compile(repl, [
4092
+ "from pprint import saferepr, isreadable, isrecursive, PrettyPrinter",
4093
+ // saferepr of normal structures == single-line repr
4094
+ "assrt.equal(saferepr([1, 2, 3]), '[1, 2, 3]')",
4095
+ "assrt.equal(saferepr({'a': 1}), \"{'a': 1}\")",
4096
+ // isreadable / isrecursive on plain structures
4097
+ "assrt.ok(isreadable([1, 2, 3]))",
4098
+ "assrt.ok(not isrecursive([1, 2, 3]))",
4099
+ // self-referential list -> recursion detected and marked
4100
+ "cyclic = [1, 2]",
4101
+ "cyclic.push(cyclic)",
4102
+ "assrt.ok(isrecursive(cyclic))",
4103
+ "assrt.ok(not isreadable(cyclic))",
4104
+ "assrt.ok(saferepr(cyclic).indexOf('<Recursion on') >= 0)",
4105
+ // PrettyPrinter class: pformat + instance predicates
4106
+ "p = PrettyPrinter(indent=2, width=8)",
4107
+ "assrt.equal(p.pformat([10, 20, 30]), '[ 10,\\n 20,\\n 30]')",
4108
+ "assrt.ok(p.isrecursive(cyclic))",
4109
+ // invalid args raise ValueError
4110
+ "caught = False",
4111
+ "try:",
4112
+ " PrettyPrinter(depth=0)",
4113
+ "except ValueError:",
4114
+ " caught = True",
4115
+ "assrt.ok(caught)",
4116
+ ].join("\n"));
4117
+ run_js(js);
4118
+ },
4119
+ },
4120
+
3917
4121
  {
3918
4122
  name: "bundle_type_enforcement_basic",
3919
4123
  description: "type_enforcement: max args, missing required, type annotations",
@@ -4275,6 +4479,150 @@ var TESTS = [
4275
4479
  },
4276
4480
  },
4277
4481
 
4482
+ // ── statistics ───────────────────────────────────────────────────────────
4483
+
4484
+ {
4485
+ name: "bundle_statistics_averages",
4486
+ description: "statistics stdlib: averages and central location in the web-repl bundle",
4487
+ run: function () {
4488
+ var repl = RS.web_repl();
4489
+ var js = bundle_compile(repl, [
4490
+ "from statistics import mean, fmean, median, median_low, median_high",
4491
+ "from statistics import mode, multimode, harmonic_mean, geometric_mean",
4492
+ "assrt.equal(mean([1, 2, 3, 4]), 2.5)",
4493
+ "assrt.equal(mean([1, 2, 3]), 2)",
4494
+ "assrt.equal(fmean([1, 2, 3, 4]), 2.5)",
4495
+ "assrt.equal(median([1, 3, 5]), 3)",
4496
+ "assrt.equal(median([1, 3, 5, 7]), 4)",
4497
+ "assrt.equal(median([5, 1, 3]), 3)", // unsorted input
4498
+ "assrt.equal(median_low([1, 3, 5, 7]), 3)",
4499
+ "assrt.equal(median_high([1, 3, 5, 7]), 5)",
4500
+ "assrt.equal(mode([1, 1, 2, 3, 3, 3]), 3)",
4501
+ "assrt.equal(mode(['x', 'y', 'x']), 'x')",
4502
+ "assrt.deepEqual(multimode([1, 1, 2, 2, 3]), [1, 2])",
4503
+ "assrt.ok(Math.abs(harmonic_mean([40, 60]) - 48.0) < 1e-9)",
4504
+ "assrt.ok(Math.abs(geometric_mean([2, 8]) - 4.0) < 1e-9)",
4505
+ ].join("\n"));
4506
+ run_js(js);
4507
+ },
4508
+ },
4509
+
4510
+ {
4511
+ name: "bundle_statistics_spread",
4512
+ description: "statistics stdlib: variance, stdev and quantiles in the web-repl bundle",
4513
+ run: function () {
4514
+ var repl = RS.web_repl();
4515
+ var js = bundle_compile(repl, [
4516
+ "from statistics import variance, pvariance, stdev, pstdev, quantiles",
4517
+ "assrt.equal(variance([1, 2, 3, 4, 5]), 2.5)",
4518
+ "assrt.equal(pvariance([1, 2, 3, 4, 5]), 2.0)",
4519
+ "assrt.ok(Math.abs(stdev([1, 2, 3, 4, 5]) - Math.sqrt(2.5)) < 1e-9)",
4520
+ "assrt.ok(Math.abs(pstdev([1, 2, 3, 4, 5]) - Math.sqrt(2.0)) < 1e-9)",
4521
+ "assrt.deepEqual(quantiles([1, 2, 3, 4]), [1.25, 2.5, 3.75])",
4522
+ "q = quantiles([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], n=10)",
4523
+ "assrt.equal(q.length, 9)",
4524
+ ].join("\n"));
4525
+ run_js(js);
4526
+ },
4527
+ },
4528
+
4529
+ {
4530
+ name: "bundle_statistics_relations",
4531
+ description: "statistics stdlib: covariance, correlation, linear_regression in the bundle",
4532
+ run: function () {
4533
+ var repl = RS.web_repl();
4534
+ var js = bundle_compile(repl, [
4535
+ "from statistics import covariance, correlation, linear_regression",
4536
+ "x = [1, 2, 3, 4, 5, 6, 7, 8, 9]",
4537
+ "y = [1, 2, 3, 1, 2, 3, 1, 2, 3]",
4538
+ "assrt.ok(Math.abs(covariance(x, y) - 0.75) < 1e-9)",
4539
+ "assrt.ok(Math.abs(correlation(x, x) - 1.0) < 1e-9)",
4540
+ "lr = linear_regression([1, 2, 3, 4, 5], [2, 4, 6, 8, 10])",
4541
+ "assrt.ok(Math.abs(lr.slope - 2.0) < 1e-9)",
4542
+ "assrt.ok(Math.abs(lr.intercept - 0.0) < 1e-9)",
4543
+ ].join("\n"));
4544
+ run_js(js);
4545
+ },
4546
+ },
4547
+
4548
+ {
4549
+ name: "bundle_statistics_normaldist",
4550
+ description: "statistics stdlib: NormalDist in the web-repl bundle",
4551
+ run: function () {
4552
+ var repl = RS.web_repl();
4553
+ var js = bundle_compile(repl, [
4554
+ "from statistics import NormalDist",
4555
+ "nd = NormalDist(100, 15)",
4556
+ "assrt.equal(nd.mean, 100)",
4557
+ "assrt.equal(nd.stdev, 15)",
4558
+ "assrt.equal(nd.variance, 225)",
4559
+ "assrt.ok(Math.abs(nd.cdf(100) - 0.5) < 1e-6)",
4560
+ "assrt.equal(nd.inv_cdf(0.5), 100)",
4561
+ "assrt.equal(nd.zscore(115), 1.0)",
4562
+ "fs = NormalDist.from_samples([1, 2, 3, 4, 5])",
4563
+ "assrt.ok(Math.abs(fs.mean - 3.0) < 1e-9)",
4564
+ // operator overloading dispatches to __add__
4565
+ "combined = nd + NormalDist(50, 20)",
4566
+ "assrt.equal(combined.mean, 150)",
4567
+ "assrt.equal(combined.stdev, 25)",
4568
+ ].join("\n"));
4569
+ run_js(js);
4570
+ },
4571
+ },
4572
+
4573
+ // ── list sort: comparator detection + __lt__ dispatch ────────────────────
4574
+
4575
+ {
4576
+ name: "bundle_sort_comparator_and_lt",
4577
+ description: "list sort: positional comparator and __lt__ dispatch in the web-repl bundle",
4578
+ run: function () {
4579
+ var repl = RS.web_repl();
4580
+ var js = bundle_compile(repl, [
4581
+ // a positional two-argument function is treated as a comparator
4582
+ "assrt.deepEqual(sorted([3, 1, 2, 10], def(x, y): return x - y;), [1, 2, 3, 10])",
4583
+ "m = [5, 2, 8, 1]",
4584
+ "m.sort(def(x, y): return y - x;)",
4585
+ "assrt.deepEqual(m, [8, 5, 2, 1])",
4586
+ // a one-argument function is still a key function
4587
+ "assrt.deepEqual(sorted([3, 1, 2], key=def(x): return -x;), [3, 2, 1])",
4588
+ // custom objects are ordered through their __lt__ method
4589
+ "class Ord:",
4590
+ " def __init__(self, v):",
4591
+ " self.v = v",
4592
+ " def __lt__(self, other):",
4593
+ " return self.v < other.v",
4594
+ "s = sorted([Ord(3), Ord(1), Ord(2)])",
4595
+ "assrt.deepEqual([s[0].v, s[1].v, s[2].v], [1, 2, 3])",
4596
+ ].join("\n"));
4597
+ run_js(js);
4598
+ },
4599
+ },
4600
+
4601
+ // ── __slots__ ──────────────────────────────────────────────────────────
4602
+ {
4603
+ name: "slots_basic",
4604
+ description: "Basic __slots__ enforcement in web-repl bundle",
4605
+ run: function () {
4606
+ var repl = RS.web_repl();
4607
+ var js = bundle_compile(repl, [
4608
+ "class Point:",
4609
+ " __slots__ = ['x', 'y']",
4610
+ " def __init__(self, x, y):",
4611
+ " self.x = x",
4612
+ " self.y = y",
4613
+ "p = Point(1, 2)",
4614
+ "assrt.equal(p.x, 1)",
4615
+ "assrt.equal(p.y, 2)",
4616
+ "try:",
4617
+ " p.z = 3",
4618
+ " assrt.ok(False, 'should have raised')",
4619
+ "except AttributeError:",
4620
+ " assrt.ok(True)",
4621
+ ].join("\n"));
4622
+ run_js(js);
4623
+ },
4624
+ },
4625
+
4278
4626
  ];
4279
4627
 
4280
4628
  // ---------------------------------------------------------------------------