rapydscript-ns 0.8.2 → 0.8.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 (141) 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 +39 -0
  5. package/HACKING.md +103 -103
  6. package/LICENSE +24 -24
  7. package/PYTHON_DIFFERENCES_REPORT.md +291 -0
  8. package/PYTHON_FEATURE_COVERAGE.md +106 -15
  9. package/README.md +831 -52
  10. package/TODO.md +4 -286
  11. package/add-toc-to-readme +2 -2
  12. package/bin/export +75 -75
  13. package/bin/rapydscript +70 -70
  14. package/bin/web-repl-export +102 -102
  15. package/build +2 -2
  16. package/language-service/index.js +4623 -0
  17. package/language-service/language-service.d.ts +40 -0
  18. package/package.json +9 -7
  19. package/publish.py +37 -37
  20. package/release/baselib-plain-pretty.js +2006 -229
  21. package/release/baselib-plain-ugly.js +70 -3
  22. package/release/compiler.js +11554 -3870
  23. package/release/signatures.json +31 -29
  24. package/session.vim +4 -4
  25. package/setup.cfg +2 -2
  26. package/src/ast.pyj +93 -1
  27. package/src/baselib-builtins.pyj +99 -2
  28. package/src/baselib-containers.pyj +107 -4
  29. package/src/baselib-errors.pyj +44 -0
  30. package/src/baselib-internal.pyj +124 -5
  31. package/src/baselib-itertools.pyj +97 -97
  32. package/src/baselib-str.pyj +32 -1
  33. package/src/compiler.pyj +36 -36
  34. package/src/errors.pyj +30 -30
  35. package/src/lib/aes.pyj +646 -646
  36. package/src/lib/collections.pyj +1 -1
  37. package/src/lib/copy.pyj +120 -0
  38. package/src/lib/elementmaker.pyj +83 -83
  39. package/src/lib/encodings.pyj +126 -126
  40. package/src/lib/gettext.pyj +569 -569
  41. package/src/lib/itertools.pyj +580 -580
  42. package/src/lib/math.pyj +193 -193
  43. package/src/lib/numpy.pyj +10 -10
  44. package/src/lib/operator.pyj +11 -11
  45. package/src/lib/pythonize.pyj +20 -20
  46. package/src/lib/random.pyj +118 -118
  47. package/src/lib/re.pyj +470 -470
  48. package/src/lib/react.pyj +74 -0
  49. package/src/lib/traceback.pyj +63 -63
  50. package/src/lib/uuid.pyj +77 -77
  51. package/src/monaco-language-service/analyzer.js +131 -9
  52. package/src/monaco-language-service/builtins.js +17 -2
  53. package/src/monaco-language-service/completions.js +170 -1
  54. package/src/monaco-language-service/diagnostics.js +25 -3
  55. package/src/monaco-language-service/dts.js +550 -550
  56. package/src/monaco-language-service/index.js +17 -0
  57. package/src/monaco-language-service/scope.js +3 -0
  58. package/src/output/classes.pyj +128 -11
  59. package/src/output/codegen.pyj +17 -3
  60. package/src/output/comments.pyj +45 -45
  61. package/src/output/exceptions.pyj +201 -105
  62. package/src/output/functions.pyj +13 -16
  63. package/src/output/jsx.pyj +164 -0
  64. package/src/output/literals.pyj +28 -2
  65. package/src/output/loops.pyj +0 -9
  66. package/src/output/modules.pyj +2 -5
  67. package/src/output/operators.pyj +22 -2
  68. package/src/output/statements.pyj +2 -2
  69. package/src/output/stream.pyj +1 -13
  70. package/src/output/treeshake.pyj +182 -182
  71. package/src/output/utils.pyj +72 -72
  72. package/src/parse.pyj +434 -114
  73. package/src/string_interpolation.pyj +72 -72
  74. package/src/tokenizer.pyj +29 -0
  75. package/src/unicode_aliases.pyj +576 -576
  76. package/src/utils.pyj +192 -192
  77. package/test/_import_one.pyj +37 -37
  78. package/test/_import_two/__init__.pyj +11 -11
  79. package/test/_import_two/level2/deep.pyj +4 -4
  80. package/test/_import_two/other.pyj +6 -6
  81. package/test/_import_two/sub.pyj +13 -13
  82. package/test/aes_vectors.pyj +421 -421
  83. package/test/annotations.pyj +80 -80
  84. package/test/baselib.pyj +4 -4
  85. package/test/classes.pyj +56 -17
  86. package/test/collections.pyj +5 -5
  87. package/test/decorators.pyj +77 -77
  88. package/test/docstrings.pyj +39 -39
  89. package/test/elementmaker_test.pyj +45 -45
  90. package/test/functions.pyj +151 -151
  91. package/test/generators.pyj +41 -41
  92. package/test/generic.pyj +370 -370
  93. package/test/imports.pyj +72 -72
  94. package/test/internationalization.pyj +73 -73
  95. package/test/lint.pyj +164 -164
  96. package/test/loops.pyj +85 -85
  97. package/test/numpy.pyj +734 -734
  98. package/test/omit_function_metadata.pyj +20 -20
  99. package/test/python_compat.pyj +326 -0
  100. package/test/python_features.pyj +129 -29
  101. package/test/regexp.pyj +55 -55
  102. package/test/repl.pyj +121 -121
  103. package/test/scoped_flags.pyj +76 -76
  104. package/test/slice.pyj +105 -0
  105. package/test/str.pyj +25 -0
  106. package/test/unit/fixtures/fibonacci_expected.js +1 -1
  107. package/test/unit/index.js +2296 -71
  108. package/test/unit/language-service-builtins.js +70 -0
  109. package/test/unit/language-service-bundle.js +5 -5
  110. package/test/unit/language-service-completions.js +180 -0
  111. package/test/unit/language-service-dts.js +543 -543
  112. package/test/unit/language-service-hover.js +455 -455
  113. package/test/unit/language-service-index.js +350 -0
  114. package/test/unit/language-service-scope.js +255 -0
  115. package/test/unit/language-service.js +625 -4
  116. package/test/unit/run-language-service.js +1 -0
  117. package/test/unit/web-repl.js +437 -0
  118. package/tools/build-language-service.js +2 -2
  119. package/tools/cli.js +547 -547
  120. package/tools/compile.js +219 -219
  121. package/tools/compiler.js +0 -24
  122. package/tools/completer.js +131 -131
  123. package/tools/embedded_compiler.js +251 -251
  124. package/tools/export.js +3 -37
  125. package/tools/gettext.js +185 -185
  126. package/tools/ini.js +65 -65
  127. package/tools/msgfmt.js +187 -187
  128. package/tools/repl.js +223 -223
  129. package/tools/test.js +118 -118
  130. package/tools/utils.js +128 -128
  131. package/tools/web_repl.js +95 -95
  132. package/try +41 -41
  133. package/web-repl/env.js +196 -74
  134. package/web-repl/index.html +163 -163
  135. package/web-repl/main.js +252 -254
  136. package/web-repl/prism.css +139 -139
  137. package/web-repl/prism.js +113 -113
  138. package/web-repl/rapydscript.js +227 -139
  139. package/web-repl/sha1.js +25 -25
  140. package/hack_demo.pyj +0 -112
  141. package/web-repl/language-service.js +0 -4187
@@ -10,6 +10,7 @@
10
10
  "use strict";
11
11
 
12
12
  var assert = require("assert");
13
+ var fs = require("fs");
13
14
  var path = require("path");
14
15
  var url = require("url");
15
16
  var compiler_module = require("../../tools/compiler");
@@ -55,7 +56,20 @@ function assert_line(markers, line, label) {
55
56
  // description {string} – what is exercised
56
57
  // run(d) – receives a Diagnostics instance; throws on failure
57
58
 
58
- function make_tests(Diagnostics, RS) {
59
+ // Path to src/lib/ relative to this test file — used for coverage checks.
60
+ var LIB_DIR = path.join(__dirname, "../../src/lib");
61
+
62
+ /**
63
+ * Return the module names of all .pyj files currently in src/lib/.
64
+ * Excludes cache files (.pyj-cached) and any non-.pyj entries.
65
+ */
66
+ function get_lib_module_names() {
67
+ return fs.readdirSync(LIB_DIR)
68
+ .filter(function (f) { return f.endsWith(".pyj") && !f.endsWith(".pyj-cached"); })
69
+ .map(function (f) { return f.replace(/\.pyj$/, ""); });
70
+ }
71
+
72
+ function make_tests(Diagnostics, RS, STDLIB_MODULES) {
59
73
 
60
74
  function d(extras) { return new Diagnostics(RS, extras); }
61
75
 
@@ -763,6 +777,612 @@ function make_tests(Diagnostics, RS) {
763
777
  },
764
778
  },
765
779
 
780
+ {
781
+ name: "slice_builtin_no_undef_error",
782
+ description: "slice() usage produces no 'Undefined symbol' diagnostic",
783
+ run: function () {
784
+ var inst = new Diagnostics(RS);
785
+ var markers = inst.check([
786
+ "s = slice(1, 5)",
787
+ "x = [1, 2, 3, 4, 5]",
788
+ "ok = isinstance(s, slice)",
789
+ ].join("\n"));
790
+ var undef = markers.filter(function (m) {
791
+ return m.message.indexOf("Undefined symbol") !== -1 &&
792
+ m.message.indexOf("slice") !== -1;
793
+ });
794
+ assert.deepStrictEqual(undef, [],
795
+ "Expected no 'Undefined symbol: slice' but got: " + JSON.stringify(undef));
796
+ },
797
+ },
798
+
799
+ {
800
+ name: "list_concat_no_diagnostics",
801
+ description: "list + list and += produce no diagnostic errors",
802
+ run: function () {
803
+ var markers = d().check([
804
+ "a = [1, 2]",
805
+ "b = [3, 4]",
806
+ "c = a + b",
807
+ "a += [5, 6]",
808
+ "print(c)",
809
+ "print(a)",
810
+ ].join("\n"));
811
+ assert_count(markers, SEV_ERROR, 0, "list concat");
812
+ },
813
+ },
814
+
815
+ // ── Stdlib import recognition ──────────────────────────────────────
816
+
817
+ {
818
+ name: "stdlib_collections_no_bad_import",
819
+ description: "from collections import defaultdict produces no bad-import error when virtualFiles are present",
820
+ run: function () {
821
+ // A virtualFile being present activates the knownModules registry.
822
+ // Before the fix, 'collections' was not in knownModules and was
823
+ // flagged as 'Unknown module'.
824
+ var markers = d().check(
825
+ "from collections import defaultdict\nd = defaultdict(list)\nd['x'].append(1)",
826
+ { virtualFiles: { mymod: "def foo(): pass" } }
827
+ );
828
+ var bad = markers.filter(function (m) { return m.message.indexOf('Unknown module') !== -1; });
829
+ assert.deepStrictEqual(bad, [],
830
+ "Expected no 'Unknown module' for stdlib collections, got: " + JSON.stringify(bad));
831
+ },
832
+ },
833
+
834
+ {
835
+ name: "stdlib_all_modules_no_bad_import",
836
+ description: "All bundled stdlib modules produce no bad-import error when a module registry is active",
837
+ run: function () {
838
+ var stdlib_mods = [
839
+ 'aes', 'collections', 'copy', 'elementmaker', 'encodings', 'functools',
840
+ 'gettext', 'itertools', 'math', 'numpy', 'operator', 'pythonize',
841
+ 'random', 're', 'traceback', 'uuid',
842
+ ];
843
+ stdlib_mods.forEach(function (mod) {
844
+ var markers = d().check(
845
+ "from " + mod + " import x\nprint(x)",
846
+ { virtualFiles: { mymod: "def foo(): pass" } }
847
+ );
848
+ var bad = markers.filter(function (m) { return m.message.indexOf('Unknown module') !== -1; });
849
+ assert.deepStrictEqual(bad, [],
850
+ "Expected no 'Unknown module' for stdlib '" + mod + "', got: " + JSON.stringify(bad));
851
+ });
852
+ },
853
+ },
854
+
855
+ {
856
+ name: "stdlib_python_pseudo_module_no_bad_import",
857
+ description: "from __python__ import overload_operators produces no bad-import error when virtualFiles are present",
858
+ run: function () {
859
+ var markers = d().check(
860
+ "from __python__ import overload_operators\nclass V:\n def __add__(self, o): return V()\nV()",
861
+ { virtualFiles: { mymod: "def foo(): pass" } }
862
+ );
863
+ var bad = markers.filter(function (m) { return m.message.indexOf('Unknown module') !== -1; });
864
+ assert.deepStrictEqual(bad, [],
865
+ "Expected no 'Unknown module' for __python__ pseudo-module, got: " + JSON.stringify(bad));
866
+ },
867
+ },
868
+
869
+ {
870
+ name: "stdlib_unknown_still_flagged",
871
+ description: "A genuinely unknown module is still flagged as bad-import when registry is active",
872
+ run: function () {
873
+ var markers = d().check(
874
+ "from definitely_not_a_stdlib_module import foo\nprint(foo())",
875
+ { virtualFiles: { mymod: "def bar(): pass" } }
876
+ );
877
+ var bad = markers.filter(function (m) { return m.message.indexOf('Unknown module') !== -1; });
878
+ assert.ok(bad.length >= 1,
879
+ "Expected at least one 'Unknown module' for a genuinely unknown module, got: " + JSON.stringify(markers));
880
+ },
881
+ },
882
+
883
+ {
884
+ name: "stdlib_collections_all_classes",
885
+ description: "Importing all collections classes produces no errors",
886
+ run: function () {
887
+ var markers = d().check([
888
+ "from collections import defaultdict, Counter, OrderedDict, deque, namedtuple",
889
+ "d = defaultdict(list)",
890
+ "c = Counter([1, 2, 2, 3])",
891
+ "od = OrderedDict()",
892
+ "dq = deque([1, 2, 3])",
893
+ "Point = namedtuple('Point', 'x y')",
894
+ "print(d, c, od, dq, Point)",
895
+ ].join("\n"),
896
+ { virtualFiles: { mymod: "def foo(): pass" } }
897
+ );
898
+ var bad = markers.filter(function (m) { return m.message.indexOf('Unknown module') !== -1; });
899
+ assert.deepStrictEqual(bad, [],
900
+ "Expected no bad-import for collections classes, got: " + JSON.stringify(bad));
901
+ },
902
+ },
903
+
904
+ {
905
+ name: "stdlib_math_no_bad_import",
906
+ description: "from math import sqrt, pi, floor, ceil produces no bad-import",
907
+ run: function () {
908
+ var markers = d().check(
909
+ "from math import sqrt, pi, floor, ceil\nprint(sqrt(pi), floor(3.7), ceil(3.2))",
910
+ { virtualFiles: { mymod: "x = 1" } }
911
+ );
912
+ var bad = markers.filter(function (m) { return m.message.indexOf('Unknown module') !== -1; });
913
+ assert.deepStrictEqual(bad, [],
914
+ "Expected no bad-import for math, got: " + JSON.stringify(bad));
915
+ },
916
+ },
917
+
918
+ {
919
+ name: "copy_no_bad_import",
920
+ description: "from copy import copy, deepcopy produces no bad-import error",
921
+ run: function () {
922
+ var markers = d().check(
923
+ "from copy import copy, deepcopy\na = [1, 2, 3]\nb = copy(a)\nc = deepcopy(a)\nprint(b, c)",
924
+ { virtualFiles: { sentinel: "x = 1" } }
925
+ );
926
+ var bad = markers.filter(function (m) { return m.message.indexOf('Unknown module') !== -1; });
927
+ assert.deepStrictEqual(bad, [],
928
+ "Expected no bad-import for copy module, got: " + JSON.stringify(bad));
929
+ },
930
+ },
931
+
932
+ {
933
+ name: "copy_symbols_no_undef",
934
+ description: "copy and deepcopy are not flagged as undefined after import",
935
+ run: function () {
936
+ var markers = d().check([
937
+ "from copy import copy, deepcopy",
938
+ "class Pt:",
939
+ " def __init__(self, x, y):",
940
+ " self.x = x",
941
+ " self.y = y",
942
+ " def __copy__(self):",
943
+ " return Pt(self.x, self.y)",
944
+ " def __deepcopy__(self, memo):",
945
+ " return Pt(self.x, self.y)",
946
+ "p = Pt(1, 2)",
947
+ "q = copy(p)",
948
+ "r = deepcopy(p)",
949
+ "print(q.x, r.y)",
950
+ ].join("\n"));
951
+ var undef = markers.filter(function (m) { return m.message.indexOf('Undefined symbol') !== -1; });
952
+ assert.deepStrictEqual(undef, [],
953
+ "Expected no undef errors, got: " + JSON.stringify(undef));
954
+ },
955
+ },
956
+
957
+ {
958
+ name: "stdlib_functools_no_bad_import",
959
+ description: "from functools import reduce, partial produces no bad-import",
960
+ run: function () {
961
+ var markers = d().check(
962
+ "from functools import reduce, partial\nadd = partial(lambda a, b: a + b, 1)\nprint(reduce(lambda a, b: a + b, [1, 2, 3]))",
963
+ { virtualFiles: { mymod: "x = 1" } }
964
+ );
965
+ var bad = markers.filter(function (m) { return m.message.indexOf('Unknown module') !== -1; });
966
+ assert.deepStrictEqual(bad, [],
967
+ "Expected no bad-import for functools, got: " + JSON.stringify(bad));
968
+ },
969
+ },
970
+
971
+ {
972
+ name: "stdlib_react_hooks_no_errors",
973
+ description: "Importing and using React hooks and utilities produces no errors",
974
+ run: function () {
975
+ var markers = d().check([
976
+ "from react import useState, useEffect, useRef, useCallback, useMemo",
977
+ "from react import useContext, useReducer, useId, useTransition",
978
+ "from react import Component, PureComponent, Fragment",
979
+ "from react import createElement, createContext, createRef, memo, lazy",
980
+ "from react import forwardRef, cloneElement, isValidElement",
981
+ "count, setCount = useState(0)",
982
+ "ref = useRef(None)",
983
+ "ref2 = createRef()",
984
+ "ctx = createContext(None)",
985
+ "cb = useCallback(lambda: None, [])",
986
+ "val = useMemo(lambda: 42, [])",
987
+ "id_ = useId()",
988
+ "el = createElement('div', None)",
989
+ "valid = isValidElement(el)",
990
+ "print(count, setCount, ref, ref2, ctx, cb, val, id_, el, valid)",
991
+ "print(Component, PureComponent, Fragment, forwardRef, cloneElement)",
992
+ "print(useEffect, useContext, useReducer, useTransition, memo, lazy)",
993
+ ].join("\n"),
994
+ { virtualFiles: { sentinel: "x = 1" } }
995
+ );
996
+ var errors = markers.filter(function (m) { return m.severity === SEV_ERROR; });
997
+ assert.deepStrictEqual(errors, [],
998
+ "Expected no errors for react imports and usage, got: " +
999
+ JSON.stringify(errors.map(function (m) { return m.message; })));
1000
+ },
1001
+ },
1002
+
1003
+ // ── STDLIB_MODULES coverage (filesystem-driven) ───────────────────
1004
+ //
1005
+ // These tests read src/lib/ at runtime and cross-check against
1006
+ // STDLIB_MODULES so that any future .pyj file addition that is
1007
+ // not reflected in the constant causes an immediate test failure.
1008
+
1009
+ {
1010
+ name: "stdlib_modules_covers_all_lib_files",
1011
+ description: "Every .pyj file in src/lib/ has its module name listed in STDLIB_MODULES",
1012
+ run: function () {
1013
+ if (!STDLIB_MODULES) {
1014
+ // Graceful skip when called without the constant (shouldn't happen)
1015
+ throw new Error("STDLIB_MODULES was not passed to make_tests()");
1016
+ }
1017
+ var lib_names = get_lib_module_names();
1018
+ var stdlib_set = Object.create(null);
1019
+ STDLIB_MODULES.forEach(function (m) { stdlib_set[m] = true; });
1020
+
1021
+ var missing = lib_names.filter(function (name) {
1022
+ return !stdlib_set[name];
1023
+ });
1024
+
1025
+ assert.deepStrictEqual(missing, [],
1026
+ "The following src/lib/ modules are missing from STDLIB_MODULES " +
1027
+ "in diagnostics.js — add them to avoid false 'Unknown module' IDE errors:\n " +
1028
+ missing.join(", ")
1029
+ );
1030
+ },
1031
+ },
1032
+
1033
+ {
1034
+ name: "stdlib_modules_no_phantom_entries",
1035
+ description: "Every non-pseudo entry in STDLIB_MODULES corresponds to an actual src/lib/*.pyj file",
1036
+ run: function () {
1037
+ if (!STDLIB_MODULES) {
1038
+ throw new Error("STDLIB_MODULES was not passed to make_tests()");
1039
+ }
1040
+ // Pseudo-modules that do not have a corresponding .pyj file are OK.
1041
+ var PSEUDO = { '__python__': true, '__builtins__': true };
1042
+
1043
+ var lib_set = Object.create(null);
1044
+ get_lib_module_names().forEach(function (n) { lib_set[n] = true; });
1045
+
1046
+ var phantom = STDLIB_MODULES.filter(function (m) {
1047
+ return !PSEUDO[m] && !lib_set[m];
1048
+ });
1049
+
1050
+ assert.deepStrictEqual(phantom, [],
1051
+ "The following STDLIB_MODULES entries have no matching src/lib/*.pyj file — " +
1052
+ "remove them from STDLIB_MODULES or add the missing library:\n " +
1053
+ phantom.join(", ")
1054
+ );
1055
+ },
1056
+ },
1057
+
1058
+ {
1059
+ name: "hash_dunder_no_errors",
1060
+ description: "__hash__ method in a class produces no error markers",
1061
+ run: function () {
1062
+ var markers = d().check([
1063
+ "class Hashable:",
1064
+ " def __init__(self, v):",
1065
+ " self.v = v",
1066
+ " def __hash__(self):",
1067
+ " return hash(self.v)",
1068
+ "h = Hashable(42)",
1069
+ "result = hash(h)",
1070
+ ].join("\n"));
1071
+ var errors = markers.filter(function (m) { return m.severity === SEV_ERROR; });
1072
+ assert.deepStrictEqual(errors, [],
1073
+ "Expected no errors but got: " + JSON.stringify(errors));
1074
+ },
1075
+ },
1076
+
1077
+ {
1078
+ name: "hash_eq_and_hash_no_errors",
1079
+ description: "class with both __eq__ and __hash__ produces no error markers",
1080
+ run: function () {
1081
+ var markers = d().check([
1082
+ "class Key:",
1083
+ " def __init__(self, v):",
1084
+ " self.v = v",
1085
+ " def __eq__(self, other):",
1086
+ " return self.v == other.v",
1087
+ " def __hash__(self):",
1088
+ " return hash(self.v)",
1089
+ "k = Key(1)",
1090
+ "h = hash(k)",
1091
+ ].join("\n"));
1092
+ var errors = markers.filter(function (m) { return m.severity === SEV_ERROR; });
1093
+ assert.deepStrictEqual(errors, [],
1094
+ "Expected no errors but got: " + JSON.stringify(errors));
1095
+ },
1096
+ },
1097
+
1098
+ {
1099
+ name: "stdlib_new_lib_file_no_bad_import",
1100
+ description: "Every .pyj module in src/lib/ produces zero error-severity markers when imported with virtualFiles present",
1101
+ run: function () {
1102
+ var lib_names = get_lib_module_names();
1103
+ lib_names.forEach(function (mod) {
1104
+ var markers = d().check(
1105
+ "from " + mod + " import x\nprint(x)",
1106
+ { virtualFiles: { sentinel: "x = 1" } }
1107
+ );
1108
+ // Check for ANY error-severity marker, not just bad-import.
1109
+ // This catches: bad-import ('Unknown module'), import-err, syntax-err,
1110
+ // and any other error category that might arise for a new lib file.
1111
+ var errors = markers.filter(function (m) {
1112
+ return m.severity === SEV_ERROR;
1113
+ });
1114
+ assert.deepStrictEqual(errors, [],
1115
+ "src/lib/" + mod + ".pyj exists but 'from " + mod + " import x' " +
1116
+ "produces error markers — if this is a bad-import, add '" + mod +
1117
+ "' to STDLIB_MODULES in diagnostics.js. Markers: " +
1118
+ JSON.stringify(errors.map(function (m) { return m.message; }))
1119
+ );
1120
+ });
1121
+ },
1122
+ },
1123
+
1124
+ // ── __getattr__ / __setattr__ / __delattr__ / __getattribute__ ────────
1125
+
1126
+ {
1127
+ name: "attr_dunders_no_errors",
1128
+ description: "Classes with __getattr__/__setattr__/__delattr__/__getattribute__ produce no error markers",
1129
+ run: function () {
1130
+ var markers = d().check([
1131
+ "class Bag:",
1132
+ " def __init__(self):",
1133
+ " self.data = {}",
1134
+ " def __getattr__(self, name):",
1135
+ " return 'missing_' + name",
1136
+ " def __setattr__(self, name, value):",
1137
+ " object.__setattr__(self, name, value)",
1138
+ " def __delattr__(self, name):",
1139
+ " object.__delattr__(self, name)",
1140
+ " def __getattribute__(self, name):",
1141
+ " return object.__getattribute__(self, name)",
1142
+ "b = Bag()",
1143
+ "x = b.foo",
1144
+ "b.bar = 1",
1145
+ "del b.bar",
1146
+ ].join("\n"));
1147
+ var errors = markers.filter(function (m) { return m.severity === SEV_ERROR; });
1148
+ assert.deepStrictEqual(errors, [],
1149
+ "Expected no errors but got: " + JSON.stringify(errors));
1150
+ },
1151
+ },
1152
+
1153
+ {
1154
+ name: "class_getitem_no_errors",
1155
+ description: "__class_getitem__ method in a class produces no error markers",
1156
+ run: function () {
1157
+ var markers = d().check([
1158
+ "class Box:",
1159
+ " def __class_getitem__(cls, item):",
1160
+ " return cls.__name__ + '[' + str(item) + ']'",
1161
+ "result = Box[42]",
1162
+ "print(result)",
1163
+ ].join("\n"));
1164
+ var errors = markers.filter(function (m) { return m.severity === SEV_ERROR; });
1165
+ assert.deepStrictEqual(errors, [],
1166
+ "Expected no errors but got: " + JSON.stringify(errors));
1167
+ },
1168
+ },
1169
+
1170
+ {
1171
+ name: "class_getitem_subclass_no_errors",
1172
+ description: "subclass inheriting __class_getitem__ produces no error markers",
1173
+ run: function () {
1174
+ var markers = d().check([
1175
+ "class Base:",
1176
+ " def __class_getitem__(cls, item):",
1177
+ " return cls",
1178
+ "class Child(Base):",
1179
+ " pass",
1180
+ "x = Child[int]",
1181
+ ].join("\n"));
1182
+ var errors = markers.filter(function (m) { return m.severity === SEV_ERROR; });
1183
+ assert.deepStrictEqual(errors, [],
1184
+ "Expected no errors but got: " + JSON.stringify(errors));
1185
+ },
1186
+ },
1187
+
1188
+ {
1189
+ name: "init_subclass_no_errors",
1190
+ description: "__init_subclass__ method in a class produces no error markers",
1191
+ run: function () {
1192
+ var markers = d().check([
1193
+ "log = []",
1194
+ "class Base:",
1195
+ " def __init_subclass__(cls, **kwargs):",
1196
+ " log.append(cls.__name__)",
1197
+ "class Child(Base):",
1198
+ " pass",
1199
+ "print(log)",
1200
+ ].join("\n"));
1201
+ var errors = markers.filter(function (m) { return m.severity === SEV_ERROR; });
1202
+ assert.deepStrictEqual(errors, [],
1203
+ "Expected no errors but got: " + JSON.stringify(errors));
1204
+ },
1205
+ },
1206
+
1207
+ {
1208
+ name: "init_subclass_kwargs_no_errors",
1209
+ description: "__init_subclass__ with class kwargs produces no error markers",
1210
+ run: function () {
1211
+ var markers = d().check([
1212
+ "class Base:",
1213
+ " def __init_subclass__(cls, tag=None, **kwargs):",
1214
+ " cls._tag = tag",
1215
+ "class Child(Base, tag='hello'):",
1216
+ " pass",
1217
+ "print(Child._tag)",
1218
+ ].join("\n"));
1219
+ var errors = markers.filter(function (m) { return m.severity === SEV_ERROR; });
1220
+ assert.deepStrictEqual(errors, [],
1221
+ "Expected no errors but got: " + JSON.stringify(errors));
1222
+ },
1223
+ },
1224
+
1225
+ {
1226
+ name: "except_star_no_errors",
1227
+ description: "except* syntax and ExceptionGroup produce no error markers",
1228
+ run: function () {
1229
+ var markers = d().check([
1230
+ 'eg = ExceptionGroup("mixed", [ValueError("v"), TypeError("t")])',
1231
+ "val_msgs = []",
1232
+ "try:",
1233
+ " raise eg",
1234
+ "except* ValueError as g:",
1235
+ " for e in g.exceptions:",
1236
+ " val_msgs.append(str(e))",
1237
+ "except* TypeError as g:",
1238
+ " pass",
1239
+ "print(val_msgs)",
1240
+ ].join("\n"));
1241
+ var errors = markers.filter(function (m) { return m.severity === SEV_ERROR; });
1242
+ assert.deepStrictEqual(errors, [],
1243
+ "Expected no errors but got: " + JSON.stringify(errors));
1244
+ },
1245
+ },
1246
+
1247
+ {
1248
+ name: "except_star_var_bound_no_errors",
1249
+ description: "the as-variable in except* is correctly bound in the language service",
1250
+ run: function () {
1251
+ var markers = d().check([
1252
+ "try:",
1253
+ ' raise ExceptionGroup("eg", [ValueError("v")])',
1254
+ "except* ValueError as grp:",
1255
+ " x = len(grp.exceptions)",
1256
+ " print(x)",
1257
+ ].join("\n"));
1258
+ var errors = markers.filter(function (m) { return m.severity === SEV_ERROR; });
1259
+ assert.deepStrictEqual(errors, [],
1260
+ "Expected no errors but got: " + JSON.stringify(errors));
1261
+ },
1262
+ },
1263
+
1264
+ {
1265
+ name: "attr_dunders_object_bypass_no_errors",
1266
+ description: "object.__setattr__/object.__getattribute__/object.__delattr__ produce no error markers",
1267
+ run: function () {
1268
+ var markers = d().check([
1269
+ "class Validated:",
1270
+ " def __setattr__(self, name, value):",
1271
+ " if jstype(value) is 'number' and value < 0:",
1272
+ " raise ValueError('negative')",
1273
+ " object.__setattr__(self, name, value)",
1274
+ " def __getattribute__(self, name):",
1275
+ " return object.__getattribute__(self, name)",
1276
+ " def __delattr__(self, name):",
1277
+ " object.__delattr__(self, name)",
1278
+ "v = Validated()",
1279
+ "v.x = 5",
1280
+ ].join("\n"));
1281
+ var errors = markers.filter(function (m) { return m.severity === SEV_ERROR; });
1282
+ assert.deepStrictEqual(errors, [],
1283
+ "Expected no errors but got: " + JSON.stringify(errors));
1284
+ },
1285
+ },
1286
+
1287
+ // ── * and ** unpacking operators ─────────────────────────────────────
1288
+ {
1289
+ name: "list_spread_no_errors",
1290
+ description: "list spread [*a, 1, *b] produces no linter errors",
1291
+ run: function () {
1292
+ var markers = d().check([
1293
+ "a = [1, 2]",
1294
+ "b = [3, 4]",
1295
+ "c = [*a, 0, *b]",
1296
+ "d2 = [*c]",
1297
+ ].join("\n"));
1298
+ var errors = markers.filter(function (m) { return m.severity === SEV_ERROR; });
1299
+ assert.deepStrictEqual(errors, [],
1300
+ "Expected no errors but got: " + JSON.stringify(errors));
1301
+ },
1302
+ },
1303
+
1304
+ {
1305
+ name: "set_spread_no_errors",
1306
+ description: "set spread {*a, 1} and function **expr produce no linter errors",
1307
+ run: function () {
1308
+ var markers = d().check([
1309
+ "a = [1, 2, 3]",
1310
+ "s = {*a, 4}",
1311
+ "opts = {'x': 10}",
1312
+ "def f(x=0):",
1313
+ " return x",
1314
+ "f(**opts)",
1315
+ ].join("\n"));
1316
+ var errors = markers.filter(function (m) { return m.severity === SEV_ERROR; });
1317
+ assert.deepStrictEqual(errors, [],
1318
+ "Expected no errors but got: " + JSON.stringify(errors));
1319
+ },
1320
+ },
1321
+
1322
+ // ── tuple type ────────────────────────────────────────────────────────
1323
+
1324
+ {
1325
+ name: "tuple_annotation_no_undef",
1326
+ description: "tuple used as a variable or argument type annotation produces no undefined-symbol errors",
1327
+ run: function () {
1328
+ var markers = d().check([
1329
+ "def swap(pair: tuple) -> tuple:",
1330
+ " return (pair[1], pair[0])",
1331
+ "coords: tuple = (3, 4)",
1332
+ "result = swap(coords)",
1333
+ ].join("\n"));
1334
+ var undef = markers.filter(function (m) { return m.message.indexOf("Undefined symbol") !== -1; });
1335
+ assert.deepStrictEqual(undef, [],
1336
+ "Expected no 'Undefined symbol' errors but got: " + JSON.stringify(undef));
1337
+ },
1338
+ },
1339
+
1340
+ {
1341
+ name: "tuple_runtime_no_errors",
1342
+ description: "calling tuple() at runtime produces no linter errors",
1343
+ run: function () {
1344
+ var markers = d().check([
1345
+ "t = tuple([1, 2, 3])",
1346
+ "x = t[0]",
1347
+ ].join("\n"));
1348
+ var errors = markers.filter(function (m) { return m.severity === SEV_ERROR; });
1349
+ assert.deepStrictEqual(errors, [],
1350
+ "Expected no errors but got: " + JSON.stringify(errors));
1351
+ },
1352
+ },
1353
+
1354
+ // ── str.expandtabs language-service tests ─────────────────────────
1355
+
1356
+ {
1357
+ name: "expandtabs_no_errors",
1358
+ description: "str.expandtabs() call produces no linter errors",
1359
+ run: function () {
1360
+ var markers = d().check([
1361
+ "s = 'hello\\tworld'",
1362
+ "result = str.expandtabs(s)",
1363
+ "result2 = str.expandtabs(s, 4)",
1364
+ ].join("\n"));
1365
+ var errors = markers.filter(function (m) { return m.severity === SEV_ERROR; });
1366
+ assert.deepStrictEqual(errors, [],
1367
+ "Expected no errors but got: " + JSON.stringify(errors));
1368
+ },
1369
+ },
1370
+
1371
+ {
1372
+ name: "expandtabs_result_used_no_errors",
1373
+ description: "assigning expandtabs result and using it produces no linter errors",
1374
+ run: function () {
1375
+ var markers = d().check([
1376
+ "lines = ['a\\tb', 'c\\td']",
1377
+ "expanded = [str.expandtabs(line, 4) for line in lines]",
1378
+ "print(expanded)",
1379
+ ].join("\n"));
1380
+ var errors = markers.filter(function (m) { return m.severity === SEV_ERROR; });
1381
+ assert.deepStrictEqual(errors, [],
1382
+ "Expected no errors but got: " + JSON.stringify(errors));
1383
+ },
1384
+ },
1385
+
766
1386
  ];
767
1387
 
768
1388
  return TESTS;
@@ -817,9 +1437,10 @@ var diagnostics_path = url.pathToFileURL(
817
1437
  var filter = process.argv[2] || null;
818
1438
 
819
1439
  import(diagnostics_path).then(function (mod) {
820
- var Diagnostics = mod.Diagnostics;
821
- var RS = compiler_module.create_compiler();
822
- var TESTS = make_tests(Diagnostics, RS);
1440
+ var Diagnostics = mod.Diagnostics;
1441
+ var STDLIB_MODULES = mod.STDLIB_MODULES;
1442
+ var RS = compiler_module.create_compiler();
1443
+ var TESTS = make_tests(Diagnostics, RS, STDLIB_MODULES);
823
1444
  run_tests(TESTS, filter);
824
1445
  }).catch(function (e) {
825
1446
  console.error(colored("Failed to load language service module:", "red"), e);
@@ -24,6 +24,7 @@ var FILES = [
24
24
  "language-service-hover.js",
25
25
  "language-service-dts.js",
26
26
  "language-service-builtins.js",
27
+ "language-service-index.js",
27
28
  "language-service-bundle.js",
28
29
  "web-repl.js",
29
30
  ];