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
@@ -0,0 +1,534 @@
1
+ ###########################################################
2
+ # RapydScript Standard Library
3
+ # asyncio — Python-compatible async I/O module
4
+ ###########################################################
5
+ #
6
+ # Maps Python asyncio APIs to JavaScript's native Promise/async-await model.
7
+ #
8
+ # async def / await syntax compiles directly to JS:
9
+ # async def main(): → async function main() {
10
+ # result = await coro() → let result = await coro()
11
+ #
12
+ # asyncio.run(coro) returns the Promise; it cannot block (JS single-threaded).
13
+ # async with / async for are not supported in the compiler; use
14
+ # .acquire()/.release() / .get()/.put() explicitly instead.
15
+ # No real event-loop management: JS always has exactly one event loop.
16
+
17
+ # ---------------------------------------------------------------------------
18
+ # Constants
19
+ # ---------------------------------------------------------------------------
20
+
21
+ FIRST_COMPLETED = 'FIRST_COMPLETED'
22
+ FIRST_EXCEPTION = 'FIRST_EXCEPTION'
23
+ ALL_COMPLETED = 'ALL_COMPLETED'
24
+
25
+
26
+ # ---------------------------------------------------------------------------
27
+ # Exceptions
28
+ # ---------------------------------------------------------------------------
29
+
30
+ class CancelledError(Exception):
31
+ pass
32
+
33
+
34
+ class TimeoutError(Exception):
35
+ pass
36
+
37
+
38
+ class InvalidStateError(Exception):
39
+ pass
40
+
41
+
42
+ class QueueEmpty(Exception):
43
+ pass
44
+
45
+
46
+ class QueueFull(Exception):
47
+ pass
48
+
49
+
50
+ class RuntimeError(Exception):
51
+ pass
52
+
53
+
54
+ # ---------------------------------------------------------------------------
55
+ # Core coroutine functions
56
+ # ---------------------------------------------------------------------------
57
+
58
+ def sleep(seconds):
59
+ """Return a Promise that resolves after `seconds` seconds.
60
+
61
+ Equivalent to Python's asyncio.sleep(). Accepts fractional seconds.
62
+ A value of 0 yields control to the event loop once (next microtask tick
63
+ in practice, since setTimeout(0) is used).
64
+ """
65
+ v"""return new Promise(function(resolve) { setTimeout(resolve, (seconds || 0) * 1000); });"""
66
+
67
+
68
+ def gather(*coros, **kwargs):
69
+ """Wait for multiple coroutines/Promises to complete.
70
+
71
+ Returns a Promise that resolves with a list of all results (in order).
72
+ If any coroutine raises, the returned Promise rejects (unless
73
+ return_exceptions=True is passed, in which case exceptions are returned
74
+ as values in the result list rather than propagating).
75
+
76
+ Equivalent to Python's asyncio.gather().
77
+ """
78
+ v"""
79
+ var _re = kwargs && kwargs.return_exceptions;
80
+ if (_re) {
81
+ return Promise.allSettled(coros).then(function(results) {
82
+ return results.map(function(r) {
83
+ return r.status === 'fulfilled' ? r.value : r.reason;
84
+ });
85
+ });
86
+ }
87
+ return Promise.all(coros);
88
+ """
89
+
90
+
91
+ def create_task(coro):
92
+ """Schedule a coroutine to run and return it.
93
+
94
+ In JS, Promises start executing immediately when created, so this is a
95
+ no-op that just returns the Promise. Equivalent to asyncio.create_task().
96
+ """
97
+ return coro
98
+
99
+
100
+ def ensure_future(coro):
101
+ """Wrap a coroutine in a Task. Alias for create_task()."""
102
+ return coro
103
+
104
+
105
+ def run(coro):
106
+ """Run a top-level coroutine (returns the underlying Promise).
107
+
108
+ In Python this blocks until the coroutine finishes. In JS there is no
109
+ blocking, so this simply returns the Promise. The JS event loop handles
110
+ it automatically.
111
+ """
112
+ return coro
113
+
114
+
115
+ def shield(coro):
116
+ """Protect a coroutine from external cancellation.
117
+
118
+ Returns a new Promise that resolves/rejects exactly when `coro` does, but
119
+ is otherwise independent (cancelling the wrapper does not cancel `coro`).
120
+ """
121
+ v"""
122
+ return new Promise(function(resolve, reject) {
123
+ Promise.resolve(coro).then(resolve, reject);
124
+ });
125
+ """
126
+
127
+
128
+ def wait_for(coro, timeout):
129
+ """Wait for a coroutine to complete with a timeout.
130
+
131
+ Rejects with TimeoutError if the coroutine does not complete within
132
+ `timeout` seconds. Equivalent to Python's asyncio.wait_for().
133
+ """
134
+ _TimeoutError = TimeoutError
135
+ v"""
136
+ return new Promise(function(resolve, reject) {
137
+ var done = false;
138
+ var timer = setTimeout(function() {
139
+ if (!done) {
140
+ done = true;
141
+ reject(new _TimeoutError('wait_for timed out after ' + timeout + 's'));
142
+ }
143
+ }, (timeout || 0) * 1000);
144
+ Promise.resolve(coro).then(
145
+ function(v) { if (!done) { done = true; clearTimeout(timer); resolve(v); } },
146
+ function(e) { if (!done) { done = true; clearTimeout(timer); reject(e); } }
147
+ );
148
+ });
149
+ """
150
+
151
+
152
+ def wait(aws, return_when=ALL_COMPLETED):
153
+ """Wait for the Promises in `aws` to complete.
154
+
155
+ Returns a Promise that resolves with {done, pending} sets.
156
+ `return_when` controls when the function returns:
157
+ ALL_COMPLETED — wait for all (default)
158
+ FIRST_COMPLETED — return as soon as one completes
159
+ FIRST_EXCEPTION — return as soon as one raises (not yet fully supported)
160
+ """
161
+ _first = FIRST_COMPLETED
162
+ v"""
163
+ var promises = Array.isArray(aws) ? aws.slice() : Array.from(aws);
164
+ if (return_when === _first) {
165
+ return Promise.race(promises).then(function(v) {
166
+ return { done: [v], pending: [] };
167
+ });
168
+ }
169
+ return Promise.allSettled(promises).then(function(results) {
170
+ var done = [], pending = [];
171
+ results.forEach(function(r) {
172
+ done.push(r.status === 'fulfilled' ? r.value : r.reason);
173
+ });
174
+ return { done: done, pending: pending };
175
+ });
176
+ """
177
+
178
+
179
+ # ---------------------------------------------------------------------------
180
+ # Introspection helpers
181
+ # ---------------------------------------------------------------------------
182
+
183
+ def iscoroutine(obj):
184
+ """Return True if obj is a coroutine/Promise (has a .then method)."""
185
+ v"""return obj !== null && obj !== undefined && typeof obj === 'object' && typeof obj.then === 'function';"""
186
+
187
+
188
+ def iscoroutinefunction(fn):
189
+ """Return True if fn is an async function."""
190
+ v"""return typeof fn === 'function' && fn.constructor && fn.constructor.name === 'AsyncFunction';"""
191
+
192
+
193
+ def current_task():
194
+ """Return the currently running Task, or None.
195
+
196
+ Always returns None in the JS implementation (no task tracking).
197
+ """
198
+ return None
199
+
200
+
201
+ def all_tasks():
202
+ """Return all running Tasks.
203
+
204
+ Always returns an empty set in the JS implementation.
205
+ """
206
+ return set()
207
+
208
+
209
+ # ---------------------------------------------------------------------------
210
+ # Event loop stubs (API-compatibility shims)
211
+ # ---------------------------------------------------------------------------
212
+
213
+ class AbstractEventLoop:
214
+ """Minimal event-loop shim for API compatibility.
215
+
216
+ In JS there is always exactly one event loop; these methods are either
217
+ no-ops or thin wrappers around the equivalent JS mechanisms.
218
+ """
219
+ def run_until_complete(self, coro):
220
+ """Start executing `coro` and return it (cannot block in JS)."""
221
+ return coro
222
+
223
+ def run_forever(self):
224
+ pass
225
+
226
+ def stop(self):
227
+ pass
228
+
229
+ def close(self):
230
+ pass
231
+
232
+ def is_closed(self):
233
+ return False
234
+
235
+ def is_running(self):
236
+ return True
237
+
238
+ def create_task(self, coro):
239
+ return coro
240
+
241
+ def call_soon(self, callback, *args):
242
+ v"""setTimeout(function() { callback.apply(null, args); }, 0);"""
243
+
244
+ def call_later(self, delay, callback, *args):
245
+ v"""setTimeout(function() { callback.apply(null, args); }, (delay || 0) * 1000);"""
246
+
247
+ def call_soon_threadsafe(self, callback, *args):
248
+ v"""setTimeout(function() { callback.apply(null, args); }, 0);"""
249
+
250
+
251
+ class _EventLoop(AbstractEventLoop):
252
+ pass
253
+
254
+
255
+ _the_loop = _EventLoop()
256
+
257
+
258
+ def get_event_loop():
259
+ """Return the running event loop."""
260
+ return _the_loop
261
+
262
+
263
+ def get_running_loop():
264
+ """Return the running event loop (same as get_event_loop() in JS)."""
265
+ return _the_loop
266
+
267
+
268
+ def new_event_loop():
269
+ """Create and return a new event loop object."""
270
+ return _EventLoop()
271
+
272
+
273
+ # ---------------------------------------------------------------------------
274
+ # Synchronization primitives
275
+ # ---------------------------------------------------------------------------
276
+
277
+ class Lock:
278
+ """Mutual-exclusion lock.
279
+
280
+ Usage (since async with is not supported):
281
+ lock = asyncio.Lock()
282
+ await lock.acquire()
283
+ try:
284
+ ...critical section...
285
+ finally:
286
+ lock.release()
287
+ """
288
+ def __init__(self):
289
+ self._locked = False
290
+ self._waiters = []
291
+
292
+ async def acquire(self):
293
+ """Acquire the lock, waiting if necessary."""
294
+ if not self._locked:
295
+ self._locked = True
296
+ return True
297
+ _self = self
298
+ v"""await new Promise(function(resolve) { _self._waiters.push(resolve); });"""
299
+ return True
300
+
301
+ def release(self):
302
+ """Release the lock."""
303
+ if not self._locked:
304
+ raise RuntimeError("Lock is not acquired")
305
+ if self._waiters.length > 0:
306
+ waiter = self._waiters.shift()
307
+ waiter()
308
+ else:
309
+ self._locked = False
310
+
311
+ def locked(self):
312
+ """Return True if the lock is currently held."""
313
+ return self._locked
314
+
315
+
316
+ class Event:
317
+ """Async event flag that coroutines can wait on.
318
+
319
+ Usage:
320
+ event = asyncio.Event()
321
+ # In one coroutine:
322
+ await event.wait()
323
+ # In another:
324
+ event.set()
325
+ """
326
+ def __init__(self):
327
+ self._is_set = False
328
+ self._waiters = []
329
+
330
+ def set(self):
331
+ """Set the event, releasing all waiting coroutines."""
332
+ self._is_set = True
333
+ for waiter in self._waiters:
334
+ waiter()
335
+ self._waiters = []
336
+
337
+ def clear(self):
338
+ """Reset the event to unset."""
339
+ self._is_set = False
340
+
341
+ def is_set(self):
342
+ """Return True if the event is set."""
343
+ return self._is_set
344
+
345
+ async def wait(self):
346
+ """Wait until the event is set, then return True."""
347
+ if self._is_set:
348
+ return True
349
+ _self = self
350
+ v"""await new Promise(function(resolve) { _self._waiters.push(resolve); });"""
351
+ return True
352
+
353
+
354
+ class Semaphore:
355
+ """Counting semaphore for limiting concurrent access.
356
+
357
+ Usage:
358
+ sem = asyncio.Semaphore(3)
359
+ await sem.acquire()
360
+ try:
361
+ ...
362
+ finally:
363
+ sem.release()
364
+ """
365
+ def __init__(self, value=1):
366
+ if value < 0:
367
+ raise ValueError("Semaphore initial value must be >= 0")
368
+ self._value = value
369
+ self._waiters = []
370
+
371
+ async def acquire(self):
372
+ """Acquire one permit, waiting if the count is zero."""
373
+ if self._value > 0:
374
+ self._value -= 1
375
+ return True
376
+ _self = self
377
+ v"""await new Promise(function(resolve) { _self._waiters.push(resolve); });"""
378
+ return True
379
+
380
+ def release(self):
381
+ """Release one permit, waking a waiting coroutine if any."""
382
+ if self._waiters.length > 0:
383
+ waiter = self._waiters.shift()
384
+ waiter()
385
+ else:
386
+ self._value += 1
387
+
388
+ def locked(self):
389
+ """Return True if the semaphore cannot be acquired without waiting."""
390
+ return self._value == 0
391
+
392
+
393
+ class BoundedSemaphore(Semaphore):
394
+ """Semaphore that raises ValueError if released more times than acquired."""
395
+ def __init__(self, value=1):
396
+ Semaphore.__init__(self, value)
397
+ self._bound_value = value
398
+
399
+ def release(self):
400
+ if self._waiters.length == 0 and self._value >= self._bound_value:
401
+ raise ValueError("BoundedSemaphore released too many times")
402
+ Semaphore.release(self)
403
+
404
+
405
+ # ---------------------------------------------------------------------------
406
+ # Async queues
407
+ # ---------------------------------------------------------------------------
408
+
409
+ class Queue:
410
+ """First-in, first-out async queue.
411
+
412
+ Usage:
413
+ q = asyncio.Queue()
414
+ await q.put(item)
415
+ item = await q.get()
416
+ q.task_done()
417
+ await q.join() # wait until all items are processed
418
+ """
419
+ def __init__(self, maxsize=0):
420
+ self._maxsize = maxsize
421
+ self._queue = []
422
+ self._getters = []
423
+ self._putters = []
424
+ self._unfinished_tasks = 0
425
+ self._join_waiters = []
426
+
427
+ def qsize(self):
428
+ """Return the number of items currently in the queue."""
429
+ return self._queue.length
430
+
431
+ def empty(self):
432
+ """Return True if the queue has no items."""
433
+ return self._queue.length == 0
434
+
435
+ def full(self):
436
+ """Return True if the queue is at maxsize (always False if maxsize=0)."""
437
+ if self._maxsize <= 0:
438
+ return False
439
+ return self._queue.length >= self._maxsize
440
+
441
+ async def put(self, item):
442
+ """Put an item into the queue, waiting if the queue is full."""
443
+ if self.full():
444
+ _self = self
445
+ _item = item
446
+ v"""await new Promise(function(resolve) { _self._putters.push({resolve: resolve, item: _item}); });"""
447
+ else:
448
+ self._put_nowait_internal(item)
449
+ self._unfinished_tasks += 1
450
+
451
+ def put_nowait(self, item):
452
+ """Put an item without waiting; raise QueueFull if the queue is full."""
453
+ if self.full():
454
+ raise QueueFull("Queue is full")
455
+ self._put_nowait_internal(item)
456
+ self._unfinished_tasks += 1
457
+
458
+ def _put_nowait_internal(self, item):
459
+ if self._getters.length > 0:
460
+ getter = self._getters.shift()
461
+ getter(item)
462
+ else:
463
+ self._queue.push(item)
464
+
465
+ async def get(self):
466
+ """Remove and return an item, waiting if the queue is empty."""
467
+ if self._queue.length > 0:
468
+ return self._get_nowait_internal()
469
+ _self = self
470
+ v"""return await new Promise(function(resolve) { _self._getters.push(resolve); });"""
471
+
472
+ def get_nowait(self):
473
+ """Remove and return an item; raise QueueEmpty if the queue is empty."""
474
+ if self.empty():
475
+ raise QueueEmpty("Queue is empty")
476
+ return self._get_nowait_internal()
477
+
478
+ def _get_nowait_internal(self):
479
+ item = self._queue.shift()
480
+ if self._putters.length > 0:
481
+ putter = self._putters.shift()
482
+ self._queue.push(putter.item)
483
+ putter.resolve()
484
+ return item
485
+
486
+ def task_done(self):
487
+ """Mark a formerly enqueued item as processed.
488
+
489
+ Raises ValueError if called more times than there have been puts.
490
+ """
491
+ if self._unfinished_tasks <= 0:
492
+ raise ValueError("task_done() called too many times")
493
+ self._unfinished_tasks -= 1
494
+ if self._unfinished_tasks == 0:
495
+ for waiter in self._join_waiters:
496
+ waiter()
497
+ self._join_waiters = []
498
+
499
+ async def join(self):
500
+ """Block until all items which have been put into the queue are processed."""
501
+ if self._unfinished_tasks > 0:
502
+ _self = self
503
+ v"""await new Promise(function(resolve) { _self._join_waiters.push(resolve); });"""
504
+
505
+
506
+ class LifoQueue(Queue):
507
+ """Last-in, first-out variant of Queue."""
508
+ def _get_nowait_internal(self):
509
+ item = self._queue.pop()
510
+ if self._putters.length > 0:
511
+ putter = self._putters.shift()
512
+ self._queue.push(putter.item)
513
+ putter.resolve()
514
+ return item
515
+
516
+
517
+ class PriorityQueue(Queue):
518
+ """Priority queue where the smallest item is retrieved first."""
519
+ def _put_nowait_internal(self, item):
520
+ if self._getters.length > 0:
521
+ getter = self._getters.shift()
522
+ getter(item)
523
+ else:
524
+ self._queue.push(item)
525
+ self._queue.sort()
526
+
527
+ def _get_nowait_internal(self):
528
+ item = self._queue.shift()
529
+ if self._putters.length > 0:
530
+ putter = self._putters.shift()
531
+ self._queue.push(putter.item)
532
+ self._queue.sort()
533
+ putter.resolve()
534
+ return item