rapydscript-ns 0.8.4 → 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/.agignore +1 -1
- package/.github/workflows/ci.yml +38 -38
- package/=template.pyj +5 -5
- package/CHANGELOG.md +18 -0
- package/HACKING.md +103 -103
- package/LICENSE +24 -24
- package/README.md +715 -169
- package/TODO.md +9 -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 +6282 -3092
- 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/copy.pyj +120 -120
- package/src/lib/dataclasses.pyj +532 -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/itertools.pyj +580 -580
- 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 +9 -4
- 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/dataclasses.pyj +253 -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/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 +804 -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/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()
|
package/test/unit/index.js
CHANGED
|
@@ -98,6 +98,16 @@ function compile_with_flags(src, flags_obj) {
|
|
|
98
98
|
return output.toString();
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
// compile_python_mode simulates the default (legacy_rapydscript=false) behavior:
|
|
102
|
+
// all Python compatibility flags are enabled globally.
|
|
103
|
+
var PYTHON_MODE_FLAGS = ['dict_literals', 'overload_getitem', 'bound_methods',
|
|
104
|
+
'hash_literals', 'overload_operators', 'truthiness', 'jsx'];
|
|
105
|
+
function compile_python_mode(src) {
|
|
106
|
+
var flags = {};
|
|
107
|
+
PYTHON_MODE_FLAGS.forEach(function(f) { flags[f] = true; });
|
|
108
|
+
return compile_with_flags(src, flags);
|
|
109
|
+
}
|
|
110
|
+
|
|
101
111
|
function compile_virtual(src, virtual_files) {
|
|
102
112
|
compiler_module.set_virtual_files(virtual_files);
|
|
103
113
|
try {
|
|
@@ -805,8 +815,8 @@ var TESTS = [
|
|
|
805
815
|
'result = v"typeof undefined"',
|
|
806
816
|
'assrt.equal(result, "undefined")',
|
|
807
817
|
"arr = [1, 2, 3]",
|
|
808
|
-
'
|
|
809
|
-
"assrt.equal(
|
|
818
|
+
'leng = v"arr.length"',
|
|
819
|
+
"assrt.equal(leng, 3)",
|
|
810
820
|
'assrt.equal(v"Math.max(4, 7)", 7)',
|
|
811
821
|
].join("\n"),
|
|
812
822
|
js_checks: ["typeof undefined", "arr.length", "Math.max(4, 7)"],
|
|
@@ -3188,10 +3198,11 @@ assrt.equal(fib(15), 610)
|
|
|
3188
3198
|
var js_with = ec_with.compile(src, { python_flags: "truthiness" });
|
|
3189
3199
|
assert.ok(/if\s*\(ρσ_bool\(/.test(js_with),
|
|
3190
3200
|
"python_flags='truthiness': expected if(ρσ_bool( in: " + js_with);
|
|
3191
|
-
|
|
3192
|
-
var
|
|
3193
|
-
|
|
3194
|
-
|
|
3201
|
+
// Use legacy_rapydscript: true to get legacy mode (no python flags by default)
|
|
3202
|
+
var ec_legacy = make_ec(RapydScript, baselib, null);
|
|
3203
|
+
var js_legacy = ec_legacy.compile(src, { legacy_rapydscript: true });
|
|
3204
|
+
assert.ok(!/if\s*\(ρσ_bool\(/.test(js_legacy),
|
|
3205
|
+
"legacy mode: if(ρσ_bool( should NOT appear in: " + js_legacy);
|
|
3195
3206
|
},
|
|
3196
3207
|
},
|
|
3197
3208
|
|
|
@@ -4807,7 +4818,7 @@ assrt.equal(fib(15), 610)
|
|
|
4807
4818
|
"assrt.equal(coords[0], 10)",
|
|
4808
4819
|
"assrt.equal(coords[1], 20)",
|
|
4809
4820
|
].join("\n"),
|
|
4810
|
-
js_checks: [
|
|
4821
|
+
js_checks: [/coords\s*=\s*ρσ_list_decorate\(\s*\[\s*10,\s*20\s*\]/],
|
|
4811
4822
|
},
|
|
4812
4823
|
|
|
4813
4824
|
{
|
|
@@ -5118,6 +5129,104 @@ assrt.equal(fib(15), 610)
|
|
|
5118
5129
|
js_checks: [],
|
|
5119
5130
|
},
|
|
5120
5131
|
|
|
5132
|
+
// ── legacy_rapydscript default ────────────────────────────────────────
|
|
5133
|
+
|
|
5134
|
+
{
|
|
5135
|
+
name: "legacy_rapydscript_false_enables_overload_operators",
|
|
5136
|
+
description: "default (legacy_rapydscript=false): overload_operators active; a+b uses ρσ_op_add",
|
|
5137
|
+
run: function() {
|
|
5138
|
+
// Use unique variable names so the pattern is only from user code, not baselib
|
|
5139
|
+
var js = compile_python_mode("myval = myarg_a + myarg_b");
|
|
5140
|
+
assert.ok(js.indexOf("ρσ_op_add(myarg_a, myarg_b)") !== -1,
|
|
5141
|
+
"expected ρσ_op_add(myarg_a, myarg_b) in python mode; got:\n" + js);
|
|
5142
|
+
},
|
|
5143
|
+
},
|
|
5144
|
+
|
|
5145
|
+
{
|
|
5146
|
+
name: "legacy_rapydscript_true_no_overload_operators",
|
|
5147
|
+
description: "legacy mode (no flags): a+b uses ρσ_list_add, not ρσ_op_add",
|
|
5148
|
+
run: function() {
|
|
5149
|
+
// compile() uses no scoped_flags → no overload_operators
|
|
5150
|
+
var js = compile("myval = myarg_a + myarg_b");
|
|
5151
|
+
assert.ok(js.indexOf("ρσ_op_add(myarg_a") === -1,
|
|
5152
|
+
"unexpected ρσ_op_add(myarg_a in legacy mode; got:\n" + js);
|
|
5153
|
+
assert.ok(js.indexOf("ρσ_list_add(myarg_a") !== -1,
|
|
5154
|
+
"expected ρσ_list_add(myarg_a in legacy mode; got:\n" + js);
|
|
5155
|
+
},
|
|
5156
|
+
},
|
|
5157
|
+
|
|
5158
|
+
{
|
|
5159
|
+
name: "legacy_rapydscript_false_enables_dict_literals",
|
|
5160
|
+
description: "default (legacy_rapydscript=false): dict_literals active; {} compiles to ρσ_dict()",
|
|
5161
|
+
run: function() {
|
|
5162
|
+
var js = compile_python_mode("mydict = {}");
|
|
5163
|
+
assert.ok(js.indexOf("dict_literal") !== -1 || js.indexOf("ρσ_dict") !== -1,
|
|
5164
|
+
"expected dict() wrapper with dict_literals enabled (python mode); got:\n" + js);
|
|
5165
|
+
},
|
|
5166
|
+
},
|
|
5167
|
+
|
|
5168
|
+
{
|
|
5169
|
+
name: "legacy_rapydscript_true_no_dict_literals",
|
|
5170
|
+
description: "legacy mode (no flags): {} compiles to a plain JS object literal",
|
|
5171
|
+
run: function() {
|
|
5172
|
+
var js = compile("mydict = {}");
|
|
5173
|
+
// The assignment line should not contain dict_literal or ρσ_dict
|
|
5174
|
+
var lines = js.split("\n").filter(function(l) { return l.indexOf("mydict") !== -1; });
|
|
5175
|
+
var userline = lines.join("\n");
|
|
5176
|
+
assert.ok(userline.indexOf("dict_literal") === -1 && userline.indexOf("ρσ_dict") === -1,
|
|
5177
|
+
"unexpected dict() wrapper in legacy mode; got:\n" + userline);
|
|
5178
|
+
},
|
|
5179
|
+
},
|
|
5180
|
+
|
|
5181
|
+
{
|
|
5182
|
+
name: "legacy_rapydscript_false_enables_truthiness",
|
|
5183
|
+
description: "default (legacy_rapydscript=false): truthiness active; if-condition wrapped in ρσ_bool()",
|
|
5184
|
+
run: function() {
|
|
5185
|
+
var js = compile_python_mode("if mycond:\n pass");
|
|
5186
|
+
assert.ok(/if\s*\(ρσ_bool\(mycond/.test(js),
|
|
5187
|
+
"expected if(ρσ_bool(mycond in python mode; got:\n" + js);
|
|
5188
|
+
},
|
|
5189
|
+
},
|
|
5190
|
+
|
|
5191
|
+
{
|
|
5192
|
+
name: "legacy_rapydscript_true_no_truthiness",
|
|
5193
|
+
description: "legacy mode (no flags): if-condition is not wrapped in ρσ_bool()",
|
|
5194
|
+
run: function() {
|
|
5195
|
+
var js = compile("if mycond:\n pass");
|
|
5196
|
+
assert.ok(!/if\s*\(ρσ_bool\(mycond/.test(js),
|
|
5197
|
+
"unexpected ρσ_bool(mycond wrapping in legacy mode; got:\n" + js);
|
|
5198
|
+
},
|
|
5199
|
+
},
|
|
5200
|
+
|
|
5201
|
+
{
|
|
5202
|
+
name: "legacy_rapydscript_false_enables_bound_methods",
|
|
5203
|
+
description: "default (legacy_rapydscript=false): bound_methods active; methods are .bind()-ed",
|
|
5204
|
+
run: function() {
|
|
5205
|
+
var js = compile_python_mode([
|
|
5206
|
+
"class MyFoo:",
|
|
5207
|
+
" def mybar(self):",
|
|
5208
|
+
" return 1",
|
|
5209
|
+
].join("\n"));
|
|
5210
|
+
// Bound methods produce a .bind(this) call in the class prototype setup
|
|
5211
|
+
assert.ok(/mybar.*bind/.test(js) || /bind.*mybar/.test(js),
|
|
5212
|
+
"expected .bind() for mybar with bound_methods enabled (python mode); got:\n" + js);
|
|
5213
|
+
},
|
|
5214
|
+
},
|
|
5215
|
+
|
|
5216
|
+
{
|
|
5217
|
+
name: "legacy_rapydscript_true_no_bound_methods",
|
|
5218
|
+
description: "legacy mode (no flags): methods are not .bind()-ed",
|
|
5219
|
+
run: function() {
|
|
5220
|
+
var js = compile([
|
|
5221
|
+
"class MyFoo:",
|
|
5222
|
+
" def mybar(self):",
|
|
5223
|
+
" return 1",
|
|
5224
|
+
].join("\n"));
|
|
5225
|
+
assert.ok(!/mybar.*bind/.test(js) && !/bind.*mybar/.test(js),
|
|
5226
|
+
"unexpected .bind() for mybar in legacy mode; got:\n" + js);
|
|
5227
|
+
},
|
|
5228
|
+
},
|
|
5229
|
+
|
|
5121
5230
|
];
|
|
5122
5231
|
|
|
5123
5232
|
// ── Runner ───────────────────────────────────────────────────────────────────
|