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
@@ -2996,6 +2996,96 @@ assrt.equal(fib(15), 610)
2996
2996
  js_checks: ["ρσ_op_add(", "ρσ_op_sub(", "ρσ_op_or(", "ρσ_op_and("],
2997
2997
  },
2998
2998
 
2999
+ {
3000
+ name: "operator_dict_or",
3001
+ description: "| on plain objects creates a new merged dict; numeric | still works",
3002
+ src: [
3003
+ "# globals: assrt",
3004
+ "from __python__ import overload_operators",
3005
+ "a = {'x': 1, 'y': 2}",
3006
+ "b = {'z': 3}",
3007
+ "c = a | b",
3008
+ "assrt.deepEqual(c, {'x': 1, 'y': 2, 'z': 3})",
3009
+ "assrt.notStrictEqual(c, a)",
3010
+ "assrt.equal(5 | 3, 7)",
3011
+ ].join("\n"),
3012
+ },
3013
+
3014
+ {
3015
+ name: "operator_dict_ior",
3016
+ description: "|= on plain objects merges in-place",
3017
+ src: [
3018
+ "# globals: assrt",
3019
+ "from __python__ import overload_operators",
3020
+ "d = {'a': 1}",
3021
+ "ref = d",
3022
+ "d |= {'b': 2}",
3023
+ "assrt.deepEqual(d, {'a': 1, 'b': 2})",
3024
+ "assrt.strictEqual(d, ref)",
3025
+ ].join("\n"),
3026
+ },
3027
+
3028
+ {
3029
+ name: "operator_dict_or_override",
3030
+ description: "| on plain objects: right-hand values override left on key collision",
3031
+ src: [
3032
+ "# globals: assrt",
3033
+ "from __python__ import overload_operators",
3034
+ "a = {'x': 1, 'y': 2}",
3035
+ "b = {'y': 99, 'z': 3}",
3036
+ "c = a | b",
3037
+ "assrt.equal(c['y'], 99)",
3038
+ "assrt.equal(c['x'], 1)",
3039
+ "assrt.equal(c['z'], 3)",
3040
+ ].join("\n"),
3041
+ },
3042
+
3043
+ {
3044
+ name: "operator_type_error_bitwise",
3045
+ description: "bitwise operators raise TypeError for invalid types",
3046
+ src: [
3047
+ "# globals: assrt",
3048
+ "from __python__ import overload_operators",
3049
+ "def check(fn, msg):",
3050
+ " try:",
3051
+ " fn()",
3052
+ " assrt.ok(False, 'expected TypeError for ' + msg)",
3053
+ " except TypeError:",
3054
+ " assrt.ok(True)",
3055
+ 'check(def(): "str" & 5;, "str & int")',
3056
+ 'check(def(): None | 5;, "None | int")',
3057
+ 'check(def(): "a" ^ 1;, "str ^ int")',
3058
+ 'check(def(): "a" << 1;, "str << int")',
3059
+ 'check(def(): "a" >> 1;, "str >> int")',
3060
+ "assrt.equal(7 & 3, 3)",
3061
+ "assrt.equal(1 | 2, 3)",
3062
+ "assrt.equal(5 ^ 3, 6)",
3063
+ "assrt.equal(1 << 3, 8)",
3064
+ "assrt.equal(8 >> 2, 2)",
3065
+ ].join("\n"),
3066
+ },
3067
+
3068
+ {
3069
+ name: "operator_type_error_unary",
3070
+ description: "unary operators raise TypeError for invalid types",
3071
+ src: [
3072
+ "# globals: assrt",
3073
+ "from __python__ import overload_operators",
3074
+ "def check(fn, msg):",
3075
+ " try:",
3076
+ " fn()",
3077
+ " assrt.ok(False, 'expected TypeError for ' + msg)",
3078
+ " except TypeError:",
3079
+ " assrt.ok(True)",
3080
+ 'check(def(): -"hello";, "neg str")',
3081
+ 'check(def(): +None;, "pos None")',
3082
+ 'check(def(): ~"world";, "invert str")',
3083
+ "assrt.equal(-5, -5)",
3084
+ "assrt.equal(+3, 3)",
3085
+ "assrt.equal(~0, -1)",
3086
+ ].join("\n"),
3087
+ },
3088
+
2999
3089
  // ── nested comprehensions ──────────────────────────────────────────────
3000
3090
 
3001
3091
  {
@@ -5275,6 +5365,86 @@ assrt.equal(fib(15), 610)
5275
5365
  js_checks: [/let\s[^;]*ρσ_with_exception/, /let\s[^;]*ρσ_with_suppress/],
5276
5366
  },
5277
5367
 
5368
+ // ── BigInt literals ────────────────────────────────────────────────────
5369
+
5370
+ {
5371
+ name: "bigint_literal_basic",
5372
+ description: "42n compiles to JS BigInt literal and has bigint type",
5373
+ src: [
5374
+ "# globals: assrt",
5375
+ "x = 42n",
5376
+ "assrt.ok(jstype(x) == 'bigint', 'type is bigint')",
5377
+ "assrt.ok(x == 42n, 'value is 42n')",
5378
+ ].join("\n"),
5379
+ js_checks: ["42n"],
5380
+ },
5381
+
5382
+ {
5383
+ name: "bigint_literal_hex",
5384
+ description: "0xFFn compiles to JS hex BigInt literal",
5385
+ src: [
5386
+ "# globals: assrt",
5387
+ "x = 0xFFn",
5388
+ "assrt.ok(x == 255n, 'hex bigint value')",
5389
+ ].join("\n"),
5390
+ js_checks: ["0xFFn"],
5391
+ },
5392
+
5393
+ {
5394
+ name: "bigint_literal_binary",
5395
+ description: "0b1010n compiles to JS binary BigInt literal",
5396
+ src: [
5397
+ "# globals: assrt",
5398
+ "x = 0b1010n",
5399
+ "assrt.ok(x == 10n, 'binary bigint value')",
5400
+ ].join("\n"),
5401
+ js_checks: ["0b1010n"],
5402
+ },
5403
+
5404
+ {
5405
+ name: "bigint_literal_octal",
5406
+ description: "0o77n compiles to JS octal BigInt literal",
5407
+ src: [
5408
+ "# globals: assrt",
5409
+ "x = 0o77n",
5410
+ "assrt.ok(x == 63n, 'octal bigint value')",
5411
+ ].join("\n"),
5412
+ js_checks: ["0o77n"],
5413
+ },
5414
+
5415
+ {
5416
+ name: "bigint_literal_zero",
5417
+ description: "0n compiles and works",
5418
+ src: [
5419
+ "# globals: assrt",
5420
+ "x = 0n",
5421
+ "assrt.ok(jstype(x) == 'bigint', 'type is bigint')",
5422
+ "assrt.ok(x == 0n, 'value is 0n')",
5423
+ ].join("\n"),
5424
+ js_checks: ["0n"],
5425
+ },
5426
+
5427
+ {
5428
+ name: "bigint_literal_arithmetic",
5429
+ description: "BigInt arithmetic works correctly",
5430
+ src: [
5431
+ "# globals: assrt",
5432
+ "x = 10n + 3n",
5433
+ "assrt.ok(x == 13n, 'addition')",
5434
+ ].join("\n"),
5435
+ },
5436
+
5437
+ {
5438
+ name: "bigint_literal_large",
5439
+ description: "Large BigInt literal preserves precision",
5440
+ src: [
5441
+ "# globals: assrt",
5442
+ "x = 999999999999999999999n",
5443
+ "assrt.ok(x == 999999999999999999999n, 'large bigint')",
5444
+ ].join("\n"),
5445
+ js_checks: ["999999999999999999999n"],
5446
+ },
5447
+
5278
5448
  {
5279
5449
  name: "with_statement_suppresses_exception",
5280
5450
  description: "with statement __exit__ returning True suppresses the exception",
@@ -5293,6 +5463,291 @@ assrt.equal(fib(15), 610)
5293
5463
  ].join("\n"),
5294
5464
  },
5295
5465
 
5466
+ // ── Exception.args ───────────────────────────────────────────────────────
5467
+ {
5468
+ name: "exception_args_single",
5469
+ description: "Exception with single arg populates .args and .message",
5470
+ src: [
5471
+ "# globals: assrt",
5472
+ "e = Exception('hello')",
5473
+ "assrt.deepEqual(e.args, ['hello'])",
5474
+ "assrt.equal(e.message, 'hello')",
5475
+ ].join("\n"),
5476
+ },
5477
+ {
5478
+ name: "exception_args_multiple",
5479
+ description: "Exception with multiple args populates .args tuple",
5480
+ src: [
5481
+ "# globals: assrt",
5482
+ "e = Exception('err', 42, 'extra')",
5483
+ "assrt.deepEqual(e.args, ['err', 42, 'extra'])",
5484
+ "assrt.equal(e.message, 'err')",
5485
+ "assrt.equal(e.args[1], 42)",
5486
+ "assrt.equal(e.args[2], 'extra')",
5487
+ ].join("\n"),
5488
+ },
5489
+ {
5490
+ name: "exception_args_empty",
5491
+ description: "Exception with no args has empty .args and empty .message",
5492
+ src: [
5493
+ "# globals: assrt",
5494
+ "e = Exception()",
5495
+ "assrt.deepEqual(e.args, [])",
5496
+ "assrt.equal(e.message, '')",
5497
+ ].join("\n"),
5498
+ },
5499
+ {
5500
+ name: "exception_args_subclass",
5501
+ description: "Exception subclass inherits .args behavior",
5502
+ src: [
5503
+ "# globals: assrt",
5504
+ "e = ValueError('bad', 'value')",
5505
+ "assrt.deepEqual(e.args, ['bad', 'value'])",
5506
+ "assrt.equal(e.message, 'bad')",
5507
+ "assrt.ok(isinstance(e, ValueError))",
5508
+ "assrt.ok(isinstance(e, Exception))",
5509
+ ].join("\n"),
5510
+ },
5511
+ {
5512
+ name: "exception_args_catch",
5513
+ description: "Caught exception preserves .args",
5514
+ src: [
5515
+ "# globals: assrt",
5516
+ "try:",
5517
+ " raise ValueError('oops', 123)",
5518
+ "except ValueError as e:",
5519
+ " assrt.deepEqual(e.args, ['oops', 123])",
5520
+ " assrt.equal(e.message, 'oops')",
5521
+ " assrt.equal(e.args[1], 123)",
5522
+ ].join("\n"),
5523
+ },
5524
+ {
5525
+ name: "exception_args_len",
5526
+ description: "len() works on exception .args",
5527
+ src: [
5528
+ "# globals: assrt",
5529
+ "e = Exception('a', 'b', 'c')",
5530
+ "assrt.equal(len(e.args), 3)",
5531
+ "e2 = Exception()",
5532
+ "assrt.equal(len(e2.args), 0)",
5533
+ ].join("\n"),
5534
+ },
5535
+ {
5536
+ name: "exception_args_exception_group",
5537
+ description: "ExceptionGroup .args contains message and exceptions list",
5538
+ src: [
5539
+ "# globals: assrt",
5540
+ "eg = ExceptionGroup('grp', [ValueError('v')])",
5541
+ "assrt.equal(eg.args[0], 'grp')",
5542
+ "assrt.equal(len(eg.args), 2)",
5543
+ "assrt.equal(eg.message, 'grp')",
5544
+ "assrt.ok(isinstance(eg.args[1][0], ValueError))",
5545
+ ].join("\n"),
5546
+ },
5547
+ {
5548
+ name: "exception_args_custom_class",
5549
+ description: "Custom exception class with __init__ can use .args",
5550
+ src: [
5551
+ "# globals: assrt",
5552
+ "class MyError(Exception):",
5553
+ " def __init__(self, code, detail):",
5554
+ " Exception.__init__(self, code, detail)",
5555
+ " self.code = code",
5556
+ " self.detail = detail",
5557
+ "e = MyError(404, 'not found')",
5558
+ "assrt.deepEqual(e.args, [404, 'not found'])",
5559
+ "assrt.equal(e.code, 404)",
5560
+ "assrt.equal(e.detail, 'not found')",
5561
+ "assrt.equal(e.message, 404)",
5562
+ ].join("\n"),
5563
+ },
5564
+
5565
+ // ── __slots__ ─────────────────────────────────────────────────────────────
5566
+ {
5567
+ name: "slots_basic",
5568
+ description: "__slots__ allows declared attributes",
5569
+ src: [
5570
+ "# globals: assrt",
5571
+ "class Point:",
5572
+ " __slots__ = ['x', 'y']",
5573
+ " def __init__(self, x, y):",
5574
+ " self.x = x",
5575
+ " self.y = y",
5576
+ "p = Point(1, 2)",
5577
+ "assrt.equal(p.x, 1)",
5578
+ "assrt.equal(p.y, 2)",
5579
+ "p.x = 10",
5580
+ "assrt.equal(p.x, 10)",
5581
+ ].join("\n"),
5582
+ },
5583
+ {
5584
+ name: "slots_raises_attributeerror",
5585
+ description: "__slots__ raises AttributeError for undeclared attrs",
5586
+ src: [
5587
+ "# globals: assrt",
5588
+ "class Point:",
5589
+ " __slots__ = ['x', 'y']",
5590
+ " def __init__(self, x, y):",
5591
+ " self.x = x",
5592
+ " self.y = y",
5593
+ "p = Point(1, 2)",
5594
+ "try:",
5595
+ " p.z = 3",
5596
+ " assrt.ok(False, 'should have raised')",
5597
+ "except AttributeError as e:",
5598
+ " assrt.ok(str(e).indexOf('z') != -1)",
5599
+ ].join("\n"),
5600
+ },
5601
+ {
5602
+ name: "slots_inherited",
5603
+ description: "Subclass with __slots__ merges parent slots",
5604
+ src: [
5605
+ "# globals: assrt",
5606
+ "class Base:",
5607
+ " __slots__ = ['x']",
5608
+ " def __init__(self):",
5609
+ " self.x = 1",
5610
+ "class Child(Base):",
5611
+ " __slots__ = ['y']",
5612
+ " def __init__(self):",
5613
+ " Base.__init__(self)",
5614
+ " self.y = 2",
5615
+ "c = Child()",
5616
+ "assrt.equal(c.x, 1)",
5617
+ "assrt.equal(c.y, 2)",
5618
+ "try:",
5619
+ " c.z = 3",
5620
+ " assrt.ok(False, 'should have raised')",
5621
+ "except AttributeError:",
5622
+ " assrt.ok(True)",
5623
+ ].join("\n"),
5624
+ },
5625
+ {
5626
+ name: "slots_subclass_no_slots",
5627
+ description: "Subclass without __slots__ is unrestricted",
5628
+ src: [
5629
+ "# globals: assrt",
5630
+ "class Base:",
5631
+ " __slots__ = ['x']",
5632
+ " def __init__(self):",
5633
+ " self.x = 1",
5634
+ "class Child(Base):",
5635
+ " def __init__(self):",
5636
+ " Base.__init__(self)",
5637
+ " self.y = 2",
5638
+ "c = Child()",
5639
+ "assrt.equal(c.x, 1)",
5640
+ "assrt.equal(c.y, 2)",
5641
+ "c.z = 3",
5642
+ "assrt.equal(c.z, 3)",
5643
+ ].join("\n"),
5644
+ },
5645
+ {
5646
+ name: "slots_empty",
5647
+ description: "__slots__ = [] allows no instance attributes",
5648
+ src: [
5649
+ "# globals: assrt",
5650
+ "class Empty:",
5651
+ " __slots__ = []",
5652
+ " pass",
5653
+ "e = Empty()",
5654
+ "try:",
5655
+ " e.x = 1",
5656
+ " assrt.ok(False, 'should have raised')",
5657
+ "except AttributeError:",
5658
+ " assrt.ok(True)",
5659
+ ].join("\n"),
5660
+ },
5661
+ {
5662
+ name: "slots_with_attr_dunders",
5663
+ description: "__setattr__ takes priority over __slots__",
5664
+ src: [
5665
+ "# globals: assrt",
5666
+ "class Tracked:",
5667
+ " __slots__ = ['x']",
5668
+ " def __init__(self):",
5669
+ " object.__setattr__(self, 'log', [])",
5670
+ " self.x = 1",
5671
+ " def __setattr__(self, name, value):",
5672
+ " self.log.append(name)",
5673
+ " object.__setattr__(self, name, value)",
5674
+ "t = Tracked()",
5675
+ "assrt.equal(t.x, 1)",
5676
+ "assrt.ok(t.log.length > 0)",
5677
+ ].join("\n"),
5678
+ },
5679
+
5680
+ // ── Container pretty-printing ─────────────────────────────────────────────
5681
+
5682
+ {
5683
+ name: "repr_list_pretty",
5684
+ description: "repr/str of lists uses repr on elements (quotes strings, shows None/True)",
5685
+ src: [
5686
+ "# globals: assrt",
5687
+ "assrt.equal(repr([1, 'hello', None, True]), \"[1, 'hello', None, True]\")",
5688
+ "assrt.equal(str([1, 'hello', None, True]), \"[1, 'hello', None, True]\")",
5689
+ "assrt.equal(repr([]), '[]')",
5690
+ "assrt.equal(str([]), '[]')",
5691
+ ].join("\n"),
5692
+ },
5693
+
5694
+ {
5695
+ name: "repr_dict_pretty",
5696
+ description: "repr/str of dicts uses single-quoted keys and values",
5697
+ src: [
5698
+ "# globals: assrt",
5699
+ "d = dict()",
5700
+ "d.set('key', 'val')",
5701
+ "assrt.equal(repr(d), \"{'key': 'val'}\")",
5702
+ "assrt.equal(str(d), \"{'key': 'val'}\")",
5703
+ ].join("\n"),
5704
+ },
5705
+
5706
+ {
5707
+ name: "repr_set_pretty",
5708
+ description: "repr/str of sets uses repr on elements",
5709
+ src: [
5710
+ "# globals: assrt",
5711
+ "s = {1, 'two'}",
5712
+ "r = repr(s)",
5713
+ "assrt.ok(r.indexOf(\"'two'\") >= 0, 'set repr should quote strings')",
5714
+ "assrt.ok(r.indexOf('1') >= 0, 'set repr should include numbers')",
5715
+ "assrt.ok(r[0] is '{' and r[r.length-1] is '}', 'set repr uses braces')",
5716
+ ].join("\n"),
5717
+ },
5718
+
5719
+ {
5720
+ name: "repr_nested_containers",
5721
+ description: "repr handles nested containers correctly",
5722
+ src: [
5723
+ "# globals: assrt",
5724
+ "assrt.equal(repr([[1], [2, 3]]), '[[1], [2, 3]]')",
5725
+ "assrt.equal(repr([None, True, False]), '[None, True, False]')",
5726
+ ].join("\n"),
5727
+ },
5728
+
5729
+ {
5730
+ name: "repr_string_single_quotes",
5731
+ description: "repr of strings uses Python-style single quotes",
5732
+ src: [
5733
+ "# globals: assrt",
5734
+ "assrt.equal(repr('hello'), \"'hello'\")",
5735
+ "assrt.equal(repr(''), \"''\")",
5736
+ ].join("\n"),
5737
+ },
5738
+
5739
+ {
5740
+ name: "repr_frozenset_pretty",
5741
+ description: "repr/str of frozenset uses repr on elements",
5742
+ src: [
5743
+ "# globals: assrt",
5744
+ "fs = frozenset([1, 'x'])",
5745
+ "r = repr(fs)",
5746
+ "assrt.ok(r.indexOf('frozenset(') is 0, 'starts with frozenset(')",
5747
+ "assrt.ok(r.indexOf(\"'x'\") >= 0, 'frozenset repr should quote strings')",
5748
+ ].join("\n"),
5749
+ },
5750
+
5296
5751
  ];
5297
5752
 
5298
5753
  // ── Runner ───────────────────────────────────────────────────────────────────
@@ -969,6 +969,7 @@ function make_tests(CompletionEngine, detect_context, SourceAnalyzer, DtsRegistr
969
969
  assert_has(list, 'Counter', 'Counter in collections completions');
970
970
  assert_has(list, 'OrderedDict', 'OrderedDict in collections completions');
971
971
  assert_has(list, 'defaultdict', 'defaultdict in collections completions');
972
+ assert_has(list, 'ChainMap', 'ChainMap in collections completions');
972
973
  },
973
974
  },
974
975
 
@@ -1370,6 +1371,7 @@ function make_tests(CompletionEngine, detect_context, SourceAnalyzer, DtsRegistr
1370
1371
  assert_has(list, 'Counter', 'Counter from collections');
1371
1372
  assert_has(list, 'OrderedDict', 'OrderedDict from collections');
1372
1373
  assert_has(list, 'defaultdict', 'defaultdict from collections');
1374
+ assert_has(list, 'ChainMap', 'ChainMap from collections');
1373
1375
  },
1374
1376
  },
1375
1377
 
@@ -458,6 +458,119 @@ function make_tests(parse_dts, DtsRegistry, TypeInfo) {
458
458
  },
459
459
  },
460
460
 
461
+ // ── Interface extends ─────────────────────────────────────────────
462
+
463
+ {
464
+ name: "parse_interface_extends",
465
+ description: "interface extends clause is parsed into extends_names",
466
+ run: function () {
467
+ var src = [
468
+ "interface Animal {",
469
+ " name: string;",
470
+ "}",
471
+ "interface Dog extends Animal {",
472
+ " breed: string;",
473
+ "}",
474
+ ].join("\n");
475
+ var types = parse_dts(src);
476
+ assert.strictEqual(types.length, 2);
477
+ var dog = types[1];
478
+ assert.strictEqual(dog.name, "Dog");
479
+ assert.ok(dog.extends_names, "should have extends_names");
480
+ assert.deepStrictEqual(dog.extends_names, ["Animal"]);
481
+ // Animal should have no extends_names
482
+ assert.strictEqual(types[0].extends_names, null);
483
+ },
484
+ },
485
+
486
+ {
487
+ name: "parse_interface_extends_multiple",
488
+ description: "interface extends multiple parents parsed correctly",
489
+ run: function () {
490
+ var src = [
491
+ "interface A { a: number; }",
492
+ "interface B { b: string; }",
493
+ "interface C extends A, B {",
494
+ " c: boolean;",
495
+ "}",
496
+ ].join("\n");
497
+ var types = parse_dts(src);
498
+ var c = types.find(function (t) { return t.name === "C"; });
499
+ assert.ok(c, "should find C");
500
+ assert.deepStrictEqual(c.extends_names, ["A", "B"]);
501
+ },
502
+ },
503
+
504
+ {
505
+ name: "registry_getAllMembers_inherits",
506
+ description: "getAllMembers includes inherited members",
507
+ run: function () {
508
+ var reg = new DtsRegistry();
509
+ reg.addDts("test", [
510
+ "interface Person {",
511
+ " name: string;",
512
+ " age: number;",
513
+ "}",
514
+ "interface Player extends Person {",
515
+ " score: number;",
516
+ "}",
517
+ ].join("\n"));
518
+ var player = reg.getGlobal("Player");
519
+ var all = reg.getAllMembers(player);
520
+ assert.ok(all.has("score"), "should have own member score");
521
+ assert.ok(all.has("name"), "should have inherited member name");
522
+ assert.ok(all.has("age"), "should have inherited member age");
523
+ assert.strictEqual(all.size, 3);
524
+ },
525
+ },
526
+
527
+ {
528
+ name: "registry_getAllMembers_override",
529
+ description: "child members shadow parent members",
530
+ run: function () {
531
+ var reg = new DtsRegistry();
532
+ reg.addDts("test", [
533
+ "interface Base {",
534
+ " value: string;",
535
+ " info(): string;",
536
+ "}",
537
+ "interface Child extends Base {",
538
+ " value: number;",
539
+ "}",
540
+ ].join("\n"));
541
+ var child = reg.getGlobal("Child");
542
+ var all = reg.getAllMembers(child);
543
+ assert.strictEqual(all.get("value").return_type, "number",
544
+ "child value should override parent");
545
+ assert.ok(all.has("info"), "should inherit info");
546
+ },
547
+ },
548
+
549
+ {
550
+ name: "registry_getMemberInfo_inherits",
551
+ description: "getMemberInfo finds inherited members",
552
+ run: function () {
553
+ var reg = new DtsRegistry();
554
+ reg.addDts("test", [
555
+ "interface Person {",
556
+ " city: string;",
557
+ "}",
558
+ "interface Player extends Person {",
559
+ " score: number;",
560
+ "}",
561
+ "declare var player: Player;",
562
+ ].join("\n"));
563
+ // Direct member
564
+ var score = reg.getMemberInfo("player", "score");
565
+ assert.ok(score, "should find direct member score");
566
+ assert.strictEqual(score.return_type, "number");
567
+ // Inherited member
568
+ var city = reg.getMemberInfo("player", "city");
569
+ assert.ok(city, "should find inherited member city");
570
+ assert.strictEqual(city.return_type, "string");
571
+ },
572
+ },
573
+
461
574
  ];
462
575
 
463
576
  return TESTS;