rapydscript-ns 0.9.1 → 0.9.3

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 (82) hide show
  1. package/CHANGELOG.md +22 -1
  2. package/PYTHON_GAPS.md +420 -0
  3. package/README.md +154 -30
  4. package/TODO.md +22 -7
  5. package/language-service/index.js +241 -12
  6. package/language-service/language-service.d.ts +1 -1
  7. package/memory/project_string_impl.md +43 -0
  8. package/package.json +6 -2
  9. package/release/baselib-plain-pretty.js +248 -38
  10. package/release/baselib-plain-ugly.js +8 -8
  11. package/release/compiler.js +821 -305
  12. package/release/signatures.json +15 -15
  13. package/src/ast.pyj +4 -1
  14. package/src/baselib-builtins.pyj +56 -2
  15. package/src/baselib-containers.pyj +2 -0
  16. package/src/baselib-errors.pyj +7 -3
  17. package/src/baselib-internal.pyj +51 -6
  18. package/src/baselib-str.pyj +5 -3
  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 +1 -1
  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/pythonize.pyj +1 -1
  31. package/src/lib/string.pyj +357 -0
  32. package/src/lib/textwrap.pyj +329 -0
  33. package/src/lib/urllib/__init__.pyj +14 -0
  34. package/src/lib/urllib/error.pyj +66 -0
  35. package/src/lib/urllib/parse.pyj +475 -0
  36. package/src/lib/urllib/request.pyj +86 -0
  37. package/src/monaco-language-service/analyzer.js +5 -2
  38. package/src/monaco-language-service/completions.js +26 -0
  39. package/src/monaco-language-service/diagnostics.js +204 -5
  40. package/src/monaco-language-service/index.js +2 -2
  41. package/src/monaco-language-service/scope.js +1 -0
  42. package/src/output/functions.pyj +152 -6
  43. package/src/output/loops.pyj +26 -2
  44. package/src/output/modules.pyj +1 -1
  45. package/src/output/operators.pyj +15 -0
  46. package/src/output/stream.pyj +0 -1
  47. package/src/parse.pyj +80 -17
  48. package/src/tokenizer.pyj +1 -1
  49. package/test/async_generators.pyj +144 -0
  50. package/test/asyncio.pyj +307 -0
  51. package/test/base64.pyj +202 -0
  52. package/test/bisect.pyj +178 -0
  53. package/test/csv.pyj +405 -0
  54. package/test/float_special.pyj +64 -0
  55. package/test/heapq.pyj +174 -0
  56. package/test/html.pyj +212 -0
  57. package/test/http.pyj +259 -0
  58. package/test/imports.pyj +7 -0
  59. package/test/logging.pyj +356 -0
  60. package/test/long.pyj +130 -0
  61. package/test/parenthesized_with.pyj +141 -0
  62. package/test/python_compat.pyj +3 -5
  63. package/test/python_modulo.pyj +76 -0
  64. package/test/python_modulo_off.pyj +21 -0
  65. package/test/str.pyj +14 -0
  66. package/test/string.pyj +245 -0
  67. package/test/textwrap.pyj +172 -0
  68. package/test/type_display.pyj +48 -0
  69. package/test/type_enforcement.pyj +164 -0
  70. package/test/unit/index.js +80 -6
  71. package/test/unit/language-service-completions.js +119 -0
  72. package/test/unit/language-service-scope.js +32 -0
  73. package/test/unit/language-service.js +128 -4
  74. package/test/unit/run-language-service.js +17 -3
  75. package/test/unit/web-repl.js +2094 -29
  76. package/test/urllib.pyj +193 -0
  77. package/tools/compile.js +1 -1
  78. package/tools/compiler.d.ts +367 -0
  79. package/tools/embedded_compiler.js +7 -7
  80. package/web-repl/main.js +1 -1
  81. package/web-repl/rapydscript.js +3 -3
  82. package/test/omit_function_metadata.pyj +0 -20
package/src/parse.pyj CHANGED
@@ -31,7 +31,7 @@ TreeWalker
31
31
  from tokenizer import tokenizer, is_token, RESERVED_WORDS
32
32
 
33
33
  COMPILER_VERSION = '__COMPILER_VERSION__'
34
- PYTHON_FLAGS = {'dict_literals':True, 'overload_getitem':True, 'bound_methods':True, 'hash_literals':True, 'overload_operators':True, 'truthiness':True, 'jsx':True, 'strict_arithmetic':True}
34
+ PYTHON_FLAGS = {'dict_literals':True, 'overload_getitem':True, 'bound_methods':True, 'hash_literals':True, 'overload_operators':True, 'truthiness':True, 'jsx':True, 'strict_arithmetic':True, 'python_modulo':True, 'type_enforcement':True}
35
35
 
36
36
 
37
37
  def get_compiler_version():
@@ -620,8 +620,15 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
620
620
  })
621
621
  elif tmp_ is "async":
622
622
  start = prev()
623
+ if is_("keyword", "for"):
624
+ # async for: consume an async iterable using JS `for await ... of`
625
+ next()
626
+ forstmt = for_()
627
+ forstmt.is_async = True
628
+ forstmt.start = start
629
+ return forstmt
623
630
  if not is_("keyword", "def"):
624
- croak("Expected 'def' after 'async'")
631
+ croak("Expected 'def' or 'for' after 'async'")
625
632
  next()
626
633
  func = function_(S.in_class[-1], False, True)
627
634
  func.start = start
@@ -708,21 +715,70 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
708
715
  def with_():
709
716
  clauses = v'[]'
710
717
  start = S.token
711
- while True:
712
- if is_('eof'):
713
- unexpected()
714
- expr = expression()
715
- alias = None
716
- if is_('keyword', 'as'):
717
- next()
718
- alias = as_symbol(AST_SymbolAlias)
719
- clauses.push(new AST_WithClause({'expression':expr, 'alias':alias}))
720
- if is_('punc', ','):
721
- next()
722
- continue
723
- if not is_('punc', ':'):
724
- unexpected()
725
- break
718
+
719
+ # Detect Python 3.10+ parenthesized form: with (cm1 as a, cm2 as b):
720
+ # vs traditional: with cm as a: or with (expr) as a:
721
+ # Lookahead: scan tokens inside the `(` for an `as` keyword at depth 0
722
+ # before the matching `)`. If found, it's the parenthesized form.
723
+ is_parenthesized_with = False
724
+ if is_('punc', '('):
725
+ depth = 0
726
+ i = 0
727
+ while True:
728
+ while S.peeked.length <= i:
729
+ S.peeked.push(S.input())
730
+ tok = S.peeked[i]
731
+ if tok.type is 'eof':
732
+ break
733
+ if tok.type is 'punc':
734
+ if tok.value is '(' or tok.value is '[' or tok.value is '{':
735
+ depth += 1
736
+ elif tok.value is ')' or tok.value is ']' or tok.value is '}':
737
+ if depth is 0:
738
+ break
739
+ depth -= 1
740
+ elif tok.type is 'keyword' and tok.value is 'as' and depth is 0:
741
+ is_parenthesized_with = True
742
+ break
743
+ i += 1
744
+
745
+ if is_parenthesized_with:
746
+ next() # consume `(`
747
+ while True:
748
+ if is_('eof'):
749
+ unexpected()
750
+ if is_('punc', ')'):
751
+ next() # trailing comma before closing `)`
752
+ break
753
+ expr = expression()
754
+ alias = None
755
+ if is_('keyword', 'as'):
756
+ next()
757
+ alias = as_symbol(AST_SymbolAlias)
758
+ clauses.push(new AST_WithClause({'expression': expr, 'alias': alias}))
759
+ if is_('punc', ','):
760
+ next()
761
+ continue
762
+ if not is_('punc', ')'):
763
+ unexpected()
764
+ next() # consume `)`
765
+ break
766
+ else:
767
+ while True:
768
+ if is_('eof'):
769
+ unexpected()
770
+ expr = expression()
771
+ alias = None
772
+ if is_('keyword', 'as'):
773
+ next()
774
+ alias = as_symbol(AST_SymbolAlias)
775
+ clauses.push(new AST_WithClause({'expression':expr, 'alias':alias}))
776
+ if is_('punc', ','):
777
+ next()
778
+ continue
779
+ if not is_('punc', ':'):
780
+ unexpected()
781
+ break
726
782
 
727
783
  if not clauses.length:
728
784
  token_error(start, 'with statement must have at least one clause')
@@ -1396,6 +1452,9 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1396
1452
  argnames.push(aname)
1397
1453
  if is_('punc', ','):
1398
1454
  next()
1455
+ if bracketed and is_('punc', ')'):
1456
+ next()
1457
+ break
1399
1458
  else:
1400
1459
  if bracketed:
1401
1460
  if is_('punc', ')'):
@@ -1824,6 +1883,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1824
1883
  )(S.in_loop, S.labels)
1825
1884
  })
1826
1885
  definition.return_annotation = return_annotation
1886
+ definition.type_enforce = S.scoped_flags.get('type_enforcement', False)
1827
1887
  definition.is_generator = is_generator[0]
1828
1888
  definition.is_async = is_async
1829
1889
  if is_node_type(definition, AST_Method):
@@ -3242,6 +3302,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
3242
3302
  'end': right.end,
3243
3303
  'overloaded': S.scoped_flags.get('overload_operators', False),
3244
3304
  'strict_arith': S.scoped_flags.get('overload_operators', False) and S.scoped_flags.get('strict_arithmetic', True),
3305
+ 'python_mod': (op is '%') and S.scoped_flags.get('python_modulo', True),
3245
3306
  'python_truthiness': S.scoped_flags.get('truthiness', False) and (op is '&&' or op is '||')
3246
3307
  })
3247
3308
  return expr_op(ret, min_prec, no_in)
@@ -3324,6 +3385,8 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
3324
3385
  if S.scoped_flags.get('overload_operators', False) and val is not '=':
3325
3386
  asgn.overloaded = True
3326
3387
  asgn.strict_arith = S.scoped_flags.get('strict_arithmetic', True)
3388
+ if val is '%=':
3389
+ asgn.python_mod = S.scoped_flags.get('python_modulo', True)
3327
3390
  return asgn
3328
3391
  return left
3329
3392
 
package/src/tokenizer.pyj CHANGED
@@ -706,7 +706,7 @@ def tokenizer(raw_text, filename):
706
706
  if (peek() is "'" or peek() is '"') and is_string_modifier(tok.value):
707
707
  mods = tok.value.toLowerCase()
708
708
  start_pos_for_string = S.tokpos
709
- stok = read_string(mods.indexOf('r') is not -1, mods.indexOf('v') is not -1)
709
+ stok = read_string(mods.indexOf('r') is not -1 or mods.indexOf('v') is not -1, mods.indexOf('v') is not -1)
710
710
  tok.endpos = stok.endpos
711
711
  if stok.type is not 'js' and mods.indexOf('f') is not -1:
712
712
  tok.col += start_pos_for_string - tok.pos
@@ -0,0 +1,144 @@
1
+ # globals: assrt
2
+ # vim:fileencoding=utf-8
3
+ #
4
+ # async_generators.pyj
5
+ # Tests for `async def` functions with `yield` (async generators).
6
+ #
7
+ # An async generator is a coroutine that produces values via `yield`. Calling
8
+ # it returns an async iterator whose `.next()` / `.asend()` return Promises.
9
+ #
10
+ # `vm.runInNewContext` (the test runner) is synchronous and does not drain the
11
+ # microtask queue, so these tests assert *synchronously observable* properties:
12
+ # the iterator shape, that .next() is thenable, that the compiled code runs
13
+ # without throwing, and that single-microtask resolution works via
14
+ # Promise.then chains observed through a shared mutable list. End-to-end
15
+ # async-for execution is covered by the web-repl bundle tests.
16
+
17
+ ae = assrt.equal
18
+ ade = assrt.deepEqual
19
+ ok = assrt.ok
20
+
21
+ # ── 1. Calling an async generator returns an iterator with the right shape ───
22
+
23
+ async def basic_gen():
24
+ yield 1
25
+ yield 2
26
+ yield 3
27
+
28
+ it = basic_gen()
29
+ ae(jstype(it.next), 'function')
30
+ ae(jstype(it.send), 'function') # Python compatibility alias (== .next)
31
+ ae(jstype(it.asend), 'function') # async-generator-specific alias (== .next)
32
+
33
+ # .next() must return a thenable Promise (sync generators return {value,done}).
34
+ p = it.next()
35
+ ae(jstype(p.then), 'function')
36
+
37
+ # ── 2. The wrapper is a sync function — no `await` needed to obtain it ───────
38
+ # An accidental `async function* aiter` (without our wrapper) would return a
39
+ # Promise here instead of an iterator.
40
+
41
+ ae(jstype(it[v'Symbol.asyncIterator']), 'function')
42
+
43
+ # ── 3. Promise resolution observable via .then ───────────────────────────────
44
+ # We collect each yielded {value, done} synchronously by walking the iterator
45
+ # manually. Each .then(...) chain runs as a microtask after this script ends,
46
+ # but we can still capture the *number* of pending links and the result list
47
+ # by chaining via Promise.all on the final .next.
48
+
49
+ results = []
50
+
51
+ def push_value(r):
52
+ results.push(r.value)
53
+
54
+ # Drive three steps; the last assertion runs in the final .then. If any link
55
+ # rejects, the final ok() in the assertion chain is skipped and the test
56
+ # process exits with a non-zero status because of the unhandled rejection.
57
+ chain = it.next()
58
+ v"""
59
+ chain = chain.then(function(r){ push_value(r); return basic_gen().next(); });
60
+ chain.then(function(r){ push_value(r); });
61
+ """
62
+
63
+ # ── 4. Empty async generator: a `yield` that never executes still makes the
64
+ # function an async generator (not a regular coroutine). ─────────────────────
65
+
66
+ async def empty_gen():
67
+ if False:
68
+ yield 0
69
+
70
+ empty_it = empty_gen()
71
+ ae(jstype(empty_it.next), 'function')
72
+ ae(jstype(empty_it[v'Symbol.asyncIterator']), 'function')
73
+
74
+ # ── 5. `await` is allowed before/between yields inside async generators ──────
75
+
76
+ def deferred(v):
77
+ return v'Promise.resolve(v)'
78
+
79
+ async def awaited_gen():
80
+ a = await deferred(10)
81
+ yield a
82
+ b = await deferred(20)
83
+ yield a + b
84
+
85
+ aw_it = awaited_gen()
86
+ ae(jstype(aw_it.next), 'function')
87
+ # .next() returns a thenable even though the body awaits before yielding
88
+ ae(jstype(aw_it.next().then), 'function')
89
+
90
+ # ── 6. `async for` parses and compiles inside an async function ──────────────
91
+ # We don't drive the loop here (microtasks won't run), but we verify the
92
+ # coroutine object is produced and is thenable.
93
+
94
+ async def consume():
95
+ out = []
96
+ async for x in basic_gen():
97
+ out.append(x)
98
+ return out
99
+
100
+ p = consume()
101
+ ae(jstype(p.then), 'function')
102
+
103
+ # ── 7. Class methods can be async generators ─────────────────────────────────
104
+
105
+ class Counter:
106
+ def __init__(self, limit):
107
+ self.limit = limit
108
+
109
+ async def values(self):
110
+ i = 0
111
+ while i < self.limit:
112
+ yield i
113
+ i += 1
114
+
115
+ c_it = Counter(3).values()
116
+ ae(jstype(c_it.next), 'function')
117
+ ae(jstype(c_it[v'Symbol.asyncIterator']), 'function')
118
+
119
+ # ── 8. Yielded values match Python semantics for the simple sync-yield case ──
120
+ # Microtask-driven: the .then below runs after all top-level code, so we
121
+ # accumulate into `results` and assert via the final chained .then. If the
122
+ # assertion throws, Node exits non-zero (unhandled rejection).
123
+
124
+ drain = []
125
+
126
+ def collect_one(r):
127
+ if not r.done:
128
+ drain.push(r.value)
129
+
130
+ def assert_drain():
131
+ # Order of resolution is sequential because each .then awaits the prior.
132
+ # Expected: yields 1, 2, 3 from basic_gen()
133
+ ade(drain, [1, 2, 3])
134
+
135
+ g = basic_gen()
136
+ v"""
137
+ g.next().then(function(r){ collect_one(r); return g.next(); })
138
+ .then(function(r){ collect_one(r); return g.next(); })
139
+ .then(function(r){ collect_one(r); return g.next(); })
140
+ .then(function(r){
141
+ // r.done should be true now; assert the accumulated values.
142
+ assert_drain();
143
+ });
144
+ """
@@ -0,0 +1,307 @@
1
+ # globals: assrt
2
+ # vim:fileencoding=utf-8
3
+ #
4
+ # asyncio.pyj
5
+ # Tests for the asyncio standard library module.
6
+ #
7
+ # Most tests verify synchronous-observable behaviour (exceptions, queue state,
8
+ # lock/event/semaphore flags, iscoroutine/iscoroutinefunction) because the
9
+ # vm.runInNewContext test runner is synchronous. Async coroutine execution
10
+ # is tested by verifying that async functions compile correctly and return
11
+ # thenable Promises.
12
+
13
+ from asyncio import (
14
+ sleep, gather, create_task, ensure_future, run,
15
+ shield, wait_for, iscoroutine, iscoroutinefunction,
16
+ current_task, all_tasks, get_event_loop, get_running_loop, new_event_loop,
17
+ CancelledError, TimeoutError, InvalidStateError, RuntimeError,
18
+ QueueEmpty, QueueFull,
19
+ Lock, Event, Semaphore, BoundedSemaphore,
20
+ Queue, LifoQueue, PriorityQueue
21
+ )
22
+
23
+ ae = assrt.equal
24
+ ade = assrt.deepEqual
25
+ ok = assrt.ok
26
+
27
+ # ── 1. Exception classes ─────────────────────────────────────────────────────
28
+
29
+ try:
30
+ raise CancelledError('cancelled')
31
+ ok(False)
32
+ except CancelledError as e:
33
+ ae(e.message, 'cancelled')
34
+
35
+ try:
36
+ raise TimeoutError('timed out')
37
+ ok(False)
38
+ except TimeoutError as e:
39
+ ae(e.message, 'timed out')
40
+
41
+ try:
42
+ raise InvalidStateError('bad state')
43
+ ok(False)
44
+ except InvalidStateError as e:
45
+ ae(e.message, 'bad state')
46
+
47
+ try:
48
+ raise QueueEmpty('empty')
49
+ ok(False)
50
+ except QueueEmpty as e:
51
+ ae(e.message, 'empty')
52
+
53
+ try:
54
+ raise QueueFull('full')
55
+ ok(False)
56
+ except QueueFull as e:
57
+ ae(e.message, 'full')
58
+
59
+ # ── 2. sleep / gather return Promises ────────────────────────────────────────
60
+
61
+ ok(iscoroutine(sleep(0)))
62
+ ok(iscoroutine(sleep(0.1)))
63
+ ok(iscoroutine(gather()))
64
+ ok(iscoroutine(gather(Promise.resolve(1), Promise.resolve(2))))
65
+
66
+ # ── 3. create_task / ensure_future / run / shield pass through Promises ──────
67
+
68
+ p = Promise.resolve(42)
69
+ ok(iscoroutine(create_task(p)))
70
+ ok(iscoroutine(ensure_future(p)))
71
+ ok(iscoroutine(run(p)))
72
+ ok(iscoroutine(shield(p)))
73
+
74
+ # ── 4. iscoroutine / iscoroutinefunction ─────────────────────────────────────
75
+
76
+ ok(not iscoroutine(42))
77
+ ok(not iscoroutine(None))
78
+ ok(not iscoroutine('string'))
79
+ ok(not iscoroutine([]))
80
+
81
+ async def _async_fn():
82
+ return 1
83
+
84
+ ok(iscoroutinefunction(_async_fn))
85
+
86
+ def _sync_fn():
87
+ return 1
88
+
89
+ ok(not iscoroutinefunction(_sync_fn))
90
+ ok(not iscoroutinefunction(42))
91
+ ok(not iscoroutinefunction(None))
92
+
93
+ # Async functions return Promises
94
+ p2 = _async_fn()
95
+ ok(iscoroutine(p2))
96
+
97
+ # ── 5. current_task / all_tasks ───────────────────────────────────────────────
98
+
99
+ ok(current_task() is None)
100
+ ok(len(all_tasks()) == 0)
101
+
102
+ # ── 6. Event loop stubs ───────────────────────────────────────────────────────
103
+
104
+ loop = get_event_loop()
105
+ ok(loop is not None)
106
+ ok(loop is get_running_loop())
107
+
108
+ loop2 = new_event_loop()
109
+ ok(loop2 is not None)
110
+ ok(not loop2.is_closed())
111
+ ok(loop2.is_running())
112
+
113
+ p3 = Promise.resolve(99)
114
+ ok(iscoroutine(loop.run_until_complete(p3)))
115
+ ok(iscoroutine(loop.create_task(p3)))
116
+
117
+ # ── 7. Lock — synchronous state ──────────────────────────────────────────────
118
+
119
+ lock = Lock()
120
+ ok(not lock.locked())
121
+
122
+ # acquire() returns a Promise (since it's async def)
123
+ acq = lock.acquire()
124
+ ok(iscoroutine(acq))
125
+
126
+ # After acquiring asynchronously lock should become locked.
127
+ # We can test the synchronous fast-path by observing the side-effect in a
128
+ # chained then (fire-and-forget style; the VM won't await it but we verify
129
+ # the structure is correct):
130
+ lock2 = Lock()
131
+ ok(not lock2.locked())
132
+
133
+ try:
134
+ lock2.release()
135
+ ok(False)
136
+ except RuntimeError:
137
+ ok(True)
138
+
139
+ # ── 8. Event — synchronous set/clear/is_set ──────────────────────────────────
140
+
141
+ ev = Event()
142
+ ok(not ev.is_set())
143
+
144
+ ev.set()
145
+ ok(ev.is_set())
146
+
147
+ ev.clear()
148
+ ok(not ev.is_set())
149
+
150
+ # wait() returns a Promise
151
+ ok(iscoroutine(ev.wait()))
152
+
153
+ # If event is already set, wait() still returns a thenable
154
+ ev.set()
155
+ ok(iscoroutine(ev.wait()))
156
+
157
+ # ── 9. Semaphore — synchronous state ─────────────────────────────────────────
158
+
159
+ sem = Semaphore(2)
160
+ ok(not sem.locked())
161
+
162
+ sem.release()
163
+ ok(not sem.locked())
164
+
165
+ sem.release()
166
+ ok(not sem.locked())
167
+
168
+ # acquire() returns a Promise
169
+ ok(iscoroutine(sem.acquire()))
170
+
171
+ try:
172
+ Semaphore(-1)
173
+ ok(False)
174
+ except ValueError:
175
+ ok(True)
176
+
177
+ # ── 10. BoundedSemaphore ─────────────────────────────────────────────────────
178
+
179
+ # BoundedSemaphore(n) starts at value=n (same as bound).
180
+ # Releasing without a prior acquire immediately exceeds the bound.
181
+ bsem = BoundedSemaphore(1)
182
+ ok(not bsem.locked())
183
+ try:
184
+ bsem.release()
185
+ ok(False)
186
+ except ValueError:
187
+ ok(True)
188
+
189
+ # BoundedSemaphore(0) is always locked and cannot be released at all.
190
+ bsem0 = BoundedSemaphore(0)
191
+ ok(bsem0.locked())
192
+ try:
193
+ bsem0.release()
194
+ ok(False)
195
+ except ValueError:
196
+ ok(True)
197
+
198
+ # ── 11. Queue — synchronous put_nowait / get_nowait ──────────────────────────
199
+
200
+ q = Queue()
201
+ ok(q.empty())
202
+ ok(q.qsize() == 0)
203
+ ok(not q.full())
204
+
205
+ q.put_nowait('a')
206
+ q.put_nowait('b')
207
+ q.put_nowait('c')
208
+ ok(q.qsize() == 3)
209
+ ok(not q.empty())
210
+
211
+ ae(q.get_nowait(), 'a')
212
+ q.task_done()
213
+ ae(q.get_nowait(), 'b')
214
+ q.task_done()
215
+ ae(q.get_nowait(), 'c')
216
+ q.task_done()
217
+ ok(q.empty())
218
+
219
+ # QueueFull with bounded queue
220
+ q2 = Queue(2)
221
+ ok(not q2.full())
222
+ q2.put_nowait(1)
223
+ q2.put_nowait(2)
224
+ ok(q2.full())
225
+ try:
226
+ q2.put_nowait(3)
227
+ ok(False)
228
+ except QueueFull:
229
+ ok(True)
230
+
231
+ # QueueEmpty
232
+ q3 = Queue()
233
+ try:
234
+ q3.get_nowait()
235
+ ok(False)
236
+ except QueueEmpty:
237
+ ok(True)
238
+
239
+ # task_done() overflow check
240
+ q4 = Queue()
241
+ q4.put_nowait('x')
242
+ ae(q4.get_nowait(), 'x')
243
+ q4.task_done()
244
+ try:
245
+ q4.task_done()
246
+ ok(False)
247
+ except ValueError:
248
+ ok(True)
249
+
250
+ # put() and get() return Promises (async methods) — test on fresh queues
251
+ q5 = Queue()
252
+ put_p = q5.put('async-item') # no waiters, goes to internal queue
253
+ ok(iscoroutine(put_p))
254
+ ae(q5.get_nowait(), 'async-item')
255
+ q5.task_done()
256
+
257
+ q6 = Queue()
258
+ get_p = q6.get() # empty queue → adds a getter waiter
259
+ ok(iscoroutine(get_p))
260
+
261
+ # join() returns a Promise
262
+ q7 = Queue()
263
+ ok(iscoroutine(q7.join()))
264
+
265
+ # ── 12. LifoQueue — last-in first-out ordering ───────────────────────────────
266
+
267
+ lq = LifoQueue()
268
+ lq.put_nowait(1)
269
+ lq.put_nowait(2)
270
+ lq.put_nowait(3)
271
+ ae(lq.get_nowait(), 3)
272
+ ae(lq.get_nowait(), 2)
273
+ ae(lq.get_nowait(), 1)
274
+
275
+ # ── 13. PriorityQueue — lowest value dequeued first ──────────────────────────
276
+
277
+ pq = PriorityQueue()
278
+ pq.put_nowait(30)
279
+ pq.put_nowait(10)
280
+ pq.put_nowait(20)
281
+ ae(pq.get_nowait(), 10)
282
+ ae(pq.get_nowait(), 20)
283
+ ae(pq.get_nowait(), 30)
284
+
285
+ # ── 14. async def / await compiles correctly ─────────────────────────────────
286
+
287
+ async def _add_async(a, b):
288
+ return a + b
289
+
290
+ ok(iscoroutinefunction(_add_async))
291
+ p4 = _add_async(3, 4)
292
+ ok(iscoroutine(p4))
293
+
294
+ async def _chain():
295
+ x = await Promise.resolve(10)
296
+ y = await Promise.resolve(20)
297
+ return x + y
298
+
299
+ p5 = _chain()
300
+ ok(iscoroutine(p5))
301
+
302
+ async def _gather_test():
303
+ results = await gather(Promise.resolve(1), Promise.resolve(2), Promise.resolve(3))
304
+ return results
305
+
306
+ p6 = _gather_test()
307
+ ok(iscoroutine(p6))