go.starlark.net@v0.0.0-20231101134539-556fd59b42f6/starlark/testdata/dict.star (about)

     1  # Tests of Starlark 'dict'
     2  
     3  load("assert.star", "assert", "freeze")
     4  
     5  # literals
     6  assert.eq({}, {})
     7  assert.eq({"a": 1}, {"a": 1})
     8  assert.eq({"a": 1,}, {"a": 1})
     9  
    10  # truth
    11  assert.true({False: False})
    12  assert.true(not {})
    13  
    14  # dict + dict is no longer supported.
    15  assert.fails(lambda: {"a": 1} + {"b": 2}, 'unknown binary op: dict \\+ dict')
    16  
    17  # dict comprehension
    18  assert.eq({x: x*x for x in range(3)}, {0: 0, 1: 1, 2: 4})
    19  
    20  # dict.pop
    21  x6 = {"a": 1, "b": 2}
    22  assert.eq(x6.pop("a"), 1)
    23  assert.eq(str(x6), '{"b": 2}')
    24  assert.fails(lambda: x6.pop("c"), "pop: missing key")
    25  assert.eq(x6.pop("c", 3), 3)
    26  assert.eq(x6.pop("c", None), None) # default=None tests an edge case of UnpackArgs
    27  assert.eq(x6.pop("b"), 2)
    28  assert.eq(len(x6), 0)
    29  
    30  # dict.popitem
    31  x7 = {"a": 1, "b": 2}
    32  assert.eq([x7.popitem(), x7.popitem()], [("a", 1), ("b", 2)])
    33  assert.fails(x7.popitem, "empty dict")
    34  assert.eq(len(x7), 0)
    35  
    36  # dict.keys, dict.values
    37  x8 = {"a": 1, "b": 2}
    38  assert.eq(x8.keys(), ["a", "b"])
    39  assert.eq(x8.values(), [1, 2])
    40  
    41  # equality
    42  assert.eq({"a": 1, "b": 2}, {"a": 1, "b": 2})
    43  assert.eq({"a": 1, "b": 2,}, {"a": 1, "b": 2})
    44  assert.eq({"a": 1, "b": 2}, {"b": 2, "a": 1})
    45  
    46  # insertion order is preserved
    47  assert.eq(dict([("a", 0), ("b", 1), ("c", 2), ("b", 3)]).keys(), ["a", "b", "c"])
    48  assert.eq(dict([("b", 0), ("a", 1), ("b", 2), ("c", 3)]).keys(), ["b", "a", "c"])
    49  assert.eq(dict([("b", 0), ("a", 1), ("b", 2), ("c", 3)])["b"], 2)
    50  # ...even after rehashing (which currently occurs after key 'i'):
    51  small = dict([("a", 0), ("b", 1), ("c", 2)])
    52  small.update([("d", 4), ("e", 5), ("f", 6), ("g", 7), ("h", 8), ("i", 9), ("j", 10), ("k", 11)])
    53  assert.eq(small.keys(), ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"])
    54  
    55  # Duplicate keys are not permitted in dictionary expressions (see b/35698444).
    56  # (Nor in keyword args to function calls---checked by resolver.)
    57  assert.fails(lambda: {"aa": 1, "bb": 2, "cc": 3, "bb": 4}, 'duplicate key: "bb"')
    58  
    59  # Check that even with many positional args, keyword collisions are detected.
    60  assert.fails(lambda: dict({'b': 3}, a=4, **dict(a=5)), 'dict: duplicate keyword arg: "a"')
    61  assert.fails(lambda: dict({'a': 2, 'b': 3}, a=4, **dict(a=5)), 'dict: duplicate keyword arg: "a"')
    62  # positional/keyword arg key collisions are ok
    63  assert.eq(dict((['a', 2], ), a=4), {'a': 4})
    64  assert.eq(dict((['a', 2], ['a', 3]), a=4), {'a': 4})
    65  
    66  # index
    67  def setIndex(d, k, v):
    68    d[k] = v
    69  
    70  x9 = {}
    71  assert.fails(lambda: x9["a"], 'key "a" not in dict')
    72  x9["a"] = 1
    73  assert.eq(x9["a"], 1)
    74  assert.eq(x9, {"a": 1})
    75  assert.fails(lambda: setIndex(x9, [], 2), 'unhashable type: list')
    76  freeze(x9)
    77  assert.fails(lambda: setIndex(x9, "a", 3), 'cannot insert into frozen hash table')
    78  
    79  x9a = {}
    80  x9a[1, 2] = 3  # unparenthesized tuple is allowed here
    81  assert.eq(x9a.keys()[0], (1, 2))
    82  
    83  # dict.get
    84  x10 = {"a": 1}
    85  assert.eq(x10.get("a"), 1)
    86  assert.eq(x10.get("b"), None)
    87  assert.eq(x10.get("a", 2), 1)
    88  assert.eq(x10.get("b", 2), 2)
    89  
    90  # dict.clear
    91  x11 = {"a": 1}
    92  assert.contains(x11, "a")
    93  assert.eq(x11["a"], 1)
    94  x11.clear()
    95  assert.fails(lambda: x11["a"], 'key "a" not in dict')
    96  assert.true("a" not in x11)
    97  freeze(x11)
    98  assert.fails(x11.clear, "cannot clear frozen hash table")
    99  
   100  # dict.setdefault
   101  x12 = {"a": 1}
   102  assert.eq(x12.setdefault("a"), 1)
   103  assert.eq(x12["a"], 1)
   104  assert.eq(x12.setdefault("b"), None)
   105  assert.eq(x12["b"], None)
   106  assert.eq(x12.setdefault("c", 2), 2)
   107  assert.eq(x12["c"], 2)
   108  assert.eq(x12.setdefault("c", 3), 2)
   109  assert.eq(x12["c"], 2)
   110  freeze(x12)
   111  assert.eq(x12.setdefault("a", 1), 1) # no change, no error
   112  assert.fails(lambda: x12.setdefault("d", 1), "cannot insert into frozen hash table")
   113  
   114  # dict.update
   115  x13 = {"a": 1}
   116  x13.update(a=2, b=3)
   117  assert.eq(x13, {"a": 2, "b": 3})
   118  x13.update([("b", 4), ("c", 5)])
   119  assert.eq(x13, {"a": 2, "b": 4, "c": 5})
   120  x13.update({"c": 6, "d": 7})
   121  assert.eq(x13, {"a": 2, "b": 4, "c": 6, "d": 7})
   122  freeze(x13)
   123  assert.fails(lambda: x13.update({"a": 8}), "cannot insert into frozen hash table")
   124  
   125  # dict as a sequence
   126  #
   127  # for loop
   128  x14 = {1:2, 3:4}
   129  def keys(dict):
   130    keys = []
   131    for k in dict: keys.append(k)
   132    return keys
   133  assert.eq(keys(x14), [1, 3])
   134  #
   135  # comprehension
   136  assert.eq([x for x in x14], [1, 3])
   137  #
   138  # varargs
   139  def varargs(*args): return args
   140  x15 = {"one": 1}
   141  assert.eq(varargs(*x15), ("one",))
   142  
   143  # kwargs parameter does not alias the **kwargs dict
   144  def kwargs(**kwargs): return kwargs
   145  x16 = kwargs(**x15)
   146  assert.eq(x16, x15)
   147  x15["two"] = 2 # mutate
   148  assert.ne(x16, x15)
   149  
   150  # iterator invalidation
   151  def iterator1():
   152    dict = {1:1, 2:1}
   153    for k in dict:
   154      dict[2*k] = dict[k]
   155  assert.fails(iterator1, "insert.*during iteration")
   156  
   157  def iterator2():
   158    dict = {1:1, 2:1}
   159    for k in dict:
   160      dict.pop(k)
   161  assert.fails(iterator2, "delete.*during iteration")
   162  
   163  def iterator3():
   164    def f(d):
   165      d[3] = 3
   166    dict = {1:1, 2:1}
   167    _ = [f(dict) for x in dict]
   168  assert.fails(iterator3, "insert.*during iteration")
   169  
   170  # This assignment is not a modification-during-iteration:
   171  # the sequence x should be completely iterated before
   172  # the assignment occurs.
   173  def f():
   174    x = {1:2, 2:4}
   175    a, x[0] = x
   176    assert.eq(a, 1)
   177    assert.eq(x, {1: 2, 2: 4, 0: 2})
   178  f()
   179  
   180  # Regression test for a bug in hashtable.delete
   181  def test_delete():
   182    d = {}
   183  
   184    # delete tail first
   185    d["one"] = 1
   186    d["two"] = 2
   187    assert.eq(str(d), '{"one": 1, "two": 2}')
   188    d.pop("two")
   189    assert.eq(str(d), '{"one": 1}')
   190    d.pop("one")
   191    assert.eq(str(d), '{}')
   192  
   193    # delete head first
   194    d["one"] = 1
   195    d["two"] = 2
   196    assert.eq(str(d), '{"one": 1, "two": 2}')
   197    d.pop("one")
   198    assert.eq(str(d), '{"two": 2}')
   199    d.pop("two")
   200    assert.eq(str(d), '{}')
   201  
   202    # delete middle
   203    d["one"] = 1
   204    d["two"] = 2
   205    d["three"] = 3
   206    assert.eq(str(d), '{"one": 1, "two": 2, "three": 3}')
   207    d.pop("two")
   208    assert.eq(str(d), '{"one": 1, "three": 3}')
   209    d.pop("three")
   210    assert.eq(str(d), '{"one": 1}')
   211    d.pop("one")
   212    assert.eq(str(d), '{}')
   213  
   214  test_delete()
   215  
   216  # Regression test for github.com/google/starlark-go/issues/128.
   217  assert.fails(lambda: dict(None), 'got NoneType, want iterable')
   218  assert.fails(lambda: {}.update(None), 'got NoneType, want iterable')
   219  
   220  ---
   221  # Verify position of an "unhashable key" error in a dict literal.
   222  
   223  _ = {
   224      "one": 1,
   225      ["two"]: 2, ### "unhashable type: list"
   226      "three": 3,
   227  }
   228  
   229  ---
   230  # Verify position of a "duplicate key" error in a dict literal.
   231  
   232  _ = {
   233      "one": 1,
   234      "one": 1, ### `duplicate key: "one"`
   235      "three": 3,
   236  }
   237  
   238  ---
   239  # Verify position of an "unhashable key" error in a dict comprehension.
   240  
   241  _ = {
   242      k: v ### "unhashable type: list"
   243      for k, v in [
   244          ("one", 1),
   245          (["two"], 2),
   246          ("three", 3),
   247      ]
   248  }
   249  
   250  
   251  ---
   252  # dict | dict (union)
   253  
   254  load("assert.star", "assert", "freeze")
   255  
   256  empty_dict = dict()
   257  dict_with_a_b = dict(a=1, b=[1, 2])
   258  dict_with_b = dict(b=[1, 2])
   259  dict_with_other_b = dict(b=[3, 4])
   260  
   261  assert.eq(empty_dict | dict_with_a_b, dict_with_a_b)
   262  # Verify iteration order.
   263  assert.eq((empty_dict | dict_with_a_b).items(), dict_with_a_b.items())
   264  assert.eq(dict_with_a_b | empty_dict, dict_with_a_b)
   265  assert.eq((dict_with_a_b | empty_dict).items(), dict_with_a_b.items())
   266  assert.eq(dict_with_b | dict_with_a_b, dict_with_a_b)
   267  assert.eq((dict_with_b | dict_with_a_b).items(), dict(b=[1, 2], a=1).items())
   268  assert.eq(dict_with_a_b | dict_with_b, dict_with_a_b)
   269  assert.eq((dict_with_a_b | dict_with_b).items(), dict_with_a_b.items())
   270  assert.eq(dict_with_b | dict_with_other_b, dict_with_other_b)
   271  assert.eq((dict_with_b | dict_with_other_b).items(), dict_with_other_b.items())
   272  assert.eq(dict_with_other_b | dict_with_b, dict_with_b)
   273  assert.eq((dict_with_other_b | dict_with_b).items(), dict_with_b.items())
   274  
   275  assert.eq(empty_dict, dict())
   276  assert.eq(dict_with_b, dict(b=[1,2]))
   277  
   278  assert.fails(lambda: dict() | [], "unknown binary op: dict [|] list")
   279  
   280  # dict |= dict (in-place union)
   281  
   282  def test_dict_union_assignment():
   283      x = dict()
   284      saved = x
   285      x |= {"a": 1}
   286      x |= {"b": 2}
   287      x |= {"c": "3", 7: 4}
   288      x |= {"b": "5", "e": 6}
   289      want = {"a": 1, "b": "5", "c": "3", 7: 4, "e": 6}
   290      assert.eq(x, want)
   291      assert.eq(x.items(), want.items())
   292      assert.eq(saved, x) # they are aliases
   293  
   294      a = {8: 1, "b": 2}
   295      b = {"b": 1, "c": 6}
   296      c = {"d": 7}
   297      d = {(5, "a"): ("c", 8)}
   298      orig_a, orig_c = a, c
   299      a |= b
   300      c |= a
   301      c |= d
   302      expected_2 = {"d": 7, 8: 1, "b": 1, "c": 6, (5, "a"): ("c", 8)}
   303      assert.eq(c, expected_2)
   304      assert.eq(c.items(), expected_2.items())
   305      assert.eq(b, {"b": 1, "c": 6})
   306  
   307      # aliasing:
   308      assert.eq(a, orig_a)
   309      assert.eq(c, orig_c)
   310      a.clear()
   311      c.clear()
   312      assert.eq(a, orig_a)
   313      assert.eq(c, orig_c)
   314  
   315  test_dict_union_assignment()
   316  
   317  def dict_union_assignment_type_mismatch():
   318      some_dict = dict()
   319      some_dict |= []
   320  
   321  assert.fails(dict_union_assignment_type_mismatch, "unknown binary op: dict [|] list")