Skip to content

Commit 2fb1215

Browse files
Implement set.update method (#573)
* implement set.update method The `update` method on the `set` is defined in the Starlark specification [1] but is not present in the starlark-go implementation. This change introduces this functionality. [1] https://github.com/bazelbuild/starlark/blob/master/spec.md#setupdate * implement set.update method Fixes from @adonovan in PR * implement set.update method Fixes #2 from @adonovan in PR * implement set.update method Fixes #3 from @adonovan in PR
1 parent 8dfa5b9 commit 2fb1215

File tree

5 files changed

+132
-3
lines changed

5 files changed

+132
-3
lines changed

doc/spec.md

+12-1
Original file line numberDiff line numberDiff line change
@@ -839,7 +839,7 @@ A set has these methods:
839839
* [`remove`](#set·remove)
840840
* [`symmetric_difference`](#set·symmetric_difference)
841841
* [`union`](#set·union)
842-
842+
* [`update`](#set·update)
843843

844844
A set used in a Boolean context is considered true if it is non-empty.
845845

@@ -3774,6 +3774,17 @@ y = set([2, 3])
37743774
x.union(y) # set([1, 2, 3])
37753775
```
37763776
3777+
<a id='set·update'></a>
3778+
### set·update
3779+
3780+
`S.update(iterable...)` adds to S each element of the iterable
3781+
sequences. The method will return `None`.
3782+
3783+
```python
3784+
x = set([1, 2])
3785+
x.update([2, 3], [4, 5])
3786+
```
3787+
37773788
<a id='string·elem_ords'></a>
37783789
### string·elem_ords
37793790

starlark/library.go

+26
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ var (
151151
"remove": NewBuiltin("remove", set_remove),
152152
"symmetric_difference": NewBuiltin("symmetric_difference", set_symmetric_difference),
153153
"union": NewBuiltin("union", set_union),
154+
"update": NewBuiltin("update", set_update),
154155
}
155156
)
156157

@@ -2349,6 +2350,31 @@ func set_union(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error)
23492350
return union, nil
23502351
}
23512352

2353+
// https://github.com/google/starlark-go/blob/master/doc/spec.md#set·update.
2354+
func set_update(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
2355+
if len(kwargs) > 0 {
2356+
return nil, nameErr(b, "update does not accept keyword arguments")
2357+
}
2358+
2359+
receiverSet := b.Receiver().(*Set)
2360+
2361+
for i, arg := range args {
2362+
iterable, ok := arg.(Iterable)
2363+
if !ok {
2364+
return nil, fmt.Errorf("update: argument #%d is not iterable: %s", i+1, arg.Type())
2365+
}
2366+
if err := func() error {
2367+
iter := iterable.Iterate()
2368+
defer iter.Done()
2369+
return receiverSet.InsertAll(iter)
2370+
}(); err != nil {
2371+
return nil, nameErr(b, err)
2372+
}
2373+
}
2374+
2375+
return None, nil
2376+
}
2377+
23522378
// Common implementation of string_{r}{find,index}.
23532379
func string_find_impl(b *Builtin, args Tuple, kwargs []Tuple, allowError, last bool) (Value, error) {
23542380
var sub string

starlark/testdata/builtins.star

+1-1
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ assert.eq(getattr(hf, "x"), 2)
196196
assert.eq(hf.x, 2)
197197
# built-in types can have attributes (methods) too.
198198
myset = set([])
199-
assert.eq(dir(myset), ["add", "clear", "difference", "discard", "intersection", "issubset", "issuperset", "pop", "remove", "symmetric_difference", "union"])
199+
assert.eq(dir(myset), ["add", "clear", "difference", "discard", "intersection", "issubset", "issuperset", "pop", "remove", "symmetric_difference", "union", "update"])
200200
assert.true(hasattr(myset, "union"))
201201
assert.true(not hasattr(myset, "onion"))
202202
assert.eq(str(getattr(myset, "union")), "<built-in method union of set value>")

starlark/testdata/set.star

+83-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99

1010
# TODO(adonovan): support set mutation:
1111
# - del set[k]
12-
# - set.update
1312
# - set += iterable, perhaps?
1413
# Test iterator invalidation.
1514

@@ -65,6 +64,83 @@ assert.eq(list(x.union([5, 1])), [1, 2, 3, 5])
6564
assert.eq(list(x.union((6, 5, 4))), [1, 2, 3, 6, 5, 4])
6665
assert.fails(lambda : x.union([1, 2, {}]), "unhashable type: dict")
6766

67+
# set.update (allows any iterable for the right operand)
68+
# The update function will mutate the set so the tests below are
69+
# scoped using a function.
70+
def test_update_elems_singular():
71+
s = set("a".elems())
72+
s.update("b".elems())
73+
assert.eq(list(s), ["a", "b"])
74+
75+
test_update_elems_singular()
76+
77+
def test_update_elems_multiple():
78+
s = set("a".elems())
79+
s.update("bc".elems())
80+
assert.eq(list(s), ["a", "b", "c"])
81+
82+
test_update_elems_multiple()
83+
84+
def test_update_empty():
85+
s = set()
86+
s.update([])
87+
assert.eq(s, set())
88+
89+
test_update_empty()
90+
91+
def test_update_set():
92+
s = set(x)
93+
s.update(y)
94+
assert.eq(list(s), [1, 2, 3, 4, 5])
95+
96+
test_update_set()
97+
98+
def test_update_set_multiple_args():
99+
s = set(x)
100+
s.update([11, 12], [11, 13, 14])
101+
assert.eq(list(s), [1, 2, 3, 11, 12, 13, 14])
102+
103+
test_update_set_multiple_args()
104+
105+
def test_update_list_intersecting():
106+
s = set(x)
107+
s.update([5, 1])
108+
assert.eq(list(s), [1, 2, 3, 5])
109+
110+
test_update_list_intersecting()
111+
112+
def test_update_list_non_intersecting():
113+
s = set(x)
114+
s.update([6, 5, 4])
115+
assert.eq(list(s), [1, 2, 3, 6, 5, 4])
116+
117+
test_update_list_non_intersecting()
118+
119+
def test_update_non_hashable():
120+
s = set(x)
121+
assert.fails(lambda: x.update([1, 2, {}]), "unhashable type: dict")
122+
123+
test_update_non_hashable()
124+
125+
def test_update_non_iterable():
126+
s = set(x)
127+
assert.fails(lambda: x.update(9), "update: argument #1 is not iterable: int")
128+
129+
test_update_non_iterable()
130+
131+
def test_update_kwargs():
132+
s = set(x)
133+
assert.fails(lambda: x.update(gee = [3, 4]), "update: update does not accept keyword arguments")
134+
135+
test_update_kwargs()
136+
137+
def test_update_no_arg():
138+
s = set(x)
139+
s.update()
140+
assert.eq(list(s), [1, 2, 3])
141+
142+
test_update_no_arg()
143+
68144
# intersection, set & set or set.intersection(iterable)
69145
assert.eq(list(set("a".elems()) & set("b".elems())), [])
70146
assert.eq(list(set("ab".elems()) & set("bc".elems())), ["b"])
@@ -143,6 +219,12 @@ freeze(discard_set)
143219
assert.eq(discard_set.discard(3), None) # no mutation of frozen set because key doesn't exist
144220
assert.fails(lambda: discard_set.discard(1), "discard: cannot delete from frozen hash table")
145221

222+
# update
223+
update_set = set([1, 2, 3])
224+
update_set.update([4])
225+
assert.true(4 in update_set)
226+
freeze(update_set)
227+
assert.fails(lambda: update_set.update([5]), "update: cannot insert into frozen hash table")
146228

147229
# pop
148230
pop_set = set([1,2,3])

starlark/value.go

+10
Original file line numberDiff line numberDiff line change
@@ -1232,6 +1232,16 @@ func (s *Set) Union(iter Iterator) (Value, error) {
12321232
return set, nil
12331233
}
12341234

1235+
func (s *Set) InsertAll(iter Iterator) error {
1236+
var x Value
1237+
for iter.Next(&x) {
1238+
if err := s.Insert(x); err != nil {
1239+
return err
1240+
}
1241+
}
1242+
return nil
1243+
}
1244+
12351245
func (s *Set) Difference(other Iterator) (Value, error) {
12361246
diff := s.clone()
12371247
var x Value

0 commit comments

Comments
 (0)