-
Notifications
You must be signed in to change notification settings - Fork 646
Add builtin filter function #374
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great! Thanks for working on this PR. I have a few comments.
runtime/builtin_types.go
Outdated
@@ -248,6 +249,82 @@ func builtinMapFn(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) { | |||
return NewList(result...).ToObject(), nil | |||
} | |||
|
|||
func builtinFilter(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) { | |||
argc := len(args) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you instead use:
if raised := checkMethodArgs(f, "filter", args, ObjectType, ObjectType); raised != nil {
return nil, raised
}
runtime/builtin_types.go
Outdated
if fn == None { | ||
if ret, raised := IsTrue(f, item); raised != nil { | ||
return nil, raised | ||
} else if ret { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the else since the if clause above returns
runtime/builtin_types.go
Outdated
} | ||
if ret, raised := IsTrue(f, ret); raised != nil { | ||
return nil, raised | ||
} else if ret { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove else
runtime/builtin_types.go
Outdated
fn := args[0] | ||
l := args[1] | ||
switch { | ||
case l.isInstance(TupleType): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was puzzled about this special case, but then I realized that CPython is doing the same kind of special casing:
>>> class Empty(tuple):
... def __iter__(self):
... return self
... def next(self):
... raise StopIteration
...
>>> filter(None, Empty([1,2,3]))
(1, 2, 3)
IMO that's bad behavior, but since our goal is compatibility, I guess we need to special case as well. But I think a comment is warranted so that readers of the code understand why we're doing this.
runtime/builtin_types.go
Outdated
} | ||
if ret, raised := IsTrue(f, ret); raised != nil { | ||
return nil, raised | ||
} else if ret { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the else
runtime/builtin_types.go
Outdated
return l, nil | ||
} | ||
var result bytes.Buffer | ||
for _, item := range toStrUnsafe(l).Value() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will iterate over l assuming it's a UTF-8 encoded string. I think what you actually want is to convert this to a []byte and iterate over each byte and WriteByte() below instead of WriteRune().
runtime/builtin_types.go
Outdated
if fn == None { | ||
if ret, raised := IsTrue(f, item); raised != nil { | ||
return raised | ||
} else if ret { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove else
runtime/builtin_types.go
Outdated
} | ||
if ret, raised := IsTrue(f, ret); raised != nil { | ||
return raised | ||
} else if ret { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove else
runtime/builtin_types.go
Outdated
default: | ||
result := make([]*Object, 0) | ||
raised := seqForEach(f, l, func(item *Object) (raised *BaseException) { | ||
if fn == None { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given you do this check a few times I wonder if you should do something like this at the top:
filterFunc := IsTrue
if fn != None {
filterFunc = func(f *Frame, o *Object) (bool, *BaseException) {
result, raised := fn.Call(f, Args{o}, nil)
if raised != nil {
return false, raised
}
return IsTrue(f, result)
}
}
And then use filterFunc throughout.
runtime/builtin_types_test.go
Outdated
{f: "filter", args: wrapArgs(BoolType, NewTuple2(NewInt(0).ToObject(), NewInt(1).ToObject())), want: NewTuple1(NewInt(1).ToObject()).ToObject()}, | ||
{f: "filter", args: wrapArgs(IntType, "012"), want: NewStr("12").ToObject()}, | ||
{f: "filter", args: wrapArgs(None, newTestList(1, 0, 3)), want: newTestList(1, 3).ToObject()}, | ||
{f: "filter", args: wrapArgs(IntType, newTestList("1", "0", "3")), want: newTestList("1", "3").ToObject()}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add tests for subclasses of str and tuple that implement __iter__ to make sure we maintain compatibility with CPython?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi can i add these tests in testing/
directory using Python?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like tests in the Go code because it's easy to measure coverage there. Additional tests in testing/ are fine though.
@trotterdylan Updated! |
No description provided.