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
@@ -0,0 +1,356 @@
1
+ # globals: assrt
2
+ # vim:fileencoding=utf-8
3
+ #
4
+ # logging.pyj
5
+ # Tests for the logging standard library module.
6
+
7
+ from logging import (
8
+ Logger, Handler, StreamHandler, NullHandler, Formatter, Filter,
9
+ LogRecord, makeLogRecord,
10
+ getLogger, basicConfig, disable,
11
+ addLevelName, getLevelName,
12
+ NOTSET, DEBUG, INFO, WARNING, ERROR, CRITICAL,
13
+ lastResort, root,
14
+ )
15
+ import logging
16
+
17
+ ae = assrt.equal
18
+ ade = assrt.deepEqual
19
+ ok = assrt.ok
20
+
21
+
22
+ # ── 1. Level constants ────────────────────────────────────────────────────────
23
+
24
+ ae(NOTSET, 0)
25
+ ae(DEBUG, 10)
26
+ ae(INFO, 20)
27
+ ae(WARNING, 30)
28
+ ae(ERROR, 40)
29
+ ae(CRITICAL, 50)
30
+ ae(logging.WARN, WARNING)
31
+ ae(logging.FATAL, CRITICAL)
32
+
33
+
34
+ # ── 2. getLevelName ───────────────────────────────────────────────────────────
35
+
36
+ ae(getLevelName(DEBUG), 'DEBUG')
37
+ ae(getLevelName(INFO), 'INFO')
38
+ ae(getLevelName(WARNING), 'WARNING')
39
+ ae(getLevelName(ERROR), 'ERROR')
40
+ ae(getLevelName(CRITICAL), 'CRITICAL')
41
+ ae(getLevelName(0), 'NOTSET')
42
+ ae(getLevelName(42), 'Level 42')
43
+
44
+ ae(getLevelName('DEBUG'), DEBUG)
45
+ ae(getLevelName('WARNING'), WARNING)
46
+ ae(getLevelName('CRITICAL'), CRITICAL)
47
+ ae(getLevelName('WARN'), WARNING)
48
+ ae(getLevelName('FATAL'), CRITICAL)
49
+
50
+
51
+ # ── 3. addLevelName ───────────────────────────────────────────────────────────
52
+
53
+ addLevelName(25, 'TRACE')
54
+ ae(getLevelName(25), 'TRACE')
55
+ ae(getLevelName('TRACE'), 25)
56
+
57
+
58
+ # ── 4. LogRecord.getMessage — %-formatting ────────────────────────────────────
59
+
60
+ _r1 = LogRecord('test', DEBUG, '', 0, 'hello world', [], None)
61
+ ae(_r1.getMessage(), 'hello world')
62
+
63
+ _r2 = LogRecord('test', INFO, '', 0, 'Hello %s', ['Alice'], None)
64
+ ae(_r2.getMessage(), 'Hello Alice')
65
+
66
+ _r3 = LogRecord('test', WARNING, '', 0, 'Val=%d x=%f', [42, 3.14], None)
67
+ ae(_r3.getMessage(), 'Val=42 x=3.140000')
68
+
69
+ _r4 = LogRecord('test', ERROR, '', 0, '100%%', [], None)
70
+ ae(_r4.getMessage(), '100%')
71
+
72
+
73
+ # ── 5. LogRecord attributes ───────────────────────────────────────────────────
74
+
75
+ _r5 = LogRecord('myapp', ERROR, '/foo.py', 10, 'boom', [], None)
76
+ ae(_r5.name, 'myapp')
77
+ ae(_r5.levelno, ERROR)
78
+ ae(_r5.levelname, 'ERROR')
79
+ ae(_r5.lineno, 10)
80
+ ae(_r5.threadName, 'MainThread')
81
+ ok(_r5.created > 0, 'created is positive')
82
+ ok(_r5.relativeCreated >= 0, 'relativeCreated is non-negative')
83
+
84
+
85
+ # ── 6. makeLogRecord ──────────────────────────────────────────────────────────
86
+
87
+ _d = {}
88
+ _d['name'] = 'foo'
89
+ _d['levelno'] = WARNING
90
+ _d['msg'] = 'hi'
91
+ _mr = makeLogRecord(_d)
92
+ ae(_mr.name, 'foo')
93
+ ae(_mr.levelno, WARNING)
94
+ ae(_mr.msg, 'hi')
95
+
96
+
97
+ # ── 7. Formatter ──────────────────────────────────────────────────────────────
98
+
99
+ class _Buf:
100
+ def __init__(self):
101
+ self.lines = []
102
+ def write(self, s):
103
+ self.lines.push(s)
104
+
105
+
106
+ _fmt1 = Formatter('%(levelname)s:%(name)s:%(message)s')
107
+ _rec = LogRecord('myapp', INFO, '', 0, 'hello', [], None)
108
+ ae(_fmt1.format(_rec), 'INFO:myapp:hello')
109
+
110
+ _fmt2 = Formatter('%(levelno)d %(name)s %(message)s')
111
+ _rec2 = LogRecord('app', WARNING, '', 0, 'watch out', [], None)
112
+ ae(_fmt2.format(_rec2), '30 app watch out')
113
+
114
+ ok(_fmt1.usesTime() is False, 'usesTime False')
115
+ _fmt3 = Formatter('%(asctime)s %(message)s')
116
+ ok(_fmt3.usesTime() is True, 'usesTime True')
117
+
118
+ # asctime must be a non-empty string
119
+ _rec3 = LogRecord('t', INFO, '', 0, 'msg', [], None)
120
+ _s3 = _fmt3.format(_rec3)
121
+ ok(_s3.length > 5, 'asctime result is non-empty')
122
+
123
+
124
+ # ── 8. Filter ─────────────────────────────────────────────────────────────────
125
+
126
+ _f_all = Filter()
127
+ _f_app = Filter('myapp')
128
+ _f_sub = Filter('myapp.sub')
129
+
130
+ _rA = LogRecord('myapp', INFO, '', 0, 'm', [], None)
131
+ _rB = LogRecord('myapp.sub', INFO, '', 0, 'm', [], None)
132
+ _rC = LogRecord('myapp.sub.x', INFO, '', 0, 'm', [], None)
133
+ _rD = LogRecord('otherapp', INFO, '', 0, 'm', [], None)
134
+
135
+ ok(_f_all.filter(_rA) is True, 'empty filter accepts all')
136
+ ok(_f_all.filter(_rD) is True, 'empty filter accepts all 2')
137
+
138
+ ok(_f_app.filter(_rA) is True, 'parent name matches')
139
+ ok(_f_app.filter(_rB) is True, 'child name matches')
140
+ ok(_f_app.filter(_rC) is True, 'deep child matches')
141
+ ok(_f_app.filter(_rD) is False, 'unrelated rejected')
142
+
143
+ ok(_f_sub.filter(_rB) is True, 'sub self-match')
144
+ ok(_f_sub.filter(_rC) is True, 'sub child match')
145
+ ok(_f_sub.filter(_rA) is False, 'parent of sub rejected')
146
+
147
+
148
+ # ── 9. StreamHandler with custom stream ──────────────────────────────────────
149
+
150
+ _buf9 = _Buf()
151
+ _sh9 = StreamHandler(_buf9)
152
+ _sh9.setFormatter(Formatter('%(levelname)s:%(name)s:%(message)s'))
153
+ _sh9.setLevel(DEBUG)
154
+
155
+ _l9 = Logger('test9')
156
+ _l9.addHandler(_sh9)
157
+ _l9.setLevel(DEBUG)
158
+ _l9.propagate = False
159
+
160
+ _l9.debug('dbg')
161
+ _l9.info('inf')
162
+ _l9.warning('wrn')
163
+ _l9.error('err')
164
+ _l9.critical('crit')
165
+
166
+ ae(_buf9.lines.length, 5)
167
+ ae(_buf9.lines[0], 'DEBUG:test9:dbg\n')
168
+ ae(_buf9.lines[1], 'INFO:test9:inf\n')
169
+ ae(_buf9.lines[2], 'WARNING:test9:wrn\n')
170
+ ae(_buf9.lines[3], 'ERROR:test9:err\n')
171
+ ae(_buf9.lines[4], 'CRITICAL:test9:crit\n')
172
+
173
+
174
+ # ── 10. NullHandler ───────────────────────────────────────────────────────────
175
+
176
+ _nh = NullHandler()
177
+ _rn = LogRecord('x', WARNING, '', 0, 'msg', [], None)
178
+ ok(_nh.handle(_rn) is None or _nh.handle(_rn) is False or True, 'NullHandler.handle does not throw')
179
+
180
+
181
+ # ── 11. Logger.isEnabledFor / getEffectiveLevel ───────────────────────────────
182
+
183
+ _la = Logger('lvltest')
184
+ _la.setLevel(WARNING)
185
+ _la.propagate = False
186
+
187
+ ok(_la.isEnabledFor(DEBUG) is False, 'DEBUG below WARNING')
188
+ ok(_la.isEnabledFor(INFO) is False, 'INFO below WARNING')
189
+ ok(_la.isEnabledFor(WARNING) is True, 'WARNING == WARNING')
190
+ ok(_la.isEnabledFor(ERROR) is True, 'ERROR above WARNING')
191
+ ok(_la.isEnabledFor(CRITICAL) is True, 'CRITICAL above WARNING')
192
+
193
+ ae(_la.getEffectiveLevel(), WARNING)
194
+
195
+ _la.setLevel(NOTSET)
196
+ _la.parent = None
197
+ ae(_la.getEffectiveLevel(), WARNING) # no parent → falls back to WARNING
198
+
199
+
200
+ # ── 12. Logger level filtering (messages suppressed below level) ──────────────
201
+
202
+ _buf12 = _Buf()
203
+ _sh12 = StreamHandler(_buf12)
204
+ _sh12.setFormatter(Formatter('%(levelname)s'))
205
+ _sh12.setLevel(NOTSET)
206
+
207
+ _l12 = Logger('filt12')
208
+ _l12.addHandler(_sh12)
209
+ _l12.setLevel(WARNING)
210
+ _l12.propagate = False
211
+
212
+ _l12.debug('no')
213
+ _l12.info('no')
214
+ _l12.warning('yes')
215
+ _l12.error('yes')
216
+
217
+ ae(_buf12.lines.length, 2)
218
+ ae(_buf12.lines[0], 'WARNING\n')
219
+ ae(_buf12.lines[1], 'ERROR\n')
220
+
221
+
222
+ # ── 13. Logger.log() with numeric level ──────────────────────────────────────
223
+
224
+ _buf13 = _Buf()
225
+ _sh13 = StreamHandler(_buf13)
226
+ _sh13.setFormatter(Formatter('%(levelno)d:%(message)s'))
227
+ _l13 = Logger('log13')
228
+ _l13.addHandler(_sh13)
229
+ _l13.setLevel(DEBUG)
230
+ _l13.propagate = False
231
+ _l13.log(ERROR, 'test via log()')
232
+ ae(_buf13.lines[0], '40:test via log()\n')
233
+
234
+
235
+ # ── 14. %-style args in Logger methods ────────────────────────────────────────
236
+
237
+ _buf14 = _Buf()
238
+ _sh14 = StreamHandler(_buf14)
239
+ _sh14.setFormatter(Formatter('%(message)s'))
240
+ _l14 = Logger('args14')
241
+ _l14.addHandler(_sh14)
242
+ _l14.setLevel(DEBUG)
243
+ _l14.propagate = False
244
+ _l14.info('x=%d y=%s', 7, 'foo')
245
+ ae(_buf14.lines[0], 'x=7 y=foo\n')
246
+
247
+
248
+ # ── 15. Logger.addFilter / Filterer ──────────────────────────────────────────
249
+
250
+ _buf15 = _Buf()
251
+ _sh15 = StreamHandler(_buf15)
252
+ _sh15.setFormatter(Formatter('%(message)s'))
253
+ _l15 = Logger('parent15')
254
+ _l15.addHandler(_sh15)
255
+ _l15.setLevel(DEBUG)
256
+ _l15.propagate = False
257
+ _l15.addFilter(Filter('parent15.child')) # only pass records from parent15.child*
258
+
259
+ _ra15 = LogRecord('parent15', DEBUG, '', 0, 'parent', [], None)
260
+ _rb15 = LogRecord('parent15.child', DEBUG, '', 0, 'child', [], None)
261
+ _l15.handle(_ra15)
262
+ _l15.handle(_rb15)
263
+ ae(_buf15.lines.length, 1)
264
+ ae(_buf15.lines[0], 'child\n')
265
+
266
+
267
+ # ── 16. Handler.addFilter ─────────────────────────────────────────────────────
268
+
269
+ _buf16 = _Buf()
270
+ _sh16 = StreamHandler(_buf16)
271
+ _sh16.setFormatter(Formatter('%(message)s'))
272
+ _sh16.addFilter(Filter('ok'))
273
+
274
+ _l16 = Logger('hdlr16')
275
+ _l16.addHandler(_sh16)
276
+ _l16.setLevel(DEBUG)
277
+ _l16.propagate = False
278
+
279
+ _l16.handle(LogRecord('ok', DEBUG, '', 0, 'pass', [], None))
280
+ _l16.handle(LogRecord('bad', DEBUG, '', 0, 'block', [], None))
281
+ ae(_buf16.lines.length, 1)
282
+ ae(_buf16.lines[0], 'pass\n')
283
+
284
+
285
+ # ── 17. getLogger — same name returns same instance ──────────────────────────
286
+
287
+ _ga = getLogger('getlogger_test')
288
+ _gb = getLogger('getlogger_test')
289
+ ok(_ga is _gb, 'getLogger returns same instance for same name')
290
+ ok(getLogger() is root, 'getLogger() returns root')
291
+ ok(getLogger('root') is root, 'getLogger("root") returns root')
292
+
293
+
294
+ # ── 18. Logger hierarchy — propagation ────────────────────────────────────────
295
+
296
+ _buf18 = _Buf()
297
+ _sh18 = StreamHandler(_buf18)
298
+ _sh18.setFormatter(Formatter('%(name)s:%(message)s'))
299
+
300
+ _parent18 = Logger('app18')
301
+ _parent18.addHandler(_sh18)
302
+ _parent18.setLevel(DEBUG)
303
+ _parent18.propagate = False
304
+
305
+ _child18 = Logger('app18.mod')
306
+ _child18.parent = _parent18
307
+ _child18.setLevel(DEBUG)
308
+ _child18.propagate = True
309
+
310
+ _child18.info('from child')
311
+ ae(_buf18.lines.length, 1)
312
+ ae(_buf18.lines[0], 'app18.mod:from child\n')
313
+
314
+
315
+ # ── 19. disable() ─────────────────────────────────────────────────────────────
316
+
317
+ _buf19 = _Buf()
318
+ _sh19 = StreamHandler(_buf19)
319
+ _sh19.setFormatter(Formatter('%(message)s'))
320
+ _l19 = Logger('dis19')
321
+ _l19.addHandler(_sh19)
322
+ _l19.setLevel(DEBUG)
323
+ _l19.propagate = False
324
+
325
+ _l19.warning('before disable')
326
+ logging.disable(CRITICAL)
327
+ _l19.warning('after disable – suppressed')
328
+ _l19.critical('also suppressed')
329
+ ae(_buf19.lines.length, 1)
330
+
331
+ # reset disable level so later tests aren't affected
332
+ logging.disable(NOTSET)
333
+
334
+
335
+ # ── 20. hasHandlers ───────────────────────────────────────────────────────────
336
+
337
+ _l20 = Logger('hashdlr20')
338
+ _l20.parent = None
339
+ _l20.propagate = False
340
+ ok(_l20.hasHandlers() is False, 'no handlers')
341
+ _l20.addHandler(NullHandler())
342
+ ok(_l20.hasHandlers() is True, 'has handler')
343
+
344
+
345
+ # ── 21. exception() logs at ERROR ─────────────────────────────────────────────
346
+
347
+ _buf21 = _Buf()
348
+ _sh21 = StreamHandler(_buf21)
349
+ _sh21.setFormatter(Formatter('%(levelname)s:%(message)s'))
350
+ _l21 = Logger('exc21')
351
+ _l21.addHandler(_sh21)
352
+ _l21.setLevel(DEBUG)
353
+ _l21.propagate = False
354
+ _l21.exception('something went wrong')
355
+ ae(_buf21.lines.length, 1)
356
+ ae(_buf21.lines[0], 'ERROR:something went wrong\n')
package/test/long.pyj ADDED
@@ -0,0 +1,130 @@
1
+ # vim:fileencoding=utf-8
2
+ # globals: assrt
3
+ from __python__ import overload_operators
4
+
5
+ ae = assrt.equal
6
+ ade = assrt.deepEqual
7
+ ok = assrt.ok
8
+ throws = assrt.throws
9
+
10
+ # ── construction ──────────────────────────────────────────────────────────────
11
+
12
+ # from int
13
+ ok(jstype(long(0)) is 'bigint')
14
+ ok(jstype(long(42)) is 'bigint')
15
+ ok(jstype(long(-99)) is 'bigint')
16
+
17
+ # from string (base 10)
18
+ ok(jstype(long('12345678901234567890')) is 'bigint')
19
+ ae(str(long('42')), '42')
20
+ ae(str(long('-7')), '-7')
21
+
22
+ # from string with explicit base 10
23
+ ae(str(long('255', 10)), '255')
24
+
25
+ # from string base 16
26
+ ae(str(long('ff', 16)), '255')
27
+ ae(str(long('FF', 16)), '255')
28
+ ae(str(long('0xff', 16)), '255')
29
+ ae(str(long('0xFF', 16)), '255')
30
+
31
+ # from string base 2
32
+ ae(str(long('1010', 2)), '10')
33
+ ae(str(long('0b1010', 2)), '10')
34
+
35
+ # from string base 8
36
+ ae(str(long('17', 8)), '15')
37
+ ae(str(long('0o17', 8)), '15')
38
+
39
+ # from bool
40
+ ae(str(long(True)), '1')
41
+ ae(str(long(False)), '0')
42
+
43
+ # identity: long of long
44
+ x = long(7)
45
+ ae(str(long(x)), '7')
46
+
47
+ # very large integer (beyond JS Number precision)
48
+ big = long('99999999999999999999999999999999')
49
+ ae(str(big), '99999999999999999999999999999999')
50
+
51
+ # ── error cases ───────────────────────────────────────────────────────────────
52
+
53
+ throws(def(): long('abc');, ValueError)
54
+ throws(def(): long('xyz', 16);, ValueError)
55
+ throws(def(): long(3.14);, TypeError)
56
+ throws(def(): long(None);, TypeError)
57
+
58
+ # ── arithmetic ────────────────────────────────────────────────────────────────
59
+
60
+ a = long(10)
61
+ b = long(3)
62
+
63
+ ae(str(a + b), '13')
64
+ ae(str(a - b), '7')
65
+ ae(str(a * b), '30')
66
+
67
+ # floor division — Python semantics (floor toward -inf)
68
+ ae(str(a // b), '3')
69
+ ae(str(long(-7) // long(2)), '-4') # Python: -4, JS truncate: -3
70
+ ae(str(long(7) // long(-2)), '-4') # Python: -4, JS truncate: -3
71
+ ae(str(long(-7) // long(-2)), '3') # both negative
72
+
73
+ # modulo — Python semantics (result has same sign as divisor)
74
+ ae(str(long(7) % long(3)), '1')
75
+ ae(str(long(-7) % long(3)), '2') # Python: 2, JS BigInt: -1
76
+ ae(str(long(7) % long(-3)), '-2') # Python: -2, JS BigInt: 1
77
+ ae(str(long(-7) % long(-3)), '-1') # both negative
78
+
79
+ # power
80
+ ae(str(long(2) ** long(10)), '1024')
81
+ ae(str(long(3) ** long(0)), '1')
82
+
83
+ # negative exponent raises ValueError
84
+ throws(def(): long(2) ** long(-1);, ValueError)
85
+
86
+ # truediv raises TypeError (use // instead)
87
+ throws(def(): long(10) / long(3);, TypeError)
88
+
89
+ # ── bitwise ───────────────────────────────────────────────────────────────────
90
+
91
+ ae(str(long(0b1010) & long(0b1100)), '8') # 0b1000 = 8
92
+ ae(str(long(0b1010) | long(0b1100)), '14') # 0b1110 = 14
93
+ ae(str(long(0b1010) ^ long(0b1100)), '6') # 0b0110 = 6
94
+ ae(str(long(1) << long(4)), '16')
95
+ ae(str(long(16) >> long(2)), '4')
96
+
97
+ # mixed long + int raises TypeError
98
+ throws(def(): long(1) + 1;, TypeError)
99
+ throws(def(): long(1) & 1;, TypeError)
100
+
101
+ # ── comparisons ───────────────────────────────────────────────────────────────
102
+
103
+ ok(long(1) < long(2))
104
+ ok(long(2) > long(1))
105
+ ok(long(3) <= long(3))
106
+ ok(long(3) >= long(3))
107
+ ok(long(1) == long(1))
108
+ ok(long(1) != long(2))
109
+
110
+ # ── isinstance / type ─────────────────────────────────────────────────────────
111
+
112
+ ok(isinstance(long(5), long))
113
+ ok(isinstance(long(0), long))
114
+ ok(isinstance(long(-1), long))
115
+ ok(not isinstance(5, long))
116
+ ok(not isinstance('5', long))
117
+
118
+ # ── str() conversion ──────────────────────────────────────────────────────────
119
+
120
+ ae(str(long(0)), '0')
121
+ ae(str(long(12345)), '12345')
122
+ ae(str(long(-42)), '-42')
123
+ ae(str(long('99999999999999999999')), '99999999999999999999')
124
+
125
+ # ── identity with large numbers ───────────────────────────────────────────────
126
+
127
+ # Verify precision is maintained (JS Number loses this)
128
+ n = long('9007199254740993') # 2^53 + 1, not representable as JS Number
129
+ ae(str(n), '9007199254740993')
130
+ ae(str(n + long(1)), '9007199254740994')
@@ -0,0 +1,141 @@
1
+ # globals: assrt
2
+ # vim:fileencoding=utf-8
3
+ #
4
+ # parenthesized_with.pyj
5
+ # Tests for Python 3.10+ parenthesized `with` statement.
6
+
7
+ ae = assrt.equal
8
+ ok = assrt.ok
9
+
10
+ # ── Helper context manager ────────────────────────────────────────────────────
11
+
12
+ class _CM:
13
+ def __init__(self, name, log):
14
+ self.name = name
15
+ self.log = log
16
+
17
+ def __enter__(self):
18
+ self.log.push('enter:' + self.name)
19
+ return self
20
+
21
+ def __exit__(self, *args):
22
+ self.log.push('exit:' + self.name)
23
+ return False
24
+
25
+ # ── 1. Single clause, alias inside parens ─────────────────────────────────────
26
+
27
+ _log1 = []
28
+ with (_CM('a', _log1) as cm):
29
+ _log1.push('body')
30
+ ae(_log1[0], 'enter:a')
31
+ ae(_log1[1], 'body')
32
+ ae(_log1[2], 'exit:a')
33
+
34
+ # ── 2. Single clause, no alias ────────────────────────────────────────────────
35
+
36
+ _log2 = []
37
+
38
+ class _SimpleCM:
39
+ def __init__(self, log):
40
+ self.log = log
41
+ def __enter__(self):
42
+ self.log.push('entered')
43
+ return self
44
+ def __exit__(self):
45
+ self.log.push('exited')
46
+
47
+ with (_SimpleCM(_log2)):
48
+ _log2.push('body')
49
+ ae(_log2[0], 'entered')
50
+ ae(_log2[1], 'body')
51
+ ae(_log2[2], 'exited')
52
+
53
+ # ── 3. Multi-clause, LIFO exit order ──────────────────────────────────────────
54
+
55
+ _log3 = []
56
+ with (_CM('a', _log3) as a, _CM('b', _log3) as b):
57
+ _log3.push('body')
58
+ ae(_log3[0], 'enter:a')
59
+ ae(_log3[1], 'enter:b')
60
+ ae(_log3[2], 'body')
61
+ ae(_log3[3], 'exit:b') # LIFO: b exits before a
62
+ ae(_log3[4], 'exit:a')
63
+
64
+ # ── 4. Trailing comma ─────────────────────────────────────────────────────────
65
+
66
+ _log4 = []
67
+ with (_CM('x', _log4) as x,):
68
+ _log4.push('body')
69
+ ae(_log4[0], 'enter:x')
70
+ ae(_log4[1], 'body')
71
+ ae(_log4[2], 'exit:x')
72
+
73
+ # ── 5. Multi-line form ────────────────────────────────────────────────────────
74
+
75
+ _log5 = []
76
+ with (
77
+ _CM('p', _log5) as p,
78
+ _CM('q', _log5) as q,
79
+ ):
80
+ _log5.push('body')
81
+ ae(_log5[0], 'enter:p')
82
+ ae(_log5[1], 'enter:q')
83
+ ae(_log5[2], 'body')
84
+ ae(_log5[3], 'exit:q')
85
+ ae(_log5[4], 'exit:p')
86
+
87
+ # ── 6. __enter__ return value assigned to alias ───────────────────────────────
88
+
89
+ class _ValCM:
90
+ def __init__(self, val):
91
+ self.val = val
92
+ def __enter__(self):
93
+ return self.val
94
+ def __exit__(self):
95
+ pass
96
+
97
+ with (_ValCM(99) as v):
98
+ ae(v, 99)
99
+
100
+ # ── 7. Exception suppression via __exit__ ─────────────────────────────────────
101
+
102
+ class _SuppressCM:
103
+ def __enter__(self):
104
+ return self
105
+ def __exit__(self, *args):
106
+ return True # suppress any exception
107
+
108
+ _raised = False
109
+ with (_SuppressCM()):
110
+ raise Error('suppressed')
111
+ _raised = True
112
+ ok(_raised)
113
+
114
+ # ── 8. Exception propagation (unsuppressed) ──────────────────────────────────
115
+
116
+ _caught = False
117
+ try:
118
+ with (_CM('err', []) as _ignored):
119
+ raise Error('propagated')
120
+ except Exception as _e:
121
+ _caught = True
122
+ ok(_caught)
123
+
124
+ # ── 9. Non-parenthesized form still works (regression) ───────────────────────
125
+
126
+ _log9 = []
127
+ with _CM('r', _log9) as r, _CM('s', _log9) as s:
128
+ _log9.push('body')
129
+ ae(_log9[0], 'enter:r')
130
+ ae(_log9[3], 'exit:s')
131
+ ae(_log9[4], 'exit:r')
132
+
133
+ # ── 10. Parenthesized expression (not parenthesized-with) still works ─────────
134
+
135
+ _log10 = []
136
+ _cm10 = _CM('t', _log10)
137
+ with (_cm10) as t:
138
+ _log10.push('body')
139
+ ae(_log10[0], 'enter:t')
140
+ ae(_log10[1], 'body')
141
+ ae(_log10[2], 'exit:t')
@@ -235,7 +235,7 @@ strings()
235
235
  ae(' hello '.strip(), 'hello')
236
236
  ae('hello world'.upper(), 'HELLO WORLD')
237
237
 
238
- # ── 15. strings() leaves replace() as JS version; split() defaults to ' ' ─────
238
+ # ── 15. strings() patches replace(); split() defaults to ' ' ──────────────────
239
239
  # (strings() is active from section 14 above)
240
240
  # Python str.split() with no args: splits on any whitespace, strips leading/trailing.
241
241
  # RapydScript .split() with no args: transpiles to .split(" ") — splits on single space.
@@ -248,11 +248,9 @@ ade(str.split('a b c'), ['a', 'b', 'c'])
248
248
  ade('a b'.split(), ['a', '', 'b']) # double space → empty token between
249
249
  ade('a b'.split(' '), ['a', 'b']) # explicit ' ' separator still works
250
250
 
251
- # Python str.replace() vs JS String.prototype.replace():
252
- # Python replaces ALL occurrences by default.
253
- # JS .replace() with a string pattern only replaces the FIRST occurrence.
251
+ # str.replace() and String.prototype.replace() both use Python semantics (replace all).
254
252
  ae(str.replace('aaa', 'a', 'b'), 'bbb') # Python: replaces all
255
- ae('aaa'.replace('a', 'b'), 'baa') # JS: replaces only first
253
+ ae('aaa'.replace('a', 'b'), 'bbb') # also replaces all (pythonized)
256
254
 
257
255
  # ── 16. Method binding — not automatic ──────────────────────────────────────
258
256
  # Python: bound methods carry a reference to self.