rapydscript-ns 0.9.3 → 0.9.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/.agignore +1 -1
  2. package/.github/workflows/ci.yml +38 -38
  3. package/=template.pyj +5 -5
  4. package/CHANGELOG.md +18 -0
  5. package/HACKING.md +103 -103
  6. package/LICENSE +24 -24
  7. package/PYTHON_GAPS.md +52 -142
  8. package/README.md +51 -21
  9. package/TODO.md +1 -26
  10. package/add-toc-to-readme +2 -2
  11. package/bin/export +75 -75
  12. package/bin/rapydscript +0 -0
  13. package/bin/web-repl-export +102 -102
  14. package/build +2 -2
  15. package/language-service/index.js +88 -36
  16. package/package.json +1 -1
  17. package/publish.py +37 -37
  18. package/release/baselib-plain-pretty.js +157 -31
  19. package/release/baselib-plain-ugly.js +5 -5
  20. package/release/compiler.js +724 -426
  21. package/release/signatures.json +29 -29
  22. package/session.vim +4 -4
  23. package/setup.cfg +2 -2
  24. package/src/ast.pyj +7 -0
  25. package/src/baselib-containers.pyj +41 -4
  26. package/src/baselib-errors.pyj +4 -3
  27. package/src/baselib-internal.pyj +47 -18
  28. package/src/baselib-str.pyj +16 -3
  29. package/src/compiler.pyj +36 -36
  30. package/src/errors.pyj +30 -30
  31. package/src/lib/aes.pyj +646 -646
  32. package/src/lib/collections.pyj +227 -3
  33. package/src/lib/copy.pyj +120 -120
  34. package/src/lib/elementmaker.pyj +83 -83
  35. package/src/lib/encodings.pyj +126 -126
  36. package/src/lib/gettext.pyj +569 -569
  37. package/src/lib/itertools.pyj +580 -580
  38. package/src/lib/math.pyj +193 -193
  39. package/src/lib/operator.pyj +11 -11
  40. package/src/lib/pprint.pyj +455 -0
  41. package/src/lib/random.pyj +118 -118
  42. package/src/lib/react.pyj +74 -74
  43. package/src/lib/statistics.pyj +0 -0
  44. package/src/lib/traceback.pyj +63 -63
  45. package/src/lib/uuid.pyj +77 -77
  46. package/src/monaco-language-service/completions.js +21 -14
  47. package/src/monaco-language-service/diagnostics.js +2 -2
  48. package/src/monaco-language-service/dts.js +58 -15
  49. package/src/monaco-language-service/package.json +3 -0
  50. package/src/output/classes.pyj +25 -2
  51. package/src/output/codegen.pyj +4 -1
  52. package/src/output/comments.pyj +45 -45
  53. package/src/output/exceptions.pyj +201 -201
  54. package/src/output/jsx.pyj +164 -164
  55. package/src/output/treeshake.pyj +182 -182
  56. package/src/output/utils.pyj +72 -72
  57. package/src/parse.pyj +42 -7
  58. package/src/string_interpolation.pyj +72 -72
  59. package/src/tokenizer.pyj +18 -2
  60. package/src/unicode_aliases.pyj +576 -576
  61. package/src/utils.pyj +192 -192
  62. package/test/_import_one.pyj +37 -37
  63. package/test/_import_two/__init__.pyj +11 -11
  64. package/test/_import_two/level2/deep.pyj +4 -4
  65. package/test/_import_two/other.pyj +6 -6
  66. package/test/_import_two/sub.pyj +13 -13
  67. package/test/aes_vectors.pyj +421 -421
  68. package/test/annotations.pyj +80 -80
  69. package/test/baselib.pyj +23 -0
  70. package/test/chainmap.pyj +185 -0
  71. package/test/dataclasses.pyj +3 -4
  72. package/test/decorators.pyj +77 -77
  73. package/test/docstrings.pyj +39 -39
  74. package/test/elementmaker_test.pyj +45 -45
  75. package/test/enum.pyj +1 -1
  76. package/test/functions.pyj +151 -151
  77. package/test/generators.pyj +41 -41
  78. package/test/generic.pyj +370 -370
  79. package/test/internationalization.pyj +73 -73
  80. package/test/lint.pyj +164 -164
  81. package/test/loops.pyj +85 -85
  82. package/test/numpy.pyj +734 -734
  83. package/test/pprint.pyj +232 -0
  84. package/test/python_features.pyj +1 -1
  85. package/test/repl.pyj +121 -121
  86. package/test/scoped_flags.pyj +76 -76
  87. package/test/statistics.pyj +224 -0
  88. package/test/str.pyj +4 -4
  89. package/test/unit/index.js +455 -0
  90. package/test/unit/language-service-completions.js +2 -0
  91. package/test/unit/language-service-dts.js +113 -0
  92. package/test/unit/language-service-hover.js +455 -455
  93. package/test/unit/language-service.js +135 -2
  94. package/test/unit/web-repl.js +349 -1
  95. package/tools/compiler.d.ts +367 -367
  96. package/tools/completer.js +131 -131
  97. package/tools/export.js +4 -2
  98. package/tools/gettext.js +185 -185
  99. package/tools/ini.js +65 -65
  100. package/tools/msgfmt.js +187 -187
  101. package/tools/repl.js +223 -223
  102. package/tools/test.js +118 -118
  103. package/tools/utils.js +141 -128
  104. package/tools/web_repl.js +95 -95
  105. package/try +41 -41
  106. package/web-repl/env.js +196 -196
  107. package/web-repl/index.html +163 -163
  108. package/web-repl/prism.css +139 -139
  109. package/web-repl/prism.js +113 -113
  110. package/web-repl/rapydscript.js +228 -226
  111. package/web-repl/sha1.js +25 -25
package/PYTHON_GAPS.md CHANGED
@@ -5,6 +5,9 @@ or behave differently in RapydScript-NS, with a focus on what is relevant and us
5
5
  browser context. Server-side features (file I/O, subprocesses, sockets, threading, etc.)
6
6
  are excluded as they do not apply.
7
7
 
8
+ Items that are fully supported — even if only behind a flag — are not listed here. See the
9
+ README for the full feature and module support tables.
10
+
8
11
  ---
9
12
 
10
13
  ## 1. Silent Behavioral Differences (Gotchas)
@@ -12,23 +15,7 @@ are excluded as they do not apply.
12
15
  These features exist but behave differently from Python in ways that will silently produce
13
16
  wrong results — no error is raised.
14
17
 
15
- ### 1.1 `%` Modulo on Negative Numbers *(partially resolved)*
16
-
17
- **Python:** `%` always returns a non-negative result (true modulo).
18
- ```python
19
- -7 % 3 # → 2 (Python)
20
- -7 % 3 # → -1 (RapydScript default — JS remainder semantics)
21
- ```
22
- **Status:** Fixed when using `from __python__ import overload_operators` or
23
- `from __python__ import python_modulo`. In bare mode (no flags) the JS remainder semantics
24
- still apply.
25
-
26
- **Impact:** Any algorithm relying on modular arithmetic (wrapping, circular indexing) may
27
- produce wrong results in code that does not use the Python operator flags.
28
-
29
- ---
30
-
31
- ### 1.2 `is` / `is not` — Identity vs. Equality
18
+ ### 1.1 `is` / `is not` Identity vs. Equality
32
19
 
33
20
  **Python:** `is` tests object identity (pointer comparison).
34
21
  **RapydScript:** `is` compiles to `===` (strict equality), so `x is y` is true whenever
@@ -48,43 +35,7 @@ The recommended pattern — using a unique sentinel object — works correctly.
48
35
 
49
36
  ---
50
37
 
51
- ### 1.3 Function Arguments: `TypeError` on Wrong Count *(opt-in, not default)*
52
-
53
- **Python:** Too few or too many positional arguments raises `TypeError` at call time.
54
- **RapydScript default:** Extra args are silently discarded; missing args become `undefined`.
55
-
56
- ```python
57
- def f(a, b):
58
- return a + b
59
-
60
- f(1, 2, 3) # Python: TypeError. RapydScript default: silently returns 3
61
- f(1) # Python: TypeError. RapydScript default: returns NaN (1 + undefined)
62
- ```
63
- **Status:** Argument count and type-annotation enforcement is now available via the
64
- `type_enforce` flag on function definitions (emits runtime checks). Not enabled by default
65
- because it adds overhead to every call. Developers writing library code with strict APIs
66
- should opt in.
67
-
68
- ---
69
-
70
- ### 1.4 Positional-Only and Keyword-Only Parameters Not Enforced
71
-
72
- **Python:** Positional-only params (`/`) cannot be passed by keyword; keyword-only params
73
- (`*`) cannot be passed positionally. Both raise `TypeError` on violations.
74
- **RapydScript:** Violations are silently discarded — the param gets `undefined` with no error.
75
-
76
- ```python
77
- def f(a, b, /, *, c):
78
- return a + b + c
79
-
80
- f(1, b=2, c=3) # Python: TypeError (b is positional-only)
81
- # RapydScript: b silently becomes undefined, a=1, c=3
82
- ```
83
- **Impact:** API design contracts expressed via param ordering are not enforced.
84
-
85
- ---
86
-
87
- ### 1.5 String Encoding — UTF-16 Surrogate Pairs
38
+ ### 1.2 String Encoding UTF-16 Surrogate Pairs
88
39
 
89
40
  **Python:** Strings are sequences of Unicode code points (full 21-bit range).
90
41
  **RapydScript:** Strings are JS strings — UTF-16. Emoji and other non-BMP characters
@@ -103,7 +54,7 @@ produce wrong lengths or corrupt characters when sliced.
103
54
 
104
55
  ---
105
56
 
106
- ### 1.6 `global` Scoping in Nested Functions
57
+ ### 1.3 `global` Scoping in Nested Functions
107
58
 
108
59
  **Python:** `global x` inside a nested function forces `x` to refer to the module-level
109
60
  variable, bypassing any intermediate closure scopes.
@@ -115,7 +66,7 @@ scope silently.
115
66
 
116
67
  ---
117
68
 
118
- ### 1.7 Numeric Dict Keys Are Coerced to Strings
69
+ ### 1.4 Numeric Dict Keys Are Coerced to Strings
119
70
 
120
71
  **Python:** `d = {}; d[1] = 'a'; d['1'] = 'b'; len(d) == 2` (integer and string keys distinct).
121
72
  **RapydScript:** The Python `dict` type (backed by ES6 `Map`) stores them distinctly, but
@@ -126,17 +77,7 @@ when interoperating with JS APIs that return plain objects.
126
77
 
127
78
  ---
128
79
 
129
- ### 1.8 `Exception.args` vs `.message`
130
-
131
- **Python:** `Exception('msg').args == ('msg',)` and `.message` is not a standard attribute.
132
- **RapydScript:** `.message` is the primary attribute (JS `Error` convention). `.args` is
133
- not populated as a tuple with the message.
134
-
135
- **Impact:** Code accessing `.args[0]` to get the error message will get `undefined`.
136
-
137
- ---
138
-
139
- ### 1.9 Multiple Inheritance MRO
80
+ ### 1.5 Multiple Inheritance MRO
140
81
 
141
82
  **Python:** C3 linearization guarantees a deterministic and consistent method resolution order.
142
83
  **RapydScript:** Built on the JS prototype chain. In diamond inheritance or complex hierarchies
@@ -149,18 +90,7 @@ name. Hard to debug because no error is raised.
149
90
 
150
91
  ## 2. Missing Language Features
151
92
 
152
- ### 2.1 `__slots__` Not Enforced
153
-
154
- `__slots__ = ['x', 'y']` is parsed and accepted but has no runtime effect — arbitrary
155
- attributes can still be set on instances. No `AttributeError` is raised for assignments to
156
- undeclared attributes.
157
-
158
- **Browser relevance:** Used frequently for memory efficiency and API documentation. Enforcement
159
- via `Object.seal()` or a `Proxy`-based guard would be possible in modern browsers.
160
-
161
- ---
162
-
163
- ### 2.2 `__del__` Destructor — No Guaranteed Finalizer
93
+ ### 2.1 `__del__` Destructor — No Guaranteed Finalizer
164
94
 
165
95
  `__del__` methods are not called reliably. JavaScript's GC is non-deterministic and provides
166
96
  no equivalent to CPython's reference-counting finalizer.
@@ -173,15 +103,17 @@ that timing is not guaranteed.
173
103
 
174
104
  ---
175
105
 
176
- ### 2.3 `locals()` Always Returns Empty Dict
106
+ ### 2.2 `locals()` Always Returns Empty Dict
177
107
 
178
- JavaScript provides no mechanism to introspect local variables at runtime. `locals()` always
179
- returns an empty dict. Python code that uses `locals()` for string template substitution
108
+ `vars()`, `locals()`, and `globals()` all exist as builtins. JavaScript provides no mechanism
109
+ to introspect local variables at runtime, so `locals()` always returns an empty dict.
110
+ `globals()` works on module-level/global state, and `vars(obj)` introspects the passed object.
111
+ Python code that uses `locals()` for string template substitution
180
112
  (e.g., `'{x}'.format(**locals())`) will break silently.
181
113
 
182
114
  ---
183
115
 
184
- ### 2.4 `from module import *` Not Allowed
116
+ ### 2.3 `from module import *` Not Allowed
185
117
 
186
118
  Star imports are intentionally unsupported to prevent namespace pollution. Python developers
187
119
  who rely on them (e.g., `from math import *`) must enumerate imports explicitly.
@@ -190,13 +122,15 @@ who rely on them (e.g., `from math import *`) must enumerate imports explicitly.
190
122
 
191
123
  ---
192
124
 
193
- ### 2.5 `asynccontextmanager` Not Available
125
+ ### 2.4 `asynccontextmanager` Not Available
194
126
 
195
127
  `contextlib.asynccontextmanager` is absent. Only synchronous `@contextmanager` is implemented.
128
+ `async with` itself is also not supported — async context managers need `.acquire()`/`.release()`
129
+ calls instead.
196
130
 
197
131
  ---
198
132
 
199
- ### 2.6 f-string Debugging Format `f'{x=}'` Not Supported
133
+ ### 2.5 f-string Debugging Format `f'{x=}'` Not Supported
200
134
 
201
135
  Python 3.8+ supports `f'{x=}'` which expands to `f'x={repr(x)}'`. This is not implemented.
202
136
 
@@ -207,7 +141,7 @@ print(f'{x=}') # Python: "x=42". RapydScript: syntax error or wrong output
207
141
 
208
142
  ---
209
143
 
210
- ### 2.7 Ellipsis Evaluates to `undefined`
144
+ ### 2.6 Ellipsis Evaluates to `undefined`
211
145
 
212
146
  `...` (Ellipsis) parses as a valid expression but evaluates to JS `undefined` rather than
213
147
  Python's `Ellipsis` singleton object. Code that stores `...` in containers or checks
@@ -219,26 +153,35 @@ Python's `Ellipsis` singleton object. Code that stores `...` in containers or ch
219
153
 
220
154
  These are absent from `src/lib/` and have no substitute.
221
155
 
222
- ### 3.1 `decimal` Decimal Arithmetic
156
+ ### 3.1 `enum.IntEnum`, `IntFlag`, and `Flag`
223
157
 
224
- `Decimal` arithmetic avoids floating-point rounding errors. Essential for financial
225
- calculations in browser apps (e-commerce, budgeting tools). JS does not have a built-in
226
- equivalent; a pure-JS implementation would need to be compiled in.
158
+ The `enum` module provides `Enum` but not `IntEnum` (auto-comparable with integers),
159
+ `StrEnum` (Python 3.11+), `IntFlag`, or `Flag` (bitfield enums). These are common in protocol
160
+ implementations, permission systems, and state machines.
161
+
162
+ ```python
163
+ from enum import IntEnum
164
+ class Color(IntEnum):
165
+ RED = 1
166
+ GREEN = 2
167
+ Color.RED < Color.GREEN # True — comparison with int semantics
168
+ ```
227
169
 
228
170
  ---
229
171
 
230
- ### 3.2 `fractions` — Rational Arithmetic
172
+ ### 3.2 `hashlib` — Cryptographic Hashing
231
173
 
232
- `Fraction(numerator, denominator)` with full arithmetic. Useful for music theory apps,
233
- math tutoring tools, and any domain requiring exact rational computation.
174
+ `hashlib.sha256`, `hashlib.md5`, etc. The Web Crypto API provides `crypto.subtle.digest`
175
+ but its async/buffer-based interface is awkward. A thin `hashlib`-compatible wrapper over
176
+ `crypto.subtle` with a synchronous-friendly API (using the sync `crypto.getRandomValues`)
177
+ for non-cryptographic hashes would be valuable.
234
178
 
235
179
  ---
236
180
 
237
- ### 3.3 `statistics` — Statistical Functions
181
+ ### 3.3 `fractions` — Rational Arithmetic
238
182
 
239
- `mean`, `median`, `mode`, `stdev`, `variance`, `quantiles`. Very commonly needed in
240
- data-visualization browser apps. Currently `numpy` covers much of this but `statistics`
241
- is lighter-weight and doesn't require the full numpy import.
183
+ `Fraction(numerator, denominator)` with full arithmetic. Useful for music theory apps,
184
+ math tutoring tools, and any domain requiring exact rational computation.
242
185
 
243
186
  ---
244
187
 
@@ -249,42 +192,11 @@ for browser-based code editors, version comparison tools, and fuzzy matching UIs
249
192
 
250
193
  ---
251
194
 
252
- ### 3.5 `pprint` — Pretty Printing
253
-
254
- `pprint.pformat` / `pprint.pprint`. Primarily a debugging/REPL aid. Given the in-browser
255
- REPL in this project, implementing `pprint` would make output more readable.
256
-
257
- ---
195
+ ### 3.5 `decimal` — Decimal Arithmetic
258
196
 
259
- ### 3.6 `hashlib` Cryptographic Hashing
260
-
261
- `hashlib.sha256`, `hashlib.md5`, etc. The Web Crypto API provides `crypto.subtle.digest`
262
- but its async/buffer-based interface is awkward. A thin `hashlib`-compatible wrapper over
263
- `crypto.subtle` with a synchronous-friendly API (using the sync `crypto.getRandomValues`)
264
- for non-cryptographic hashes would be valuable.
265
-
266
- ---
267
-
268
- ### 3.7 `enum.IntEnum` and `enum.Flag`
269
-
270
- The `enum` module provides `Enum` but not `IntEnum` (auto-comparable with integers),
271
- `StrEnum` (Python 3.11+), or `Flag` (bitfield enums). These are common in protocol
272
- implementations, permission systems, and state machines.
273
-
274
- ```python
275
- from enum import IntEnum
276
- class Color(IntEnum):
277
- RED = 1
278
- GREEN = 2
279
- Color.RED < Color.GREEN # True — comparison with int semantics
280
- ```
281
-
282
- ---
283
-
284
- ### 3.8 `collections.ChainMap`
285
-
286
- `ChainMap` provides a multi-level dict lookup (like layered config or scope chains) without
287
- copying. Not currently in `collections.pyj`.
197
+ `Decimal` arithmetic avoids floating-point rounding errors. Essential for financial
198
+ calculations in browser apps (e-commerce, budgeting tools). JS does not have a built-in
199
+ equivalent; a pure-JS implementation would need to be compiled in.
288
200
 
289
201
  ---
290
202
 
@@ -404,17 +316,15 @@ The `jstype()` builtin is the RS equivalent of JS's `typeof`.
404
316
 
405
317
  ## 5. Summary Priority Table
406
318
 
319
+ Priority weighs frequency-of-need, effort-to-implement, and whether a workaround exists.
320
+
407
321
  | Priority | Feature | Effort | Impact |
408
322
  |---|---|---|---|
409
- | High | `enum.IntEnum`, `Flag` | Medium | Protocol and permission modeling |
410
- | High | `statistics` module | Low | Data analysis without full numpy import |
411
- | Medium | `pprint` module | Low | REPL output quality |
412
- | Medium | `collections.ChainMap` | Low | Multi-level scope/config dicts |
413
- | Medium | f-string `f'{x=}'` debugging format | Low | Developer experience |
414
- | Medium | `__slots__` enforcement via `Proxy` | Medium | Memory + API documentation |
323
+ | High | `enum.IntEnum`, `IntFlag`, `Flag` | Medium | Protocol and permission modeling; bitfield enums |
324
+ | Medium | `hashlib` shim over Web Crypto | Medium | Avoids verbatim Web Crypto calls in user code |
415
325
  | Medium | `fractions` module | Medium | Exact rational arithmetic |
416
- | Medium | `hashlib` shim over Web Crypto | Medium | Hashing without verbatim JS |
417
- | Low | `decimal` module | High | Financial calculations |
418
- | Low | `difflib` module | High | Text diff, fuzzy matching |
419
- | Low | `asynccontextmanager` | Medium | Async resource management |
326
+ | Medium | f-string `f'{x=}'` debugging format | Low | Developer experience |
327
+ | Low | `asynccontextmanager` + `async with` | Medium | Async resource management |
420
328
  | Low | `__del__` via `FinalizationRegistry` | Medium | Resource cleanup (best-effort) |
329
+ | Low | `difflib` module | High | Text diff, fuzzy matching |
330
+ | Low | `decimal` module | High | Financial calculations |
package/README.md CHANGED
@@ -1381,19 +1381,28 @@ ba += bytearray([7, 8]) # in-place concatenation
1381
1381
 
1382
1382
  RapydScript provides a `long` builtin backed by JavaScript's native `BigInt`,
1383
1383
  giving you arbitrary-precision integers beyond the safe range of JS `Number`.
1384
+ You can use the `long()` function or Python-style `n` suffix literals:
1384
1385
 
1385
1386
  ```python
1387
+ # Literal syntax (preferred)
1388
+ a = 10n
1389
+ b = 3n
1390
+ c = 0xFFn # hex
1391
+ d = 0b1010n # binary
1392
+ e = 0o77n # octal
1393
+
1394
+ # Function syntax
1386
1395
  a = long(10)
1387
1396
  b = long(3)
1388
1397
 
1389
- a + b # long(13)
1390
- a - b # long(7)
1391
- a * b # long(30)
1392
- a // b # long(3) — floor division (Python semantics, not JS truncation)
1393
- a % b # long(1) — Python-style modulo (result has same sign as divisor)
1394
- a ** b # long(1000)
1395
- long(-7) // long(2) # long(-4) — floors toward −∞ (JS BigInt would give −3)
1396
- long(-7) % long(3) # long(2) — result matches sign of divisor
1398
+ a + b # 13n
1399
+ a - b # 7n
1400
+ a * b # 30n
1401
+ a // b # 3n — floor division (Python semantics, not JS truncation)
1402
+ a % b # 1n — Python-style modulo (result has same sign as divisor)
1403
+ a ** b # 1000n
1404
+ long(-7) // long(2) # -4n — floors toward −∞ (JS BigInt would give −3)
1405
+ long(-7) % long(3) # 2n — result matches sign of divisor
1397
1406
  ```
1398
1407
 
1399
1408
  #### Precision beyond JS Number
@@ -1402,9 +1411,13 @@ The main motivation for `long` is numbers outside the safe integer range of
1402
1411
  JavaScript `Number` (`2^53 − 1 = 9007199254740991`):
1403
1412
 
1404
1413
  ```python
1405
- n = long('9007199254740993') # 2^53 + 1 — cannot be represented as a JS Number
1406
- str(n) # '9007199254740993' (exact)
1407
- str(n + long(1)) # '9007199254740994' (still exact)
1414
+ n = 9007199254740993n # 2^53 + 1 — cannot be represented as a JS Number
1415
+ str(n) # '9007199254740993' (exact)
1416
+ str(n + 1n) # '9007199254740994' (still exact)
1417
+
1418
+ # equivalent using long():
1419
+ n = long('9007199254740993')
1420
+ str(n + long(1))
1408
1421
  ```
1409
1422
 
1410
1423
  #### Operator overloading
@@ -3299,16 +3312,26 @@ finally:
3299
3312
  cleanup()
3300
3313
  ```
3301
3314
 
3315
+ Like Python, exceptions accept variadic arguments stored in `.args`:
3316
+
3317
+ ```py
3318
+ e = ValueError('bad input', 42)
3319
+ print(e.args) # ['bad input', 42]
3320
+ print(e.args[0]) # 'bad input'
3321
+ print(e.message) # 'bad input' (first arg, for JS Error compatibility)
3322
+ ```
3323
+
3302
3324
  You can create your own Exception classes by inheriting from `Exception`, which
3303
3325
  is the JavaScript Error class, for more details on this, see the [MDN documentation](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Error).
3304
3326
 
3305
3327
  ```py
3306
3328
  class MyError(Exception):
3307
- def __init__(self, message):
3308
- self.name = 'MyError'
3309
- self.message = message
3329
+ def __init__(self, code, detail):
3330
+ Exception.__init__(self, code, detail)
3331
+ self.code = code
3332
+ self.detail = detail
3310
3333
 
3311
- raise MyError('This is a custom error!')
3334
+ raise MyError(404, 'not found')
3312
3335
  ```
3313
3336
 
3314
3337
  You can catch multiple exception types in one `except` clause. Both the comma form and the tuple form are supported:
@@ -3455,7 +3478,7 @@ One of Python's main strengths is the number of libraries available to the devel
3455
3478
  # fields(), asdict(), astuple(), replace(), is_dataclass(), frozen=True, order=True
3456
3479
  abc # ABC base class, @abstractmethod, Protocol, @runtime_checkable;
3457
3480
  # abstract enforcement at instantiation; ABC.register() virtual subclasses
3458
- collections # namedtuple, deque, Counter, OrderedDict, defaultdict
3481
+ collections # namedtuple, deque, Counter, OrderedDict, defaultdict, ChainMap
3459
3482
  copy # copy (shallow), deepcopy; honours __copy__ / __deepcopy__ hooks
3460
3483
  typing # TYPE_CHECKING, Any, Union, Optional, List, Dict, Set, Tuple, TypeVar,
3461
3484
  # Generic, Protocol, Callable, Literal, Final, TypedDict, NamedTuple,
@@ -3465,6 +3488,8 @@ One of Python's main strengths is the number of libraries available to the devel
3465
3488
  # product, permutations, combinations, combinations_with_replacement
3466
3489
  io # StringIO (in-memory text stream), BytesIO (in-memory binary stream)
3467
3490
  base64 # b64encode/decode, urlsafe_b64encode/decode, b32encode/decode, b16encode/decode, encodebytes/decodebytes
3491
+ statistics # mean, median, mode, variance, stdev, quantiles, correlation,
3492
+ # linear_regression, NormalDist
3468
3493
 
3469
3494
  For the most part, the logic implemented in these libraries functions identically to the Python versions. I'd be happy to include more libraries, if other members of the community want them. However, unlike most other Python-to-JavaScript compilers, RapydScript doesn't need them to be complete since there are already tons of available JavaScript libraries that it can use natively.
3470
3495
 
@@ -3998,6 +4023,7 @@ Python Feature Coverage
3998
4023
  | `super()` — 0-arg and 2-arg forms | `super().method()` and `super(Cls, self).method()` both work |
3999
4024
  | `except TypeA, TypeB as e:` | RapydScript comma-separated form; catches multiple exception types |
4000
4025
  | `except (TypeA, TypeError) as e:` | Tuple form also supported |
4026
+ | `Exception.args` variadic constructor | `Exception('a', 'b').args == ['a', 'b']`; `.message` set to first arg for JS compatibility; works on all builtin and custom exception subclasses |
4001
4027
  | `except*` / `ExceptionGroup` (Python 3.11+) | Full support: `ExceptionGroup` class with `subgroup()`/`split()`; `except*` dispatches to typed handlers, re-raises unmatched; bare `except*:` catches all remaining |
4002
4028
  | `try / else` | `else` block runs only when no exception was raised |
4003
4029
  | `for / else` | `else` block runs when loop completes without `break`; nested break isolation works |
@@ -4044,6 +4070,7 @@ Python Feature Coverage
4044
4070
  | f-strings, `str.format()`, `format()` builtin, all common `str.*` methods | Fully supported; includes `f'{x=}'` debug format (prints `x=<value>`), format-spec (`f'{x=:.2f}'`), and conversion (`f'{x=!r}'`) |
4045
4071
  | `abs()`, `divmod()`, `any()`, `all()`, `sum()`, `min()`, `max()` | All work |
4046
4072
  | `sorted()`, `reversed()`, `zip()`, `map()`, `filter()` | All work |
4073
+ | `list.sort()` / `sorted()` — `key`, `reverse`, comparators, `__lt__` | `key` and `reverse` keyword arguments both work; a positional **two-argument** function is auto-detected as a comparator (JS-style `.sort((a, b) => …)` / `functools.cmp_to_key` semantics) while a one-argument function is treated as a `key`; custom objects are ordered through their `__lt__` method (with a reflected `__gt__` fallback); the sort is stable, so equal elements keep their original order even under `reverse=True` |
4047
4074
  | `zip(strict=True)` | Raises `ValueError` when iterables have different lengths; equal-length iterables work normally |
4048
4075
  | `set` with full union/intersection/difference API | Fully supported |
4049
4076
  | `isinstance()`, `hasattr()`, `getattr()`, `setattr()`, `dir()` | All work |
@@ -4078,7 +4105,7 @@ Python Feature Coverage
4078
4105
  | `float.is_integer()` | Returns `True` if the float has no fractional part (i.e. is a whole number), `False` otherwise. `float('inf').is_integer()` and `float('nan').is_integer()` both return `False`, matching Python semantics. Added to `Number.prototype` in the baselib so it works on any numeric literal or variable. |
4079
4106
  | `int.bit_length()` | Returns the number of bits needed to represent the integer in binary, excluding the sign and leading zeros. `(0).bit_length()` → `0`; `(255).bit_length()` → `8`; `(256).bit_length()` → `9`; sign is ignored (`(-5).bit_length()` → `3`). Added to `Number.prototype` in the baselib. |
4080
4107
  | Arithmetic type coercion — `TypeError` on incompatible operands | `1 + '1'` raises `TypeError: unsupported operand type(s) for +: 'int' and 'str'`; all arithmetic operators (`+`, `-`, `*`, `/`, `//`, `%`, `**`) enforce compatible types in their `ρσ_op_*` helpers. `bool` is treated as numeric (like Python's `int` subclass). Activated by `overload_operators` (on by default). String `+` string and numeric `+` numeric are allowed; mixed types raise `TypeError` with a Python-style message. |
4081
- | `long(val[, base])` — arbitrary-precision integers | Backed by JS `BigInt`. `long(42)`, `long('ff', 16)`, `long('1010', 2)`, `long(True)`. Arithmetic: `+`, `-`, `*`, `//`, `%`, `**` — `//` and `%` follow Python floor-division semantics (floor toward −∞), not JS BigInt truncation. `/` raises `TypeError` (use `//`). Bitwise: `&`, `\|`, `^`, `<<`, `>>`. Comparisons: `<`, `<=`, `>`, `>=`, `==`, `!=`. `isinstance(x, long)` works. Mixing `long` with `int` or `float` raises `TypeError`. Requires `BigInt` (Chrome 67+, Firefox 68+, Safari 14+, Node 10.3+). |
4108
+ | `long(val[, base])` and `42n` literal — arbitrary-precision integers | Backed by JS `BigInt`. Literal syntax: `42n`, `0xFFn`, `0b1010n`, `0o77n`. Function syntax: `long(42)`, `long('ff', 16)`, `long('1010', 2)`, `long(True)`. Arithmetic: `+`, `-`, `*`, `//`, `%`, `**` — `//` and `%` follow Python floor-division semantics (floor toward −∞), not JS BigInt truncation. `/` raises `TypeError` (use `//`). Bitwise: `&`, `\|`, `^`, `<<`, `>>`. Comparisons: `<`, `<=`, `>`, `>=`, `==`, `!=`. `isinstance(x, long)` works. Mixing `long` with `int` or `float` raises `TypeError`. Requires `BigInt` (Chrome 67+, Firefox 68+, Safari 14+, Node 10.3+). |
4082
4109
  | `complex(real=0, imag=0)` and complex literals `3+4j` | Full complex number type via `ρσ_complex` class. `complex(real, imag)`, `complex(string)` (parses `'3+4j'`), and `j`/`J` imaginary literal suffix (e.g. `4j`, `3.5J`). Attributes: `.real`, `.imag`. Methods: `conjugate()`, `__abs__()`, `__bool__()`, `__repr__()`, `__str__()`. Arithmetic: `+`, `-`, `*`, `/`, `**` via dunder methods (or operator overloading with `overload_operators`). `abs(z)` dispatches `__abs__`. `isinstance(z, complex)` works. String representation matches Python: `(3+4j)`, `4j`, `(3-0j)`. |
4083
4110
  | `eval(expr[, globals[, locals]])` | String literals are compiled as **RapydScript source** at compile time (the compiler parses and transpiles the string, just like Python's `eval` takes Python source). `eval(expr)` maps to native JS direct `eval` for scope access. `eval(expr, globals)` / `eval(expr, globals, locals)` use `Function` constructor with explicit bindings; `locals` override `globals`. Runtime `ρσ_` helpers referenced in the compiled string are automatically injected into the Function scope. Only string *literals* are transformed at compile time; dynamic strings are passed through unchanged. |
4084
4111
  | `exec(code[, globals[, locals]])` | String literals are compiled as **RapydScript source** at compile time. Executes the compiled code string; always returns `None`. Without `globals`/`locals` uses native `eval` (scope access). With `globals`/`locals` uses `Function` constructor — mutable objects (lists, dicts) passed in `globals` are accessible by reference, so side-effects are visible after the call. `ρσ_dict` instances (created when `dict_literals` flag is active) are correctly unwrapped via their `jsmap` backing store. |
@@ -4142,7 +4169,6 @@ This restores the original RapydScript behavior: plain JS objects for `{}`, no o
4142
4169
 
4143
4170
  | Feature | Notes |
4144
4171
  |---------------------------------------|-----------------------------------------------------------------------------------------|
4145
- | `__slots__` enforcement | Accepted, but does not restrict attribute assignment |
4146
4172
  | `locals()` | Returns an empty `dict`. JS has no runtime mechanism for introspecting local variables. |
4147
4173
  | `input(prompt)` | There is no simple cli input in browser; use `prompt()` |
4148
4174
  | `compile()` | Python compile/code objects have no JS equivalent |
@@ -4163,7 +4189,7 @@ Modules with a `src/lib/` implementation available are marked ✅. All others ar
4163
4189
  | `random` | ✅ | RC4-seeded PRNG in `src/lib/random.pyj` |
4164
4190
  | `re` | ✅ | Regex wrapper in `src/lib/re.pyj`; uses the JS engine — full PCRE-level support on modern runtimes: positive/negative lookbehind (ES2018+, including variable-width), unicode via automatic `u` flag (ES2015+), `re.fullmatch()`, `re.S`/`re.NOFLAG` aliases. `MatchObject.start()`/`.end()` return exact positions on runtimes with the ES2022 `d` flag (Node 18+); heuristic fallback on older runtimes. Conditional groups `(?(id)yes\|no)` are not supported (JS limitation) and raise `re.error`. |
4165
4191
  | `encodings` | ✅ | UTF-8 encode/decode helpers and low-level base64 utilities; for the standard Python API use the `base64` module instead |
4166
- | `collections` | ✅ | `defaultdict`, `Counter`, `OrderedDict`, `deque` |
4192
+ | `collections` | ✅ | `defaultdict`, `Counter`, `OrderedDict`, `deque`, `namedtuple`, `ChainMap` |
4167
4193
  | `functools` | ✅ | `reduce`, `partial`, `wraps`, `lru_cache` |
4168
4194
  | `itertools` | ✅ | Common iteration tools |
4169
4195
  | `numpy` | ✅ | Full numpy-like library in `src/lib/numpy.pyj`; `numpy.random` and `numpy.linalg` sub-modules |
@@ -4187,11 +4213,15 @@ Modules with a `src/lib/` implementation available are marked ✅. All others ar
4187
4213
  | `textwrap` | ✅ | `wrap(text[, width=70[, ...]])`, `fill(text[, width=70[, ...]])`, `shorten(text, width[, ...])`, `dedent(text)`, `indent(text, prefix[, predicate])`, `TextWrapper` class in `src/lib/textwrap.pyj`; `TextWrapper` supports all standard options: `width`, `initial_indent`, `subsequent_indent`, `expand_tabs`, `tabsize`, `replace_whitespace`, `fix_sentence_endings`, `break_long_words`, `break_on_hyphens`, `drop_whitespace`, `max_lines`, `placeholder`; module-level `wrap`/`fill`/`shorten` accept the same options as explicit keyword parameters; `shorten()` normalises internal whitespace before truncating; `break_on_hyphens=True` (default) splits on em-dashes (2+ hyphens) between word characters, matching Python 3 behaviour |
4188
4214
  | `logging` | ✅ | `getLogger(name)`, `basicConfig(**kwargs)`, `debug/info/warning/error/critical/exception/log()` module-level shortcuts, `disable(level)`, `addLevelName(level, name)`, `getLevelName(level)`, `captureWarnings(capture)`, `makeLogRecord(dict)`, `setLogRecordFactory(factory)`, `getLogRecordFactory()` in `src/lib/logging.pyj`; `Logger` class with `setLevel`, `isEnabledFor`, `getEffectiveLevel`, `addHandler`, `removeHandler`, `hasHandlers`, `addFilter`, `removeFilter`, `debug/info/warning/error/critical/exception/log` methods; `Handler` base class; `StreamHandler(stream=None)` (writes to `console.debug/info/warn/error` when no stream given, or to any object with `.write(s)`); `NullHandler`; `Formatter(fmt, datefmt, style)` with full `%(attr)s/d/f` record formatting and `formatTime(record, datefmt)` (supports `%Y %m %d %H %M %S %f` strftime codes); `Filter(name)` and `Filterer` mixin; `LogRecord` with `getMessage()` supporting `%s/%d/%f/%e/%g/%x/%o/%%` %-formatting; level constants `NOTSET=0`, `DEBUG=10`, `INFO=20`, `WARNING=30`, `ERROR=40`, `CRITICAL=50`; full logger hierarchy (dotted names, `propagate`, `parent`); `lastResort` handler; note: `FileHandler` raises `NotImplementedError` (no file I/O in JS); `exception()` logs at ERROR without automatic exc_info capture |
4189
4215
  | `heapq` | ✅ | `heappush(heap, item)`, `heappop(heap)`, `heapify(x)`, `heapreplace(heap, item)`, `heappushpop(heap, item)`, `nsmallest(n, iterable[, key])`, `nlargest(n, iterable[, key])` in `src/lib/heapq.pyj`; min-heap operations on plain lists; heap invariant maintained by sift-up/sift-down (ported from CPython); `nsmallest`/`nlargest` support an optional `key` function; `heappop` and `heapreplace` raise `IndexError` on an empty heap; original iterable is not mutated by `nsmallest`/`nlargest` |
4216
+ | `pprint` | ✅ | `pformat(object, ...)` → str, `pprint(object[, stream], ...)`, `pp(object, ...)` (same as `pprint` but `sort_dicts=False` by default), `saferepr(object)`, `isreadable(object)`, `isrecursive(object)`, `PrettyPrinter` class in `src/lib/pprint.pyj`; keyword options `indent` (default 1), `width` (default 80), `depth`, `compact`, `sort_dicts` (default True), `underscore_numbers` (accepted, currently a no-op); a value's `repr()` is used when it fits within `width`, otherwise lists/tuples/dicts/sets/frozensets are broken across multiple lines with Python-style indentation; recursive references are detected and rendered as `<Recursion on … with id=…>`; pretty-prints RapydScript `dict`/`set`/`frozenset`, plain JS objects, and arrays; `pprint`/`pp` write to a `stream` object's `.write()` (defaulting to stdout via `print()`) |
4217
+ | `statistics` | ✅ | `mean`, `fmean`, `geometric_mean`, `harmonic_mean`, `median`, `median_low`, `median_high`, `median_grouped`, `mode`, `multimode`, `variance`, `pvariance`, `stdev`, `pstdev`, `quantiles`, `covariance`, `correlation`, `linear_regression` in `src/lib/statistics.pyj`; `NormalDist` class (`.mean`/`.median`/`.mode`/`.stdev`/`.variance` properties, `pdf()`, `cdf()`, `inv_cdf()`, `quantiles()`, `zscore()`, `overlap()`, `samples(n[, seed])`, `from_samples()`, plus arithmetic with scalars and other `NormalDist` instances); `StatisticsError` (a `ValueError` subclass); every data argument may be a list, a JS array, a string, or any iterable; `correlation()` supports `method='ranked'` (Spearman); `quantiles()` supports `method='inclusive'`/`'exclusive'`; `linear_regression()` returns an object with `.slope`/`.intercept`; all computation is IEEE-754 double precision (no `Fraction`/`Decimal` preservation), and `cdf()`/`overlap()` use a rational error-function approximation accurate to ~1e-7 |
4218
+ | `decimal` | ❌ | Fixed-precision decimal arithmetic not available; JS `Number` is IEEE-754 only |
4219
+ | `fractions` | ❌ | Rational arithmetic (`Fraction(num, den)`) not available |
4220
+ | `difflib` | ❌ | `unified_diff`, `SequenceMatcher`, `get_close_matches` not available |
4190
4221
  | `inspect` | ❌ | `signature`, `getmembers`, `isfunction` etc. not available |
4191
4222
  | `struct` | ❌ | Binary packing/unpacking not available |
4192
4223
  | `hashlib` | ❌ | MD5, SHA-256 etc. not available; use Web Crypto API via verbatim JS |
4193
4224
  | `hmac` | ❌ | Keyed hashing not available |
4194
- | `pprint` | ❌ | Pretty-printing not available |
4195
4225
  | `unittest` | ❌ | Not available; RapydScript uses a custom test runner (`node bin/rapydscript test`) |
4196
4226
 
4197
4227
  ---
@@ -4206,7 +4236,7 @@ Features that exist in RapydScript but behave differently from standard Python:
4206
4236
  | `//` floor division on floats | `math.floor(a/b)` always | uses `Math.floor` - same result for well-behaved floats |
4207
4237
  | `%` on negative numbers | Result takes the sign of the divisor (`-7 % 3 == 2`) | Same as Python by default (via `python_modulo`, on by default). Disable with `from __python__ import no_python_modulo` to revert to raw JS remainder semantics. |
4208
4238
  | `global` / `nonlocal` scoping | Full cross-scope declaration | `global` works for module-level; if a variable exists in both an intermediate outer scope **and** the module-level scope, the outer scope takes precedence (differs from Python where `global` always forces module-level) |
4209
- | `Exception.message` | Not standard; use `.args[0]` | `.message` is the standard attribute (JS `Error` style) |
4239
+ | `Exception.message` | Not standard; use `.args[0]` | `.args` is populated (like Python); `.message` also set to first arg for JS `Error` compatibility |
4210
4240
  | Function call argument count | Too few args → `TypeError`; too many → `TypeError` | Too few args → extra params are `undefined`; too many → extras silently discarded. No `TypeError` is raised in either case. |
4211
4241
  | Positional-only param enforcement | Passing by keyword raises `TypeError` | Passing by keyword is silently ignored — the named arg is discarded and the parameter gets `undefined` (no error raised) |
4212
4242
  | Keyword-only param enforcement | Passing positionally raises `TypeError` | Passing positionally raises no error — the extra positional arg is silently discarded and the default value is used |
package/TODO.md CHANGED
@@ -1,34 +1,9 @@
1
1
 
2
- ### libraries
3
-
4
- - The 7n literal isn't supported by the parser — use BigInt()
5
-
6
2
  - remove repl_mode and make repl make a new context for each "run" press
7
3
 
8
4
  - vscode plugin based on language service?
9
5
 
10
6
 
11
- I would like you to add support for [python async def functions with yield (async generators)] to rapydscript. It should have the same syntax as the Python implementation, and be transpiled into equivalent javascript. Ensure with unit tests that it transpiles and the output JS runs correctly, and that the language service correctly handles it in parsed code. Make sure it works in the web-repl. Update the README if it has any outdated info about this, and the PYTHON_FEATURE_COVERAGE report. Add a simple example to the bottom of the TODO document using this feature (make no other changes to that file). Remove the suggestion from PYTHON_GAPS if it is there.
12
-
13
- ### Async generator example
14
-
15
- ```py
16
- # `async def` + `yield` makes an async generator. The function returns an
17
- # async iterator immediately; values are pulled with `await it.next()` or
18
- # consumed with `async for`.
19
-
20
- from asyncio import sleep
21
-
22
- async def countdown(n):
23
- while n > 0:
24
- await sleep(0) # cooperative yield to the event loop
25
- yield n
26
- n -= 1
27
-
28
- async def main():
29
- async for x in countdown(3):
30
- print(x) # prints 3, then 2, then 1
7
+ I would like you to add support for [python style __slots__ enforcement] to rapydscript. It should have the same syntax as the Python implementation, and be transpiled into equivalent javascript. Ensure with unit tests that it transpiles and the output JS runs correctly, and that the language service correctly handles it in parsed code. Make sure it works in the web-repl. Update the README if it has any outdated info about this, and the PYTHON_FEATURE_COVERAGE report. Add a simple example to the bottom of the TODO document using this feature (make no other changes to that file). Remove the suggestion from PYTHON_GAPS if it is there. Run the full unit test suite to check for regressions.
31
8
 
32
- main()
33
- ```
34
9
 
package/add-toc-to-readme CHANGED
@@ -1,2 +1,2 @@
1
- #!/bin/sh
2
- exec node_modules/doctoc/doctoc.js --title '**Contents**' README.md
1
+ #!/bin/sh
2
+ exec node_modules/doctoc/doctoc.js --title '**Contents**' README.md