Skip to content

Commit 13f5466

Browse files
committed
Make tracebacks work and show something useful
1 parent cbcda57 commit 13f5466

File tree

6 files changed

+149
-19
lines changed

6 files changed

+149
-19
lines changed

main.go

+1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ func main() {
6565
module := py.NewModule("__main__", "", nil, nil)
6666
res, err := vm.Run(module.Globals, module.Globals, code, nil)
6767
if err != nil {
68+
py.TracebackDump(err)
6869
log.Fatal(err)
6970
}
7071
fmt.Printf("Return = %v\n", res)

marshal/marshal.go

+1
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,7 @@ func LoadFrozenModule(name string, data []byte) *py.Module {
377377
module := py.NewModule(name, "", nil, nil)
378378
_, err = vm.Run(module.Globals, module.Globals, code, nil)
379379
if err != nil {
380+
py.TracebackDump(err)
380381
panic(err)
381382
}
382383
return module

py/code.go

+16
Original file line numberDiff line numberDiff line change
@@ -204,3 +204,19 @@ func NewCode(argcount int32, kwonlyargcount int32,
204204
Weakreflist: nil,
205205
}
206206
}
207+
208+
// Use co_lnotab to compute the line number from a bytecode index,
209+
// addrq. See lnotab_notes.txt for the details of the lnotab
210+
// representation.
211+
func (co *Code) Addr2Line(addrq int32) int32 {
212+
line := co.Firstlineno
213+
addr := int32(0)
214+
for i := 0; i < len(co.Lnotab); i += 2 {
215+
addr += int32(co.Lnotab[i])
216+
if addr > addrq {
217+
break
218+
}
219+
line += int32(co.Lnotab[i+1])
220+
}
221+
return line
222+
}

py/exception.go

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

55
import (
66
"fmt"
7+
"io"
78
)
89

910
// A python Exception object
@@ -19,9 +20,9 @@ type Exception struct {
1920

2021
// A python exception info block
2122
type ExceptionInfo struct {
22-
Type Object
23+
Type *Type
2324
Value Object
24-
Traceback Object
25+
Traceback *Traceback
2526
}
2627

2728
// Make Exception info statisfy the error interface
@@ -111,6 +112,13 @@ func (e ExceptionInfo) Error() string {
111112
return e.Value.Type().Name
112113
}
113114

115+
// Dump a traceback for exc to w
116+
func (exc *ExceptionInfo) TracebackDump(w io.Writer) {
117+
fmt.Fprintf(w, "Traceback (most recent call last):\n")
118+
exc.Traceback.TracebackDump(w)
119+
fmt.Fprintf(w, "%v: %v\n", exc.Type.Name, exc.Value)
120+
}
121+
114122
// ExceptionNew
115123
func ExceptionNew(metatype *Type, args Tuple, kwargs StringDict) Object {
116124
if len(kwargs) != 0 {

py/traceback.go

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// Traceback objects
2+
3+
package py
4+
5+
import (
6+
"fmt"
7+
"io"
8+
"os"
9+
)
10+
11+
// A python Traceback object
12+
type Traceback struct {
13+
Next *Traceback
14+
Frame *Frame
15+
Lasti int32
16+
Lineno int32
17+
}
18+
19+
var TracebackType = NewType("traceback", "A python traceback")
20+
21+
// Type of this object
22+
func (o *Traceback) Type() *Type {
23+
return TracebackType
24+
}
25+
26+
// Make a new traceback
27+
func NewTraceback(next *Traceback, frame *Frame, lasti, lineno int32) *Traceback {
28+
return &Traceback{
29+
Next: next,
30+
Frame: frame,
31+
Lasti: lasti,
32+
Lineno: lineno,
33+
}
34+
}
35+
36+
/*
37+
Traceback (most recent call last):
38+
File "throws.py", line 8, in <module>
39+
main()
40+
File "throws.py", line 5, in main
41+
throws()
42+
File "throws.py", line 2, in throws
43+
raise RuntimeError('this is the error message')
44+
RuntimeError: this is the error message
45+
*/
46+
47+
// Dump a traceback for tb to w
48+
func (tb *Traceback) TracebackDump(w io.Writer) {
49+
for ; tb != nil; tb = tb.Next {
50+
fmt.Fprintf(w, " File %q, line %d, in %s\n", tb.Frame.Code.Filename, tb.Lineno, tb.Frame.Code.Name)
51+
fmt.Fprintf(w, " %s\n", "FIXME line of source goes here")
52+
}
53+
}
54+
55+
// Dumps a traceback to stderr
56+
func TracebackDump(err interface{}) {
57+
switch e := err.(type) {
58+
case ExceptionInfo:
59+
e.TracebackDump(os.Stderr)
60+
case *ExceptionInfo:
61+
e.TracebackDump(os.Stderr)
62+
case *Exception:
63+
fmt.Fprintf(os.Stderr, "Exception %#v\n", e)
64+
fmt.Fprintf(os.Stderr, "-- No traceback available --\n")
65+
default:
66+
fmt.Fprintf(os.Stderr, "Error %#v\n", err)
67+
fmt.Fprintf(os.Stderr, "-- No traceback available --\n")
68+
}
69+
}
70+
71+
// Properties
72+
func init() {
73+
TracebackType.Dict["__tb_next__"] = &Property{
74+
Fget: func(self Object) Object {
75+
next := self.(*Traceback).Next
76+
if next == nil {
77+
return None
78+
}
79+
return next
80+
},
81+
}
82+
TracebackType.Dict["__tb_frame__"] = &Property{
83+
Fget: func(self Object) Object {
84+
return self.(*Traceback).Frame
85+
},
86+
}
87+
TracebackType.Dict["__tb_lasti__"] = &Property{
88+
Fget: func(self Object) Object {
89+
return Int(self.(*Traceback).Lasti)
90+
},
91+
}
92+
TracebackType.Dict["__tb_lineno__"] = &Property{
93+
Fget: func(self Object) Object {
94+
return Int(self.(*Traceback).Lineno)
95+
},
96+
}
97+
}
98+
99+
// Make sure it satisfies the interface
100+
var _ Object = (*Traceback)(nil)

vm/eval.go

+21-17
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,16 @@ func (vm *Vm) PUSH(obj py.Object) {
7171
vm.frame.Stack = append(vm.frame.Stack, obj)
7272
}
7373

74+
// Adds a traceback to the exc passed in for the current vm state
75+
func (vm *Vm) AddTraceback(exc *py.ExceptionInfo) {
76+
exc.Traceback = &py.Traceback{
77+
Next: exc.Traceback,
78+
Frame: vm.frame,
79+
Lasti: vm.frame.Lasti,
80+
Lineno: vm.frame.Code.Addr2Line(vm.frame.Lasti),
81+
}
82+
}
83+
7484
// Set an exception in the VM
7585
//
7686
// The exception must be a valid exception instance (eg as returned by
@@ -81,7 +91,8 @@ func (vm *Vm) SetException(exception py.Object) {
8191
vm.old_exc = vm.exc
8292
vm.exc.Value = exception
8393
vm.exc.Type = exception.Type()
84-
vm.exc.Traceback = py.None // FIXME start the traceback
94+
vm.exc.Traceback = nil
95+
vm.AddTraceback(&vm.exc)
8596
vm.exit = exitException
8697
}
8798

@@ -93,6 +104,7 @@ func (vm *Vm) CheckExceptionRecover(r interface{}) {
93104
if exc, ok := r.(py.ExceptionInfo); ok {
94105
vm.old_exc = vm.exc
95106
vm.exc = exc
107+
vm.AddTraceback(&vm.exc)
96108
vm.exit = exitException
97109
fmt.Printf("*** Propagating exception: %s\n", exc.Error())
98110
} else {
@@ -636,9 +648,9 @@ func do_END_FINALLY(vm *Vm, arg int32) {
636648
w := vm.POP()
637649
u := vm.POP()
638650
// FIXME PyErr_Restore(v, w, u)
639-
vm.exc.Type = v
651+
vm.exc.Type = v.(*py.Type)
640652
vm.exc.Value = w
641-
vm.exc.Traceback = u
653+
vm.exc.Traceback = u.(*py.Traceback)
642654
vm.exit = exitReraise
643655
} else if v != py.None {
644656
vm.SetException(py.ExceptionNewf(py.SystemError, "'finally' pops bad exception %#v", v))
@@ -1315,9 +1327,9 @@ func (vm *Vm) UnwindExceptHandler(frame *py.Frame, block *py.TryBlock) {
13151327
} else {
13161328
frame.Stack = frame.Stack[:block.Level+3]
13171329
}
1318-
vm.exc.Type = vm.POP()
1330+
vm.exc.Type = vm.POP().(*py.Type)
13191331
vm.exc.Value = vm.POP()
1320-
vm.exc.Traceback = vm.POP()
1332+
vm.exc.Traceback = vm.POP().(*py.Traceback)
13211333
}
13221334

13231335
// Run the virtual machine on a Frame object
@@ -1405,21 +1417,16 @@ func RunFrame(frame *py.Frame) (res py.Object, err error) {
14051417
}
14061418
if vm.exit&(exitException|exitReraise) != 0 && (b.Type == SETUP_EXCEPT || b.Type == SETUP_FINALLY) {
14071419
fmt.Printf("*** Exception\n")
1408-
var exc, val, tb py.Object
14091420
handler := b.Handler
14101421
// This invalidates b
14111422
frame.PushBlock(EXCEPT_HANDLER, -1, vm.STACK_LEVEL())
14121423
vm.PUSH(vm.old_exc.Traceback)
14131424
vm.PUSH(vm.old_exc.Value)
1414-
if vm.old_exc.Type != nil {
1415-
vm.PUSH(vm.exc.Type)
1416-
} else {
1417-
vm.PUSH(py.None)
1418-
}
1425+
vm.PUSH(vm.exc.Type) // can be nil
14191426
// FIXME PyErr_Fetch(&exc, &val, &tb)
1420-
exc = vm.exc.Type
1421-
val = vm.exc.Value
1422-
tb = vm.exc.Traceback
1427+
exc := vm.exc.Type
1428+
val := vm.exc.Value
1429+
tb := vm.exc.Traceback
14231430
// Make the raw exception data
14241431
// available to the handler,
14251432
// so a program can emulate the
@@ -1429,9 +1436,6 @@ func RunFrame(frame *py.Frame) (res py.Object, err error) {
14291436
vm.exc.Type = exc
14301437
vm.exc.Value = val
14311438
vm.exc.Traceback = tb
1432-
if tb == nil {
1433-
tb = py.None
1434-
}
14351439
vm.PUSH(tb)
14361440
vm.PUSH(val)
14371441
vm.PUSH(exc)

0 commit comments

Comments
 (0)