Skip to content

Commit 80d61cf

Browse files
committed
all: support filter builtin feature and fix iterable object
1 parent acd458b commit 80d61cf

File tree

12 files changed

+228
-47
lines changed

12 files changed

+228
-47
lines changed

py/arithmetic.go

-18
Original file line numberDiff line numberDiff line change
@@ -147,24 +147,6 @@ func MakeFloat(a Object) (Object, error) {
147147
return nil, ExceptionNewf(TypeError, "unsupported operand type(s) for float: '%s'", a.Type().Name)
148148
}
149149

150-
// Iter the python Object returning an Object
151-
//
152-
// Will raise TypeError if Iter can't be run on this object
153-
func Iter(a Object) (Object, error) {
154-
155-
if A, ok := a.(I__iter__); ok {
156-
res, err := A.M__iter__()
157-
if err != nil {
158-
return nil, err
159-
}
160-
if res != NotImplemented {
161-
return res, nil
162-
}
163-
}
164-
165-
return nil, ExceptionNewf(TypeError, "unsupported operand type(s) for iter: '%s'", a.Type().Name)
166-
}
167-
168150
// Add two python objects together returning an Object
169151
//
170152
// Will raise TypeError if can't be add can't be run on these objects

py/dict.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ func init() {
3636
return nil, err
3737
}
3838
sMap := self.(StringDict)
39-
o := make([]Object, 0, len(sMap))
39+
o := make(Tuple, 0, len(sMap))
4040
for k, v := range sMap {
4141
o = append(o, Tuple{String(k), v})
4242
}
@@ -49,7 +49,7 @@ func init() {
4949
return nil, err
5050
}
5151
sMap := self.(StringDict)
52-
o := make([]Object, 0, len(sMap))
52+
o := make(Tuple, 0, len(sMap))
5353
for k := range sMap {
5454
o = append(o, String(k))
5555
}
@@ -62,7 +62,7 @@ func init() {
6262
return nil, err
6363
}
6464
sMap := self.(StringDict)
65-
o := make([]Object, 0, len(sMap))
65+
o := make(Tuple, 0, len(sMap))
6666
for _, v := range sMap {
6767
o = append(o, v)
6868
}
@@ -204,7 +204,7 @@ func (a StringDict) M__repr__() (Object, error) {
204204

205205
// Returns a list of keys from the dict
206206
func (d StringDict) M__iter__() (Object, error) {
207-
o := make([]Object, 0, len(d))
207+
o := make(Tuple, 0, len(d))
208208
for k := range d {
209209
o = append(o, String(k))
210210
}

py/exception.go

+2
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,8 @@ func ExceptionGivenMatches(err, exc Object) bool {
336336
func IsException(exception *Type, r interface{}) bool {
337337
var t *Type
338338
switch ex := r.(type) {
339+
case ExceptionInfo:
340+
t = ex.Type
339341
case *Exception:
340342
t = ex.Type()
341343
case *Type:

py/filter.go

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Copyright 2018 The go-python Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package py
6+
7+
// A python Filter object
8+
type Filter struct {
9+
it Object
10+
fun Object
11+
}
12+
13+
var FilterType = NewTypeX("filter", `filter(function or None, iterable) --> filter object
14+
15+
Return an iterator yielding those items of iterable for which function(item)
16+
is true. If function is None, return the items that are true.`,
17+
FilterTypeNew, nil)
18+
19+
// Type of this object
20+
func (f *Filter) Type() *Type {
21+
return FilterType
22+
}
23+
24+
// FilterTypeNew
25+
func FilterTypeNew(metatype *Type, args Tuple, kwargs StringDict) (res Object, err error) {
26+
var fun, seq Object
27+
var it Object
28+
err = UnpackTuple(args, kwargs, "filter", 2, 2, &fun, &seq)
29+
if err != nil {
30+
return nil, err
31+
}
32+
it, err = Iter(seq)
33+
if err != nil {
34+
return nil, err
35+
}
36+
return &Filter{it: it, fun: fun}, nil
37+
}
38+
39+
func (f *Filter) M__iter__() (Object, error) {
40+
return f, nil
41+
}
42+
43+
func (f *Filter) M__next__() (Object, error) {
44+
var ok bool
45+
for {
46+
item, err := Next(f.it)
47+
if err != nil {
48+
return nil, err
49+
}
50+
// if (lz->func == Py_None || lz->func == (PyObject *)&PyBool_Type)
51+
if _, _ok := f.fun.(Bool); _ok || f.fun == None {
52+
ok, err = ObjectIsTrue(item)
53+
} else {
54+
var good Object
55+
good, err = Call(f.fun, Tuple{item}, nil)
56+
if err != nil {
57+
return nil, err
58+
}
59+
ok, err = ObjectIsTrue(good)
60+
}
61+
if ok {
62+
return item, nil
63+
}
64+
if err != nil {
65+
return nil, err
66+
}
67+
}
68+
}
69+
70+
// Check interface is satisfied
71+
var _ I__iter__ = (*Filter)(nil)
72+
var _ I__next__ = (*Filter)(nil)

py/gen.go

-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ var data = Data{
4545
{Name: "complex", Title: "MakeComplex", Operator: "complex", Unary: true, Conversion: "Complex"},
4646
{Name: "int", Title: "MakeInt", Operator: "int", Unary: true, Conversion: "Int"},
4747
{Name: "float", Title: "MakeFloat", Operator: "float", Unary: true, Conversion: "Float"},
48-
{Name: "iter", Title: "Iter", Operator: "iter", Unary: true},
4948
},
5049
BinaryOps: Ops{
5150
{Name: "add", Title: "Add", Operator: "+", Binary: true},

py/internal.go

+17
Original file line numberDiff line numberDiff line change
@@ -430,3 +430,20 @@ func ReprAsString(self Object) (string, error) {
430430
}
431431
return string(str), nil
432432
}
433+
434+
// Returns an iterator object
435+
//
436+
// Call __Iter__ Returns an iterator object
437+
//
438+
// If object is sequence object, create an iterator
439+
func Iter(self Object) (res Object, err error) {
440+
if I, ok := self.(I__iter__); ok {
441+
return I.M__iter__()
442+
} else if res, ok, err = TypeCall0(self, "__iter__"); ok {
443+
return res, err
444+
}
445+
if ObjectIsSequence(self) {
446+
return NewIterator(self), nil
447+
}
448+
return nil, ExceptionNewf(TypeError, "'%s' object is not iterable", self.Type().Name)
449+
}

py/iterator.go

+26-10
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ package py
88

99
// A python Iterator object
1010
type Iterator struct {
11-
Pos int
12-
Objs []Object
11+
Pos int
12+
Seq Object
1313
}
1414

1515
var IteratorType = NewType("iterator", "iterator type")
@@ -20,10 +20,10 @@ func (o *Iterator) Type() *Type {
2020
}
2121

2222
// Define a new iterator
23-
func NewIterator(Objs []Object) *Iterator {
23+
func NewIterator(Seq Object) *Iterator {
2424
m := &Iterator{
25-
Pos: 0,
26-
Objs: Objs,
25+
Pos: 0,
26+
Seq: Seq,
2727
}
2828
return m
2929
}
@@ -33,13 +33,29 @@ func (it *Iterator) M__iter__() (Object, error) {
3333
}
3434

3535
// Get next one from the iteration
36-
func (it *Iterator) M__next__() (Object, error) {
37-
if it.Pos >= len(it.Objs) {
38-
return nil, StopIteration
36+
func (it *Iterator) M__next__() (res Object, err error) {
37+
if tuple, ok := it.Seq.(Tuple); ok {
38+
if it.Pos >= len(tuple) {
39+
return nil, StopIteration
40+
}
41+
res = tuple[it.Pos]
42+
it.Pos++
43+
return res, nil
44+
}
45+
index := Int(it.Pos)
46+
if I, ok := it.Seq.(I__getitem__); ok {
47+
res, err = I.M__getitem__(index)
48+
} else if res, ok, err = TypeCall1(it.Seq, "__getitem__", index); !ok {
49+
return nil, ExceptionNewf(TypeError, "'%s' object is not iterable", it.Type().Name)
50+
}
51+
if err != nil {
52+
if IsException(IndexError, err) {
53+
return nil, StopIteration
54+
}
55+
return nil, err
3956
}
40-
r := it.Objs[it.Pos]
4157
it.Pos++
42-
return r, nil
58+
return res, nil
4359
}
4460

4561
// Check interface is satisfied

py/list.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ func (l *List) M__bool__() (Object, error) {
186186
}
187187

188188
func (l *List) M__iter__() (Object, error) {
189-
return NewIterator(l.Items), nil
189+
return NewIterator(Tuple(l.Items)), nil
190190
}
191191

192192
func (l *List) M__getitem__(key Object) (Object, error) {
@@ -496,7 +496,11 @@ func SortInPlace(l *List, kwargs StringDict, funcName string) error {
496496
reverse = False
497497
}
498498
// FIXME: requires the same bool-check like CPython (or better "|$Op" that doesn't panic on nil).
499-
s := ptrSortable{&sortable{l, keyFunc, ObjectIsTrue(reverse), nil}}
499+
ok, err := ObjectIsTrue(reverse)
500+
if err != nil {
501+
return err
502+
}
503+
s := ptrSortable{&sortable{l, keyFunc, ok, nil}}
500504
sort.Stable(s)
501505
return s.s.firstErr
502506
}

py/object.go

+33-7
Original file line numberDiff line numberDiff line change
@@ -23,24 +23,50 @@ func ObjectRepr(o Object) Object {
2323
}
2424

2525
// Return whether the object is True or not
26-
func ObjectIsTrue(o Object) bool {
26+
func ObjectIsTrue(o Object) (bool, error) {
2727
if o == True {
28-
return true
28+
return true, nil
2929
}
3030
if o == False {
31-
return false
31+
return false, nil
3232
}
3333

3434
if o == None {
35-
return false
35+
return false, nil
3636
}
3737

3838
if I, ok := o.(I__bool__); ok {
3939
cmp, err := I.M__bool__()
40-
if err == nil && cmp == True {
40+
if err != nil {
41+
return false, err
42+
}
43+
if cmp == True {
44+
return true, nil
45+
} else if cmp == False {
46+
return false, nil
47+
}
48+
}
49+
if I, ok := o.(I__len__); ok {
50+
l, err := I.M__len__()
51+
if err != nil {
52+
return false, err
53+
}
54+
if l.(Int) > 0 {
55+
return true, nil
56+
} else {
57+
return false, nil
58+
}
59+
}
60+
return false, nil
61+
}
62+
63+
// Return whether the object is True or not
64+
func ObjectIsSequence(o Object) bool {
65+
if _, ok := o.(I__getitem__); ok {
66+
return true
67+
} else if t, ok := o.(*Type); ok {
68+
if t.GetAttrOrNil("__getitem__") != nil {
4169
return true
42-
} else if err == nil && cmp == False {
43-
return false
4470
}
4571
}
4672
return false

py/tests/filter.py

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# test_builtin.py:BuiltinTest.test_filter()
2+
from libtest import assertRaises
3+
4+
doc="filter"
5+
class Squares:
6+
def __init__(self, max):
7+
self.max = max
8+
self.sofar = []
9+
10+
def __len__(self): return len(self.sofar)
11+
12+
def __getitem__(self, i):
13+
if not 0 <= i < self.max: raise IndexError
14+
n = len(self.sofar)
15+
while n <= i:
16+
self.sofar.append(n*n)
17+
n += 1
18+
return self.sofar[i]
19+
20+
assert list(filter(lambda c: 'a' <= c <= 'z', 'Hello World')) == list('elloorld')
21+
assert list(filter(None, [1, 'hello', [], [3], '', None, 9, 0])) == [1, 'hello', [3], 9]
22+
assert list(filter(lambda x: x > 0, [1, -3, 9, 0, 2])) == [1, 9, 2]
23+
assert list(filter(None, Squares(10))) == [1, 4, 9, 16, 25, 36, 49, 64, 81]
24+
assert list(filter(lambda x: x%2, Squares(10))) == [1, 9, 25, 49, 81]
25+
def identity(item):
26+
return 1
27+
filter(identity, Squares(5))
28+
assertRaises(TypeError, filter)
29+
class BadSeq(object):
30+
def __getitem__(self, index):
31+
if index<4:
32+
return 42
33+
raise ValueError
34+
assertRaises(ValueError, list, filter(lambda x: x, BadSeq()))
35+
def badfunc():
36+
pass
37+
assertRaises(TypeError, list, filter(badfunc, range(5)))
38+
39+
# test bltinmodule.c::filtertuple()
40+
assert list(filter(None, (1, 2))) == [1, 2]
41+
assert list(filter(lambda x: x>=3, (1, 2, 3, 4))) == [3, 4]
42+
assertRaises(TypeError, list, filter(42, (1, 2)))
43+
44+
doc="finished"

py/tests/iter.py

+11
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Copyright 2019 The go-python Authors. All rights reserved.
22
# Use of this source code is governed by a BSD-style
33
# license that can be found in the LICENSE file.
4+
from libtest import assertRaises
45

56
doc="iter"
67
cnt = 0
@@ -18,4 +19,14 @@ def f():
1819
words2 = list(iter(words1))
1920
for w1, w2 in zip(words1, words2):
2021
assert w1 == w2
22+
23+
# test_builtin.py:BuiltinTest.test_iter()
24+
assertRaises(TypeError, iter)
25+
assertRaises(TypeError, iter, 42, 42)
26+
lists = [("1", "2"), ["1", "2"], "12"]
27+
for l in lists:
28+
i = iter(l)
29+
assert next(i) == '1'
30+
assert next(i) == '2'
31+
assertRaises(StopIteration, next, i)
2132
doc="finished"

0 commit comments

Comments
 (0)