rapydscript-ns 0.8.3 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/README.md +1351 -141
- package/TODO.md +12 -6
- package/language-service/index.js +184 -26
- package/package.json +1 -1
- package/release/baselib-plain-pretty.js +5895 -1928
- package/release/baselib-plain-ugly.js +140 -3
- package/release/compiler.js +16282 -5408
- package/release/signatures.json +25 -22
- package/src/ast.pyj +94 -1
- package/src/baselib-builtins.pyj +362 -3
- package/src/baselib-bytes.pyj +664 -0
- package/src/baselib-containers.pyj +99 -0
- package/src/baselib-errors.pyj +45 -1
- package/src/baselib-internal.pyj +346 -49
- package/src/baselib-itertools.pyj +17 -4
- package/src/baselib-str.pyj +46 -4
- package/src/lib/abc.pyj +317 -0
- package/src/lib/copy.pyj +120 -0
- package/src/lib/dataclasses.pyj +532 -0
- package/src/lib/enum.pyj +125 -0
- package/src/lib/pythonize.pyj +1 -1
- package/src/lib/re.pyj +35 -1
- package/src/lib/react.pyj +74 -0
- package/src/lib/typing.pyj +577 -0
- package/src/monaco-language-service/builtins.js +19 -4
- package/src/monaco-language-service/diagnostics.js +40 -19
- package/src/output/classes.pyj +161 -25
- package/src/output/codegen.pyj +16 -2
- package/src/output/exceptions.pyj +97 -1
- package/src/output/functions.pyj +87 -5
- package/src/output/jsx.pyj +164 -0
- package/src/output/literals.pyj +28 -2
- package/src/output/loops.pyj +5 -2
- package/src/output/modules.pyj +1 -1
- package/src/output/operators.pyj +108 -36
- package/src/output/statements.pyj +2 -2
- package/src/output/stream.pyj +1 -0
- package/src/parse.pyj +496 -128
- package/src/tokenizer.pyj +38 -4
- package/test/abc.pyj +291 -0
- 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/dataclasses.pyj +253 -0
- package/test/enum.pyj +134 -0
- package/test/eval_exec.pyj +56 -0
- package/test/format.pyj +148 -0
- package/test/object.pyj +64 -0
- package/test/python_compat.pyj +17 -15
- package/test/python_features.pyj +89 -21
- package/test/regexp.pyj +29 -1
- package/test/tuples.pyj +96 -0
- package/test/typing.pyj +469 -0
- package/test/unit/index.js +2292 -70
- package/test/unit/language-service.js +674 -4
- package/test/unit/web-repl.js +1106 -0
- package/test/vars_locals_globals.pyj +94 -0
- package/tools/cli.js +11 -0
- package/tools/compile.js +5 -0
- package/tools/embedded_compiler.js +15 -4
- package/tools/lint.js +16 -19
- package/tools/repl.js +1 -1
- package/web-repl/env.js +122 -0
- package/web-repl/main.js +1 -3
- package/web-repl/rapydscript.js +125 -3
- package/PYTHON_DIFFERENCES_REPORT.md +0 -291
- package/PYTHON_FEATURE_COVERAGE.md +0 -200
- package/hack_demo.pyj +0 -112
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
from __python__ import overload_operators
|
|
2
|
+
|
|
3
|
+
# ── helpers ──────────────────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
def _raises(fn, exc_type):
|
|
6
|
+
"""Return True if fn() raises exc_type, False otherwise."""
|
|
7
|
+
try:
|
|
8
|
+
fn()
|
|
9
|
+
except exc_type:
|
|
10
|
+
return True
|
|
11
|
+
return False
|
|
12
|
+
|
|
13
|
+
# ── 1. Numeric comparisons (should still work correctly) ─────────────────────
|
|
14
|
+
|
|
15
|
+
assert 1 < 2
|
|
16
|
+
assert not (2 < 1)
|
|
17
|
+
assert 2 > 1
|
|
18
|
+
assert not (1 > 2)
|
|
19
|
+
assert 1 <= 1
|
|
20
|
+
assert 2 <= 3
|
|
21
|
+
assert not (3 <= 2)
|
|
22
|
+
assert 3 >= 3
|
|
23
|
+
assert 3 >= 2
|
|
24
|
+
assert not (2 >= 3)
|
|
25
|
+
|
|
26
|
+
# ── 2. String comparisons ────────────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
assert 'abc' < 'abd'
|
|
29
|
+
assert 'abc' <= 'abc'
|
|
30
|
+
assert 'z' > 'a'
|
|
31
|
+
assert 'abc' >= 'abc'
|
|
32
|
+
assert not ('b' < 'a')
|
|
33
|
+
|
|
34
|
+
# ── 3. List lexicographic ordering ───────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
# First differing element determines order
|
|
37
|
+
assert [1, 2] < [1, 3]
|
|
38
|
+
assert not ([1, 3] < [1, 2])
|
|
39
|
+
|
|
40
|
+
# Prefix rule: shorter list with equal prefix is smaller
|
|
41
|
+
assert [1, 2] < [1, 2, 0]
|
|
42
|
+
assert not ([1, 2, 0] < [1, 2])
|
|
43
|
+
|
|
44
|
+
# First element dominates
|
|
45
|
+
assert [2] > [1, 99]
|
|
46
|
+
assert not ([1, 99] > [2])
|
|
47
|
+
|
|
48
|
+
# Equal lists
|
|
49
|
+
assert [1, 2] <= [1, 2]
|
|
50
|
+
assert [1, 2] >= [1, 2]
|
|
51
|
+
assert not ([1, 2] < [1, 2])
|
|
52
|
+
assert not ([1, 2] > [1, 2])
|
|
53
|
+
|
|
54
|
+
# Empty lists
|
|
55
|
+
assert [] < [1]
|
|
56
|
+
assert [] <= []
|
|
57
|
+
assert [1] > []
|
|
58
|
+
assert not ([] > [])
|
|
59
|
+
|
|
60
|
+
# ── 4. Nested list lexicographic ordering ────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
assert [[1, 2], [3]] < [[1, 2], [4]]
|
|
63
|
+
assert [[1], [2]] < [[1], [2], []]
|
|
64
|
+
|
|
65
|
+
# ── 5. Bool operands (bool is int in Python) ─────────────────────────────────
|
|
66
|
+
|
|
67
|
+
assert not (True < True)
|
|
68
|
+
assert False < True
|
|
69
|
+
assert True > False
|
|
70
|
+
assert True >= True
|
|
71
|
+
|
|
72
|
+
# ── 6. Custom __lt__ / __le__ / __gt__ / __ge__ dispatch ────────────────────
|
|
73
|
+
|
|
74
|
+
class Vec:
|
|
75
|
+
def __init__(self, x):
|
|
76
|
+
self.x = x
|
|
77
|
+
def __lt__(self, other):
|
|
78
|
+
return self.x < other.x
|
|
79
|
+
def __le__(self, other):
|
|
80
|
+
return self.x <= other.x
|
|
81
|
+
def __gt__(self, other):
|
|
82
|
+
return self.x > other.x
|
|
83
|
+
def __ge__(self, other):
|
|
84
|
+
return self.x >= other.x
|
|
85
|
+
|
|
86
|
+
v1 = Vec(1)
|
|
87
|
+
v2 = Vec(2)
|
|
88
|
+
v3 = Vec(2)
|
|
89
|
+
|
|
90
|
+
assert v1 < v2
|
|
91
|
+
assert not (v2 < v1)
|
|
92
|
+
assert v2 > v1
|
|
93
|
+
assert not (v1 > v2)
|
|
94
|
+
assert v1 <= v2
|
|
95
|
+
assert v2 <= v3
|
|
96
|
+
assert v2 >= v3
|
|
97
|
+
assert v2 >= v1
|
|
98
|
+
assert not (v1 >= v2)
|
|
99
|
+
|
|
100
|
+
# ── 7. Reflected dunder dispatch ─────────────────────────────────────────────
|
|
101
|
+
# When left has no __gt__, Python calls right.__lt__
|
|
102
|
+
|
|
103
|
+
class LtOnly:
|
|
104
|
+
"""Only defines __lt__; reflected ops depend on the right-hand operand."""
|
|
105
|
+
def __init__(self, x):
|
|
106
|
+
self.x = x
|
|
107
|
+
def __lt__(self, other):
|
|
108
|
+
return self.x < other.x
|
|
109
|
+
|
|
110
|
+
a = LtOnly(1)
|
|
111
|
+
b = LtOnly(2)
|
|
112
|
+
|
|
113
|
+
assert a < b # a.__lt__(b)
|
|
114
|
+
assert b > a # b.__gt__ absent → ρσ_op_gt falls back → ρσ_op_lt(a, b) → a.__lt__(b)
|
|
115
|
+
|
|
116
|
+
# ── 8. TypeError for incompatible types ──────────────────────────────────────
|
|
117
|
+
|
|
118
|
+
assert _raises(def(): return 1 < 'x';, TypeError)
|
|
119
|
+
assert _raises(def(): return 'x' < 1;, TypeError)
|
|
120
|
+
assert _raises(def(): return [1] < 1;, TypeError)
|
|
121
|
+
assert _raises(def(): return 1 < [1];, TypeError)
|
|
122
|
+
assert _raises(def(): return 1 > 'x';, TypeError)
|
|
123
|
+
assert _raises(def(): return 1 <= 'x';, TypeError)
|
|
124
|
+
assert _raises(def(): return 1 >= 'x';, TypeError)
|
|
125
|
+
|
|
126
|
+
# ── 9. TypeError message contains operator symbol ────────────────────────────
|
|
127
|
+
|
|
128
|
+
def _err_msg(fn):
|
|
129
|
+
try:
|
|
130
|
+
fn()
|
|
131
|
+
except TypeError as e:
|
|
132
|
+
return str(e)
|
|
133
|
+
return ''
|
|
134
|
+
|
|
135
|
+
msg = _err_msg(def(): return 1 < 'x';)
|
|
136
|
+
assert "'<'" in msg, msg
|
|
137
|
+
assert 'int' in msg, msg
|
|
138
|
+
assert 'str' in msg, msg
|
|
139
|
+
|
|
140
|
+
msg = _err_msg(def(): return 1 > [1];)
|
|
141
|
+
assert "'>'" in msg, msg
|
|
142
|
+
|
|
143
|
+
msg = _err_msg(def(): return 1 <= {};)
|
|
144
|
+
assert "'<='" in msg, msg
|
|
145
|
+
|
|
146
|
+
msg = _err_msg(def(): return 1 >= {};)
|
|
147
|
+
assert "'>='" in msg, msg
|
|
148
|
+
|
|
149
|
+
# ── 10. Chained comparisons with overloaded ops ───────────────────────────────
|
|
150
|
+
|
|
151
|
+
assert 1 < 2 < 3
|
|
152
|
+
assert not (1 < 2 < 2)
|
|
153
|
+
assert 3 > 2 > 1
|
|
154
|
+
assert 1 <= 1 <= 2
|
|
155
|
+
assert [1] < [2] < [3]
|
|
156
|
+
assert [3] > [2] > [1]
|
|
157
|
+
|
|
158
|
+
# Mixed direction chain
|
|
159
|
+
assert 1 < 2 > 0
|
|
160
|
+
assert not (1 < 2 > 3)
|
|
161
|
+
|
|
162
|
+
# ── 11. Chained comparison: middle expression evaluated once ─────────────────
|
|
163
|
+
|
|
164
|
+
counter = [0]
|
|
165
|
+
def inc_and_return(val):
|
|
166
|
+
counter[0] += 1
|
|
167
|
+
return val
|
|
168
|
+
|
|
169
|
+
# a < inc_and_return(b) < c — middle must be evaluated exactly once
|
|
170
|
+
assert 1 < inc_and_return(2) < 3
|
|
171
|
+
assert counter[0] == 1, counter[0]
|
|
172
|
+
|
|
173
|
+
print('All comparison_ops tests passed.')
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# globals: assrt
|
|
2
|
+
# vim:fileencoding=utf-8
|
|
3
|
+
#
|
|
4
|
+
# dataclasses.pyj
|
|
5
|
+
# Tests for the dataclasses standard library.
|
|
6
|
+
|
|
7
|
+
ae = assrt.equal
|
|
8
|
+
ade = assrt.deepEqual
|
|
9
|
+
ok = assrt.ok
|
|
10
|
+
|
|
11
|
+
from dataclasses import dataclass, field, fields, asdict, astuple, replace, is_dataclass, MISSING
|
|
12
|
+
|
|
13
|
+
# ── 1. Basic @dataclass: __init__, __repr__, __eq__ ───────────────────────────
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class _Point:
|
|
17
|
+
x: float
|
|
18
|
+
y: float
|
|
19
|
+
|
|
20
|
+
p1 = _Point(1, 2)
|
|
21
|
+
ae(p1.x, 1)
|
|
22
|
+
ae(p1.y, 2)
|
|
23
|
+
ae(repr(p1), '_Point(x=1, y=2)')
|
|
24
|
+
ok(p1 == _Point(1, 2))
|
|
25
|
+
ok(p1 is not _Point(1, 2)) # different instances
|
|
26
|
+
ok(not (p1 == _Point(1, 9)))
|
|
27
|
+
|
|
28
|
+
# ── 2. Default values ─────────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class _Config:
|
|
32
|
+
name: str
|
|
33
|
+
debug: bool = False
|
|
34
|
+
count: int = 0
|
|
35
|
+
|
|
36
|
+
c1 = _Config('myapp')
|
|
37
|
+
ae(c1.name, 'myapp')
|
|
38
|
+
ok(c1.debug is False)
|
|
39
|
+
ae(c1.count, 0)
|
|
40
|
+
|
|
41
|
+
c2 = _Config('other', True, 5)
|
|
42
|
+
ae(c2.name, 'other')
|
|
43
|
+
ok(c2.debug is True)
|
|
44
|
+
ae(c2.count, 5)
|
|
45
|
+
|
|
46
|
+
# ── 3. Keyword arguments ──────────────────────────────────────────────────────
|
|
47
|
+
|
|
48
|
+
c3 = _Config(name='test', debug=True)
|
|
49
|
+
ae(c3.name, 'test')
|
|
50
|
+
ok(c3.debug is True)
|
|
51
|
+
ae(c3.count, 0)
|
|
52
|
+
|
|
53
|
+
# ── 4. field() with default_factory ──────────────────────────────────────────
|
|
54
|
+
|
|
55
|
+
@dataclass
|
|
56
|
+
class _Container:
|
|
57
|
+
items: list = field(default_factory=list)
|
|
58
|
+
label: str = 'default'
|
|
59
|
+
|
|
60
|
+
a = _Container()
|
|
61
|
+
b = _Container()
|
|
62
|
+
# Each instance gets its own list
|
|
63
|
+
a.items.push(1)
|
|
64
|
+
ae(a.items.length, 1)
|
|
65
|
+
ae(b.items.length, 0)
|
|
66
|
+
ae(a.label, 'default')
|
|
67
|
+
|
|
68
|
+
d = _Container(label='custom')
|
|
69
|
+
ae(d.label, 'custom')
|
|
70
|
+
|
|
71
|
+
# ── 5. field() repr=False ─────────────────────────────────────────────────────
|
|
72
|
+
|
|
73
|
+
@dataclass
|
|
74
|
+
class _Hidden:
|
|
75
|
+
public: str
|
|
76
|
+
secret: str = field('s3cr3t', repr=False)
|
|
77
|
+
|
|
78
|
+
h = _Hidden("visible")
|
|
79
|
+
ae(repr(h), '_Hidden(public="visible")')
|
|
80
|
+
ae(h.secret, 's3cr3t')
|
|
81
|
+
|
|
82
|
+
# ── 6. field() init=False ─────────────────────────────────────────────────────
|
|
83
|
+
|
|
84
|
+
@dataclass
|
|
85
|
+
class _WithPostInit:
|
|
86
|
+
x: int
|
|
87
|
+
y: int
|
|
88
|
+
dist: int = field(init=False, repr=True)
|
|
89
|
+
|
|
90
|
+
def __post_init__(self):
|
|
91
|
+
this.dist = this.x + this.y
|
|
92
|
+
|
|
93
|
+
w = _WithPostInit(3, 4)
|
|
94
|
+
ae(w.x, 3)
|
|
95
|
+
ae(w.y, 4)
|
|
96
|
+
ae(w.dist, 7)
|
|
97
|
+
|
|
98
|
+
# ── 7. fields() returns Field objects ────────────────────────────────────────
|
|
99
|
+
|
|
100
|
+
fs = fields(_Point)
|
|
101
|
+
ae(fs.length, 2)
|
|
102
|
+
ae(fs[0].name, 'x')
|
|
103
|
+
ae(fs[1].name, 'y')
|
|
104
|
+
|
|
105
|
+
# Also works on an instance
|
|
106
|
+
fs2 = fields(p1)
|
|
107
|
+
ae(fs2.length, 2)
|
|
108
|
+
ae(fs2[0].name, 'x')
|
|
109
|
+
|
|
110
|
+
# ── 8. asdict() ───────────────────────────────────────────────────────────────
|
|
111
|
+
|
|
112
|
+
@dataclass
|
|
113
|
+
class _Inner:
|
|
114
|
+
value: int
|
|
115
|
+
|
|
116
|
+
@dataclass
|
|
117
|
+
class _Outer:
|
|
118
|
+
inner: object
|
|
119
|
+
tag: str
|
|
120
|
+
|
|
121
|
+
d_obj = _Outer(_Inner(42), 'hello')
|
|
122
|
+
d_dict = asdict(d_obj)
|
|
123
|
+
ae(d_dict['tag'], 'hello')
|
|
124
|
+
ae(d_dict['inner']['value'], 42)
|
|
125
|
+
|
|
126
|
+
# ── 9. astuple() ──────────────────────────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
pt = _Point(3, 4)
|
|
129
|
+
t = astuple(pt)
|
|
130
|
+
ae(t[0], 3)
|
|
131
|
+
ae(t[1], 4)
|
|
132
|
+
|
|
133
|
+
# ── 10. replace() ────────────────────────────────────────────────────────────
|
|
134
|
+
|
|
135
|
+
p2 = _Point(1, 2)
|
|
136
|
+
p3 = replace(p2, y=99)
|
|
137
|
+
ae(p3.x, 1)
|
|
138
|
+
ae(p3.y, 99)
|
|
139
|
+
ae(p2.y, 2) # original unchanged
|
|
140
|
+
|
|
141
|
+
# ── 11. is_dataclass() ───────────────────────────────────────────────────────
|
|
142
|
+
|
|
143
|
+
ok(is_dataclass(_Point))
|
|
144
|
+
ok(is_dataclass(p1))
|
|
145
|
+
ok(not is_dataclass(42))
|
|
146
|
+
ok(not is_dataclass('hello'))
|
|
147
|
+
ok(not is_dataclass({}))
|
|
148
|
+
ok(not is_dataclass(None))
|
|
149
|
+
|
|
150
|
+
# ── 12. @dataclass(order=True) ───────────────────────────────────────────────
|
|
151
|
+
# Note: RapydScript's < / > operators do not dispatch to __lt__/__gt__, so
|
|
152
|
+
# order=True is tested by calling the generated comparison methods directly.
|
|
153
|
+
|
|
154
|
+
@dataclass(order=True)
|
|
155
|
+
class _Version:
|
|
156
|
+
major: int
|
|
157
|
+
minor: int
|
|
158
|
+
|
|
159
|
+
v1 = _Version(1, 0)
|
|
160
|
+
v2 = _Version(1, 5)
|
|
161
|
+
v3 = _Version(2, 0)
|
|
162
|
+
|
|
163
|
+
ok(v1.__lt__(v2))
|
|
164
|
+
ok(v2.__lt__(v3))
|
|
165
|
+
ok(v3.__gt__(v1))
|
|
166
|
+
ok(v1.__le__(v1))
|
|
167
|
+
ok(v1.__ge__(v1))
|
|
168
|
+
ok(v2.__ge__(v1))
|
|
169
|
+
ok(not v1.__lt__(v1))
|
|
170
|
+
|
|
171
|
+
# ── 13. @dataclass(frozen=True) ──────────────────────────────────────────────
|
|
172
|
+
|
|
173
|
+
@dataclass(frozen=True)
|
|
174
|
+
class _ImmutablePoint:
|
|
175
|
+
x: int
|
|
176
|
+
y: int
|
|
177
|
+
|
|
178
|
+
ip = _ImmutablePoint(1, 2)
|
|
179
|
+
ae(ip.x, 1)
|
|
180
|
+
|
|
181
|
+
# Assigning to a frozen dataclass field raises an error (TypeError in JS,
|
|
182
|
+
# since writable:false is enforced via Object.defineProperty).
|
|
183
|
+
_frozen_error = None
|
|
184
|
+
try:
|
|
185
|
+
ip.x = 99
|
|
186
|
+
except Exception as e:
|
|
187
|
+
_frozen_error = str(e)
|
|
188
|
+
ok(_frozen_error is not None)
|
|
189
|
+
|
|
190
|
+
# ── 14. Inheritance ───────────────────────────────────────────────────────────
|
|
191
|
+
|
|
192
|
+
@dataclass
|
|
193
|
+
class _Base:
|
|
194
|
+
x: int
|
|
195
|
+
y: int = 0
|
|
196
|
+
|
|
197
|
+
@dataclass
|
|
198
|
+
class _Child(_Base):
|
|
199
|
+
z: str = 'default'
|
|
200
|
+
|
|
201
|
+
ch = _Child(x=1, y=2, z='hi')
|
|
202
|
+
ae(ch.x, 1)
|
|
203
|
+
ae(ch.y, 2)
|
|
204
|
+
ae(ch.z, 'hi')
|
|
205
|
+
|
|
206
|
+
ch2 = _Child(10)
|
|
207
|
+
ae(ch2.x, 10)
|
|
208
|
+
ae(ch2.y, 0)
|
|
209
|
+
ae(ch2.z, 'default')
|
|
210
|
+
|
|
211
|
+
# repr shows all fields including inherited
|
|
212
|
+
# (RapydScript repr() uses double quotes for strings, like JavaScript)
|
|
213
|
+
r = repr(ch2)
|
|
214
|
+
ok('x=10' in r)
|
|
215
|
+
ok('y=0' in r)
|
|
216
|
+
ok('z="default"' in r)
|
|
217
|
+
|
|
218
|
+
# ── 15. MISSING sentinel ──────────────────────────────────────────────────────
|
|
219
|
+
|
|
220
|
+
ae(repr(MISSING), 'MISSING')
|
|
221
|
+
|
|
222
|
+
# ── 16. Multiple independent dataclasses ─────────────────────────────────────
|
|
223
|
+
|
|
224
|
+
@dataclass
|
|
225
|
+
class _A:
|
|
226
|
+
val: int = 1
|
|
227
|
+
|
|
228
|
+
@dataclass
|
|
229
|
+
class _B:
|
|
230
|
+
val: int = 2
|
|
231
|
+
|
|
232
|
+
ok(_A().val is 1)
|
|
233
|
+
ok(_B().val is 2)
|
|
234
|
+
|
|
235
|
+
# ── 17. @dataclass as decorator factory (explicit parens, no args) ────────────
|
|
236
|
+
|
|
237
|
+
@dataclass()
|
|
238
|
+
class _Explicit:
|
|
239
|
+
name: str = 'x'
|
|
240
|
+
|
|
241
|
+
ex = _Explicit()
|
|
242
|
+
ae(ex.name, 'x')
|
|
243
|
+
ae(repr(ex), '_Explicit(name="x")')
|
|
244
|
+
|
|
245
|
+
# ── 18. Nested list field with asdict ────────────────────────────────────────
|
|
246
|
+
|
|
247
|
+
@dataclass
|
|
248
|
+
class _WithList:
|
|
249
|
+
numbers: list = field(default_factory=list)
|
|
250
|
+
|
|
251
|
+
wl = _WithList([1, 2, 3])
|
|
252
|
+
d2 = asdict(wl)
|
|
253
|
+
ade(d2['numbers'], [1, 2, 3])
|
package/test/enum.pyj
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# globals: assrt
|
|
2
|
+
# vim:fileencoding=utf-8
|
|
3
|
+
#
|
|
4
|
+
# enum.pyj
|
|
5
|
+
# Tests for the enum standard library (from enum import Enum).
|
|
6
|
+
|
|
7
|
+
ae = assrt.equal
|
|
8
|
+
ade = assrt.deepEqual
|
|
9
|
+
ok = assrt.ok
|
|
10
|
+
|
|
11
|
+
from enum import Enum
|
|
12
|
+
|
|
13
|
+
# ── 1. Basic member access: .name and .value ─────────────────────────────────
|
|
14
|
+
# STATUS: ✓ WORKS
|
|
15
|
+
|
|
16
|
+
class _Color(Enum):
|
|
17
|
+
RED = 1
|
|
18
|
+
GREEN = 2
|
|
19
|
+
BLUE = 3
|
|
20
|
+
|
|
21
|
+
ae(_Color.RED.name, 'RED')
|
|
22
|
+
ae(_Color.RED.value, 1)
|
|
23
|
+
ae(_Color.GREEN.name, 'GREEN')
|
|
24
|
+
ae(_Color.GREEN.value, 2)
|
|
25
|
+
ae(_Color.BLUE.name, 'BLUE')
|
|
26
|
+
ae(_Color.BLUE.value, 3)
|
|
27
|
+
|
|
28
|
+
# ── 2. Member singletons (identity) ──────────────────────────────────────────
|
|
29
|
+
# STATUS: ✓ WORKS
|
|
30
|
+
|
|
31
|
+
ok(_Color.RED is _Color.RED)
|
|
32
|
+
ok(_Color.RED is not _Color.GREEN)
|
|
33
|
+
ok(_Color.GREEN is not _Color.BLUE)
|
|
34
|
+
|
|
35
|
+
# ── 3. repr and str ──────────────────────────────────────────────────────────
|
|
36
|
+
# STATUS: ✓ WORKS
|
|
37
|
+
# Note: repr() for integer values uses the numeric representation.
|
|
38
|
+
# The class name in repr/str reflects the actual JS constructor name.
|
|
39
|
+
|
|
40
|
+
ae(repr(_Color.RED), '<_Color.RED: 1>')
|
|
41
|
+
ae(repr(_Color.GREEN), '<_Color.GREEN: 2>')
|
|
42
|
+
ae(str(_Color.RED), '_Color.RED')
|
|
43
|
+
ae(str(_Color.BLUE), '_Color.BLUE')
|
|
44
|
+
|
|
45
|
+
# ── 4. isinstance checks ─────────────────────────────────────────────────────
|
|
46
|
+
# STATUS: ✓ WORKS
|
|
47
|
+
|
|
48
|
+
ok(isinstance(_Color.RED, _Color))
|
|
49
|
+
ok(isinstance(_Color.RED, Enum))
|
|
50
|
+
ok(isinstance(_Color.GREEN, _Color))
|
|
51
|
+
ok(not isinstance(1, _Color))
|
|
52
|
+
ok(not isinstance(1, Enum))
|
|
53
|
+
|
|
54
|
+
# ── 5. Iteration via list() ───────────────────────────────────────────────────
|
|
55
|
+
# STATUS: ✓ WORKS
|
|
56
|
+
|
|
57
|
+
members = list(_Color)
|
|
58
|
+
ae(len(members), 3)
|
|
59
|
+
ae(members[0].name, 'RED')
|
|
60
|
+
ae(members[1].name, 'GREEN')
|
|
61
|
+
ae(members[2].name, 'BLUE')
|
|
62
|
+
ae(members[0].value, 1)
|
|
63
|
+
ae(members[1].value, 2)
|
|
64
|
+
ae(members[2].value, 3)
|
|
65
|
+
|
|
66
|
+
# ── 6. for loop iteration ─────────────────────────────────────────────────────
|
|
67
|
+
# STATUS: ✓ WORKS
|
|
68
|
+
|
|
69
|
+
_found_names = []
|
|
70
|
+
for _m in _Color:
|
|
71
|
+
_found_names.push(_m.name)
|
|
72
|
+
ade(_found_names, ['RED', 'GREEN', 'BLUE'])
|
|
73
|
+
|
|
74
|
+
_found_values = []
|
|
75
|
+
for _m in _Color:
|
|
76
|
+
_found_values.push(_m.value)
|
|
77
|
+
ade(_found_values, [1, 2, 3])
|
|
78
|
+
|
|
79
|
+
# ── 7. _member_names_ registry ───────────────────────────────────────────────
|
|
80
|
+
# STATUS: ✓ WORKS
|
|
81
|
+
|
|
82
|
+
ade(_Color._member_names_, ['RED', 'GREEN', 'BLUE'])
|
|
83
|
+
|
|
84
|
+
# ── 8. String-valued enum ─────────────────────────────────────────────────────
|
|
85
|
+
# STATUS: ✓ WORKS
|
|
86
|
+
|
|
87
|
+
class _Direction(Enum):
|
|
88
|
+
NORTH = 'north'
|
|
89
|
+
SOUTH = 'south'
|
|
90
|
+
EAST = 'east'
|
|
91
|
+
WEST = 'west'
|
|
92
|
+
|
|
93
|
+
ae(_Direction.NORTH.name, 'NORTH')
|
|
94
|
+
ae(_Direction.NORTH.value, 'north')
|
|
95
|
+
ae(_Direction.SOUTH.value, 'south')
|
|
96
|
+
# RapydScript repr() wraps strings in double quotes (JS convention)
|
|
97
|
+
ae(repr(_Direction.NORTH), '<_Direction.NORTH: "north">')
|
|
98
|
+
ae(str(_Direction.EAST), '_Direction.EAST')
|
|
99
|
+
ae(len(list(_Direction)), 4)
|
|
100
|
+
|
|
101
|
+
# ── 9. Enum subclass with extra methods ───────────────────────────────────────
|
|
102
|
+
# STATUS: ✓ WORKS
|
|
103
|
+
|
|
104
|
+
class _Status(Enum):
|
|
105
|
+
ACTIVE = 1
|
|
106
|
+
INACTIVE = 0
|
|
107
|
+
|
|
108
|
+
def is_active(self):
|
|
109
|
+
return this is _Status.ACTIVE
|
|
110
|
+
|
|
111
|
+
ok(_Status.ACTIVE.is_active())
|
|
112
|
+
ok(not _Status.INACTIVE.is_active())
|
|
113
|
+
|
|
114
|
+
# ── 10. Multiple Enum subclasses are independent ──────────────────────────────
|
|
115
|
+
# STATUS: ✓ WORKS
|
|
116
|
+
|
|
117
|
+
ae(len(list(_Color)), 3)
|
|
118
|
+
ae(len(list(_Direction)), 4)
|
|
119
|
+
ae(len(list(_Status)), 2)
|
|
120
|
+
ok(_Color._member_names_ is not _Direction._member_names_)
|
|
121
|
+
ok(_Color.RED is not _Status.ACTIVE) # different classes, different members
|
|
122
|
+
|
|
123
|
+
# ── 11. Members are not confused with plain class instances ───────────────────
|
|
124
|
+
# STATUS: ✓ WORKS
|
|
125
|
+
|
|
126
|
+
ok(isinstance(_Color.RED, _Color))
|
|
127
|
+
ok(_Color.RED is _Color.RED) # same object every time
|
|
128
|
+
|
|
129
|
+
# ── 12. Values from _members_by_value_ lookup ────────────────────────────────
|
|
130
|
+
# STATUS: ✓ WORKS
|
|
131
|
+
|
|
132
|
+
ae(_Color._members_by_value_[1].name, 'RED')
|
|
133
|
+
ae(_Color._members_by_value_[2].name, 'GREEN')
|
|
134
|
+
ae(_Color._members_by_value_[3].name, 'BLUE')
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# vim:fileencoding=utf-8
|
|
2
|
+
# globals: assrt
|
|
3
|
+
|
|
4
|
+
ae = assrt.equal
|
|
5
|
+
ade = assrt.deepEqual
|
|
6
|
+
ok = assrt.ok
|
|
7
|
+
|
|
8
|
+
# ── eval — basic expression evaluation ────────────────────────────────────────
|
|
9
|
+
|
|
10
|
+
ae(eval("1 + 2"), 3)
|
|
11
|
+
ae(eval("10 * 5"), 50)
|
|
12
|
+
ae(eval("100 - 37"), 63)
|
|
13
|
+
ae(eval('"hello" + " world"'), "hello world")
|
|
14
|
+
ae(eval("Math.pow(2, 8)"), 256)
|
|
15
|
+
ae(eval("True"), True)
|
|
16
|
+
ae(eval("None"), None)
|
|
17
|
+
|
|
18
|
+
# ── eval — with explicit globals dict ─────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
ae(eval("x + y", {"x": 10, "y": 5}), 15)
|
|
21
|
+
ae(eval("a * b", {"a": 3, "b": 4}), 12)
|
|
22
|
+
ae(eval("n * n", {"n": 7}), 49)
|
|
23
|
+
|
|
24
|
+
# ── eval — locals override globals ────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
ae(eval("x", {"x": 1}, {"x": 99}), 99)
|
|
27
|
+
ae(eval("x + y", {"x": 10, "y": 20}, {"y": 1}), 11)
|
|
28
|
+
|
|
29
|
+
# ── exec — always returns None ────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
ae(exec("1 + 2"), None)
|
|
32
|
+
ae(exec("_ignored = 42"), None)
|
|
33
|
+
|
|
34
|
+
# ── exec — side effects via mutable objects in globals ────────────────────────
|
|
35
|
+
|
|
36
|
+
log = []
|
|
37
|
+
exec("log.push('hello')", {"log": log})
|
|
38
|
+
ae(log[0], "hello")
|
|
39
|
+
|
|
40
|
+
exec("log.push(1 + 2)", {"log": log})
|
|
41
|
+
ae(log[1], 3)
|
|
42
|
+
|
|
43
|
+
# exec with a function in globals
|
|
44
|
+
out = []
|
|
45
|
+
def _adder(a, b): out.push(a + b);
|
|
46
|
+
exec("fn(10, 7)", {"fn": _adder, "out": out})
|
|
47
|
+
ae(out[0], 17)
|
|
48
|
+
|
|
49
|
+
# exec returns None even when globals/locals provided
|
|
50
|
+
ae(exec("1 + 1", {"x": 5}), None)
|
|
51
|
+
|
|
52
|
+
# ── exec — JS loop, mutation visible in caller ────────────────────────────────
|
|
53
|
+
|
|
54
|
+
nums = []
|
|
55
|
+
exec("for _i in range(4):\n nums.append(_i * _i)", {"nums": nums})
|
|
56
|
+
ade(nums, [0, 1, 4, 9])
|