Skip to content

Commit d908c3e

Browse files
correct set.union method (#574)
* correct set.union method The `union` function on `set` is inconsistent with the behaviour of `update` as it does not support multiple iterable positional arguments as is the case in the Bazel specification of the language. This PR will align `starlark-go` with the Bazel spec. Note that the `set.union` method no longer uses the `Union` function defined in `value.go` in order to avoid making a new `set` instance for each interable processed. * correct set.union method Fixes in PR from @adonovan. * correct set.union method function name change
1 parent 2fb1215 commit d908c3e

File tree

3 files changed

+41
-33
lines changed

3 files changed

+41
-33
lines changed

doc/spec.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -3762,11 +3762,11 @@ x.symmetric_difference([3, 4, 5]) # set([1, 2, 4, 5])
37623762
<a id='set·union'></a>
37633763
### set·union
37643764
3765-
`S.union(iterable)` returns a new set into which have been inserted
3766-
all the elements of set S and all the elements of the argument, which
3767-
must be iterable.
3765+
`S.union(iterable...)` returns a new set into which have been inserted
3766+
all the elements of set S and each element of the iterable sequences.
37683767
3769-
`union` fails if any element of the iterable is not hashable.
3768+
`union` fails if any argument is not an iterable sequence, or if any
3769+
sequence element is not hashable.
37703770
37713771
```python
37723772
x = set([1, 2])

starlark/library.go

+27-28
Original file line numberDiff line numberDiff line change
@@ -2337,41 +2337,18 @@ func set_symmetric_difference(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple)
23372337

23382338
// https://github.com/google/starlark-go/blob/master/doc/spec.md#set·union.
23392339
func set_union(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
2340-
var iterable Iterable
2341-
if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0, &iterable); err != nil {
2342-
return nil, err
2343-
}
2344-
iter := iterable.Iterate()
2345-
defer iter.Done()
2346-
union, err := b.Receiver().(*Set).Union(iter)
2347-
if err != nil {
2340+
receiverSet := b.Receiver().(*Set).clone()
2341+
if err := setUpdate(receiverSet, args, kwargs); err != nil {
23482342
return nil, nameErr(b, err)
23492343
}
2350-
return union, nil
2344+
return receiverSet, nil
23512345
}
23522346

23532347
// https://github.com/google/starlark-go/blob/master/doc/spec.md#set·update.
23542348
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-
}
2349+
if err := setUpdate(b.Receiver().(*Set), args, kwargs); err != nil {
2350+
return nil, nameErr(b, err)
23732351
}
2374-
23752352
return None, nil
23762353
}
23772354

@@ -2474,6 +2451,28 @@ func updateDict(dict *Dict, updates Tuple, kwargs []Tuple) error {
24742451
return nil
24752452
}
24762453

2454+
func setUpdate(s *Set, args Tuple, kwargs []Tuple) error {
2455+
if len(kwargs) > 0 {
2456+
return errors.New("does not accept keyword arguments")
2457+
}
2458+
2459+
for i, arg := range args {
2460+
iterable, ok := arg.(Iterable)
2461+
if !ok {
2462+
return fmt.Errorf("argument #%d is not iterable: %s", i+1, arg.Type())
2463+
}
2464+
if err := func() error {
2465+
iter := iterable.Iterate()
2466+
defer iter.Done()
2467+
return s.InsertAll(iter)
2468+
}(); err != nil {
2469+
return err
2470+
}
2471+
}
2472+
2473+
return nil
2474+
}
2475+
24772476
// nameErr returns an error message of the form "name: msg"
24782477
// where name is b.Name() and msg is a string or error.
24792478
func nameErr(b *Builtin, msg interface{}) error {

starlark/testdata/set.star

+10-1
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,23 @@ assert.eq(list(set("a".elems()).union("b".elems())), ["a", "b"])
5959
assert.eq(list(set("ab".elems()).union("bc".elems())), ["a", "b", "c"])
6060
assert.eq(set().union([]), set())
6161
assert.eq(type(x.union(y)), "set")
62+
assert.eq(list(x.union()), [1, 2, 3])
6263
assert.eq(list(x.union(y)), [1, 2, 3, 4, 5])
64+
assert.eq(list(x.union(y, [6, 7])), [1, 2, 3, 4, 5, 6, 7])
6365
assert.eq(list(x.union([5, 1])), [1, 2, 3, 5])
6466
assert.eq(list(x.union((6, 5, 4))), [1, 2, 3, 6, 5, 4])
6567
assert.fails(lambda : x.union([1, 2, {}]), "unhashable type: dict")
68+
assert.fails(lambda : x.union(1, 2, 3), "argument #1 is not iterable: int")
6669

6770
# set.update (allows any iterable for the right operand)
6871
# The update function will mutate the set so the tests below are
6972
# scoped using a function.
73+
74+
def test_update_return_value():
75+
assert.eq(set(x).update(y), None)
76+
77+
test_update_return_value()
78+
7079
def test_update_elems_singular():
7180
s = set("a".elems())
7281
s.update("b".elems())
@@ -130,7 +139,7 @@ test_update_non_iterable()
130139

131140
def test_update_kwargs():
132141
s = set(x)
133-
assert.fails(lambda: x.update(gee = [3, 4]), "update: update does not accept keyword arguments")
142+
assert.fails(lambda: x.update(gee = [3, 4]), "update: does not accept keyword arguments")
134143

135144
test_update_kwargs()
136145

0 commit comments

Comments
 (0)