Skip to content

Commit 512e241

Browse files
committed
Merged master
2 parents a4d8dc4 + f3df7a4 commit 512e241

26 files changed

+1030
-291
lines changed

.travis.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,16 @@ matrix:
1313
allow_failures:
1414
- go: master
1515
include:
16-
- go: 1.9.x
16+
- go: 1.13.x
1717
env:
1818
- TAGS="-tags travis"
19-
- go: 1.10.x
19+
- COVERAGE="-cover"
20+
- go: 1.12.x
2021
env:
2122
- TAGS="-tags travis"
2223
- go: 1.11.x
2324
env:
2425
- TAGS="-tags travis"
25-
- COVERAGE="-cover"
2626
- go: master
2727
env:
2828
- TAGS="-tags travis"

appveyor.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ environment:
1212
matrix:
1313
- TARGET: x86_64-pc-windows-gnu
1414

15-
stack: go 1.11
15+
stack: go 1.13
1616

1717
build_script:
1818
- go get -v -t -race ./...

builtin/builtin.go

Lines changed: 27 additions & 2 deletions
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
}
@@ -88,7 +88,7 @@ func init() {
8888
"range": py.RangeType,
8989
// "reversed": py.ReversedType,
9090
"set": py.SetType,
91-
// "slice": py.SliceType,
91+
"slice": py.SliceType,
9292
"staticmethod": py.StaticMethodType,
9393
"str": py.StringType,
9494
// "super": py.SuperType,
@@ -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

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/dict.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,30 @@ func init() {
3636
}
3737
return NewIterator(o), nil
3838
}, 0, "items() -> list of D's (key, value) pairs, as 2-tuples")
39+
40+
StringDictType.Dict["get"] = MustNewMethod("get", func(self Object, args Tuple) (Object, error) {
41+
var length = len(args)
42+
switch {
43+
case length == 0:
44+
return nil, ExceptionNewf(TypeError, "%s expected at least 1 arguments, got %d", "items()", length)
45+
case length > 2:
46+
return nil, ExceptionNewf(TypeError, "%s expected at most 2 arguments, got %d", "items()", length)
47+
}
48+
sMap := self.(StringDict)
49+
if str, ok := args[0].(String); ok {
50+
if res, ok := sMap[string(str)]; ok {
51+
return res, nil
52+
}
53+
54+
switch length {
55+
case 2:
56+
return args[1], nil
57+
default:
58+
return None, nil
59+
}
60+
}
61+
return nil, ExceptionNewf(KeyError, "%v", args[0])
62+
}, 0, "gets(key, default) -> If there is a val corresponding to key, return val, otherwise default")
3963
}
4064

4165
// String to object dictionary

py/float.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ func FloatNew(metatype *Type, args Tuple, kwargs StringDict) (Object, error) {
4848
}
4949

5050
func (a Float) M__str__() (Object, error) {
51+
if i := int64(a); Float(i) == a {
52+
return String(fmt.Sprintf("%d.0", i)), nil
53+
}
5154
return String(fmt.Sprintf("%g", a)), nil
5255
}
5356

@@ -391,6 +394,20 @@ func (a Float) M__ge__(other Object) (Object, error) {
391394
return NotImplemented, nil
392395
}
393396

397+
// Properties
398+
func init() {
399+
FloatType.Dict["is_integer"] = MustNewMethod("is_integer", func(self Object) (Object, error) {
400+
if a, ok := convertToFloat(self); ok {
401+
f, err := FloatAsFloat64(a)
402+
if err != nil {
403+
return nil, err
404+
}
405+
return NewBool(math.Floor(f) == f), nil
406+
}
407+
return cantConvert(self, "float")
408+
}, 0, "is_integer() -> Return True if the float instance is finite with integral value, and False otherwise.")
409+
}
410+
394411
// Check interface is satisfied
395412
var _ floatArithmetic = Float(0)
396413
var _ conversionBetweenTypes = Float(0)

py/internal.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,22 @@ func MakeGoInt64(a Object) (int64, error) {
8787
//
8888
// Will raise TypeError if Index can't be run on this object
8989
func Index(a Object) (Int, error) {
90-
A, ok := a.(I__index__)
91-
if ok {
90+
if A, ok := a.(I__index__); ok {
9291
return A.M__index__()
9392
}
93+
94+
if A, ok, err := TypeCall0(a, "__index__"); ok {
95+
if err != nil {
96+
return 0, err
97+
}
98+
99+
if res, ok := A.(Int); ok {
100+
return res, nil
101+
}
102+
103+
return 0, ExceptionNewf(TypeError, "__index__ returned non-int: (type %s)", A.Type().Name)
104+
}
105+
94106
return 0, ExceptionNewf(TypeError, "unsupported operand type(s) for index: '%s'", a.Type().Name)
95107
}
96108

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+
}

0 commit comments

Comments
 (0)