Skip to content

Commit af17d7d

Browse files
Tim-Stcorona10
authored andcommitted
Add sorted and list.sort (#81)
* Add `sorted` and `list.sort` Mostly finished, needs some argument parsing changes. * Unpacking of args works now for `sorted` and `list.sort` * add tests * new try * Add tests for `list.sort` * Update list.py * support `list.sort([], **kwargs)` * Update list.go * better NoneType comparison * put tests for `sorted` in `builtin.py`
1 parent 4b996c8 commit af17d7d

File tree

4 files changed

+298
-3
lines changed

4 files changed

+298
-3
lines changed

builtin/builtin.go

+26-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ func init() {
6060
py.MustNewMethod("repr", builtin_repr, 0, repr_doc),
6161
py.MustNewMethod("round", builtin_round, 0, round_doc),
6262
py.MustNewMethod("setattr", builtin_setattr, 0, setattr_doc),
63-
// py.MustNewMethod("sorted", builtin_sorted, 0, sorted_doc),
63+
py.MustNewMethod("sorted", builtin_sorted, 0, sorted_doc),
6464
py.MustNewMethod("sum", builtin_sum, 0, sum_doc),
6565
// py.MustNewMethod("vars", builtin_vars, 0, vars_doc),
6666
}
@@ -1074,3 +1074,28 @@ func builtin_sum(self py.Object, args py.Tuple) (py.Object, error) {
10741074
}
10751075
return start, nil
10761076
}
1077+
1078+
const sorted_doc = `sorted(iterable, key=None, reverse=False)
1079+
1080+
Return a new list containing all items from the iterable in ascending order.
1081+
1082+
A custom key function can be supplied to customize the sort order, and the
1083+
reverse flag can be set to request the result in descending order.`
1084+
1085+
func builtin_sorted(self py.Object, args py.Tuple, kwargs py.StringDict) (py.Object, error) {
1086+
const funcName = "sorted"
1087+
var iterable py.Object
1088+
err := py.UnpackTuple(args, nil, funcName, 1, 1, &iterable)
1089+
if err != nil {
1090+
return nil, err
1091+
}
1092+
l, err := py.SequenceList(iterable)
1093+
if err != nil {
1094+
return nil, err
1095+
}
1096+
err = py.SortInPlace(l, kwargs, funcName)
1097+
if err != nil {
1098+
return nil, err
1099+
}
1100+
return l, nil
1101+
}

builtin/tests/builtin.py

+45-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2018 The go-python Authors. All rights reserved.
1+
# 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.
44

@@ -329,6 +329,50 @@ class C: pass
329329
finally:
330330
assert ok
331331

332+
doc="sorted"
333+
a = [3, 1.1, 1, 2]
334+
assert sorted(a) == [1, 1.1, 2, 3]
335+
assert sorted(sorted(a)) == [1, 1.1, 2, 3]
336+
assert sorted(a, reverse=True) == [3, 2, 1.1, 1]
337+
assert sorted(a, key=lambda l: l+1) == [1, 1.1, 2, 3]
338+
s = [2.0, 2, 1, 1.0]
339+
assert sorted(s, key=lambda l: 0) == [2.0, 2, 1, 1.0]
340+
assert [type(t) for t in sorted(s, key=lambda l: 0)] == [float, int, int, float]
341+
assert sorted(s) == [1, 1.0, 2.0, 2]
342+
assert [type(t) for t in sorted(s)] == [int, float, float, int]
343+
344+
try:
345+
sorted([2.0, "abc"])
346+
except TypeError:
347+
pass
348+
else:
349+
assert False
350+
351+
assert sorted([]) == []
352+
assert sorted([0]) == [0]
353+
s = [0, 1]
354+
try:
355+
# Sorting a list of len >= 2 with uncallable key must fail on all Python implementations.
356+
sorted(s, key=1)
357+
except TypeError:
358+
pass
359+
else:
360+
assert False
361+
362+
try:
363+
sorted(1)
364+
except TypeError:
365+
pass
366+
else:
367+
assert False
368+
369+
try:
370+
sorted()
371+
except TypeError:
372+
pass
373+
else:
374+
assert False
375+
332376
doc="sum"
333377
assert sum([1,2,3]) == 6
334378
assert sum([1,2,3], 3) == 9

py/list.go

+154
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66

77
package py
88

9+
import (
10+
"sort"
11+
)
12+
913
var ListType = ObjectType.NewType("list", "list() -> new empty list\nlist(iterable) -> new list initialized from iterable's items", ListNew, nil)
1014

1115
// FIXME lists are mutable so this should probably be struct { Tuple } then can use the sub methods on Tuple
@@ -14,6 +18,7 @@ type List struct {
1418
}
1519

1620
func init() {
21+
// FIXME: all methods should be callable using list.method([], *args, **kwargs) or [].method(*args, **kwargs)
1722
ListType.Dict["append"] = MustNewMethod("append", func(self Object, args Tuple) (Object, error) {
1823
listSelf := self.(*List)
1924
if len(args) != 1 {
@@ -34,6 +39,36 @@ func init() {
3439
return NoneType{}, nil
3540
}, 0, "extend([item])")
3641

42+
ListType.Dict["sort"] = MustNewMethod("sort", func(self Object, args Tuple, kwargs StringDict) (Object, error) {
43+
const funcName = "sort"
44+
var l *List
45+
if self == None {
46+
// method called using `list.sort([], **kwargs)`
47+
var o Object
48+
err := UnpackTuple(args, nil, funcName, 1, 1, &o)
49+
if err != nil {
50+
return nil, err
51+
}
52+
var ok bool
53+
l, ok = o.(*List)
54+
if !ok {
55+
return nil, ExceptionNewf(TypeError, "descriptor 'sort' requires a 'list' object but received a '%s'", o.Type())
56+
}
57+
} else {
58+
// method called using `[].sort(**kargs)`
59+
err := UnpackTuple(args, nil, funcName, 0, 0)
60+
if err != nil {
61+
return nil, err
62+
}
63+
l = self.(*List)
64+
}
65+
err := SortInPlace(l, kwargs, funcName)
66+
if err != nil {
67+
return nil, err
68+
}
69+
return NoneType{}, nil
70+
}, 0, "sort(key=None, reverse=False)")
71+
3772
}
3873

3974
// Type of this List object
@@ -331,3 +366,122 @@ func (a *List) M__ne__(other Object) (Object, error) {
331366
}
332367
return False, nil
333368
}
369+
370+
type sortable struct {
371+
l *List
372+
keyFunc Object
373+
reverse bool
374+
firstErr error
375+
}
376+
377+
type ptrSortable struct {
378+
s *sortable
379+
}
380+
381+
func (s ptrSortable) Len() int {
382+
return s.s.l.Len()
383+
}
384+
385+
func (s ptrSortable) Swap(i, j int) {
386+
itemI, err := s.s.l.M__getitem__(Int(i))
387+
if err != nil {
388+
if s.s.firstErr == nil {
389+
s.s.firstErr = err
390+
}
391+
return
392+
}
393+
itemJ, err := s.s.l.M__getitem__(Int(j))
394+
if err != nil {
395+
if s.s.firstErr == nil {
396+
s.s.firstErr = err
397+
}
398+
return
399+
}
400+
_, err = s.s.l.M__setitem__(Int(i), itemJ)
401+
if err != nil {
402+
if s.s.firstErr == nil {
403+
s.s.firstErr = err
404+
}
405+
}
406+
_, err = s.s.l.M__setitem__(Int(j), itemI)
407+
if err != nil {
408+
if s.s.firstErr == nil {
409+
s.s.firstErr = err
410+
}
411+
}
412+
}
413+
414+
func (s ptrSortable) Less(i, j int) bool {
415+
itemI, err := s.s.l.M__getitem__(Int(i))
416+
if err != nil {
417+
if s.s.firstErr == nil {
418+
s.s.firstErr = err
419+
}
420+
return false
421+
}
422+
itemJ, err := s.s.l.M__getitem__(Int(j))
423+
if err != nil {
424+
if s.s.firstErr == nil {
425+
s.s.firstErr = err
426+
}
427+
return false
428+
}
429+
430+
if s.s.keyFunc != None {
431+
itemI, err = Call(s.s.keyFunc, Tuple{itemI}, nil)
432+
if err != nil {
433+
if s.s.firstErr == nil {
434+
s.s.firstErr = err
435+
}
436+
return false
437+
}
438+
itemJ, err = Call(s.s.keyFunc, Tuple{itemJ}, nil)
439+
if err != nil {
440+
if s.s.firstErr == nil {
441+
s.s.firstErr = err
442+
}
443+
return false
444+
}
445+
}
446+
447+
var cmpResult Object
448+
if s.s.reverse {
449+
cmpResult, err = Lt(itemJ, itemI)
450+
} else {
451+
cmpResult, err = Lt(itemI, itemJ)
452+
}
453+
454+
if err != nil {
455+
if s.s.firstErr == nil {
456+
s.s.firstErr = err
457+
}
458+
return false
459+
}
460+
461+
if boolResult, ok := cmpResult.(Bool); ok {
462+
return bool(boolResult)
463+
}
464+
465+
return false
466+
}
467+
468+
// SortInPlace sorts the given List in place using a stable sort.
469+
// kwargs can have the keys "key" and "reverse".
470+
func SortInPlace(l *List, kwargs StringDict, funcName string) error {
471+
var keyFunc Object
472+
var reverse Object
473+
err := ParseTupleAndKeywords(nil, kwargs, "|$OO:"+funcName, []string{"key", "reverse"}, &keyFunc, &reverse)
474+
if err != nil {
475+
return err
476+
}
477+
if keyFunc == nil {
478+
keyFunc = None
479+
}
480+
if reverse == nil {
481+
reverse = False
482+
}
483+
// FIXME: requires the same bool-check like CPython (or better "|$Op" that doesn't panic on nil).
484+
s := ptrSortable{&sortable{l, keyFunc, ObjectIsTrue(reverse), nil}}
485+
sort.Stable(s)
486+
return s.s.firstErr
487+
}

py/tests/list.py

+73-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2018 The go-python Authors. All rights reserved.
1+
# 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.
44

@@ -39,4 +39,76 @@
3939
assert a * 0 == []
4040
assert a * -1 == []
4141

42+
doc="sort"
43+
# [].sort
44+
a = [3, 1.1, 1, 2]
45+
s1 = list(a)
46+
s1.sort()
47+
assert s1 == [1, 1.1, 2, 3]
48+
s1.sort() # sort a sorted list
49+
assert s1 == [1, 1.1, 2, 3]
50+
s2 = list(a)
51+
s2.sort(reverse=True)
52+
assert s2 == [3, 2, 1.1, 1]
53+
s2.sort() # sort a reversed list
54+
assert s2 == [1, 1.1, 2, 3]
55+
s3 = list(a)
56+
s3.sort(key=lambda l: l+1) # test lambda key
57+
assert s3 == [1, 1.1, 2, 3]
58+
s4 = [2.0, 2, 1, 1.0]
59+
s4.sort(key=lambda l: 0) # test stability
60+
assert s4 == [2.0, 2, 1, 1.0]
61+
assert [type(t) for t in s4] == [float, int, int, float]
62+
s4 = [2.0, 2, 1, 1.0]
63+
s4.sort() # test stability
64+
assert s4 == [1, 1.0, 2.0, 2]
65+
assert [type(t) for t in s4] == [int, float, float, int]
66+
s5 = [2.0, "abc"]
67+
assertRaises(TypeError, lambda: s5.sort())
68+
s5 = []
69+
s5.sort()
70+
assert s5 == []
71+
s5 = [0]
72+
s5.sort()
73+
assert s5 == [0]
74+
s5 = [0, 1]
75+
# Sorting a list of len >= 2 with uncallable key must fail on all Python implementations.
76+
assertRaises(TypeError, lambda: s5.sort(key=1))
77+
78+
# list.sort([])
79+
a = [3, 1.1, 1, 2]
80+
s1 = list(a)
81+
assert list.sort(s1) is None
82+
assert s1 == [1, 1.1, 2, 3]
83+
assert list.sort(s1) is None # sort a sorted list
84+
assert s1 == [1, 1.1, 2, 3]
85+
s2 = list(a)
86+
list.sort(s2, reverse=True)
87+
assert s2 == [3, 2, 1.1, 1]
88+
list.sort(s2) # sort a reversed list
89+
assert s2 == [1, 1.1, 2, 3]
90+
s3 = list(a)
91+
list.sort(s3, key=lambda l: l+1) # test lambda key
92+
assert s3 == [1, 1.1, 2, 3]
93+
s4 = [2.0, 2, 1, 1.0]
94+
list.sort(s4, key=lambda l: 0) # test stability
95+
assert s4 == [2.0, 2, 1, 1.0]
96+
assert [type(t) for t in s4] == [float, int, int, float]
97+
s4 = [2.0, 2, 1, 1.0]
98+
list.sort(s4) # test stability
99+
assert s4 == [1, 1.0, 2.0, 2]
100+
assert [type(t) for t in s4] == [int, float, float, int]
101+
s5 = [2.0, "abc"]
102+
assertRaises(TypeError, lambda: list.sort(s5))
103+
s5 = []
104+
list.sort(s5)
105+
assert s5 == []
106+
s5 = [0]
107+
list.sort(s5)
108+
assert s5 == [0]
109+
s5 = [0, 1]
110+
# Sorting a list of len >= 2 with uncallable key must fail on all Python implementations.
111+
assertRaises(TypeError, lambda: list.sort(s5, key=1))
112+
assertRaises(TypeError, lambda: list.sort(1))
113+
42114
doc="finished"

0 commit comments

Comments
 (0)