Add `sorted` and `list.sort` (#81) · go-python/gpython@af17d7d · GitHub
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

4 files changed

Lines changed: 298 additions & 3 deletions

File tree

builtin/builtin.go

Lines changed: 26 additions & 1 deletion

builtin/tests/builtin.py

Lines changed: 45 additions & 1 deletion
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

Lines changed: 154 additions & 0 deletions
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

Lines changed: 73 additions & 1 deletion

0 commit comments

Comments
 (0)