Skip to content
This repository has been archived by the owner on Mar 23, 2023. It is now read-only.

Commit

Permalink
Browse files Browse the repository at this point in the history
Use traceback.format_exc for top level exceptions (#344)
This change makes it so that all Grumpy binaries import the traceback
module and then updates grumpy.FormatException (now FormatExc) to use
traceback.format_exc if the traceback module is present in SysModules.
  • Loading branch information
trotterdylan committed Jul 3, 2017
1 parent 401813e commit 5856d13
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 42 deletions.
4 changes: 2 additions & 2 deletions compiler/stmt_test.py
Expand Up @@ -452,8 +452,8 @@ def testTryFinally(self):
finally:
print 'bar'"""))
self.assertEqual(1, result[0])
# Some platforms show "exit status 1" message so don't test strict equality.
self.assertIn('foo bar\nfoo bar\nException\n', result[1])
self.assertIn('foo bar\nfoo bar\n', result[1])
self.assertIn('Exception\n', result[1])

def testWhile(self):
self.assertEqual((0, '2\n1\n'), _GrumpRun(textwrap.dedent("""\
Expand Down
39 changes: 26 additions & 13 deletions runtime/core.go
Expand Up @@ -182,17 +182,34 @@ func FloorDiv(f *Frame, v, w *Object) (*Object, *BaseException) {
return binaryOp(f, v, w, v.typ.slots.FloorDiv, v.typ.slots.RFloorDiv, w.typ.slots.RFloorDiv, "//")
}

// FormatException returns a single-line exception string for the given
// exception object, e.g. "NameError: name 'x' is not defined\n".
func FormatException(f *Frame, e *BaseException) (string, *BaseException) {
s, raised := ToStr(f, e.ToObject())
// FormatExc calls traceback.format_exc, falling back to the single line
// exception message if that fails, e.g. "NameError: name 'x' is not defined\n".
func FormatExc(f *Frame) (s string) {
exc, tb := f.ExcInfo()
defer func() {
if s == "" {
strResult, raised := ToStr(f, exc.ToObject())
if raised == nil && strResult.Value() != "" {
s = fmt.Sprintf("%s: %s\n", exc.typ.Name(), strResult.Value())
} else {
s = exc.typ.Name() + "\n"
}
}
f.RestoreExc(exc, tb)
}()
tbMod, raised := SysModules.GetItemString(f, "traceback")
if raised != nil || tbMod == nil {
return
}
formatExc, raised := GetAttr(f, tbMod, NewStr("format_exc"), nil)
if raised != nil {
return "", raised
return
}
if len(s.Value()) == 0 {
return e.typ.Name() + "\n", nil
result, raised := formatExc.Call(f, nil, nil)
if raised != nil || !result.isInstance(StrType) {
return
}
return fmt.Sprintf("%s: %s\n", e.typ.Name(), s.Value()), nil
return toStrUnsafe(result).Value()
}

// GE returns the result of operation v >= w.
Expand Down Expand Up @@ -783,11 +800,7 @@ func StartThread(callable *Object) {
f := NewRootFrame()
_, raised := callable.Call(f, nil, nil)
if raised != nil {
s, raised := FormatException(f, raised)
if raised != nil {
s = raised.String()
}
Stderr.writeString(s)
Stderr.writeString(FormatExc(f))
}
}()
}
Expand Down
32 changes: 17 additions & 15 deletions runtime/core_test.go
Expand Up @@ -327,23 +327,25 @@ func TestDelItem(t *testing.T) {
}

func TestFormatException(t *testing.T) {
f := NewRootFrame()
cases := []struct {
o *Object
want string
}{
{mustNotRaise(ExceptionType.Call(f, nil, nil)), "Exception\n"},
{mustNotRaise(AttributeErrorType.Call(f, wrapArgs(""), nil)), "AttributeError\n"},
{mustNotRaise(TypeErrorType.Call(f, wrapArgs(123), nil)), "TypeError: 123\n"},
{mustNotRaise(AttributeErrorType.Call(f, wrapArgs("hello", "there"), nil)), "AttributeError: ('hello', 'there')\n"},
fun := wrapFuncForTest(func(f *Frame, t *Type, args ...*Object) (string, *BaseException) {
e, raised := t.Call(f, args, nil)
if raised != nil {
return "", raised
}
f.Raise(e, nil, nil)
s := FormatExc(f)
f.RestoreExc(nil, nil)
return s, nil
})
cases := []invokeTestCase{
{args: wrapArgs(ExceptionType), want: NewStr("Exception\n").ToObject()},
{args: wrapArgs(AttributeErrorType, ""), want: NewStr("AttributeError\n").ToObject()},
{args: wrapArgs(TypeErrorType, 123), want: NewStr("TypeError: 123\n").ToObject()},
{args: wrapArgs(AttributeErrorType, "hello", "there"), want: NewStr("AttributeError: ('hello', 'there')\n").ToObject()},
}
for _, cas := range cases {
if !cas.o.isInstance(BaseExceptionType) {
t.Errorf("expected FormatException() input to be BaseException, got %s", cas.o.typ.Name())
} else if got, raised := FormatException(f, toBaseExceptionUnsafe(cas.o)); raised != nil {
t.Errorf("FormatException(%v) raised %v, want nil", cas.o, raised)
} else if got != cas.want {
t.Errorf("FormatException(%v) = %q, want %q", cas.o, got, cas.want)
if err := runInvokeTestCase(fun, &cas); err != "" {
t.Error(err)
}
}
}
Expand Down
10 changes: 4 additions & 6 deletions runtime/module.go
Expand Up @@ -288,19 +288,17 @@ func RunMain(code *Code) int {
m := newModule("__main__", code.filename)
m.state = moduleStateInitializing
f := NewRootFrame()
f.code = code
f.globals = m.Dict()
if raised := SysModules.SetItemString(f, "__main__", m.ToObject()); raised != nil {
Stderr.writeString(raised.String())
}
_, e := code.Eval(f, m.Dict(), nil, nil)
_, e := code.fn(f, nil)
if e == nil {
return 0
}
if !e.isInstance(SystemExitType) {
s, raised := FormatException(f, e)
if raised != nil {
s = e.String()
}
Stderr.writeString(s)
Stderr.writeString(FormatExc(f))
return 1
}
f.RestoreExc(nil, nil)
Expand Down
6 changes: 1 addition & 5 deletions runtime/weakref.go
Expand Up @@ -189,11 +189,7 @@ func weakRefFinalizeReferent(o *Object) {
for i := numCallbacks - 1; i >= 0; i-- {
f := NewRootFrame()
if _, raised := callbacks[i].Call(f, Args{r.ToObject()}, nil); raised != nil {
s, raised := FormatException(f, raised)
if raised != nil {
s = raised.String()
}
Stderr.writeString(s)
Stderr.writeString(FormatExc(f))
}
}
}
13 changes: 12 additions & 1 deletion tools/grumpc
Expand Up @@ -69,6 +69,13 @@ def main(args):
mod_block = block.ModuleBlock(importer, full_package_name, args.script,
py_contents, future_features)
mod_block.add_native_import('grumpy')

# Import the traceback module in __main__ so that it's present in sys.modules.
traceback_package = None
has_main = args.modname == '__main__'
if has_main:
traceback_package = mod_block.add_import('traceback')

visitor = stmt.StatementVisitor(mod_block, future_node)
# Indent so that the module body is aligned with the goto labels.
with visitor.writer.indent_block():
Expand All @@ -79,7 +86,6 @@ def main(args):
return 2

imports = dict(mod_block.imports)
has_main = args.modname == '__main__'
if has_main:
imports['os'] = block.Package('os')

Expand All @@ -93,6 +99,11 @@ def main(args):
writer.write('func initModule(πF *πg.Frame, '
'_ []*πg.Object) (*πg.Object, *πg.BaseException) {')
with writer.indent_block():
if traceback_package:
writer.write_tmpl(textwrap.dedent("""\
if _, e := πg.ImportModule(πF, "traceback", []*πg.Code{$tb.Code}); e != nil {
\treturn nil, e
}"""), tb=traceback_package.alias)
for s in sorted(mod_block.strings):
writer.write('ß{} := πg.InternStr({})'.format(s, util.go_str(s)))
writer.write_temp_decls(mod_block)
Expand Down

0 comments on commit 5856d13

Please sign in to comment.