Skip to content

Commit 5148b8e

Browse files
committed
py: __repr__ and __str__ for bool, bytes, dict, ellipsis, list, module, none, string, tuple
1 parent 3f14f3d commit 5148b8e

14 files changed

+218
-34
lines changed

py/bool.go

+5
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ func (a Bool) M__index__() (Int, error) {
3636
}
3737

3838
func (a Bool) M__str__() (Object, error) {
39+
return a.M__repr__()
40+
}
41+
42+
func (a Bool) M__repr__() (Object, error) {
3943
if a {
4044
return String("True"), nil
4145
}
@@ -89,5 +93,6 @@ func (a Bool) M__ne__(other Object) (Object, error) {
8993
var _ I__bool__ = Bool(false)
9094
var _ I__index__ = Bool(false)
9195
var _ I__str__ = Bool(false)
96+
var _ I__repr__ = Bool(false)
9297
var _ I__eq__ = Bool(false)
9398
var _ I__ne__ = Bool(false)

py/bytes.go

+40
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package py
44

55
import (
66
"bytes"
7+
"fmt"
78
"strings"
89
)
910

@@ -141,6 +142,45 @@ func BytesFromObject(x Object) (Bytes, error) {
141142
return b, nil
142143
}
143144

145+
func (a Bytes) M__str__() (Object, error) {
146+
return a.M__repr__()
147+
}
148+
149+
func (a Bytes) M__repr__() (Object, error) {
150+
// FIXME combine this with parser/stringescape.go into file in py?
151+
var out bytes.Buffer
152+
quote := '\''
153+
if bytes.IndexByte(a, byte('\'')) >= 0 && !(bytes.IndexByte(a, byte('"')) >= 0) {
154+
quote = '"'
155+
}
156+
out.WriteRune('b')
157+
out.WriteRune(quote)
158+
for _, c := range a {
159+
switch {
160+
case c < 0x20:
161+
switch c {
162+
case '\t':
163+
out.WriteString(`\t`)
164+
case '\n':
165+
out.WriteString(`\n`)
166+
case '\r':
167+
out.WriteString(`\r`)
168+
default:
169+
fmt.Fprintf(&out, `\x%02x`, c)
170+
}
171+
case c < 0x7F:
172+
if c == '\\' || (quote == '\'' && c == '\'') || (quote == '"' && c == '"') {
173+
out.WriteRune('\\')
174+
}
175+
out.WriteByte(c)
176+
default:
177+
fmt.Fprintf(&out, "\\x%02x", c)
178+
}
179+
}
180+
out.WriteRune(quote)
181+
return String(out.String()), nil
182+
}
183+
144184
// Convert an Object to an Bytes
145185
//
146186
// Retrurns ok as to whether the conversion worked or not

py/dict.go

+31
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
package py
77

8+
import "bytes"
9+
810
const dictDoc = `dict() -> new empty dictionary
911
dict(mapping) -> new dictionary initialized from a mapping object's
1012
(key, value) pairs
@@ -49,6 +51,35 @@ func (d StringDict) Copy() StringDict {
4951
return e
5052
}
5153

54+
func (a StringDict) M__str__() (Object, error) {
55+
return a.M__repr__()
56+
}
57+
58+
func (a StringDict) M__repr__() (Object, error) {
59+
var out bytes.Buffer
60+
out.WriteRune('{')
61+
spacer := false
62+
for key, value := range a {
63+
if spacer {
64+
out.WriteString(", ")
65+
}
66+
keyStr, err := ReprAsString(String(key))
67+
if err != nil {
68+
return nil, err
69+
}
70+
valueStr, err := ReprAsString(value)
71+
if err != nil {
72+
return nil, err
73+
}
74+
out.WriteString(keyStr)
75+
out.WriteString(": ")
76+
out.WriteString(valueStr)
77+
spacer = true
78+
}
79+
out.WriteRune('}')
80+
return String(out.String()), nil
81+
}
82+
5283
func (d StringDict) M__getitem__(key Object) (Object, error) {
5384
str, ok := key.(String)
5485
if ok {

py/ellipsis.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ func (a EllipsisType) M__bool__() (Object, error) {
1818
return False, nil
1919
}
2020

21-
func (a EllipsisType) M__str__() (Object, error) {
21+
func (a EllipsisType) M__repr__() (Object, error) {
2222
return String("Ellipsis"), nil
2323
}
2424

@@ -38,6 +38,6 @@ func (a EllipsisType) M__ne__(other Object) (Object, error) {
3838

3939
// Check interface is satisfied
4040
var _ I__bool__ = Ellipsis
41-
var _ I__str__ = Ellipsis
41+
var _ I__repr__ = Ellipsis
4242
var _ I__eq__ = Ellipsis
4343
var _ I__eq__ = Ellipsis

py/internal.go

+31-31
Original file line numberDiff line numberDiff line change
@@ -326,52 +326,52 @@ func DeleteAttr(self Object, keyObj Object) error {
326326

327327
// Calls __str__ on the object
328328
//
329-
// If no method was found, returns ok as false
330-
func str(self Object) (res Object, ok bool, err error) {
331-
if _, ok = self.(String); ok {
332-
return self, true, nil
329+
// Calls __repr__ on the object or returns a sensible default
330+
func Repr(self Object) (Object, error) {
331+
if I, ok := self.(I__repr__); ok {
332+
return I.M__repr__()
333+
} else if res, ok, err := TypeCall0(self, "__repr__"); ok {
334+
return res, err
333335
}
336+
return String(fmt.Sprintf("<%s instance at %p>", self.Type().Name, self)), nil
337+
}
338+
339+
// Calls __str__ on the object and if not found calls __repr__
340+
func Str(self Object) (Object, error) {
334341
if I, ok := self.(I__str__); ok {
335-
res, err = I.M__str__()
336-
return res, true, err
342+
return I.M__str__()
337343
} else if res, ok, err := TypeCall0(self, "__str__"); ok {
338-
return res, true, err
344+
return res, err
339345
}
340-
return nil, false, nil
346+
return Repr(self)
341347
}
342348

343-
// Calls __str__ on the object
344-
func Str(self Object) (Object, error) {
345-
res, ok, err := str(self)
349+
// Returns object as a string
350+
//
351+
// Calls Str then makes sure the output is a string
352+
func StrAsString(self Object) (string, error) {
353+
res, err := Str(self)
346354
if err != nil {
347-
return nil, err
355+
return "", err
348356
}
357+
str, ok := res.(String)
349358
if !ok {
350-
return nil, ExceptionNewf(TypeError, "object of type '%s' has no __str__()", self.Type().Name)
351-
}
352-
return res, err
353-
}
354-
355-
// Calls __repr__ on the object or returns a sensible default
356-
func Repr(self Object) (Object, error) {
357-
if I, ok := self.(I__repr__); ok {
358-
return I.M__repr__()
359-
} else if res, ok, err := TypeCall0(self, "__repr__"); ok {
360-
return res, err
359+
return "", ExceptionNewf(TypeError, "result of __str__ must be string, not '%s'", res.Type().Name)
361360
}
362-
return String(fmt.Sprintf("<%s instance at %p>", self.Type().Name, self)), nil
361+
return string(str), nil
363362
}
364363

365364
// Returns object as a string
366365
//
367-
// Calls __str__ then __repr__ on the object then makes something up
368-
func AsString(self Object) (Object, error) {
369-
res, ok, err := str(self)
366+
// Calls Repr then makes sure the output is a string
367+
func ReprAsString(self Object) (string, error) {
368+
res, err := Repr(self)
370369
if err != nil {
371-
return nil, err
370+
return "", err
372371
}
373-
if ok {
374-
return res, err
372+
str, ok := res.(String)
373+
if !ok {
374+
return "", ExceptionNewf(TypeError, "result of __repr__ must be string, not '%s'", res.Type().Name)
375375
}
376-
return Repr(self)
376+
return string(str), nil
377377
}

py/list.go

+10
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,14 @@ func (l *List) Len() int {
9292
return len(l.Items)
9393
}
9494

95+
func (l *List) M__str__() (Object, error) {
96+
return l.M__repr__()
97+
}
98+
99+
func (l *List) M__repr__() (Object, error) {
100+
return Tuple(l.Items).repr("[", "]")
101+
}
102+
95103
func (l *List) M__len__() (Object, error) {
96104
return Int(len(l.Items)), nil
97105
}
@@ -243,6 +251,8 @@ func (a *List) M__imul__(other Object) (Object, error) {
243251

244252
// Check interface is satisfied
245253
var _ sequenceArithmetic = (*List)(nil)
254+
var _ I__str__ = (*List)(nil)
255+
var _ I__repr__ = (*List)(nil)
246256
var _ I__len__ = (*List)(nil)
247257
var _ I__len__ = (*List)(nil)
248258
var _ I__bool__ = (*List)(nil)

py/module.go

+6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
package py
44

5+
import "fmt"
6+
57
var (
68
// Registry of installed modules
79
modules = make(map[string]*Module)
@@ -27,6 +29,10 @@ func (o *Module) Type() *Type {
2729
return ModuleType
2830
}
2931

32+
func (m *Module) M__repr__() (Object, error) {
33+
return String(fmt.Sprintf("<module %s>", m.Name)), nil
34+
}
35+
3036
// Get the Dict
3137
func (m *Module) GetDict() StringDict {
3238
return m.Globals

py/none.go

+5
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ func (a NoneType) M__bool__() (Object, error) {
2020
}
2121

2222
func (a NoneType) M__str__() (Object, error) {
23+
return a.M__repr__()
24+
}
25+
26+
func (a NoneType) M__repr__() (Object, error) {
2327
return String("None"), nil
2428
}
2529

@@ -51,5 +55,6 @@ func (a NoneType) M__ne__(other Object) (Object, error) {
5155
// Check interface is satisfied
5256
var _ I__bool__ = None
5357
var _ I__str__ = None
58+
var _ I__repr__ = None
5459
var _ I__eq__ = None
5560
var _ I__ne__ = None

py/string.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func StrNew(metatype *Type, args Tuple, kwargs StringDict) (Object, error) {
4949
}
5050
// FIXME ignoring encoding
5151
// FIXME ignoring buffer protocol
52-
return AsString(sObj)
52+
return Str(sObj)
5353
}
5454

5555
// Intern s possibly returning a reference to an already interned string

py/tests/bytes.py

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
doc="str"
2+
assert str(b"") == "b''"
3+
assert str(b"hello") == r"b'hello'"
4+
assert str(rb"""hel"lo""") == r"""b'hel"lo'"""
5+
assert str(b"""he
6+
llo""") == r"""b'he\nllo'"""
7+
assert str(rb"""hel'lo""") == r'''b"hel'lo"'''
8+
assert str(b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff') == r"""b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff'"""
9+
10+
doc="repr"
11+
assert repr(b"") == "b''"
12+
assert repr(b"hello") == r"b'hello'"
13+
assert repr(rb"""hel"lo""") == r"""b'hel"lo'"""
14+
assert repr(b"""he
15+
llo""") == r"""b'he\nllo'"""
16+
assert repr(rb"""hel'lo""") == r'''b"hel'lo"'''
17+
assert repr(b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff') == r"""b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff'"""
18+
19+
doc="finished"

py/tests/dict.py

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
doc="str"
2+
assert str({}) == "{}"
3+
a = str({"a":"b","c":5.5})
4+
assert a == "{'a': 'b', 'c': 5.5}" or a == "{'c': 5.5, 'a': 'b'}"
5+
6+
doc="repr"
7+
assert repr({}) == "{}"
8+
a = repr({"a":"b","c":5.5})
9+
assert a == "{'a': 'b', 'c': 5.5}" or a == "{'c': 5.5, 'a': 'b'}"
10+
11+
doc="finished"

py/tests/list.py

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
doc="str"
2+
assert str([]) == "[]"
3+
assert str([1,2,3]) == "[1, 2, 3]"
4+
assert str([1,[2,3],4]) == "[1, [2, 3], 4]"
5+
assert str(["1",[2.5,17,[]]]) == "['1', [2.5, 17, []]]"
6+
7+
doc="repr"
8+
assert repr([]) == "[]"
9+
assert repr([1,2,3]) == "[1, 2, 3]"
10+
assert repr([1,[2,3],4]) == "[1, [2, 3], 4]"
11+
assert repr(["1",[2.5,17,[]]]) == "['1', [2.5, 17, []]]"
12+
13+
doc="finished"

py/tests/tuple.py

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
doc="str"
2+
assert str(()) == "()"
3+
assert str((1,2,3)) == "(1, 2, 3)"
4+
assert str((1,(2,3),4)) == "(1, (2, 3), 4)"
5+
assert str(("1",(2.5,17,()))) == "('1', (2.5, 17, ()))"
6+
7+
doc="repr"
8+
assert repr(()) == "()"
9+
assert repr((1,2,3)) == "(1, 2, 3)"
10+
assert repr((1,(2,3),4)) == "(1, (2, 3), 4)"
11+
assert repr(("1",(2.5,17,()))) == "('1', (2.5, 17, ()))"
12+
13+
doc="finished"

0 commit comments

Comments
 (0)