rapydscript-ns 0.9.2 → 0.9.4

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 (88) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/PYTHON_GAPS.md +352 -0
  3. package/README.md +176 -32
  4. package/TODO.md +1 -128
  5. package/bin/rapydscript +70 -70
  6. package/language-service/index.js +242 -11
  7. package/memory/project_string_impl.md +43 -0
  8. package/package.json +1 -1
  9. package/release/baselib-plain-pretty.js +248 -38
  10. package/release/baselib-plain-ugly.js +8 -8
  11. package/release/compiler.js +778 -277
  12. package/release/signatures.json +30 -30
  13. package/src/ast.pyj +10 -1
  14. package/src/baselib-builtins.pyj +56 -2
  15. package/src/baselib-containers.pyj +25 -1
  16. package/src/baselib-errors.pyj +7 -3
  17. package/src/baselib-internal.pyj +51 -6
  18. package/src/baselib-str.pyj +18 -5
  19. package/src/lib/asyncio.pyj +534 -0
  20. package/src/lib/base64.pyj +399 -0
  21. package/src/lib/bisect.pyj +73 -0
  22. package/src/lib/collections.pyj +228 -4
  23. package/src/lib/csv.pyj +494 -0
  24. package/src/lib/heapq.pyj +98 -0
  25. package/src/lib/html.pyj +382 -0
  26. package/src/lib/http/__init__.pyj +98 -0
  27. package/src/lib/http/client.pyj +304 -0
  28. package/src/lib/http/cookies.pyj +236 -0
  29. package/src/lib/logging.pyj +672 -0
  30. package/src/lib/pprint.pyj +455 -0
  31. package/src/lib/pythonize.pyj +20 -20
  32. package/src/lib/statistics.pyj +0 -0
  33. package/src/lib/string.pyj +357 -0
  34. package/src/lib/textwrap.pyj +329 -0
  35. package/src/lib/urllib/__init__.pyj +14 -0
  36. package/src/lib/urllib/error.pyj +66 -0
  37. package/src/lib/urllib/parse.pyj +475 -0
  38. package/src/lib/urllib/request.pyj +86 -0
  39. package/src/monaco-language-service/analyzer.js +5 -2
  40. package/src/monaco-language-service/completions.js +26 -0
  41. package/src/monaco-language-service/diagnostics.js +203 -4
  42. package/src/monaco-language-service/scope.js +1 -0
  43. package/src/output/codegen.pyj +4 -1
  44. package/src/output/functions.pyj +152 -6
  45. package/src/output/loops.pyj +17 -2
  46. package/src/output/modules.pyj +1 -1
  47. package/src/output/operators.pyj +15 -0
  48. package/src/output/stream.pyj +0 -1
  49. package/src/parse.pyj +108 -24
  50. package/src/tokenizer.pyj +19 -3
  51. package/test/async_generators.pyj +144 -0
  52. package/test/asyncio.pyj +307 -0
  53. package/test/base64.pyj +202 -0
  54. package/test/baselib.pyj +23 -0
  55. package/test/bisect.pyj +178 -0
  56. package/test/chainmap.pyj +185 -0
  57. package/test/csv.pyj +405 -0
  58. package/test/float_special.pyj +64 -0
  59. package/test/heapq.pyj +174 -0
  60. package/test/html.pyj +212 -0
  61. package/test/http.pyj +259 -0
  62. package/test/imports.pyj +79 -72
  63. package/test/logging.pyj +356 -0
  64. package/test/long.pyj +130 -0
  65. package/test/parenthesized_with.pyj +141 -0
  66. package/test/pprint.pyj +232 -0
  67. package/test/python_compat.pyj +3 -5
  68. package/test/python_modulo.pyj +76 -0
  69. package/test/python_modulo_off.pyj +21 -0
  70. package/test/statistics.pyj +224 -0
  71. package/test/str.pyj +14 -0
  72. package/test/string.pyj +245 -0
  73. package/test/textwrap.pyj +172 -0
  74. package/test/type_display.pyj +48 -0
  75. package/test/type_enforcement.pyj +164 -0
  76. package/test/unit/index.js +94 -6
  77. package/test/unit/language-service-completions.js +121 -0
  78. package/test/unit/language-service-scope.js +32 -0
  79. package/test/unit/language-service.js +190 -5
  80. package/test/unit/run-language-service.js +17 -3
  81. package/test/unit/web-repl.js +2401 -13
  82. package/test/urllib.pyj +193 -0
  83. package/tools/compile.js +1 -1
  84. package/tools/embedded_compiler.js +7 -7
  85. package/tools/export.js +4 -2
  86. package/web-repl/main.js +1 -1
  87. package/web-repl/rapydscript.js +7 -5
  88. package/test/omit_function_metadata.pyj +0 -20
package/CHANGELOG.md CHANGED
@@ -1,3 +1,31 @@
1
+ version 0.9.4
2
+ =======================
3
+ * Added the `statistics` standard library module (`mean`, `fmean`, `median`, `median_low`, `median_high`, `median_grouped`, `mode`, `multimode`, `stdev`, `pstdev`, `variance`, `pvariance`, `harmonic_mean`, `geometric_mean`, `quantiles`, `covariance`, `correlation`, `linear_regression`, `NormalDist`)
4
+ * Added the `pprint` standard library module (`pprint`, `pformat`, `PrettyPrinter`, `isreadable`, `isrecursive`, `saferepr`)
5
+ * Added `ChainMap` to the `collections` standard library module
6
+ * Added support for `bigint` literals and the `bigint()` builtin
7
+ * Fix: Added the `type` builtin to global scope
8
+ * Fix: Performance problem during long editor sessions in the language service
9
+
10
+ version 0.9.3
11
+ =======================
12
+ * Added the `bisect` standard library module (`bisect_left`, `bisect_right`, `bisect`, `insort_left`, `insort_right`, `insort`, optional `key` parameter)
13
+ * Added the `base64` standard library module
14
+ * Added the `string` standard library module (character constants, `Template`, `Formatter`)
15
+ * Added the `html` standard library module (`escape`, `unescape`, `HTMLParser`, etc.)
16
+ * Added the `asyncio` standard library module (`sleep()`, `gather()`, `create_task()`, etc.)
17
+ * Added the `urllib` standard library module
18
+ * Added the `http` standard library module (`HTTPStatus`, `HTTPConnection`, `HTTPSConnection`, `HTTPResponse`, `SimpleCookie`, `Morsel`, `CookieError`)
19
+ * Added the `csv` standard library module (`reader`, `writer`, `DictReader`, `DictWriter`, `Dialect`, `register_dialect`, QUOTE_* constants, etc.)
20
+ * Added the `textwrap` standard library module (`TextWrapper`, `wrap`, `fill`, `shorten`, `dedent`, `indent`)
21
+ * Added the `logging` standard library module (`getLogger`, `basicConfig`, `Logger`, `Handler`, `StreamHandler`, `Formatter`, `Filter`, level constants, etc.)
22
+ * Added the `heapq` standard library module (`heappush`, `heappop`, `heapify`, `heapreplace`, `heappushpop`, `nlargest`, `nsmallest`)
23
+ * Added support for parenthesized `with` statements: `with (A() as a, B() as b):` — multi-line and trailing comma supported
24
+ * Added support for async generators (`async def` with `yield`) and `async for` — compiles to JS `async function*` (wrapped) and `for await ... of`
25
+ * Added support for `float("inf")` and `float("-inf")`
26
+ * Added `long()` type and improved pretty-printing of `type()` return values
27
+ * Language service: bare imports now correctly provide type hints; infinite loop warning
28
+
1
29
  version 0.9.2
2
30
  =======================
3
31
  * Added the contextlib standard library
package/PYTHON_GAPS.md ADDED
@@ -0,0 +1,352 @@
1
+ # RapydScript-NS: Python Compatibility Gaps & Browser-Friendly Additions
2
+
3
+ This document identifies features an experienced Python developer would expect that are missing
4
+ or behave differently in RapydScript-NS, with a focus on what is relevant and useful in a
5
+ browser context. Server-side features (file I/O, subprocesses, sockets, threading, etc.)
6
+ are excluded as they do not apply.
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
+
11
+ ---
12
+
13
+ ## 1. Silent Behavioral Differences (Gotchas)
14
+
15
+ These features exist but behave differently from Python in ways that will silently produce
16
+ wrong results — no error is raised.
17
+
18
+ ### 1.1 `is` / `is not` — Identity vs. Equality
19
+
20
+ **Python:** `is` tests object identity (pointer comparison).
21
+ **RapydScript:** `is` compiles to `===` (strict equality), so `x is y` is true whenever
22
+ `x === y`, including for equal primitive values that would be distinct Python objects.
23
+
24
+ ```python
25
+ a = 1000
26
+ b = 1000
27
+ a is b # False in Python (separate int objects)
28
+ # True in RapydScript (1000 === 1000)
29
+ ```
30
+ **Impact:** Code that checks `obj is None` or `obj is sentinel_value` works correctly.
31
+ Code that expects `is` to return `False` for equal-but-distinct numeric values will be wrong.
32
+
33
+ **Note:** This is already documented in the README, but it is subtle and trips up experts.
34
+ The recommended pattern — using a unique sentinel object — works correctly.
35
+
36
+ ---
37
+
38
+ ### 1.2 String Encoding — UTF-16 Surrogate Pairs
39
+
40
+ **Python:** Strings are sequences of Unicode code points (full 21-bit range).
41
+ **RapydScript:** Strings are JS strings — UTF-16. Emoji and other non-BMP characters
42
+ (U+10000–U+10FFFF) are represented as surrogate pairs and count as length 2.
43
+
44
+ ```python
45
+ s = '😀'
46
+ len(s) # 1 in Python, 2 in RapydScript
47
+ s[0] # '😀' in Python, '\uD83D' (broken surrogate) in RapydScript
48
+ ```
49
+ **RapydScript provides:** `str.ulen()`, `str.uchrs()`, `str.uslice()` for code-point-aware
50
+ operations, but they must be used consciously.
51
+
52
+ **Impact:** Any code handling emoji, Asian CJK extension, or mathematical symbols may silently
53
+ produce wrong lengths or corrupt characters when sliced.
54
+
55
+ ---
56
+
57
+ ### 1.3 `global` Scoping in Nested Functions
58
+
59
+ **Python:** `global x` inside a nested function forces `x` to refer to the module-level
60
+ variable, bypassing any intermediate closure scopes.
61
+ **RapydScript:** If a variable named `x` exists in an intermediate outer function scope,
62
+ that scope takes precedence over the module-level scope, even with `global x`.
63
+
64
+ **Impact:** Code with complex nested closures + `global` declarations may write to the wrong
65
+ scope silently.
66
+
67
+ ---
68
+
69
+ ### 1.4 Numeric Dict Keys Are Coerced to Strings
70
+
71
+ **Python:** `d = {}; d[1] = 'a'; d['1'] = 'b'; len(d) == 2` (integer and string keys distinct).
72
+ **RapydScript:** The Python `dict` type (backed by ES6 `Map`) stores them distinctly, but
73
+ literal `{1: 'a', '1': 'b'}` and plain JS object interop may coerce integer keys to strings.
74
+
75
+ **Impact:** Code mixing numeric keys with the same string representation may produce collisions
76
+ when interoperating with JS APIs that return plain objects.
77
+
78
+ ---
79
+
80
+ ### 1.5 `Exception.args` vs `.message`
81
+
82
+ **Python:** `Exception('msg').args == ('msg',)` and `.message` is not a standard attribute.
83
+ **RapydScript:** `.message` is the primary attribute (JS `Error` convention). `.args` is
84
+ not populated as a tuple with the message.
85
+
86
+ **Impact:** Code accessing `.args[0]` to get the error message will get `undefined`.
87
+
88
+ ---
89
+
90
+ ### 1.6 Multiple Inheritance MRO
91
+
92
+ **Python:** C3 linearization guarantees a deterministic and consistent method resolution order.
93
+ **RapydScript:** Built on the JS prototype chain. In diamond inheritance or complex hierarchies
94
+ the order may differ from Python's C3 MRO.
95
+
96
+ **Impact:** Unexpected method is called when multiple parent classes define the same method
97
+ name. Hard to debug because no error is raised.
98
+
99
+ ---
100
+
101
+ ## 2. Missing Language Features
102
+
103
+ ### 2.1 `__slots__` Not Enforced
104
+
105
+ `__slots__ = ['x', 'y']` is parsed and accepted but has no runtime effect — arbitrary
106
+ attributes can still be set on instances. No `AttributeError` is raised for assignments to
107
+ undeclared attributes.
108
+
109
+ **Browser relevance:** Used frequently for memory efficiency and API documentation. Enforcement
110
+ via `Object.seal()` or a `Proxy`-based guard would be possible in modern browsers.
111
+
112
+ ---
113
+
114
+ ### 2.2 `__del__` Destructor — No Guaranteed Finalizer
115
+
116
+ `__del__` methods are not called reliably. JavaScript's GC is non-deterministic and provides
117
+ no equivalent to CPython's reference-counting finalizer.
118
+
119
+ **Browser relevance:** Low — most `__del__` usage is for file handles or network connections
120
+ that do not exist in the browser. However, the `FinalizationRegistry` API (available in all
121
+ modern browsers since 2021) could provide best-effort `__del__` support for cleanup of
122
+ external resources like WebGL buffers, WebSockets, etc. Worth adding with a clear caveat
123
+ that timing is not guaranteed.
124
+
125
+ ---
126
+
127
+ ### 2.3 `locals()` Always Returns Empty Dict
128
+
129
+ `vars()`, `locals()`, and `globals()` all exist as builtins. JavaScript provides no mechanism
130
+ to introspect local variables at runtime, so `locals()` always returns an empty dict.
131
+ `globals()` works on module-level/global state, and `vars(obj)` introspects the passed object.
132
+ Python code that uses `locals()` for string template substitution
133
+ (e.g., `'{x}'.format(**locals())`) will break silently.
134
+
135
+ ---
136
+
137
+ ### 2.4 `from module import *` Not Allowed
138
+
139
+ Star imports are intentionally unsupported to prevent namespace pollution. Python developers
140
+ who rely on them (e.g., `from math import *`) must enumerate imports explicitly.
141
+
142
+ **Impact:** Not a behavioral difference, but a friction point when porting code.
143
+
144
+ ---
145
+
146
+ ### 2.5 `asynccontextmanager` Not Available
147
+
148
+ `contextlib.asynccontextmanager` is absent. Only synchronous `@contextmanager` is implemented.
149
+ `async with` itself is also not supported — async context managers need `.acquire()`/`.release()`
150
+ calls instead.
151
+
152
+ ---
153
+
154
+ ### 2.6 f-string Debugging Format `f'{x=}'` Not Supported
155
+
156
+ Python 3.8+ supports `f'{x=}'` which expands to `f'x={repr(x)}'`. This is not implemented.
157
+
158
+ ```python
159
+ x = 42
160
+ print(f'{x=}') # Python: "x=42". RapydScript: syntax error or wrong output
161
+ ```
162
+
163
+ ---
164
+
165
+ ### 2.7 Ellipsis Evaluates to `undefined`
166
+
167
+ `...` (Ellipsis) parses as a valid expression but evaluates to JS `undefined` rather than
168
+ Python's `Ellipsis` singleton object. Code that stores `...` in containers or checks
169
+ `x is Ellipsis` will behave incorrectly.
170
+
171
+ ---
172
+
173
+ ## 3. Missing Standard Library Modules (Browser-Relevant)
174
+
175
+ These are absent from `src/lib/` and have no substitute.
176
+
177
+ ### 3.1 `enum.IntEnum`, `IntFlag`, and `Flag`
178
+
179
+ The `enum` module provides `Enum` but not `IntEnum` (auto-comparable with integers),
180
+ `StrEnum` (Python 3.11+), `IntFlag`, or `Flag` (bitfield enums). These are common in protocol
181
+ implementations, permission systems, and state machines.
182
+
183
+ ```python
184
+ from enum import IntEnum
185
+ class Color(IntEnum):
186
+ RED = 1
187
+ GREEN = 2
188
+ Color.RED < Color.GREEN # True — comparison with int semantics
189
+ ```
190
+
191
+ ---
192
+
193
+ ### 3.2 `hashlib` — Cryptographic Hashing
194
+
195
+ `hashlib.sha256`, `hashlib.md5`, etc. The Web Crypto API provides `crypto.subtle.digest`
196
+ but its async/buffer-based interface is awkward. A thin `hashlib`-compatible wrapper over
197
+ `crypto.subtle` with a synchronous-friendly API (using the sync `crypto.getRandomValues`)
198
+ for non-cryptographic hashes would be valuable.
199
+
200
+ ---
201
+
202
+ ### 3.3 `fractions` — Rational Arithmetic
203
+
204
+ `Fraction(numerator, denominator)` with full arithmetic. Useful for music theory apps,
205
+ math tutoring tools, and any domain requiring exact rational computation.
206
+
207
+ ---
208
+
209
+ ### 3.4 `difflib` — Sequence Comparison
210
+
211
+ `difflib.unified_diff`, `difflib.SequenceMatcher`, `difflib.get_close_matches`. Useful
212
+ for browser-based code editors, version comparison tools, and fuzzy matching UIs.
213
+
214
+ ---
215
+
216
+ ### 3.5 `decimal` — Decimal Arithmetic
217
+
218
+ `Decimal` arithmetic avoids floating-point rounding errors. Essential for financial
219
+ calculations in browser apps (e-commerce, budgeting tools). JS does not have a built-in
220
+ equivalent; a pure-JS implementation would need to be compiled in.
221
+
222
+ ---
223
+
224
+ ## 4. Tricky Patterns That Require Workarounds
225
+
226
+ These are not missing features but patterns that silently work differently or require
227
+ non-obvious syntax.
228
+
229
+ ### 4.1 Python String Methods on String Literals
230
+
231
+ String literals in RapydScript do NOT have Python methods (`.strip()`, `.join()`, etc.)
232
+ available by default in all contexts. Using string methods requires:
233
+
234
+ - `from pythonize import strings; strings()` (patches `String.prototype` at runtime), or
235
+ - the `--pythonize-strings` compiler option, or
236
+ - calling `str.strip(s)` (module-level form) instead of `s.strip()`
237
+
238
+ **Impact:** The most common pattern in Python — `s.strip().split(',')` — will throw a
239
+ `TypeError` in some contexts without the above setup.
240
+
241
+ ---
242
+
243
+ ### 4.2 Multi-line Anonymous Functions in Call Arguments
244
+
245
+ Multi-line `def` blocks cannot be used as inline arguments to function calls:
246
+
247
+ ```python
248
+ # Does NOT compile correctly:
249
+ result = map(def(x):
250
+ return x * 2
251
+ , my_list)
252
+
253
+ # Must use a named helper:
254
+ def double(x):
255
+ return x * 2
256
+ result = map(double, my_list)
257
+ ```
258
+
259
+ ---
260
+
261
+ ### 4.3 JavaScript Reserved Words as Identifiers
262
+
263
+ All JavaScript reserved words are also forbidden in RapydScript. Common Python identifiers
264
+ that break: `default`, `delete`, `switch`, `case`, `break`, `var`, `void`, `typeof`,
265
+ `instanceof`. Also cannot be used as keyword argument names in function calls.
266
+
267
+ ```python
268
+ def configure(default=None): # 'default' is reserved — compile error
269
+ ...
270
+ # Must rename: def configure(dflt=None):
271
+ ```
272
+
273
+ ---
274
+
275
+ ### 4.4 Class Named `Error` Shadows JS `Error`
276
+
277
+ Defining a class named `Error` (e.g., `class Error(Exception)`) compiles to a JS function
278
+ that shadows the global `JS Error` constructor. If that class is then imported in another
279
+ module context (e.g., the web REPL), the import line `var Error = ρσ_modules.mymod.Error`
280
+ shadows the native `Error`, causing infinite recursion in `Exception.__init__`.
281
+
282
+ **Workaround:** Never name a RapydScript class `Error`. Use `MyError`, `AppError`,
283
+ `ValueError` (which is already defined in baselib), etc.
284
+
285
+ ---
286
+
287
+ ### 4.5 `Cls.method(arg)` vs `@Cls.method`
288
+
289
+ `Cls.method(arg)` compiles to `Cls.prototype.method.call(arg)` (unbound Python 2 style).
290
+ `@Cls.method` as a decorator stores the constructor property and calls it differently.
291
+ These are different lookup paths. A method that must work both as a decorator and as a
292
+ direct class call needs to be installed on both `cls.property` and `cls.prototype.property`.
293
+
294
+ ---
295
+
296
+ ### 4.6 `range` Cannot Be Shadowed as a Parameter Name
297
+
298
+ ```python
299
+ def histogram(data, range): # compile/runtime error — range shadows builtin
300
+ ...
301
+ # Must rename: def histogram(data, data_range):
302
+ ```
303
+
304
+ This extends to other builtins: prefer prefixed parameter names when a parameter naturally
305
+ matches a builtin name.
306
+
307
+ ---
308
+
309
+ ### 4.7 Verbatim Blocks Are Truly Verbatim — No Escape Processing
310
+
311
+ Inside `v'...'` blocks, Python escape sequences are NOT processed. `\n` in a v-block
312
+ becomes a literal backslash-n in the JS output, not a newline.
313
+
314
+ ```python
315
+ # Wrong — \n is not a newline in the regex:
316
+ pat = v'/foo\nbar/'
317
+
318
+ # Right — write the exact JS you want:
319
+ pat = v'/foo\nbar/' # only works as a real newline if you have a literal newline
320
+ ```
321
+ For regex literals, write exactly the JS you want. Double-escaping (`\\n`) produces `\\n`
322
+ in JS (two characters), not a newline.
323
+
324
+ ---
325
+
326
+ ### 4.8 `jstype(x) is 'number'` for `typeof` Checks
327
+
328
+ Python's `type(x)` does not return a string. For JS-style `typeof` checks:
329
+
330
+ ```python
331
+ jstype(x) is 'number' # correct
332
+ type(x) is int # also works for pure RS objects
333
+ ```
334
+ The `jstype()` builtin is the RS equivalent of JS's `typeof`.
335
+
336
+ ---
337
+
338
+ ## 5. Summary Priority Table
339
+
340
+ Priority weighs frequency-of-need, effort-to-implement, and whether a workaround exists.
341
+
342
+ | Priority | Feature | Effort | Impact |
343
+ |---|---|---|---|
344
+ | High | `enum.IntEnum`, `IntFlag`, `Flag` | Medium | Protocol and permission modeling; bitfield enums |
345
+ | Medium | `__slots__` enforcement via `Proxy` | Medium | Memory and API documentation |
346
+ | Medium | `hashlib` shim over Web Crypto | Medium | Avoids verbatim Web Crypto calls in user code |
347
+ | Medium | `fractions` module | Medium | Exact rational arithmetic |
348
+ | Medium | f-string `f'{x=}'` debugging format | Low | Developer experience |
349
+ | Low | `asynccontextmanager` + `async with` | Medium | Async resource management |
350
+ | Low | `__del__` via `FinalizationRegistry` | Medium | Resource cleanup (best-effort) |
351
+ | Low | `difflib` module | High | Text diff, fuzzy matching |
352
+ | Low | `decimal` module | High | Financial calculations |