rapydscript-ns 0.8.4 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agignore +1 -1
- package/.github/workflows/ci.yml +38 -38
- package/=template.pyj +5 -5
- package/CHANGELOG.md +26 -0
- package/HACKING.md +103 -103
- package/LICENSE +24 -24
- package/README.md +716 -169
- package/TODO.md +7 -2
- package/add-toc-to-readme +2 -2
- package/bin/export +75 -75
- package/bin/rapydscript +70 -70
- package/bin/web-repl-export +102 -102
- package/build +2 -2
- package/language-service/index.js +36 -27
- package/package.json +1 -1
- package/publish.py +37 -37
- package/release/baselib-plain-pretty.js +2358 -168
- package/release/baselib-plain-ugly.js +73 -3
- package/release/compiler.js +6283 -3093
- package/release/signatures.json +31 -30
- package/session.vim +4 -4
- package/setup.cfg +2 -2
- package/src/ast.pyj +1 -0
- package/src/baselib-builtins.pyj +340 -2
- package/src/baselib-bytes.pyj +664 -0
- package/src/baselib-errors.pyj +1 -1
- package/src/baselib-internal.pyj +267 -60
- package/src/baselib-itertools.pyj +110 -97
- package/src/baselib-str.pyj +22 -4
- package/src/compiler.pyj +36 -36
- package/src/errors.pyj +30 -30
- package/src/lib/abc.pyj +317 -0
- package/src/lib/aes.pyj +646 -646
- package/src/lib/contextlib.pyj +379 -0
- package/src/lib/copy.pyj +120 -120
- package/src/lib/dataclasses.pyj +532 -0
- package/src/lib/datetime.pyj +712 -0
- package/src/lib/elementmaker.pyj +83 -83
- package/src/lib/encodings.pyj +126 -126
- package/src/lib/enum.pyj +125 -0
- package/src/lib/gettext.pyj +569 -569
- package/src/lib/io.pyj +500 -0
- package/src/lib/itertools.pyj +580 -580
- package/src/lib/json.pyj +227 -0
- package/src/lib/math.pyj +193 -193
- package/src/lib/operator.pyj +11 -11
- package/src/lib/pythonize.pyj +20 -20
- package/src/lib/random.pyj +118 -118
- package/src/lib/re.pyj +504 -470
- package/src/lib/react.pyj +74 -74
- package/src/lib/traceback.pyj +63 -63
- package/src/lib/typing.pyj +577 -0
- package/src/lib/uuid.pyj +77 -77
- package/src/monaco-language-service/builtins.js +14 -4
- package/src/monaco-language-service/diagnostics.js +19 -20
- package/src/monaco-language-service/dts.js +550 -550
- package/src/output/classes.pyj +62 -26
- package/src/output/comments.pyj +45 -45
- package/src/output/exceptions.pyj +201 -201
- package/src/output/functions.pyj +78 -5
- package/src/output/jsx.pyj +164 -164
- package/src/output/loops.pyj +5 -2
- package/src/output/operators.pyj +100 -34
- package/src/output/treeshake.pyj +182 -182
- package/src/output/utils.pyj +72 -72
- package/src/parse.pyj +80 -16
- package/src/string_interpolation.pyj +72 -72
- package/src/tokenizer.pyj +10 -5
- package/src/unicode_aliases.pyj +576 -576
- package/src/utils.pyj +192 -192
- package/test/_import_one.pyj +37 -37
- package/test/_import_two/__init__.pyj +11 -11
- package/test/_import_two/level2/deep.pyj +4 -4
- package/test/_import_two/other.pyj +6 -6
- package/test/_import_two/sub.pyj +13 -13
- package/test/abc.pyj +291 -0
- package/test/aes_vectors.pyj +421 -421
- package/test/annotations.pyj +80 -80
- package/test/arithmetic_nostrict.pyj +88 -0
- package/test/arithmetic_types.pyj +169 -0
- package/test/baselib.pyj +91 -0
- package/test/bytes.pyj +467 -0
- package/test/classes.pyj +1 -0
- package/test/comparison_ops.pyj +173 -0
- package/test/contextlib.pyj +362 -0
- package/test/dataclasses.pyj +253 -0
- package/test/datetime.pyj +500 -0
- package/test/debugger_stmt.pyj +41 -0
- package/test/decorators.pyj +77 -77
- package/test/docstrings.pyj +39 -39
- package/test/elementmaker_test.pyj +45 -45
- package/test/enum.pyj +134 -0
- package/test/eval_exec.pyj +56 -0
- package/test/format.pyj +148 -0
- package/test/functions.pyj +151 -151
- package/test/generators.pyj +41 -41
- package/test/generic.pyj +370 -370
- package/test/imports.pyj +72 -72
- package/test/internationalization.pyj +73 -73
- package/test/io.pyj +316 -0
- package/test/json.pyj +196 -0
- package/test/lint.pyj +164 -164
- package/test/loops.pyj +85 -85
- package/test/numpy.pyj +734 -734
- package/test/object.pyj +64 -0
- package/test/omit_function_metadata.pyj +20 -20
- package/test/python_compat.pyj +17 -15
- package/test/python_features.pyj +70 -15
- package/test/regexp.pyj +83 -55
- package/test/repl.pyj +121 -121
- package/test/scoped_flags.pyj +76 -76
- package/test/tuples.pyj +96 -0
- package/test/typing.pyj +469 -0
- package/test/unit/index.js +116 -7
- package/test/unit/language-service-dts.js +543 -543
- package/test/unit/language-service-hover.js +455 -455
- package/test/unit/language-service.js +84 -0
- package/test/unit/web-repl.js +1337 -1
- package/test/vars_locals_globals.pyj +94 -0
- package/tools/cli.js +558 -547
- package/tools/compile.js +224 -219
- package/tools/completer.js +131 -131
- package/tools/embedded_compiler.js +262 -251
- package/tools/gettext.js +185 -185
- package/tools/ini.js +65 -65
- package/tools/lint.js +16 -19
- package/tools/msgfmt.js +187 -187
- package/tools/repl.js +223 -223
- package/tools/test.js +118 -118
- package/tools/utils.js +128 -128
- package/tools/web_repl.js +95 -95
- package/try +41 -41
- package/web-repl/env.js +196 -196
- package/web-repl/index.html +163 -163
- package/web-repl/main.js +252 -252
- package/web-repl/prism.css +139 -139
- package/web-repl/prism.js +113 -113
- package/web-repl/rapydscript.js +224 -224
- package/web-repl/sha1.js +25 -25
- package/PYTHON_DIFFERENCES_REPORT.md +0 -291
- package/PYTHON_FEATURE_COVERAGE.md +0 -200
package/test/unit/web-repl.js
CHANGED
|
@@ -798,7 +798,7 @@ var TESTS = [
|
|
|
798
798
|
var repl = RS.web_repl();
|
|
799
799
|
var js = bundle_compile(repl, [
|
|
800
800
|
"from react import forwardRef",
|
|
801
|
-
"def render(props, ref): return props
|
|
801
|
+
"def render(props, ref): return props['x']",
|
|
802
802
|
"FancyInput = forwardRef(render)",
|
|
803
803
|
"assrt.equal(jstype(FancyInput), 'function')",
|
|
804
804
|
"assrt.equal(FancyInput({'x': 99}), 99)",
|
|
@@ -864,6 +864,136 @@ var TESTS = [
|
|
|
864
864
|
},
|
|
865
865
|
},
|
|
866
866
|
|
|
867
|
+
// ── bytes / bytearray ────────────────────────────────────────────────────
|
|
868
|
+
|
|
869
|
+
{
|
|
870
|
+
name: "bundle_bytes_basic",
|
|
871
|
+
description: "bytes() construction and basic operations work in bundled baselib",
|
|
872
|
+
run: function () {
|
|
873
|
+
var repl = RS.web_repl();
|
|
874
|
+
var js = bundle_compile(repl, [
|
|
875
|
+
"b = bytes([72, 101, 108, 108, 111])",
|
|
876
|
+
"assrt.equal(len(b), 5)",
|
|
877
|
+
"assrt.equal(b[0], 72)",
|
|
878
|
+
"assrt.equal(b[-1], 111)",
|
|
879
|
+
"assrt.ok(isinstance(b, bytes))",
|
|
880
|
+
].join("\n"));
|
|
881
|
+
run_js(js);
|
|
882
|
+
},
|
|
883
|
+
},
|
|
884
|
+
|
|
885
|
+
{
|
|
886
|
+
name: "bundle_bytes_from_string",
|
|
887
|
+
description: "bytes(str, encoding) encodes a string to UTF-8 bytes in bundled baselib",
|
|
888
|
+
run: function () {
|
|
889
|
+
var repl = RS.web_repl();
|
|
890
|
+
var js = bundle_compile(repl, [
|
|
891
|
+
"b = bytes('Hello', 'utf-8')",
|
|
892
|
+
"assrt.equal(len(b), 5)",
|
|
893
|
+
"assrt.equal(b[0], 72)",
|
|
894
|
+
"assrt.equal(b.decode('utf-8'), 'Hello')",
|
|
895
|
+
].join("\n"));
|
|
896
|
+
run_js(js);
|
|
897
|
+
},
|
|
898
|
+
},
|
|
899
|
+
|
|
900
|
+
{
|
|
901
|
+
name: "bundle_bytes_hex",
|
|
902
|
+
description: "bytes.hex() and bytes.fromhex() round-trip in bundled baselib",
|
|
903
|
+
run: function () {
|
|
904
|
+
var repl = RS.web_repl();
|
|
905
|
+
var js = bundle_compile(repl, [
|
|
906
|
+
"b = bytes([0, 15, 255])",
|
|
907
|
+
"assrt.equal(b.hex(), '000fff')",
|
|
908
|
+
"b2 = bytes.fromhex('000fff')",
|
|
909
|
+
"assrt.ok(b == b2)",
|
|
910
|
+
].join("\n"));
|
|
911
|
+
run_js(js);
|
|
912
|
+
},
|
|
913
|
+
},
|
|
914
|
+
|
|
915
|
+
{
|
|
916
|
+
name: "bundle_bytes_slice",
|
|
917
|
+
description: "bytes slice returns bytes in bundled baselib",
|
|
918
|
+
run: function () {
|
|
919
|
+
var repl = RS.web_repl();
|
|
920
|
+
var js = bundle_compile(repl, [
|
|
921
|
+
"b = bytes([10, 20, 30, 40, 50])",
|
|
922
|
+
"s = b[1:4]",
|
|
923
|
+
"assrt.ok(isinstance(s, bytes))",
|
|
924
|
+
"assrt.equal(len(s), 3)",
|
|
925
|
+
"assrt.equal(s[0], 20)",
|
|
926
|
+
"assrt.equal(s[2], 40)",
|
|
927
|
+
].join("\n"));
|
|
928
|
+
run_js(js);
|
|
929
|
+
},
|
|
930
|
+
},
|
|
931
|
+
|
|
932
|
+
{
|
|
933
|
+
name: "bundle_bytearray_mutation",
|
|
934
|
+
description: "bytearray append/extend/pop work in bundled baselib",
|
|
935
|
+
run: function () {
|
|
936
|
+
var repl = RS.web_repl();
|
|
937
|
+
var js = bundle_compile(repl, [
|
|
938
|
+
"ba = bytearray([1, 2, 3])",
|
|
939
|
+
"assrt.ok(isinstance(ba, bytearray))",
|
|
940
|
+
"ba.append(4)",
|
|
941
|
+
"assrt.equal(len(ba), 4)",
|
|
942
|
+
"assrt.equal(ba[3], 4)",
|
|
943
|
+
"p = ba.pop()",
|
|
944
|
+
"assrt.equal(p, 4)",
|
|
945
|
+
"assrt.equal(len(ba), 3)",
|
|
946
|
+
].join("\n"));
|
|
947
|
+
run_js(js);
|
|
948
|
+
},
|
|
949
|
+
},
|
|
950
|
+
|
|
951
|
+
{
|
|
952
|
+
name: "bundle_bytes_repr",
|
|
953
|
+
description: "repr(bytes(...)) returns b'...' notation in bundled baselib",
|
|
954
|
+
run: function () {
|
|
955
|
+
var repl = RS.web_repl();
|
|
956
|
+
var js = bundle_compile(repl, [
|
|
957
|
+
"b = bytes([72, 101, 108, 108, 111])",
|
|
958
|
+
"assrt.equal(repr(b), \"b'Hello'\")",
|
|
959
|
+
].join("\n"));
|
|
960
|
+
run_js(js);
|
|
961
|
+
},
|
|
962
|
+
},
|
|
963
|
+
|
|
964
|
+
{
|
|
965
|
+
name: "bundle_bytes_literal_syntax",
|
|
966
|
+
description: "b'...' bytes literal compiles and runs correctly in bundled compiler",
|
|
967
|
+
run: function () {
|
|
968
|
+
var repl = RS.web_repl();
|
|
969
|
+
var js = bundle_compile(repl, [
|
|
970
|
+
"b = b'Hello'",
|
|
971
|
+
"assrt.ok(isinstance(b, bytes))",
|
|
972
|
+
"assrt.equal(len(b), 5)",
|
|
973
|
+
"assrt.equal(b[0], 72)",
|
|
974
|
+
"assrt.equal(b[4], 111)",
|
|
975
|
+
"assrt.ok(b == bytes([72, 101, 108, 108, 111]))",
|
|
976
|
+
"# hex escapes",
|
|
977
|
+
"b2 = b'\\x48\\x65\\x6c\\x6c\\x6f'",
|
|
978
|
+
"assrt.ok(b2 == b)",
|
|
979
|
+
"# empty literal",
|
|
980
|
+
"e = b''",
|
|
981
|
+
"assrt.equal(len(e), 0)",
|
|
982
|
+
"# adjacent literal concatenation",
|
|
983
|
+
"c = b'foo' b'bar'",
|
|
984
|
+
"assrt.equal(len(c), 6)",
|
|
985
|
+
"assrt.ok(c == bytes([102, 111, 111, 98, 97, 114]))",
|
|
986
|
+
"# uppercase B prefix",
|
|
987
|
+
"u = B'ABC'",
|
|
988
|
+
"assrt.ok(isinstance(u, bytes))",
|
|
989
|
+
"assrt.equal(u[0], 65)",
|
|
990
|
+
"# repr shows b'...' notation",
|
|
991
|
+
"assrt.equal(repr(b'Hi'), \"b'Hi'\")",
|
|
992
|
+
].join("\n"));
|
|
993
|
+
run_js(js);
|
|
994
|
+
},
|
|
995
|
+
},
|
|
996
|
+
|
|
867
997
|
{
|
|
868
998
|
name: "bundle___import__-error",
|
|
869
999
|
description: "__import__ raises ModuleNotFoundError for unknown module in web-repl",
|
|
@@ -882,6 +1012,1212 @@ var TESTS = [
|
|
|
882
1012
|
},
|
|
883
1013
|
},
|
|
884
1014
|
|
|
1015
|
+
// ── Enum ─────────────────────────────────────────────────────────────────
|
|
1016
|
+
|
|
1017
|
+
{
|
|
1018
|
+
name: "bundle_enum_basic",
|
|
1019
|
+
description: "Enum subclass .name and .value attributes work in bundled baselib",
|
|
1020
|
+
run: function () {
|
|
1021
|
+
var repl = RS.web_repl();
|
|
1022
|
+
var js = bundle_compile(repl, [
|
|
1023
|
+
"from enum import Enum",
|
|
1024
|
+
"class Color(Enum):",
|
|
1025
|
+
" RED = 1",
|
|
1026
|
+
" GREEN = 2",
|
|
1027
|
+
" BLUE = 3",
|
|
1028
|
+
"assrt.equal(Color.RED.name, 'RED')",
|
|
1029
|
+
"assrt.equal(Color.RED.value, 1)",
|
|
1030
|
+
"assrt.equal(Color.GREEN.name, 'GREEN')",
|
|
1031
|
+
"assrt.equal(Color.GREEN.value, 2)",
|
|
1032
|
+
"assrt.equal(Color.BLUE.name, 'BLUE')",
|
|
1033
|
+
"assrt.equal(Color.BLUE.value, 3)",
|
|
1034
|
+
].join("\n"));
|
|
1035
|
+
run_js(js);
|
|
1036
|
+
},
|
|
1037
|
+
},
|
|
1038
|
+
|
|
1039
|
+
{
|
|
1040
|
+
name: "bundle_enum_identity",
|
|
1041
|
+
description: "Enum members are singletons — Color.RED is Color.RED",
|
|
1042
|
+
run: function () {
|
|
1043
|
+
var repl = RS.web_repl();
|
|
1044
|
+
var js = bundle_compile(repl, [
|
|
1045
|
+
"from enum import Enum",
|
|
1046
|
+
"class Color(Enum):",
|
|
1047
|
+
" RED = 1",
|
|
1048
|
+
" GREEN = 2",
|
|
1049
|
+
"assrt.ok(Color.RED is Color.RED)",
|
|
1050
|
+
"assrt.ok(Color.RED is not Color.GREEN)",
|
|
1051
|
+
].join("\n"));
|
|
1052
|
+
run_js(js);
|
|
1053
|
+
},
|
|
1054
|
+
},
|
|
1055
|
+
|
|
1056
|
+
{
|
|
1057
|
+
name: "bundle_enum_iteration",
|
|
1058
|
+
description: "list(Color) and for-loop over an Enum class yield all members in order",
|
|
1059
|
+
run: function () {
|
|
1060
|
+
var repl = RS.web_repl();
|
|
1061
|
+
var js = bundle_compile(repl, [
|
|
1062
|
+
"from enum import Enum",
|
|
1063
|
+
"class Color(Enum):",
|
|
1064
|
+
" RED = 1",
|
|
1065
|
+
" GREEN = 2",
|
|
1066
|
+
" BLUE = 3",
|
|
1067
|
+
"members = list(Color)",
|
|
1068
|
+
"assrt.equal(len(members), 3)",
|
|
1069
|
+
"assrt.equal(members[0].name, 'RED')",
|
|
1070
|
+
"assrt.equal(members[1].name, 'GREEN')",
|
|
1071
|
+
"assrt.equal(members[2].name, 'BLUE')",
|
|
1072
|
+
"names = []",
|
|
1073
|
+
"for m in Color:",
|
|
1074
|
+
" names.push(m.name)",
|
|
1075
|
+
"assrt.deepEqual(names, ['RED', 'GREEN', 'BLUE'])",
|
|
1076
|
+
].join("\n"));
|
|
1077
|
+
run_js(js);
|
|
1078
|
+
},
|
|
1079
|
+
},
|
|
1080
|
+
|
|
1081
|
+
{
|
|
1082
|
+
name: "bundle_enum_isinstance",
|
|
1083
|
+
description: "isinstance(Color.RED, Color) and isinstance(Color.RED, Enum) both return True",
|
|
1084
|
+
run: function () {
|
|
1085
|
+
var repl = RS.web_repl();
|
|
1086
|
+
var js = bundle_compile(repl, [
|
|
1087
|
+
"from enum import Enum",
|
|
1088
|
+
"class Color(Enum):",
|
|
1089
|
+
" RED = 1",
|
|
1090
|
+
"assrt.ok(isinstance(Color.RED, Color))",
|
|
1091
|
+
"assrt.ok(isinstance(Color.RED, Enum))",
|
|
1092
|
+
"assrt.ok(not isinstance(1, Color))",
|
|
1093
|
+
].join("\n"));
|
|
1094
|
+
run_js(js);
|
|
1095
|
+
},
|
|
1096
|
+
},
|
|
1097
|
+
|
|
1098
|
+
{
|
|
1099
|
+
name: "bundle_enum_repr_str",
|
|
1100
|
+
description: "repr() and str() of an enum member produce the expected strings",
|
|
1101
|
+
run: function () {
|
|
1102
|
+
var repl = RS.web_repl();
|
|
1103
|
+
var js = bundle_compile(repl, [
|
|
1104
|
+
"from enum import Enum",
|
|
1105
|
+
"class Color(Enum):",
|
|
1106
|
+
" RED = 1",
|
|
1107
|
+
"assrt.equal(repr(Color.RED), '<Color.RED: 1>')",
|
|
1108
|
+
"assrt.equal(str(Color.RED), 'Color.RED')",
|
|
1109
|
+
].join("\n"));
|
|
1110
|
+
run_js(js);
|
|
1111
|
+
},
|
|
1112
|
+
},
|
|
1113
|
+
|
|
1114
|
+
{
|
|
1115
|
+
name: "bundle_dataclasses_basic",
|
|
1116
|
+
description: "@dataclass generates __init__, __repr__, __eq__ in bundled baselib",
|
|
1117
|
+
run: function () {
|
|
1118
|
+
var repl = RS.web_repl();
|
|
1119
|
+
var js = bundle_compile(repl, [
|
|
1120
|
+
"from dataclasses import dataclass, field, is_dataclass",
|
|
1121
|
+
"@dataclass",
|
|
1122
|
+
"class Point:",
|
|
1123
|
+
" x: int",
|
|
1124
|
+
" y: int = 0",
|
|
1125
|
+
"p = Point(3, 4)",
|
|
1126
|
+
"assrt.equal(p.x, 3)",
|
|
1127
|
+
"assrt.equal(p.y, 4)",
|
|
1128
|
+
"assrt.ok(p == Point(3, 4))",
|
|
1129
|
+
"assrt.ok(p is not Point(3, 4))",
|
|
1130
|
+
"assrt.equal(repr(p), 'Point(x=3, y=4)')",
|
|
1131
|
+
"assrt.ok(is_dataclass(p))",
|
|
1132
|
+
"assrt.ok(is_dataclass(Point))",
|
|
1133
|
+
].join("\n"));
|
|
1134
|
+
run_js(js);
|
|
1135
|
+
},
|
|
1136
|
+
},
|
|
1137
|
+
|
|
1138
|
+
{
|
|
1139
|
+
name: "bundle_dataclasses_field_factory",
|
|
1140
|
+
description: "field(default_factory=...) gives each instance its own mutable default",
|
|
1141
|
+
run: function () {
|
|
1142
|
+
var repl = RS.web_repl();
|
|
1143
|
+
var js = bundle_compile(repl, [
|
|
1144
|
+
"from dataclasses import dataclass, field",
|
|
1145
|
+
"@dataclass",
|
|
1146
|
+
"class Container:",
|
|
1147
|
+
" items: list = field(default_factory=list)",
|
|
1148
|
+
"a = Container()",
|
|
1149
|
+
"b = Container()",
|
|
1150
|
+
"a.items.push(1)",
|
|
1151
|
+
"assrt.equal(a.items.length, 1)",
|
|
1152
|
+
"assrt.equal(b.items.length, 0)",
|
|
1153
|
+
].join("\n"));
|
|
1154
|
+
run_js(js);
|
|
1155
|
+
},
|
|
1156
|
+
},
|
|
1157
|
+
|
|
1158
|
+
{
|
|
1159
|
+
name: "bundle_dataclasses_asdict",
|
|
1160
|
+
description: "asdict() recursively converts a dataclass instance to a plain dict",
|
|
1161
|
+
run: function () {
|
|
1162
|
+
var repl = RS.web_repl();
|
|
1163
|
+
var js = bundle_compile(repl, [
|
|
1164
|
+
"from dataclasses import dataclass, asdict",
|
|
1165
|
+
"@dataclass",
|
|
1166
|
+
"class Inner:",
|
|
1167
|
+
" value: int",
|
|
1168
|
+
"@dataclass",
|
|
1169
|
+
"class Outer:",
|
|
1170
|
+
" inner: object",
|
|
1171
|
+
" tag: str",
|
|
1172
|
+
"d = asdict(Outer(Inner(42), 'hello'))",
|
|
1173
|
+
"assrt.equal(d['tag'], 'hello')",
|
|
1174
|
+
"assrt.equal(d['inner']['value'], 42)",
|
|
1175
|
+
].join("\n"));
|
|
1176
|
+
run_js(js);
|
|
1177
|
+
},
|
|
1178
|
+
},
|
|
1179
|
+
|
|
1180
|
+
{
|
|
1181
|
+
name: "bundle_dataclasses_frozen",
|
|
1182
|
+
description: "frozen=True makes fields read-only (writable:false, assignment silently ignored outside strict mode)",
|
|
1183
|
+
run: function () {
|
|
1184
|
+
var repl = RS.web_repl();
|
|
1185
|
+
var js = bundle_compile(repl, [
|
|
1186
|
+
"from dataclasses import dataclass",
|
|
1187
|
+
"@dataclass(frozen=True)",
|
|
1188
|
+
"class FP:",
|
|
1189
|
+
" x: int",
|
|
1190
|
+
" y: int",
|
|
1191
|
+
"fp = FP(1, 2)",
|
|
1192
|
+
"assrt.equal(fp.x, 1)",
|
|
1193
|
+
"assrt.equal(fp.y, 2)",
|
|
1194
|
+
"fp.x = 99",
|
|
1195
|
+
// In non-strict mode, writable:false assignment is silently ignored
|
|
1196
|
+
"assrt.equal(fp.x, 1)",
|
|
1197
|
+
].join("\n"));
|
|
1198
|
+
run_js(js);
|
|
1199
|
+
},
|
|
1200
|
+
},
|
|
1201
|
+
|
|
1202
|
+
{
|
|
1203
|
+
name: "bundle_format_dunder",
|
|
1204
|
+
description: "__format__ dunder dispatches from format(), str.format(), and f-strings in web-repl bundle",
|
|
1205
|
+
run: function () {
|
|
1206
|
+
var repl = RS.web_repl();
|
|
1207
|
+
var js = bundle_compile(repl, [
|
|
1208
|
+
"class Money:",
|
|
1209
|
+
" def __init__(self, amount):",
|
|
1210
|
+
" self.amount = amount",
|
|
1211
|
+
" def __str__(self):",
|
|
1212
|
+
" return str(self.amount)",
|
|
1213
|
+
" def __format__(self, spec):",
|
|
1214
|
+
" if spec == 'usd':",
|
|
1215
|
+
" return '$' + str(self.amount)",
|
|
1216
|
+
" return format(self.amount, spec)",
|
|
1217
|
+
"m = Money(42)",
|
|
1218
|
+
"assrt.equal(format(m, 'usd'), '$42')",
|
|
1219
|
+
"assrt.equal(str.format('{:usd}', m), '$42')",
|
|
1220
|
+
"assrt.equal(f'{m:usd}', '$42')",
|
|
1221
|
+
"assrt.equal(format(m, '.2f'), '42.00')",
|
|
1222
|
+
"assrt.equal(f'{m:.2f}', '42.00')",
|
|
1223
|
+
// Default __format__ (no custom __format__ defined)
|
|
1224
|
+
"class Plain:",
|
|
1225
|
+
" def __str__(self):",
|
|
1226
|
+
" return 'plain'",
|
|
1227
|
+
"p = Plain()",
|
|
1228
|
+
"assrt.equal(format(p), 'plain')",
|
|
1229
|
+
"assrt.equal(str.format('{}', p), 'plain')",
|
|
1230
|
+
"assrt.equal(f'{p}', 'plain')",
|
|
1231
|
+
].join("\n"));
|
|
1232
|
+
run_js(js);
|
|
1233
|
+
},
|
|
1234
|
+
},
|
|
1235
|
+
|
|
1236
|
+
// ── object() builtin ──────────────────────────────────────────────────────
|
|
1237
|
+
|
|
1238
|
+
{
|
|
1239
|
+
name: "bundle_object_sentinel",
|
|
1240
|
+
description: "object() returns distinct instances usable as sentinels",
|
|
1241
|
+
run: function () {
|
|
1242
|
+
var repl = RS.web_repl();
|
|
1243
|
+
var js = bundle_compile(repl, [
|
|
1244
|
+
"s1 = object()",
|
|
1245
|
+
"s2 = object()",
|
|
1246
|
+
"assrt.ok(s1 is not s2)",
|
|
1247
|
+
"assrt.ok(isinstance(s1, object))",
|
|
1248
|
+
"assrt.ok(isinstance(s2, object))",
|
|
1249
|
+
"h1 = hash(s1)",
|
|
1250
|
+
"h2 = hash(s2)",
|
|
1251
|
+
"assrt.ok(h1 is not h2)",
|
|
1252
|
+
"assrt.equal(hash(s1), h1)",
|
|
1253
|
+
].join("\n"));
|
|
1254
|
+
run_js(js);
|
|
1255
|
+
},
|
|
1256
|
+
},
|
|
1257
|
+
|
|
1258
|
+
{
|
|
1259
|
+
name: "bundle_object_subclass",
|
|
1260
|
+
description: "class Foo(object) works and isinstance checks succeed",
|
|
1261
|
+
run: function () {
|
|
1262
|
+
var repl = RS.web_repl();
|
|
1263
|
+
var js = bundle_compile(repl, [
|
|
1264
|
+
"class Node(object):",
|
|
1265
|
+
" def __init__(self, val):",
|
|
1266
|
+
" self.val = val",
|
|
1267
|
+
"n = Node(42)",
|
|
1268
|
+
"assrt.ok(isinstance(n, Node))",
|
|
1269
|
+
"assrt.ok(isinstance(n, object))",
|
|
1270
|
+
"assrt.equal(n.val, 42)",
|
|
1271
|
+
].join("\n"));
|
|
1272
|
+
run_js(js);
|
|
1273
|
+
},
|
|
1274
|
+
},
|
|
1275
|
+
|
|
1276
|
+
// ── float.is_integer() ───────────────────────────────────────────────────
|
|
1277
|
+
|
|
1278
|
+
{
|
|
1279
|
+
name: "bundle_float_is_integer",
|
|
1280
|
+
description: "float.is_integer() returns True for whole numbers and False for fractions/inf/nan",
|
|
1281
|
+
run: function () {
|
|
1282
|
+
var repl = RS.web_repl();
|
|
1283
|
+
var js = bundle_compile(repl, [
|
|
1284
|
+
"assrt.equal((1.0).is_integer(), True)",
|
|
1285
|
+
"assrt.equal((1.5).is_integer(), False)",
|
|
1286
|
+
"assrt.equal((0.0).is_integer(), True)",
|
|
1287
|
+
"assrt.equal((-2.0).is_integer(), True)",
|
|
1288
|
+
"assrt.equal((-2.5).is_integer(), False)",
|
|
1289
|
+
"assrt.equal((1e10).is_integer(), True)",
|
|
1290
|
+
"assrt.equal(v'Infinity'.is_integer(), False)",
|
|
1291
|
+
"assrt.equal(v'NaN'.is_integer(), False)",
|
|
1292
|
+
].join("\n"));
|
|
1293
|
+
run_js(js);
|
|
1294
|
+
},
|
|
1295
|
+
},
|
|
1296
|
+
|
|
1297
|
+
// ── int.bit_length() ─────────────────────────────────────────────────────
|
|
1298
|
+
|
|
1299
|
+
{
|
|
1300
|
+
name: "bundle_int_bit_length",
|
|
1301
|
+
description: "int.bit_length() returns the number of bits needed to represent the integer",
|
|
1302
|
+
run: function () {
|
|
1303
|
+
var repl = RS.web_repl();
|
|
1304
|
+
var js = bundle_compile(repl, [
|
|
1305
|
+
"assrt.equal((0).bit_length(), 0)",
|
|
1306
|
+
"assrt.equal((1).bit_length(), 1)",
|
|
1307
|
+
"assrt.equal((255).bit_length(), 8)",
|
|
1308
|
+
"assrt.equal((256).bit_length(), 9)",
|
|
1309
|
+
"assrt.equal((1023).bit_length(), 10)",
|
|
1310
|
+
"assrt.equal((1024).bit_length(), 11)",
|
|
1311
|
+
"assrt.equal((-1).bit_length(), 1)",
|
|
1312
|
+
"assrt.equal((-5).bit_length(), 3)",
|
|
1313
|
+
"assrt.equal((-255).bit_length(), 8)",
|
|
1314
|
+
].join("\n"));
|
|
1315
|
+
run_js(js);
|
|
1316
|
+
},
|
|
1317
|
+
},
|
|
1318
|
+
|
|
1319
|
+
// ── abc module ───────────────────────────────────────────────────────────
|
|
1320
|
+
|
|
1321
|
+
{
|
|
1322
|
+
name: "bundle_abc_basic",
|
|
1323
|
+
description: "ABC + @abstractmethod raises TypeError when abstract class is instantiated",
|
|
1324
|
+
run: function () {
|
|
1325
|
+
var repl = RS.web_repl();
|
|
1326
|
+
var js = bundle_compile(repl, [
|
|
1327
|
+
"from abc import ABC, abstractmethod",
|
|
1328
|
+
"class Shape(ABC):",
|
|
1329
|
+
" @abstractmethod",
|
|
1330
|
+
" def area(self): pass",
|
|
1331
|
+
"raised = False",
|
|
1332
|
+
"try:",
|
|
1333
|
+
" Shape()",
|
|
1334
|
+
"except TypeError:",
|
|
1335
|
+
" raised = True",
|
|
1336
|
+
"assrt.ok(raised)",
|
|
1337
|
+
"assrt.ok('area' in Shape.__abstractmethods__)",
|
|
1338
|
+
"assrt.equal(len(Shape.__abstractmethods__), 1)",
|
|
1339
|
+
].join("\n"));
|
|
1340
|
+
run_js(js);
|
|
1341
|
+
},
|
|
1342
|
+
},
|
|
1343
|
+
|
|
1344
|
+
{
|
|
1345
|
+
name: "bundle_abc_concrete",
|
|
1346
|
+
description: "Concrete ABC subclass can be instantiated; isinstance sees the full chain",
|
|
1347
|
+
run: function () {
|
|
1348
|
+
var repl = RS.web_repl();
|
|
1349
|
+
var js = bundle_compile(repl, [
|
|
1350
|
+
"from abc import ABC, abstractmethod",
|
|
1351
|
+
"class Shape(ABC):",
|
|
1352
|
+
" @abstractmethod",
|
|
1353
|
+
" def area(self): pass",
|
|
1354
|
+
"class Circle(Shape):",
|
|
1355
|
+
" def __init__(self, r):",
|
|
1356
|
+
" self.r = r",
|
|
1357
|
+
" def area(self):",
|
|
1358
|
+
" return 3.14159 * self.r * self.r",
|
|
1359
|
+
"c = Circle(5)",
|
|
1360
|
+
"assrt.ok(isinstance(c, Circle))",
|
|
1361
|
+
"assrt.ok(isinstance(c, Shape))",
|
|
1362
|
+
"assrt.ok(isinstance(c, ABC))",
|
|
1363
|
+
"assrt.equal(c.r, 5)",
|
|
1364
|
+
"assrt.ok(abs(c.area() - 78.53975) < 0.001)",
|
|
1365
|
+
"assrt.equal(len(Circle.__abstractmethods__), 0)",
|
|
1366
|
+
].join("\n"));
|
|
1367
|
+
run_js(js);
|
|
1368
|
+
},
|
|
1369
|
+
},
|
|
1370
|
+
|
|
1371
|
+
{
|
|
1372
|
+
name: "bundle_abc_register",
|
|
1373
|
+
description: "ABC.register() makes isinstance return True for virtual subclasses",
|
|
1374
|
+
run: function () {
|
|
1375
|
+
var repl = RS.web_repl();
|
|
1376
|
+
var js = bundle_compile(repl, [
|
|
1377
|
+
"from abc import ABC, abstractmethod",
|
|
1378
|
+
"class MyABC(ABC):",
|
|
1379
|
+
" @abstractmethod",
|
|
1380
|
+
" def do_it(self): pass",
|
|
1381
|
+
"class External:",
|
|
1382
|
+
" def do_it(self): return 42",
|
|
1383
|
+
"MyABC.register(External)",
|
|
1384
|
+
"assrt.ok(isinstance(External(), MyABC))",
|
|
1385
|
+
"class Unrelated:",
|
|
1386
|
+
" def do_it(self): return 99",
|
|
1387
|
+
"assrt.ok(not isinstance(Unrelated(), MyABC))",
|
|
1388
|
+
].join("\n"));
|
|
1389
|
+
run_js(js);
|
|
1390
|
+
},
|
|
1391
|
+
},
|
|
1392
|
+
|
|
1393
|
+
{
|
|
1394
|
+
name: "bundle_abc_protocol",
|
|
1395
|
+
description: "@runtime_checkable Protocol enables structural isinstance() checks",
|
|
1396
|
+
run: function () {
|
|
1397
|
+
var repl = RS.web_repl();
|
|
1398
|
+
var js = bundle_compile(repl, [
|
|
1399
|
+
"from abc import Protocol, runtime_checkable",
|
|
1400
|
+
"@runtime_checkable",
|
|
1401
|
+
"class Drawable(Protocol):",
|
|
1402
|
+
" def draw(self): pass",
|
|
1403
|
+
"class Canvas:",
|
|
1404
|
+
" def draw(self):",
|
|
1405
|
+
" return 'painting'",
|
|
1406
|
+
"class NotDrawable:",
|
|
1407
|
+
" def paint(self): pass",
|
|
1408
|
+
"assrt.ok(isinstance(Canvas(), Drawable))",
|
|
1409
|
+
"assrt.ok(not isinstance(NotDrawable(), Drawable))",
|
|
1410
|
+
"assrt.ok(not isinstance(42, Drawable))",
|
|
1411
|
+
"assrt.ok('draw' in Drawable.__protocol_attrs__)",
|
|
1412
|
+
].join("\n"));
|
|
1413
|
+
run_js(js);
|
|
1414
|
+
},
|
|
1415
|
+
},
|
|
1416
|
+
|
|
1417
|
+
// ── eval / exec ───────────────────────────────────────────────────────
|
|
1418
|
+
|
|
1419
|
+
{
|
|
1420
|
+
name: "bundle_zip_strict",
|
|
1421
|
+
description: "zip(strict=True) raises ValueError on length mismatch in the web-repl bundle",
|
|
1422
|
+
run: function () {
|
|
1423
|
+
var repl = RS.web_repl();
|
|
1424
|
+
var js = bundle_compile(repl, [
|
|
1425
|
+
// equal-length: no error
|
|
1426
|
+
"assrt.deepEqual(list(zip([1,2], [3,4], strict=True)), [[1,3],[2,4]])",
|
|
1427
|
+
// zero iterables with strict: empty result, no error
|
|
1428
|
+
"assrt.deepEqual(list(zip(strict=True)), [])",
|
|
1429
|
+
// first longer than second
|
|
1430
|
+
"err1 = False",
|
|
1431
|
+
"try:",
|
|
1432
|
+
" list(zip([1,2], [3], strict=True))",
|
|
1433
|
+
"except ValueError:",
|
|
1434
|
+
" err1 = True",
|
|
1435
|
+
"assrt.ok(err1)",
|
|
1436
|
+
// second longer than first
|
|
1437
|
+
"err2 = False",
|
|
1438
|
+
"try:",
|
|
1439
|
+
" list(zip([1], [3,4], strict=True))",
|
|
1440
|
+
"except ValueError:",
|
|
1441
|
+
" err2 = True",
|
|
1442
|
+
"assrt.ok(err2)",
|
|
1443
|
+
].join("\n"));
|
|
1444
|
+
run_js(js);
|
|
1445
|
+
},
|
|
1446
|
+
},
|
|
1447
|
+
|
|
1448
|
+
{
|
|
1449
|
+
name: "bundle_eval_exec",
|
|
1450
|
+
description: "eval and exec builtins work in the web-repl bundle",
|
|
1451
|
+
run: function () {
|
|
1452
|
+
var repl = RS.web_repl();
|
|
1453
|
+
var js = bundle_compile(repl, [
|
|
1454
|
+
// eval — basic expression
|
|
1455
|
+
"assrt.equal(eval('1 + 2'), 3)",
|
|
1456
|
+
"assrt.equal(eval('10 * 5'), 50)",
|
|
1457
|
+
"assrt.equal(eval('True'), True)",
|
|
1458
|
+
// eval with globals dict
|
|
1459
|
+
"assrt.equal(eval('x + y', {'x': 10, 'y': 5}), 15)",
|
|
1460
|
+
"assrt.equal(eval('a * b', {'a': 3, 'b': 4}), 12)",
|
|
1461
|
+
// eval with locals overriding globals
|
|
1462
|
+
"assrt.equal(eval('x', {'x': 1}, {'x': 99}), 99)",
|
|
1463
|
+
// exec returns None (null in JS)
|
|
1464
|
+
"assrt.equal(exec('1 + 2'), None)",
|
|
1465
|
+
// exec side-effects via mutable object in globals
|
|
1466
|
+
"log = []",
|
|
1467
|
+
"exec(\"log.push('hello')\", {'log': log})",
|
|
1468
|
+
"assrt.equal(log[0], 'hello')",
|
|
1469
|
+
"exec('log.push(1 + 2)', {'log': log})",
|
|
1470
|
+
"assrt.equal(log[1], 3)",
|
|
1471
|
+
// exec with function in globals
|
|
1472
|
+
"out = []",
|
|
1473
|
+
"def _add(a, b): out.push(a + b);",
|
|
1474
|
+
"exec('fn(10, 7)', {'fn': _add, 'out': out})",
|
|
1475
|
+
"assrt.equal(out[0], 17)",
|
|
1476
|
+
].join("\n"));
|
|
1477
|
+
run_js(js);
|
|
1478
|
+
},
|
|
1479
|
+
},
|
|
1480
|
+
|
|
1481
|
+
// ── complex numbers ───────────────────────────────────────────────────────
|
|
1482
|
+
|
|
1483
|
+
{
|
|
1484
|
+
name: "bundle_complex_constructor",
|
|
1485
|
+
description: "complex() constructor and .real/.imag attributes work in bundled baselib",
|
|
1486
|
+
run: function () {
|
|
1487
|
+
var repl = RS.web_repl();
|
|
1488
|
+
var js = bundle_compile(repl, [
|
|
1489
|
+
"c = complex(3, 4)",
|
|
1490
|
+
"assrt.equal(c.real, 3)",
|
|
1491
|
+
"assrt.equal(c.imag, 4)",
|
|
1492
|
+
"c0 = complex()",
|
|
1493
|
+
"assrt.equal(c0.real, 0)",
|
|
1494
|
+
"assrt.equal(c0.imag, 0)",
|
|
1495
|
+
"c1 = complex(5)",
|
|
1496
|
+
"assrt.equal(c1.real, 5)",
|
|
1497
|
+
"assrt.equal(c1.imag, 0)",
|
|
1498
|
+
].join("\n"));
|
|
1499
|
+
run_js(js);
|
|
1500
|
+
},
|
|
1501
|
+
},
|
|
1502
|
+
|
|
1503
|
+
{
|
|
1504
|
+
name: "bundle_complex_j_literal",
|
|
1505
|
+
description: "j-suffix imaginary literal compiles and runs correctly in bundled compiler",
|
|
1506
|
+
run: function () {
|
|
1507
|
+
var repl = RS.web_repl();
|
|
1508
|
+
var js = bundle_compile(repl, [
|
|
1509
|
+
"c = 4j",
|
|
1510
|
+
"assrt.equal(c.real, 0)",
|
|
1511
|
+
"assrt.equal(c.imag, 4)",
|
|
1512
|
+
"c2 = 3+4j",
|
|
1513
|
+
"assrt.equal(c2.real, 3)",
|
|
1514
|
+
"assrt.equal(c2.imag, 4)",
|
|
1515
|
+
"assrt.ok(isinstance(c, complex))",
|
|
1516
|
+
].join("\n"));
|
|
1517
|
+
run_js(js);
|
|
1518
|
+
},
|
|
1519
|
+
},
|
|
1520
|
+
|
|
1521
|
+
{
|
|
1522
|
+
name: "bundle_complex_arithmetic",
|
|
1523
|
+
description: "complex arithmetic (+, -, *, /) and abs() work in bundled baselib",
|
|
1524
|
+
run: function () {
|
|
1525
|
+
var repl = RS.web_repl();
|
|
1526
|
+
var js = bundle_compile(repl, [
|
|
1527
|
+
"a = complex(1, 2)",
|
|
1528
|
+
"b = complex(3, 4)",
|
|
1529
|
+
"assrt.ok(a + b == complex(4, 6))",
|
|
1530
|
+
"assrt.ok(b - a == complex(2, 2))",
|
|
1531
|
+
"assrt.ok(a * b == complex(-5, 10))",
|
|
1532
|
+
"assrt.ok(b / complex(1, 0) == b)",
|
|
1533
|
+
"assrt.equal(abs(complex(3, 4)), 5)",
|
|
1534
|
+
"assrt.ok(-complex(3, 4) == complex(-3, -4))",
|
|
1535
|
+
"assrt.ok(complex(3, 4).conjugate() == complex(3, -4))",
|
|
1536
|
+
].join("\n"));
|
|
1537
|
+
run_js(js);
|
|
1538
|
+
},
|
|
1539
|
+
},
|
|
1540
|
+
|
|
1541
|
+
{
|
|
1542
|
+
name: "bundle_complex_repr",
|
|
1543
|
+
description: "repr() and str() of complex produce Python-style notation in bundled baselib",
|
|
1544
|
+
run: function () {
|
|
1545
|
+
var repl = RS.web_repl();
|
|
1546
|
+
var js = bundle_compile(repl, [
|
|
1547
|
+
"assrt.equal(repr(complex(0, 0)), '0j')",
|
|
1548
|
+
"assrt.equal(repr(complex(1, 0)), '(1+0j)')",
|
|
1549
|
+
"assrt.equal(repr(complex(0, 1)), '1j')",
|
|
1550
|
+
"assrt.equal(repr(complex(3, 4)), '(3+4j)')",
|
|
1551
|
+
"assrt.equal(repr(complex(3, -4)), '(3-4j)')",
|
|
1552
|
+
"assrt.equal(str(complex(3, 4)), '(3+4j)')",
|
|
1553
|
+
].join("\n"));
|
|
1554
|
+
run_js(js);
|
|
1555
|
+
},
|
|
1556
|
+
},
|
|
1557
|
+
|
|
1558
|
+
{
|
|
1559
|
+
name: "bundle_vars_locals_globals",
|
|
1560
|
+
description: "vars(), locals(), globals() work in bundled baselib",
|
|
1561
|
+
run: function () {
|
|
1562
|
+
var repl = RS.web_repl();
|
|
1563
|
+
var js = bundle_compile(repl, [
|
|
1564
|
+
"from __python__ import overload_getitem",
|
|
1565
|
+
"class Point:",
|
|
1566
|
+
" def __init__(self, x, y):",
|
|
1567
|
+
" self.x = x",
|
|
1568
|
+
" self.y = y",
|
|
1569
|
+
"p = Point(7, 9)",
|
|
1570
|
+
"d = vars(p)",
|
|
1571
|
+
"assrt.equal(d['x'], 7)",
|
|
1572
|
+
"assrt.equal(d['y'], 9)",
|
|
1573
|
+
// snapshot — mutation does not affect original
|
|
1574
|
+
"d['x'] = 0",
|
|
1575
|
+
"assrt.equal(p.x, 7)",
|
|
1576
|
+
// vars() with no arg returns empty dict
|
|
1577
|
+
"v = vars()",
|
|
1578
|
+
"assrt.ok(v is not None)",
|
|
1579
|
+
"assrt.ok(isinstance(v, dict))",
|
|
1580
|
+
// locals() returns empty dict
|
|
1581
|
+
"loc = locals()",
|
|
1582
|
+
"assrt.ok(isinstance(loc, dict))",
|
|
1583
|
+
// globals() returns dict
|
|
1584
|
+
"g = globals()",
|
|
1585
|
+
"assrt.ok(isinstance(g, dict))",
|
|
1586
|
+
// vars(obj) returns dict instance
|
|
1587
|
+
"assrt.ok(isinstance(vars(Point(1, 2)), dict))",
|
|
1588
|
+
].join("\n"));
|
|
1589
|
+
run_js(js);
|
|
1590
|
+
},
|
|
1591
|
+
},
|
|
1592
|
+
|
|
1593
|
+
// -------------------------------------------------------------------------
|
|
1594
|
+
// Context-persistence tests: compile WITHOUT keep_baselib, run in the repl
|
|
1595
|
+
// vm context. These catch "ReferenceError: ρσ_X is not defined" bugs that
|
|
1596
|
+
// only surface when baselib symbols are `let`-declared (not `var` / function
|
|
1597
|
+
// declarations) and therefore don't persist between vm.runInContext calls.
|
|
1598
|
+
// -------------------------------------------------------------------------
|
|
1599
|
+
|
|
1600
|
+
{
|
|
1601
|
+
name: "repl_in_operator_persistence",
|
|
1602
|
+
description: "ρσ_in accessible after baselib init — 'in' operator regression in web-repl context",
|
|
1603
|
+
run: function () {
|
|
1604
|
+
var repl = RS.web_repl();
|
|
1605
|
+
// Compile WITHOUT keep_baselib so the output does not include the
|
|
1606
|
+
// baselib — the compiled code references ρσ_in from the persistent ctx.
|
|
1607
|
+
var js = repl.compile([
|
|
1608
|
+
"assert 'x' in ['x', 'y', 'z'], \"'x' in list should be True\"",
|
|
1609
|
+
"assert 'a' not in ['x', 'y', 'z'], \"'a' not in list should be True\"",
|
|
1610
|
+
"assert 2 in {1: 'one', 2: 'two'}, '2 in dict should be True'",
|
|
1611
|
+
// Original failing case: 'Math' in globals() must not throw
|
|
1612
|
+
"g = globals()",
|
|
1613
|
+
"result = 'Math' in g", // just must not throw ReferenceError
|
|
1614
|
+
].join("\n"), {export_main: true, tree_shake: false});
|
|
1615
|
+
repl.runjs(js);
|
|
1616
|
+
},
|
|
1617
|
+
},
|
|
1618
|
+
|
|
1619
|
+
{
|
|
1620
|
+
name: "repl_kwargs_persistence",
|
|
1621
|
+
description: "ρσ_desugar_kwargs accessible after baselib init — **kwargs in web-repl context",
|
|
1622
|
+
run: function () {
|
|
1623
|
+
var repl = RS.web_repl();
|
|
1624
|
+
var js = repl.compile([
|
|
1625
|
+
"def greet(name, greeting='Hello'):",
|
|
1626
|
+
" return greeting + ', ' + name + '!'",
|
|
1627
|
+
"result = greet('World', greeting='Hi')",
|
|
1628
|
+
"assert result == 'Hi, World!', 'kwargs call should produce Hi, World!'",
|
|
1629
|
+
].join("\n"), {export_main: true, tree_shake: false});
|
|
1630
|
+
repl.runjs(js);
|
|
1631
|
+
},
|
|
1632
|
+
},
|
|
1633
|
+
|
|
1634
|
+
{
|
|
1635
|
+
name: "repl_nameerror_persistence",
|
|
1636
|
+
description: "NameError accessible after baselib init — except NameError in web-repl context",
|
|
1637
|
+
run: function () {
|
|
1638
|
+
var repl = RS.web_repl();
|
|
1639
|
+
var js = repl.compile([
|
|
1640
|
+
"caught = False",
|
|
1641
|
+
"try:",
|
|
1642
|
+
" raise NameError('test error')",
|
|
1643
|
+
"except NameError:",
|
|
1644
|
+
" caught = True",
|
|
1645
|
+
"assert caught, 'NameError should be catchable in except clause'",
|
|
1646
|
+
].join("\n"), {export_main: true, tree_shake: false});
|
|
1647
|
+
repl.runjs(js);
|
|
1648
|
+
},
|
|
1649
|
+
},
|
|
1650
|
+
|
|
1651
|
+
{
|
|
1652
|
+
name: "repl_getattr_persistence",
|
|
1653
|
+
description: "ρσ_JS_Proxy/ρσ_attr_proxy_handler accessible after baselib init — __getattr__ in web-repl context",
|
|
1654
|
+
run: function () {
|
|
1655
|
+
var repl = RS.web_repl();
|
|
1656
|
+
var js = repl.compile([
|
|
1657
|
+
"class Magic:",
|
|
1658
|
+
" def __getattr__(self, name):",
|
|
1659
|
+
" return name + '_value'",
|
|
1660
|
+
"m = Magic()",
|
|
1661
|
+
"assert m.foo == 'foo_value', '__getattr__ should return foo_value'",
|
|
1662
|
+
"assert m.bar == 'bar_value', '__getattr__ should return bar_value'",
|
|
1663
|
+
].join("\n"), {export_main: true, tree_shake: false});
|
|
1664
|
+
repl.runjs(js);
|
|
1665
|
+
},
|
|
1666
|
+
},
|
|
1667
|
+
|
|
1668
|
+
// ── datetime stdlib ───────────────────────────────────────────────────────
|
|
1669
|
+
|
|
1670
|
+
{
|
|
1671
|
+
name: "bundle_datetime_basic",
|
|
1672
|
+
description: "datetime stdlib: date/time/datetime/timedelta construct and report attributes correctly",
|
|
1673
|
+
run: function () {
|
|
1674
|
+
var repl = RS.web_repl();
|
|
1675
|
+
var js = bundle_compile(repl, [
|
|
1676
|
+
"from datetime import date, time, datetime, timedelta, MINYEAR, MAXYEAR",
|
|
1677
|
+
"assrt.equal(MINYEAR, 1)",
|
|
1678
|
+
"assrt.equal(MAXYEAR, 9999)",
|
|
1679
|
+
// date
|
|
1680
|
+
"d = date(2024, 6, 15)",
|
|
1681
|
+
"assrt.equal(d.year, 2024)",
|
|
1682
|
+
"assrt.equal(d.month, 6)",
|
|
1683
|
+
"assrt.equal(d.day, 15)",
|
|
1684
|
+
"assrt.equal(str(d), '2024-06-15')",
|
|
1685
|
+
// time
|
|
1686
|
+
"t = time(14, 30, 5)",
|
|
1687
|
+
"assrt.equal(t.hour, 14)",
|
|
1688
|
+
"assrt.equal(t.minute, 30)",
|
|
1689
|
+
"assrt.equal(str(t), '14:30:05')",
|
|
1690
|
+
// datetime
|
|
1691
|
+
"dt = datetime(2024, 6, 15, 14, 30, 5)",
|
|
1692
|
+
"assrt.equal(dt.year, 2024)",
|
|
1693
|
+
"assrt.equal(dt.hour, 14)",
|
|
1694
|
+
"assrt.equal(str(dt), '2024-06-15 14:30:05')",
|
|
1695
|
+
// timedelta
|
|
1696
|
+
"td = timedelta(1)",
|
|
1697
|
+
"assrt.equal(td.days, 1)",
|
|
1698
|
+
"assrt.equal(td.total_seconds(), 86400)",
|
|
1699
|
+
"assrt.equal(str(td), '1 day, 0:00:00')",
|
|
1700
|
+
].join("\n"));
|
|
1701
|
+
run_js(js);
|
|
1702
|
+
},
|
|
1703
|
+
},
|
|
1704
|
+
|
|
1705
|
+
{
|
|
1706
|
+
name: "bundle_datetime_arithmetic",
|
|
1707
|
+
description: "datetime stdlib: date/datetime arithmetic via overload_operators",
|
|
1708
|
+
run: function () {
|
|
1709
|
+
var repl = RS.web_repl();
|
|
1710
|
+
var js = bundle_compile(repl, [
|
|
1711
|
+
"from __python__ import overload_operators",
|
|
1712
|
+
"from datetime import date, datetime, timedelta",
|
|
1713
|
+
// date + timedelta
|
|
1714
|
+
"d1 = date(2024, 1, 31)",
|
|
1715
|
+
"d2 = d1 + timedelta(1)",
|
|
1716
|
+
"assrt.equal(d2.month, 2)",
|
|
1717
|
+
"assrt.equal(d2.day, 1)",
|
|
1718
|
+
// date - date
|
|
1719
|
+
"delta = date(2024, 6, 15) - date(2024, 1, 1)",
|
|
1720
|
+
"assrt.ok(delta.days > 0)",
|
|
1721
|
+
// datetime + timedelta
|
|
1722
|
+
"dt1 = datetime(2024, 1, 15, 23, 30, 0)",
|
|
1723
|
+
"dt2 = dt1 + timedelta(0, 3600)",
|
|
1724
|
+
"assrt.equal(dt2.day, 16)",
|
|
1725
|
+
"assrt.equal(dt2.hour, 0)",
|
|
1726
|
+
"assrt.equal(dt2.minute, 30)",
|
|
1727
|
+
// datetime - datetime
|
|
1728
|
+
"diff = datetime(2024, 1, 10, 0, 0) - datetime(2024, 1, 5, 0, 0)",
|
|
1729
|
+
"assrt.equal(diff.days, 5)",
|
|
1730
|
+
// timedelta + timedelta
|
|
1731
|
+
"td = timedelta(1) + timedelta(0, 3600)",
|
|
1732
|
+
"assrt.equal(td.days, 1)",
|
|
1733
|
+
"assrt.equal(td.seconds, 3600)",
|
|
1734
|
+
].join("\n"));
|
|
1735
|
+
run_js(js);
|
|
1736
|
+
},
|
|
1737
|
+
},
|
|
1738
|
+
|
|
1739
|
+
{
|
|
1740
|
+
name: "bundle_datetime_fromisoformat",
|
|
1741
|
+
description: "datetime stdlib: fromisoformat parses ISO strings correctly",
|
|
1742
|
+
run: function () {
|
|
1743
|
+
var repl = RS.web_repl();
|
|
1744
|
+
var js = bundle_compile(repl, [
|
|
1745
|
+
"from datetime import date, datetime",
|
|
1746
|
+
"d = date.fromisoformat('2024-06-15')",
|
|
1747
|
+
"assrt.equal(d.year, 2024)",
|
|
1748
|
+
"assrt.equal(d.month, 6)",
|
|
1749
|
+
"assrt.equal(d.day, 15)",
|
|
1750
|
+
"dt = datetime.fromisoformat('2024-06-15T09:30:45.123456')",
|
|
1751
|
+
"assrt.equal(dt.hour, 9)",
|
|
1752
|
+
"assrt.equal(dt.minute, 30)",
|
|
1753
|
+
"assrt.equal(dt.second, 45)",
|
|
1754
|
+
"assrt.equal(dt.microsecond, 123456)",
|
|
1755
|
+
].join("\n"));
|
|
1756
|
+
run_js(js);
|
|
1757
|
+
},
|
|
1758
|
+
},
|
|
1759
|
+
|
|
1760
|
+
{
|
|
1761
|
+
name: "bundle_datetime_strftime",
|
|
1762
|
+
description: "datetime stdlib: strftime formats date and datetime correctly",
|
|
1763
|
+
run: function () {
|
|
1764
|
+
var repl = RS.web_repl();
|
|
1765
|
+
var js = bundle_compile(repl, [
|
|
1766
|
+
"from datetime import date, datetime",
|
|
1767
|
+
"d = date(2024, 1, 15)",
|
|
1768
|
+
"assrt.equal(d.strftime('%Y-%m-%d'), '2024-01-15')",
|
|
1769
|
+
"assrt.equal(d.strftime('%B %d, %Y'), 'January 15, 2024')",
|
|
1770
|
+
"assrt.equal(d.strftime('%a'), 'Mon')",
|
|
1771
|
+
"assrt.equal(d.strftime('%%'), '%')",
|
|
1772
|
+
"dt = datetime(2024, 1, 15, 14, 5, 9)",
|
|
1773
|
+
"assrt.equal(dt.strftime('%H:%M:%S'), '14:05:09')",
|
|
1774
|
+
"assrt.equal(dt.strftime('%I:%M %p'), '02:05 PM')",
|
|
1775
|
+
].join("\n"));
|
|
1776
|
+
run_js(js);
|
|
1777
|
+
},
|
|
1778
|
+
},
|
|
1779
|
+
|
|
1780
|
+
// ── contextlib ────────────────────────────────────────────────────────
|
|
1781
|
+
|
|
1782
|
+
{
|
|
1783
|
+
name: "bundle_contextlib_suppress",
|
|
1784
|
+
description: "contextlib.suppress silences listed exceptions in the web-repl bundle",
|
|
1785
|
+
run: function () {
|
|
1786
|
+
var repl = RS.web_repl();
|
|
1787
|
+
var js = bundle_compile(repl, [
|
|
1788
|
+
"from contextlib import suppress",
|
|
1789
|
+
// matched exception is suppressed
|
|
1790
|
+
"reached = False",
|
|
1791
|
+
"with suppress(ValueError):",
|
|
1792
|
+
" raise ValueError('ignored')",
|
|
1793
|
+
"reached = True",
|
|
1794
|
+
"assrt.equal(reached, True)",
|
|
1795
|
+
// non-matching exception propagates
|
|
1796
|
+
"propagated = False",
|
|
1797
|
+
"try:",
|
|
1798
|
+
" with suppress(KeyError):",
|
|
1799
|
+
" raise ValueError('not suppressed')",
|
|
1800
|
+
"except ValueError:",
|
|
1801
|
+
" propagated = True",
|
|
1802
|
+
"assrt.equal(propagated, True)",
|
|
1803
|
+
// suppress multiple types
|
|
1804
|
+
"hit = False",
|
|
1805
|
+
"with suppress(TypeError, KeyError):",
|
|
1806
|
+
" raise KeyError('one of two')",
|
|
1807
|
+
"hit = True",
|
|
1808
|
+
"assrt.equal(hit, True)",
|
|
1809
|
+
].join("\n"));
|
|
1810
|
+
run_js(js);
|
|
1811
|
+
},
|
|
1812
|
+
},
|
|
1813
|
+
|
|
1814
|
+
{
|
|
1815
|
+
name: "bundle_contextlib_closing",
|
|
1816
|
+
description: "contextlib.closing calls close() on exit in the web-repl bundle",
|
|
1817
|
+
run: function () {
|
|
1818
|
+
var repl = RS.web_repl();
|
|
1819
|
+
var js = bundle_compile(repl, [
|
|
1820
|
+
"from contextlib import closing",
|
|
1821
|
+
"class _Obj:",
|
|
1822
|
+
" def __init__(self):",
|
|
1823
|
+
" self.closed = False",
|
|
1824
|
+
" def close(self):",
|
|
1825
|
+
" self.closed = True",
|
|
1826
|
+
"obj = _Obj()",
|
|
1827
|
+
"with closing(obj) as c:",
|
|
1828
|
+
" assrt.equal(c, obj)",
|
|
1829
|
+
" assrt.equal(c.closed, False)",
|
|
1830
|
+
"assrt.equal(obj.closed, True)",
|
|
1831
|
+
// close() is called even on exception
|
|
1832
|
+
"obj2 = _Obj()",
|
|
1833
|
+
"try:",
|
|
1834
|
+
" with closing(obj2):",
|
|
1835
|
+
" raise ValueError('test')",
|
|
1836
|
+
"except ValueError:",
|
|
1837
|
+
" pass",
|
|
1838
|
+
"assrt.equal(obj2.closed, True)",
|
|
1839
|
+
].join("\n"));
|
|
1840
|
+
run_js(js);
|
|
1841
|
+
},
|
|
1842
|
+
},
|
|
1843
|
+
|
|
1844
|
+
// ── json stdlib ──────────────────────────────────────────────────────────
|
|
1845
|
+
|
|
1846
|
+
{
|
|
1847
|
+
name: "bundle_json_dumps_loads_basic",
|
|
1848
|
+
description: "json stdlib: dumps/loads round-trip for basic types and collections",
|
|
1849
|
+
run: function () {
|
|
1850
|
+
var repl = RS.web_repl();
|
|
1851
|
+
var js = bundle_compile(repl, [
|
|
1852
|
+
"from json import dumps, loads",
|
|
1853
|
+
// basic scalars
|
|
1854
|
+
"assrt.equal(dumps(None), 'null')",
|
|
1855
|
+
"assrt.equal(dumps(True), 'true')",
|
|
1856
|
+
"assrt.equal(dumps(False), 'false')",
|
|
1857
|
+
"assrt.equal(dumps(42), '42')",
|
|
1858
|
+
"assrt.equal(dumps('hello'), '\"hello\"')",
|
|
1859
|
+
// list round-trip
|
|
1860
|
+
"assrt.deepEqual(loads(dumps([1, 2, 3])), [1, 2, 3])",
|
|
1861
|
+
// object round-trip
|
|
1862
|
+
"obj = loads(dumps({'a': 1, 'b': 2}))",
|
|
1863
|
+
"assrt.equal(obj.a, 1)",
|
|
1864
|
+
"assrt.equal(obj.b, 2)",
|
|
1865
|
+
].join("\n"));
|
|
1866
|
+
run_js(js);
|
|
1867
|
+
},
|
|
1868
|
+
},
|
|
1869
|
+
|
|
1870
|
+
{
|
|
1871
|
+
name: "bundle_json_dumps_options",
|
|
1872
|
+
description: "json stdlib: dumps respects sort_keys, indent, and separators",
|
|
1873
|
+
run: function () {
|
|
1874
|
+
var repl = RS.web_repl();
|
|
1875
|
+
var js = bundle_compile(repl, [
|
|
1876
|
+
"from json import dumps",
|
|
1877
|
+
// sort_keys
|
|
1878
|
+
"assrt.equal(dumps({'b': 2, 'a': 1}, sort_keys=True), '{\"a\":1,\"b\":2}')",
|
|
1879
|
+
// indent produces newlines
|
|
1880
|
+
"pretty = dumps([1, 2], indent=2)",
|
|
1881
|
+
"assrt.ok(pretty.indexOf('\\n') >= 0)",
|
|
1882
|
+
// compact separators
|
|
1883
|
+
"assrt.equal(dumps({'a': 1}, sort_keys=True, separators=(',', ':')), '{\"a\":1}')",
|
|
1884
|
+
].join("\n"));
|
|
1885
|
+
run_js(js);
|
|
1886
|
+
},
|
|
1887
|
+
},
|
|
1888
|
+
|
|
1889
|
+
{
|
|
1890
|
+
name: "bundle_json_loads_hooks",
|
|
1891
|
+
description: "json stdlib: loads supports object_hook and JSONDecodeError",
|
|
1892
|
+
run: function () {
|
|
1893
|
+
var repl = RS.web_repl();
|
|
1894
|
+
var js = bundle_compile(repl, [
|
|
1895
|
+
"from json import loads, JSONDecodeError",
|
|
1896
|
+
// object_hook
|
|
1897
|
+
"hits = []",
|
|
1898
|
+
"def hook(d):",
|
|
1899
|
+
" hits.push(1)",
|
|
1900
|
+
" return d",
|
|
1901
|
+
"loads('{\"x\": 1}', object_hook=hook)",
|
|
1902
|
+
"assrt.equal(hits.length, 1)",
|
|
1903
|
+
// JSONDecodeError on bad JSON
|
|
1904
|
+
"got_err = False",
|
|
1905
|
+
"try:",
|
|
1906
|
+
" loads('{bad json}')",
|
|
1907
|
+
"except JSONDecodeError:",
|
|
1908
|
+
" got_err = True",
|
|
1909
|
+
"assrt.ok(got_err)",
|
|
1910
|
+
].join("\n"));
|
|
1911
|
+
run_js(js);
|
|
1912
|
+
},
|
|
1913
|
+
},
|
|
1914
|
+
|
|
1915
|
+
{
|
|
1916
|
+
name: "bundle_json_dump_load_file",
|
|
1917
|
+
description: "json stdlib: dump/load work with file-like objects",
|
|
1918
|
+
run: function () {
|
|
1919
|
+
var repl = RS.web_repl();
|
|
1920
|
+
var js = bundle_compile(repl, [
|
|
1921
|
+
"from json import dump, load",
|
|
1922
|
+
"class _SIO:",
|
|
1923
|
+
" def __init__(self): self._buf = ''",
|
|
1924
|
+
" def write(self, s): self._buf += s",
|
|
1925
|
+
" def read(self): return self._buf",
|
|
1926
|
+
"sio = _SIO()",
|
|
1927
|
+
"dump({'key': 'value', 'n': 7}, sio)",
|
|
1928
|
+
"result = load(sio)",
|
|
1929
|
+
"assrt.equal(result.key, 'value')",
|
|
1930
|
+
"assrt.equal(result.n, 7)",
|
|
1931
|
+
].join("\n"));
|
|
1932
|
+
run_js(js);
|
|
1933
|
+
},
|
|
1934
|
+
},
|
|
1935
|
+
|
|
1936
|
+
{
|
|
1937
|
+
name: "bundle_contextlib_nullcontext",
|
|
1938
|
+
description: "contextlib.nullcontext works as a no-op context manager in the web-repl bundle",
|
|
1939
|
+
run: function () {
|
|
1940
|
+
var repl = RS.web_repl();
|
|
1941
|
+
var js = bundle_compile(repl, [
|
|
1942
|
+
"from contextlib import nullcontext",
|
|
1943
|
+
"with nullcontext() as v:",
|
|
1944
|
+
" assrt.equal(v, None)",
|
|
1945
|
+
"with nullcontext(42) as v:",
|
|
1946
|
+
" assrt.equal(v, 42)",
|
|
1947
|
+
"with nullcontext('hi') as v:",
|
|
1948
|
+
" assrt.equal(v, 'hi')",
|
|
1949
|
+
].join("\n"));
|
|
1950
|
+
run_js(js);
|
|
1951
|
+
},
|
|
1952
|
+
},
|
|
1953
|
+
|
|
1954
|
+
{
|
|
1955
|
+
name: "bundle_contextlib_contextmanager",
|
|
1956
|
+
description: "contextlib.contextmanager creates generator-based context managers in the web-repl bundle",
|
|
1957
|
+
run: function () {
|
|
1958
|
+
var repl = RS.web_repl();
|
|
1959
|
+
var js = bundle_compile(repl, [
|
|
1960
|
+
"from contextlib import contextmanager",
|
|
1961
|
+
"log = []",
|
|
1962
|
+
"@contextmanager",
|
|
1963
|
+
"def _cm(name):",
|
|
1964
|
+
" log.append('enter:' + name)",
|
|
1965
|
+
" try:",
|
|
1966
|
+
" yield name.upper()",
|
|
1967
|
+
" finally:",
|
|
1968
|
+
" log.append('exit:' + name)",
|
|
1969
|
+
"with _cm('hello') as v:",
|
|
1970
|
+
" assrt.equal(v, 'HELLO')",
|
|
1971
|
+
" log.append('body')",
|
|
1972
|
+
"assrt.deepEqual(log, ['enter:hello', 'body', 'exit:hello'])",
|
|
1973
|
+
// exception propagates through finally
|
|
1974
|
+
"log2 = []",
|
|
1975
|
+
"@contextmanager",
|
|
1976
|
+
"def _cm2():",
|
|
1977
|
+
" try:",
|
|
1978
|
+
" yield",
|
|
1979
|
+
" finally:",
|
|
1980
|
+
" log2.append('cleanup')",
|
|
1981
|
+
"caught = False",
|
|
1982
|
+
"try:",
|
|
1983
|
+
" with _cm2():",
|
|
1984
|
+
" raise ValueError('oops')",
|
|
1985
|
+
"except ValueError:",
|
|
1986
|
+
" caught = True",
|
|
1987
|
+
"assrt.equal(caught, True)",
|
|
1988
|
+
"assrt.deepEqual(log2, ['cleanup'])",
|
|
1989
|
+
// generator can suppress exceptions
|
|
1990
|
+
"@contextmanager",
|
|
1991
|
+
"def _cm3():",
|
|
1992
|
+
" try:",
|
|
1993
|
+
" yield",
|
|
1994
|
+
" except ValueError:",
|
|
1995
|
+
" pass",
|
|
1996
|
+
"after = False",
|
|
1997
|
+
"with _cm3():",
|
|
1998
|
+
" raise ValueError('suppressed')",
|
|
1999
|
+
"after = True",
|
|
2000
|
+
"assrt.equal(after, True)",
|
|
2001
|
+
].join("\n"));
|
|
2002
|
+
run_js(js);
|
|
2003
|
+
},
|
|
2004
|
+
},
|
|
2005
|
+
|
|
2006
|
+
{
|
|
2007
|
+
name: "bundle_contextlib_exitstack",
|
|
2008
|
+
description: "contextlib.ExitStack enters/exits context managers in LIFO order in the web-repl bundle",
|
|
2009
|
+
run: function () {
|
|
2010
|
+
var repl = RS.web_repl();
|
|
2011
|
+
var js = bundle_compile(repl, [
|
|
2012
|
+
"from contextlib import ExitStack",
|
|
2013
|
+
"exit_order = []",
|
|
2014
|
+
"class _CM:",
|
|
2015
|
+
" def __init__(self, label):",
|
|
2016
|
+
" self.label = label",
|
|
2017
|
+
" def __enter__(self):",
|
|
2018
|
+
" return self.label",
|
|
2019
|
+
" def __exit__(self, exc_type=None, exc_val=None, exc_tb=None):",
|
|
2020
|
+
" exit_order.append(self.label)",
|
|
2021
|
+
" return False",
|
|
2022
|
+
"with ExitStack() as stack:",
|
|
2023
|
+
" a = stack.enter_context(_CM('a'))",
|
|
2024
|
+
" b = stack.enter_context(_CM('b'))",
|
|
2025
|
+
" c = stack.enter_context(_CM('c'))",
|
|
2026
|
+
" assrt.equal(a, 'a')",
|
|
2027
|
+
" assrt.equal(b, 'b')",
|
|
2028
|
+
" assrt.equal(c, 'c')",
|
|
2029
|
+
"assrt.deepEqual(exit_order, ['c', 'b', 'a'])",
|
|
2030
|
+
// callback
|
|
2031
|
+
"cb_log = []",
|
|
2032
|
+
"def _append_done():",
|
|
2033
|
+
" cb_log.append('done')",
|
|
2034
|
+
"with ExitStack() as stack:",
|
|
2035
|
+
" stack.callback(_append_done)",
|
|
2036
|
+
"assrt.deepEqual(cb_log, ['done'])",
|
|
2037
|
+
// close() outside with
|
|
2038
|
+
"close_log = []",
|
|
2039
|
+
"class _CM2:",
|
|
2040
|
+
" def __enter__(self): return self",
|
|
2041
|
+
" def __exit__(self, exc_type=None, exc_val=None, exc_tb=None):",
|
|
2042
|
+
" close_log.append('closed')",
|
|
2043
|
+
" return False",
|
|
2044
|
+
"s2 = ExitStack()",
|
|
2045
|
+
"s2.enter_context(_CM2())",
|
|
2046
|
+
"s2.close()",
|
|
2047
|
+
"assrt.deepEqual(close_log, ['closed'])",
|
|
2048
|
+
].join("\n"));
|
|
2049
|
+
run_js(js);
|
|
2050
|
+
},
|
|
2051
|
+
},
|
|
2052
|
+
|
|
2053
|
+
{
|
|
2054
|
+
name: "bundle_io_stringio_basic",
|
|
2055
|
+
description: "io.StringIO: write, seek, read, getvalue work in the web-repl bundle",
|
|
2056
|
+
run: function () {
|
|
2057
|
+
var repl = RS.web_repl();
|
|
2058
|
+
var js = bundle_compile(repl, [
|
|
2059
|
+
"from io import StringIO",
|
|
2060
|
+
// basic write + seek + read
|
|
2061
|
+
"sio = StringIO()",
|
|
2062
|
+
"sio.write('hello')",
|
|
2063
|
+
"sio.write(' world')",
|
|
2064
|
+
"sio.seek(0)",
|
|
2065
|
+
"assrt.equal(sio.read(), 'hello world')",
|
|
2066
|
+
// getvalue independent of position
|
|
2067
|
+
"sio.seek(3)",
|
|
2068
|
+
"assrt.equal(sio.getvalue(), 'hello world')",
|
|
2069
|
+
// tell / seek with whence
|
|
2070
|
+
"sio.seek(0)",
|
|
2071
|
+
"assrt.equal(sio.tell(), 0)",
|
|
2072
|
+
"sio.seek(0, 2)",
|
|
2073
|
+
"assrt.equal(sio.tell(), 11)",
|
|
2074
|
+
// readline
|
|
2075
|
+
"ml = StringIO('line1\\nline2\\nline3')",
|
|
2076
|
+
"assrt.equal(ml.readline(), 'line1\\n')",
|
|
2077
|
+
"assrt.equal(ml.readline(), 'line2\\n')",
|
|
2078
|
+
"assrt.equal(ml.readline(), 'line3')",
|
|
2079
|
+
"assrt.equal(ml.readline(), '')",
|
|
2080
|
+
// truncate
|
|
2081
|
+
"tr = StringIO('hello world')",
|
|
2082
|
+
"tr.truncate(5)",
|
|
2083
|
+
"assrt.equal(tr.getvalue(), 'hello')",
|
|
2084
|
+
].join("\n"));
|
|
2085
|
+
run_js(js);
|
|
2086
|
+
},
|
|
2087
|
+
},
|
|
2088
|
+
|
|
2089
|
+
{
|
|
2090
|
+
name: "bundle_io_stringio_context",
|
|
2091
|
+
description: "io.StringIO: context manager and closed attribute work in the web-repl bundle",
|
|
2092
|
+
run: function () {
|
|
2093
|
+
var repl = RS.web_repl();
|
|
2094
|
+
var js = bundle_compile(repl, [
|
|
2095
|
+
"from io import StringIO",
|
|
2096
|
+
// context manager
|
|
2097
|
+
"result = ''",
|
|
2098
|
+
"cm = StringIO('context text')",
|
|
2099
|
+
"with cm as f:",
|
|
2100
|
+
" result = f.read()",
|
|
2101
|
+
"assrt.equal(result, 'context text')",
|
|
2102
|
+
"assrt.equal(cm.closed, True)",
|
|
2103
|
+
// ValueError on read after close
|
|
2104
|
+
"got_err = False",
|
|
2105
|
+
"try:",
|
|
2106
|
+
" cm.read()",
|
|
2107
|
+
"except ValueError:",
|
|
2108
|
+
" got_err = True",
|
|
2109
|
+
"assrt.ok(got_err)",
|
|
2110
|
+
// readable / writable / seekable
|
|
2111
|
+
"s2 = StringIO()",
|
|
2112
|
+
"assrt.ok(s2.readable())",
|
|
2113
|
+
"assrt.ok(s2.writable())",
|
|
2114
|
+
"assrt.ok(s2.seekable())",
|
|
2115
|
+
// init with value
|
|
2116
|
+
"s3 = StringIO('preset')",
|
|
2117
|
+
"assrt.equal(s3.read(), 'preset')",
|
|
2118
|
+
].join("\n"));
|
|
2119
|
+
run_js(js);
|
|
2120
|
+
},
|
|
2121
|
+
},
|
|
2122
|
+
|
|
2123
|
+
{
|
|
2124
|
+
name: "bundle_io_bytesio_basic",
|
|
2125
|
+
description: "io.BytesIO: write, seek, read, getvalue work in the web-repl bundle",
|
|
2126
|
+
run: function () {
|
|
2127
|
+
var repl = RS.web_repl();
|
|
2128
|
+
var js = bundle_compile(repl, [
|
|
2129
|
+
"from io import BytesIO",
|
|
2130
|
+
// basic write + seek + read
|
|
2131
|
+
"bio = BytesIO()",
|
|
2132
|
+
"bio.write(bytes([1, 2, 3]))",
|
|
2133
|
+
"bio.write(bytes([4, 5]))",
|
|
2134
|
+
"bio.seek(0)",
|
|
2135
|
+
"data = bio.read()",
|
|
2136
|
+
"assrt.equal(len(data), 5)",
|
|
2137
|
+
"assrt.equal(data[0], 1)",
|
|
2138
|
+
"assrt.equal(data[4], 5)",
|
|
2139
|
+
// getvalue
|
|
2140
|
+
"bio.seek(2)",
|
|
2141
|
+
"assrt.equal(len(bio.getvalue()), 5)",
|
|
2142
|
+
// partial read
|
|
2143
|
+
"bio.seek(0)",
|
|
2144
|
+
"chunk = bio.read(3)",
|
|
2145
|
+
"assrt.equal(len(chunk), 3)",
|
|
2146
|
+
"assrt.equal(chunk[2], 3)",
|
|
2147
|
+
// init from bytes
|
|
2148
|
+
"bio2 = BytesIO(bytes([10, 20, 30]))",
|
|
2149
|
+
"bio2.seek(0)",
|
|
2150
|
+
"assrt.equal(bio2.read()[1], 20)",
|
|
2151
|
+
// seek whence=2
|
|
2152
|
+
"bio2.seek(0)",
|
|
2153
|
+
"bio2.seek(-1, 2)",
|
|
2154
|
+
"assrt.equal(bio2.tell(), 2)",
|
|
2155
|
+
// truncate
|
|
2156
|
+
"bt = BytesIO(bytes([1, 2, 3, 4, 5]))",
|
|
2157
|
+
"bt.truncate(3)",
|
|
2158
|
+
"assrt.equal(len(bt.getvalue()), 3)",
|
|
2159
|
+
].join("\n"));
|
|
2160
|
+
run_js(js);
|
|
2161
|
+
},
|
|
2162
|
+
},
|
|
2163
|
+
|
|
2164
|
+
{
|
|
2165
|
+
name: "bundle_io_bytesio_context",
|
|
2166
|
+
description: "io.BytesIO: context manager, readline, and writelines in the web-repl bundle",
|
|
2167
|
+
run: function () {
|
|
2168
|
+
var repl = RS.web_repl();
|
|
2169
|
+
var js = bundle_compile(repl, [
|
|
2170
|
+
"from io import BytesIO",
|
|
2171
|
+
// context manager
|
|
2172
|
+
"bcm = BytesIO(bytes([7, 8, 9]))",
|
|
2173
|
+
"with bcm as bf:",
|
|
2174
|
+
" bf.seek(0)",
|
|
2175
|
+
" got = bf.read()",
|
|
2176
|
+
"assrt.equal(got[0], 7)",
|
|
2177
|
+
"assrt.equal(bcm.closed, True)",
|
|
2178
|
+
// ValueError on closed
|
|
2179
|
+
"berr = False",
|
|
2180
|
+
"try:",
|
|
2181
|
+
" bcm.read()",
|
|
2182
|
+
"except ValueError:",
|
|
2183
|
+
" berr = True",
|
|
2184
|
+
"assrt.ok(berr)",
|
|
2185
|
+
// writelines
|
|
2186
|
+
"bwl = BytesIO()",
|
|
2187
|
+
"bwl.writelines([bytes([1, 2]), bytes([3, 4])])",
|
|
2188
|
+
"assrt.equal(len(bwl.getvalue()), 4)",
|
|
2189
|
+
// readline
|
|
2190
|
+
"brl = BytesIO(bytes([65, 66, 10, 67, 68]))",
|
|
2191
|
+
"first = brl.readline()",
|
|
2192
|
+
"assrt.equal(len(first), 3)",
|
|
2193
|
+
"assrt.equal(first[2], 10)",
|
|
2194
|
+
"second = brl.readline()",
|
|
2195
|
+
"assrt.equal(len(second), 2)",
|
|
2196
|
+
].join("\n"));
|
|
2197
|
+
run_js(js);
|
|
2198
|
+
},
|
|
2199
|
+
},
|
|
2200
|
+
|
|
2201
|
+
{
|
|
2202
|
+
name: "repl_exists_persistence",
|
|
2203
|
+
description: "ρσ_exists accessible after baselib init — existential operator on non-SymbolRef in web-repl context",
|
|
2204
|
+
run: function () {
|
|
2205
|
+
var repl = RS.web_repl();
|
|
2206
|
+
var js = repl.compile([
|
|
2207
|
+
// fns['key'] is a subscript (not SymbolRef) — emits ρσ_exists.c(fns['key'])()
|
|
2208
|
+
"def get_val(): return 42",
|
|
2209
|
+
"fns = {'key': get_val}",
|
|
2210
|
+
"result = fns['key']?()",
|
|
2211
|
+
"assert result == 42, 'existential call via subscript should return 42'",
|
|
2212
|
+
// None value: dict lookup miss returns None, existential function call returns undefined
|
|
2213
|
+
"missing = fns.get('nokey')",
|
|
2214
|
+
"result2 = missing?()",
|
|
2215
|
+
"assert result2 is undefined, 'existential call on None should return undefined'",
|
|
2216
|
+
].join("\n"), {export_main: true, tree_shake: false});
|
|
2217
|
+
repl.runjs(js);
|
|
2218
|
+
},
|
|
2219
|
+
},
|
|
2220
|
+
|
|
885
2221
|
];
|
|
886
2222
|
|
|
887
2223
|
// ---------------------------------------------------------------------------
|