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
package/test/typing.pyj
ADDED
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
# globals: assrt
|
|
2
|
+
# Tests for the typing standard library module.
|
|
3
|
+
# Verifies that typing constructs transpile correctly to JavaScript and
|
|
4
|
+
# that the resulting runtime objects carry the expected attributes.
|
|
5
|
+
|
|
6
|
+
from typing import (
|
|
7
|
+
TYPE_CHECKING, Any, Union, Optional, ClassVar, Final, Literal, NoReturn,
|
|
8
|
+
List, Dict, Set, FrozenSet, Tuple, Type, Callable,
|
|
9
|
+
Iterator, Iterable, Generator, Sequence, MutableSequence,
|
|
10
|
+
Mapping, MutableMapping, AsyncGenerator, AsyncIterator, AsyncIterable,
|
|
11
|
+
Awaitable, Coroutine, IO, TextIO, BinaryIO, Pattern, Match,
|
|
12
|
+
TypeVar, Generic, Protocol,
|
|
13
|
+
cast, overload, no_type_check, no_type_check_decorator,
|
|
14
|
+
runtime_checkable, get_type_hints,
|
|
15
|
+
TypedDict, NamedTuple, AnyStr, ByteString, Text
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# ── TYPE_CHECKING constant ─────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
assrt.equal(TYPE_CHECKING, False)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# ── Generic aliases: __origin__ and __args__ ──────────────────────────────────
|
|
25
|
+
|
|
26
|
+
def _test_list_alias():
|
|
27
|
+
alias = List[int]
|
|
28
|
+
assrt.ok(alias is not None)
|
|
29
|
+
assrt.equal(alias.__origin__, list)
|
|
30
|
+
assrt.equal(alias.__args__[0], int)
|
|
31
|
+
assrt.equal(alias._name, 'List')
|
|
32
|
+
_test_list_alias()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _test_dict_alias():
|
|
36
|
+
alias = Dict[str, int]
|
|
37
|
+
assrt.equal(alias.__origin__, dict)
|
|
38
|
+
assrt.equal(alias.__args__[0], str)
|
|
39
|
+
assrt.equal(alias.__args__[1], int)
|
|
40
|
+
assrt.equal(alias._name, 'Dict')
|
|
41
|
+
_test_dict_alias()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _test_set_alias():
|
|
45
|
+
alias = Set[str]
|
|
46
|
+
assrt.equal(alias.__origin__, set)
|
|
47
|
+
assrt.equal(alias.__args__[0], str)
|
|
48
|
+
_test_set_alias()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _test_frozenset_alias():
|
|
52
|
+
alias = FrozenSet[int]
|
|
53
|
+
assrt.equal(alias.__origin__, frozenset)
|
|
54
|
+
assrt.equal(alias.__args__[0], int)
|
|
55
|
+
_test_frozenset_alias()
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _test_tuple_alias():
|
|
59
|
+
# Homogeneous variable-length tuple: Tuple[int, ...]
|
|
60
|
+
alias = Tuple[int, ...]
|
|
61
|
+
assrt.equal(alias.__origin__, tuple)
|
|
62
|
+
assrt.equal(alias.__args__[0], int)
|
|
63
|
+
assrt.equal(alias._name, 'Tuple')
|
|
64
|
+
_test_tuple_alias()
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _test_type_alias():
|
|
68
|
+
alias = Type[int]
|
|
69
|
+
assrt.equal(alias.__origin__, type)
|
|
70
|
+
assrt.equal(alias.__args__[0], int)
|
|
71
|
+
_test_type_alias()
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _test_callable_alias():
|
|
75
|
+
alias = Callable[[int, str], bool]
|
|
76
|
+
assrt.equal(alias._name, 'Callable')
|
|
77
|
+
assrt.ok(alias.__args__ is not None)
|
|
78
|
+
_test_callable_alias()
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# ── Abstract aliases ───────────────────────────────────────────────────────────
|
|
82
|
+
|
|
83
|
+
def _test_abstract_aliases():
|
|
84
|
+
assrt.equal(Iterator[int]._name, 'Iterator')
|
|
85
|
+
assrt.equal(Iterable[str]._name, 'Iterable')
|
|
86
|
+
assrt.equal(Generator[int, None, None]._name, 'Generator')
|
|
87
|
+
assrt.equal(Sequence[float]._name, 'Sequence')
|
|
88
|
+
assrt.equal(MutableSequence[float]._name, 'MutableSequence')
|
|
89
|
+
assrt.equal(Mapping[str, int]._name, 'Mapping')
|
|
90
|
+
assrt.equal(MutableMapping[str, int]._name, 'MutableMapping')
|
|
91
|
+
assrt.equal(Awaitable[int]._name, 'Awaitable')
|
|
92
|
+
assrt.equal(Coroutine[int, None, None]._name, 'Coroutine')
|
|
93
|
+
assrt.equal(AsyncGenerator[int, None]._name, 'AsyncGenerator')
|
|
94
|
+
assrt.equal(AsyncIterator[int]._name, 'AsyncIterator')
|
|
95
|
+
assrt.equal(AsyncIterable[int]._name, 'AsyncIterable')
|
|
96
|
+
_test_abstract_aliases()
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
# ── IO and pattern aliases ─────────────────────────────────────────────────────
|
|
100
|
+
|
|
101
|
+
def _test_io_aliases():
|
|
102
|
+
assrt.equal(IO[str]._name, 'IO')
|
|
103
|
+
assrt.ok(TextIO is not None)
|
|
104
|
+
assrt.ok(BinaryIO is not None)
|
|
105
|
+
assrt.equal(Pattern[str]._name, 'Pattern')
|
|
106
|
+
assrt.equal(Match[str]._name, 'Match')
|
|
107
|
+
_test_io_aliases()
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
# ── Special forms ──────────────────────────────────────────────────────────────
|
|
111
|
+
|
|
112
|
+
def _test_optional():
|
|
113
|
+
alias = Optional[str]
|
|
114
|
+
assrt.equal(alias._name, 'Optional')
|
|
115
|
+
assrt.equal(alias.__args__[0], str)
|
|
116
|
+
assrt.equal(alias.__args__[1], None)
|
|
117
|
+
_test_optional()
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _test_union():
|
|
121
|
+
alias = Union[str, int]
|
|
122
|
+
assrt.equal(alias._name, 'Union')
|
|
123
|
+
assrt.equal(alias.__args__.length, 2)
|
|
124
|
+
assrt.equal(alias.__args__[0], str)
|
|
125
|
+
assrt.equal(alias.__args__[1], int)
|
|
126
|
+
_test_union()
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _test_classvar():
|
|
130
|
+
alias = ClassVar[str]
|
|
131
|
+
assrt.equal(alias._name, 'ClassVar')
|
|
132
|
+
assrt.equal(alias.__args__[0], str)
|
|
133
|
+
_test_classvar()
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _test_final():
|
|
137
|
+
alias = Final[int]
|
|
138
|
+
assrt.equal(alias._name, 'Final')
|
|
139
|
+
assrt.equal(alias.__args__[0], int)
|
|
140
|
+
_test_final()
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _test_literal():
|
|
144
|
+
alias = Literal['r', 'w', 'a']
|
|
145
|
+
assrt.equal(alias._name, 'Literal')
|
|
146
|
+
assrt.equal(alias.__args__.length, 3)
|
|
147
|
+
assrt.equal(alias.__args__[0], 'r')
|
|
148
|
+
assrt.equal(alias.__args__[1], 'w')
|
|
149
|
+
assrt.equal(alias.__args__[2], 'a')
|
|
150
|
+
_test_literal()
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _test_any():
|
|
154
|
+
assrt.ok(Any is not None)
|
|
155
|
+
assrt.ok(repr(Any).indexOf('Any') >= 0)
|
|
156
|
+
_test_any()
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _test_noreturn():
|
|
160
|
+
assrt.ok(NoReturn is not None)
|
|
161
|
+
_test_noreturn()
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
# ── TypeVar ────────────────────────────────────────────────────────────────────
|
|
165
|
+
|
|
166
|
+
def _test_typevar():
|
|
167
|
+
T = TypeVar('T')
|
|
168
|
+
assrt.equal(T.__name__, 'T')
|
|
169
|
+
assrt.ok(repr(T).indexOf('T') >= 0)
|
|
170
|
+
|
|
171
|
+
S = TypeVar('S', str, int)
|
|
172
|
+
assrt.equal(S.__name__, 'S')
|
|
173
|
+
assrt.equal(S.__constraints__.length, 2)
|
|
174
|
+
assrt.equal(S.__constraints__[0], str)
|
|
175
|
+
assrt.equal(S.__constraints__[1], int)
|
|
176
|
+
|
|
177
|
+
assrt.equal(AnyStr.__name__, 'AnyStr')
|
|
178
|
+
# AnyStr is constrained to both str and bytes now that bytes is supported
|
|
179
|
+
assrt.equal(AnyStr.__constraints__.length, 2)
|
|
180
|
+
assrt.equal(AnyStr.__constraints__[0], str)
|
|
181
|
+
assrt.equal(AnyStr.__constraints__[1], bytes)
|
|
182
|
+
_test_typevar()
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
# ── Generic base class ─────────────────────────────────────────────────────────
|
|
186
|
+
|
|
187
|
+
def _test_generic():
|
|
188
|
+
T = TypeVar('T')
|
|
189
|
+
|
|
190
|
+
class Stack(Generic[T]):
|
|
191
|
+
def __init__(self):
|
|
192
|
+
self._items = []
|
|
193
|
+
def push(self, item):
|
|
194
|
+
self._items.append(item)
|
|
195
|
+
def pop(self):
|
|
196
|
+
return self._items.pop()
|
|
197
|
+
def empty(self):
|
|
198
|
+
return len(self._items) == 0
|
|
199
|
+
|
|
200
|
+
s = Stack()
|
|
201
|
+
s.push(1)
|
|
202
|
+
s.push(2)
|
|
203
|
+
assrt.equal(s.pop(), 2)
|
|
204
|
+
assrt.equal(s.pop(), 1)
|
|
205
|
+
assrt.ok(s.empty())
|
|
206
|
+
|
|
207
|
+
# __class_getitem__ on Generic subclass
|
|
208
|
+
alias = Stack[int]
|
|
209
|
+
assrt.ok(alias is not None)
|
|
210
|
+
_test_generic()
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
# ── Protocol base class ────────────────────────────────────────────────────────
|
|
214
|
+
|
|
215
|
+
def _test_protocol():
|
|
216
|
+
@runtime_checkable
|
|
217
|
+
class Drawable(Protocol):
|
|
218
|
+
def draw(self):
|
|
219
|
+
pass
|
|
220
|
+
|
|
221
|
+
class Circle:
|
|
222
|
+
def draw(self):
|
|
223
|
+
return 'circle'
|
|
224
|
+
|
|
225
|
+
c = Circle()
|
|
226
|
+
assrt.equal(c.draw(), 'circle')
|
|
227
|
+
assrt.ok(Drawable is not None)
|
|
228
|
+
_test_protocol()
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
# ── cast ───────────────────────────────────────────────────────────────────────
|
|
232
|
+
|
|
233
|
+
def _test_cast():
|
|
234
|
+
result = cast(int, 'hello')
|
|
235
|
+
assrt.equal(result, 'hello')
|
|
236
|
+
assrt.deepEqual(cast(List[int], [1, 2, 3]), [1, 2, 3])
|
|
237
|
+
assrt.equal(cast(str, 42), 42)
|
|
238
|
+
_test_cast()
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
# ── overload decorator ─────────────────────────────────────────────────────────
|
|
242
|
+
|
|
243
|
+
def _test_overload():
|
|
244
|
+
@overload
|
|
245
|
+
def process(x):
|
|
246
|
+
return x * 2
|
|
247
|
+
|
|
248
|
+
assrt.equal(process(5), 10)
|
|
249
|
+
_test_overload()
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
# ── no_type_check decorator ────────────────────────────────────────────────────
|
|
253
|
+
|
|
254
|
+
def _test_no_type_check():
|
|
255
|
+
@no_type_check
|
|
256
|
+
def f(x):
|
|
257
|
+
return x + 1
|
|
258
|
+
|
|
259
|
+
assrt.equal(f(3), 4)
|
|
260
|
+
_test_no_type_check()
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
# ── get_type_hints ─────────────────────────────────────────────────────────────
|
|
264
|
+
|
|
265
|
+
def _test_get_type_hints():
|
|
266
|
+
def annotated(x: int, y: str) -> bool:
|
|
267
|
+
pass
|
|
268
|
+
|
|
269
|
+
hints = get_type_hints(annotated)
|
|
270
|
+
assrt.equal(hints['x'], int)
|
|
271
|
+
assrt.equal(hints['y'], str)
|
|
272
|
+
assrt.equal(hints['return'], bool)
|
|
273
|
+
|
|
274
|
+
def no_hints():
|
|
275
|
+
pass
|
|
276
|
+
|
|
277
|
+
assrt.deepEqual(get_type_hints(no_hints), {})
|
|
278
|
+
_test_get_type_hints()
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
# ── Function annotations with typing types ─────────────────────────────────────
|
|
282
|
+
|
|
283
|
+
def _test_annotations_with_typing():
|
|
284
|
+
def process(items: List[int], label: Optional[str]) -> Dict[str, int]:
|
|
285
|
+
pass
|
|
286
|
+
|
|
287
|
+
annot = process.__annotations__
|
|
288
|
+
assrt.ok(annot is not None)
|
|
289
|
+
assrt.equal(annot['items'].__origin__, list)
|
|
290
|
+
assrt.equal(annot['items'].__args__[0], int)
|
|
291
|
+
assrt.equal(annot['label']._name, 'Optional')
|
|
292
|
+
assrt.equal(annot['return'].__origin__, dict)
|
|
293
|
+
_test_annotations_with_typing()
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
# ── TypedDict ──────────────────────────────────────────────────────────────────
|
|
297
|
+
|
|
298
|
+
def _test_typeddict():
|
|
299
|
+
class Movie(TypedDict):
|
|
300
|
+
pass
|
|
301
|
+
|
|
302
|
+
# TypedDict subclass is a valid class that can be instantiated
|
|
303
|
+
m = Movie()
|
|
304
|
+
assrt.ok(m is not None)
|
|
305
|
+
# TypedDict itself is importable
|
|
306
|
+
assrt.ok(TypedDict is not None)
|
|
307
|
+
_test_typeddict()
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
# ── NamedTuple (functional form) ───────────────────────────────────────────────
|
|
311
|
+
|
|
312
|
+
def _test_namedtuple():
|
|
313
|
+
from __python__ import overload_getitem
|
|
314
|
+
Point = NamedTuple('Point', [('x', float), ('y', float)])
|
|
315
|
+
|
|
316
|
+
p = Point(3.0, 4.0)
|
|
317
|
+
assrt.equal(p.x, 3.0)
|
|
318
|
+
assrt.equal(p.y, 4.0)
|
|
319
|
+
|
|
320
|
+
# Annotations carry type information
|
|
321
|
+
assrt.equal(Point.__annotations__['x'], float)
|
|
322
|
+
assrt.equal(Point.__annotations__['y'], float)
|
|
323
|
+
|
|
324
|
+
# Integer indexing
|
|
325
|
+
assrt.equal(p[0], 3.0)
|
|
326
|
+
assrt.equal(p[1], 4.0)
|
|
327
|
+
|
|
328
|
+
# Negative indexing
|
|
329
|
+
assrt.equal(p[-1], 4.0)
|
|
330
|
+
_test_namedtuple()
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
# ── ByteString ─────────────────────────────────────────────────────────────────
|
|
334
|
+
|
|
335
|
+
def _test_bytestring():
|
|
336
|
+
from __python__ import overload_operators
|
|
337
|
+
# ByteString is importable and is a class
|
|
338
|
+
assrt.ok(ByteString is not None)
|
|
339
|
+
|
|
340
|
+
# Use ByteString as a type annotation — it is a no-op at runtime
|
|
341
|
+
def consume(data: ByteString) -> int:
|
|
342
|
+
return len(data)
|
|
343
|
+
|
|
344
|
+
assrt.equal(consume(bytes([1, 2, 3])), 3)
|
|
345
|
+
assrt.equal(consume(bytearray([10, 20])), 2)
|
|
346
|
+
|
|
347
|
+
# Optional[ByteString] works via _GenericAlias
|
|
348
|
+
from typing import Optional
|
|
349
|
+
alias = Optional[ByteString]
|
|
350
|
+
assrt.equal(alias._name, 'Optional')
|
|
351
|
+
assrt.equal(alias.__args__[0], ByteString)
|
|
352
|
+
|
|
353
|
+
# Union[bytes, bytearray] round-trip
|
|
354
|
+
from typing import Union
|
|
355
|
+
u = Union[bytes, bytearray]
|
|
356
|
+
assrt.equal(u._name, 'Union')
|
|
357
|
+
assrt.equal(u.__args__[0], bytes)
|
|
358
|
+
assrt.equal(u.__args__[1], bytearray)
|
|
359
|
+
_test_bytestring()
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
# ── bytes / bytearray in generic aliases ───────────────────────────────────────
|
|
363
|
+
|
|
364
|
+
def _test_bytes_in_generic_aliases():
|
|
365
|
+
from __python__ import overload_operators
|
|
366
|
+
# List[bytes]
|
|
367
|
+
alias = List[bytes]
|
|
368
|
+
assrt.equal(alias.__origin__, list)
|
|
369
|
+
assrt.equal(alias.__args__[0], bytes)
|
|
370
|
+
|
|
371
|
+
# Dict[str, bytes]
|
|
372
|
+
alias = Dict[str, bytes]
|
|
373
|
+
assrt.equal(alias.__args__[0], str)
|
|
374
|
+
assrt.equal(alias.__args__[1], bytes)
|
|
375
|
+
|
|
376
|
+
# Optional[bytes]
|
|
377
|
+
from typing import Optional
|
|
378
|
+
alias = Optional[bytes]
|
|
379
|
+
assrt.equal(alias._name, 'Optional')
|
|
380
|
+
assrt.equal(alias.__args__[0], bytes)
|
|
381
|
+
|
|
382
|
+
# IO[bytes] — used to annotate binary-mode streams
|
|
383
|
+
alias = IO[bytes]
|
|
384
|
+
assrt.equal(alias._name, 'IO')
|
|
385
|
+
assrt.equal(alias.__args__[0], bytes)
|
|
386
|
+
_test_bytes_in_generic_aliases()
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
# ── Text alias ─────────────────────────────────────────────────────────────────
|
|
390
|
+
|
|
391
|
+
def _test_text_alias():
|
|
392
|
+
assrt.equal(Text, str)
|
|
393
|
+
_test_text_alias()
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
# ── repr smoke test ────────────────────────────────────────────────────────────
|
|
397
|
+
|
|
398
|
+
def _test_repr():
|
|
399
|
+
assrt.ok(repr(List[int]).indexOf('List') >= 0)
|
|
400
|
+
assrt.ok(repr(Dict[str, int]).indexOf('Dict') >= 0)
|
|
401
|
+
assrt.ok(repr(Optional[str]).indexOf('Optional') >= 0)
|
|
402
|
+
assrt.ok(repr(Union[str, int]).indexOf('Union') >= 0)
|
|
403
|
+
_test_repr()
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
# ── Annotated assignment with attribute target (self.x: Type = value) ──────────
|
|
407
|
+
# Regression: these were previously mis-parsed, causing "invalid left-hand side"
|
|
408
|
+
# in the generated JS output.
|
|
409
|
+
|
|
410
|
+
def _test_attr_annotated_assign():
|
|
411
|
+
T = TypeVar('T')
|
|
412
|
+
|
|
413
|
+
class Stack(Generic[T]):
|
|
414
|
+
def __init__(self):
|
|
415
|
+
self._items: List[T] = []
|
|
416
|
+
|
|
417
|
+
def push(self, item: T) -> None:
|
|
418
|
+
self._items.append(item)
|
|
419
|
+
|
|
420
|
+
def pop(self) -> Optional[T]:
|
|
421
|
+
return self._items.pop() if self._items else None
|
|
422
|
+
|
|
423
|
+
s = Stack()
|
|
424
|
+
s.push(10)
|
|
425
|
+
s.push(20)
|
|
426
|
+
assrt.equal(s.pop(), 20)
|
|
427
|
+
assrt.equal(s.pop(), 10)
|
|
428
|
+
# _items list was assigned correctly (not left as undefined)
|
|
429
|
+
assrt.equal(s._items.length, 0)
|
|
430
|
+
_test_attr_annotated_assign()
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
def _test_attr_annotated_assign_no_value():
|
|
434
|
+
# Annotation-only on an attribute (no rhs) should compile without error
|
|
435
|
+
# and emit nothing (attribute remains unset).
|
|
436
|
+
class Container:
|
|
437
|
+
def __init__(self):
|
|
438
|
+
self.x: int # annotation only — no assignment
|
|
439
|
+
self.y: str = 'hello'
|
|
440
|
+
|
|
441
|
+
c = Container()
|
|
442
|
+
assrt.equal(c.x, undefined)
|
|
443
|
+
assrt.equal(c.y, 'hello')
|
|
444
|
+
_test_attr_annotated_assign_no_value()
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
def _test_local_annotated_assign_complex_type():
|
|
448
|
+
# Local variable with a parameterised type annotation — the type is erased,
|
|
449
|
+
# only the assignment runs.
|
|
450
|
+
def make_pair() -> List[int]:
|
|
451
|
+
pair: List[int] = [1, 2]
|
|
452
|
+
return pair
|
|
453
|
+
|
|
454
|
+
result = make_pair()
|
|
455
|
+
assrt.equal(result.length, 2)
|
|
456
|
+
assrt.equal(result[0], 1)
|
|
457
|
+
assrt.equal(result[1], 2)
|
|
458
|
+
_test_local_annotated_assign_complex_type()
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
def _test_module_level_attr_annotated_assign():
|
|
462
|
+
# Annotated assignment on a plain object attribute at module level.
|
|
463
|
+
class Box:
|
|
464
|
+
pass
|
|
465
|
+
|
|
466
|
+
b = Box()
|
|
467
|
+
b.value: int = 42
|
|
468
|
+
assrt.equal(b.value, 42)
|
|
469
|
+
_test_module_level_attr_annotated_assign()
|