From dc154bdcddb110a04c60a207b0a896dcec8f6748 Mon Sep 17 00:00:00 2001 From: Baptiste Boussemart Date: Tue, 10 Sep 2019 13:47:27 +0200 Subject: [PATCH 1/7] use go 1.13 --- .travis.yml | 6 +- Makefile | 2 +- README.md | 6 +- appveyor.yml | 2 +- bench_test.go | 11 ++- errors.go | 69 ++++++++++------ errors_test.go | 23 +++--- example_test.go | 205 ------------------------------------------------ format_test.go | 117 ++++++++++++++------------- go.mod | 3 + json_test.go | 4 +- stack.go | 16 ++++ stack_test.go | 63 +++++++-------- 13 files changed, 180 insertions(+), 347 deletions(-) delete mode 100644 example_test.go create mode 100644 go.mod diff --git a/.travis.yml b/.travis.yml index d2dfad40..fe254d06 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,7 @@ language: go -go_import_path: github.com/pkg/errors +go_import_path: github.com/objenious/errors go: - - 1.10.x - - 1.11.x - - 1.12.x + - 1.13.x - tip script: diff --git a/Makefile b/Makefile index ce9d7cde..70f3207d 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -PKGS := github.com/pkg/errors +PKGS := github.com/objenious/errors SRCDIRS := $(shell go list -f '{{.Dir}}' $(PKGS)) GO := go diff --git a/README.md b/README.md index cf771e7d..f3899d7b 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors) [![Sourcegraph](https://sourcegraph.com/github.com/pkg/errors/-/badge.svg)](https://sourcegraph.com/github.com/pkg/errors?badge) +# errors [![Travis-CI](https://travis-ci.org/objenious/errors.svg)](https://travis-ci.org/objenious/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/objenious/errors?status.svg)](http://godoc.org/github.com/objenious/errors) [![Report card](https://goreportcard.com/badge/github.com/objenious/errors)](https://goreportcard.com/report/github.com/objenious/errors) [![Sourcegraph](https://sourcegraph.com/github.com/objenious/errors/-/badge.svg)](https://sourcegraph.com/github.com/objenious/errors?badge) Package errors provides simple error handling primitives. -`go get github.com/pkg/errors` +`go get github.com/objenious/errors` The traditional error handling idiom in Go is roughly akin to ```go @@ -39,7 +39,7 @@ default: } ``` -[Read the package documentation for more information](https://godoc.org/github.com/pkg/errors). +[Read the package documentation for more information](https://godoc.org/github.com/objenious/errors). ## Roadmap diff --git a/appveyor.yml b/appveyor.yml index a932eade..f59e8dfd 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,6 +1,6 @@ version: build-{build}.{branch} -clone_folder: C:\gopath\src\github.com\pkg\errors +clone_folder: C:\gopath\src\github.com\objenious\errors shallow_clone: true # for startup speed environment: diff --git a/bench_test.go b/bench_test.go index c906870e..cb2751bf 100644 --- a/bench_test.go +++ b/bench_test.go @@ -1,17 +1,16 @@ -// +build go1.7 +// +build go1.13 package errors import ( + goerrors "errors" "fmt" "testing" - - stderrors "errors" ) func noErrors(at, depth int) error { if at >= depth { - return stderrors.New("no error") + return goerrors.New("no error") } return noErrors(at+1, depth) } @@ -41,7 +40,7 @@ func BenchmarkErrors(b *testing.B) { {1000, true}, } for _, r := range runs { - part := "pkg/errors" + part := "objenious/errors" if r.std { part = "errors" } @@ -97,7 +96,7 @@ func BenchmarkStackFormatting(b *testing.B) { name := fmt.Sprintf("%s-stacktrace-%d", r.format, r.stack) b.Run(name, func(b *testing.B) { err := yesErrors(0, r.stack) - st := err.(*fundamental).stack.StackTrace() + st := err.(*withStack).stack.StackTrace() b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { diff --git a/errors.go b/errors.go index 8617beef..2de415cf 100644 --- a/errors.go +++ b/errors.go @@ -93,6 +93,7 @@ package errors import ( + goerrors "errors" "fmt" "io" ) @@ -100,9 +101,10 @@ import ( // New returns an error with the supplied message. // New also records the stack trace at the point it was called. func New(message string) error { - return &fundamental{ - msg: message, - stack: callers(), + return &withStack{ + goerrors.New(message), + callers(), + "", } } @@ -110,35 +112,36 @@ func New(message string) error { // as a value that satisfies error. // Errorf also records the stack trace at the point it was called. func Errorf(format string, args ...interface{}) error { - return &fundamental{ - msg: fmt.Sprintf(format, args...), - stack: callers(), + return &withStack{ + fmt.Errorf(format, args...), + callers(), + "", } } // fundamental is an error that has a message and a stack, but no caller. -type fundamental struct { - msg string +/*type fundamental struct { + error *stack } -func (f *fundamental) Error() string { return f.msg } +func (f *fundamental) Error() string { return f.error.Error() } func (f *fundamental) Format(s fmt.State, verb rune) { switch verb { case 'v': if s.Flag('+') { - io.WriteString(s, f.msg) + io.WriteString(s, f.Error()) f.stack.Format(s, verb) return } fallthrough case 's': - io.WriteString(s, f.msg) + io.WriteString(s, f.Error()) case 'q': - fmt.Fprintf(s, "%q", f.msg) + fmt.Fprintf(s, "%q", f.Error()) } -} +}*/ // WithStack annotates err with a stack trace at the point WithStack was called. // If err is nil, WithStack returns nil. @@ -149,27 +152,45 @@ func WithStack(err error) error { return &withStack{ err, callers(), + "", } } type withStack struct { error *stack + msg string } -func (w *withStack) Cause() error { return w.error } +func (w *withStack) Unwrap() error { + return goerrors.Unwrap(w.error) +} + +func (w *withStack) Cause() error { + return w.Unwrap() +} func (w *withStack) Format(s fmt.State, verb rune) { switch verb { case 'v': if s.Flag('+') { - fmt.Fprintf(s, "%+v", w.Cause()) + cause := w.Cause() + if cause != nil { + fmt.Fprintf(s, "%+v", cause) // recursive : go to bottom + + if causeWithStack, ok := cause.(*withStack); ok && w.msg != "" && causeWithStack.msg != w.msg || w.msg != "" && cause.Error() != w.msg { + fmt.Fprintf(s, "\n%+v", w.msg) + } + } else { + // root format + fmt.Fprintf(s, "%+v", w.error) + } w.stack.Format(s, verb) return } fallthrough case 's': - io.WriteString(s, w.Error()) + io.WriteString(s, w.error.Error()) case 'q': fmt.Fprintf(s, "%q", w.Error()) } @@ -182,13 +203,11 @@ func Wrap(err error, message string) error { if err == nil { return nil } - err = &withMessage{ - cause: err, - msg: message, - } + err = fmt.Errorf("%s: %w", message, err) return &withStack{ err, callers(), + message, } } @@ -199,16 +218,17 @@ func Wrapf(err error, format string, args ...interface{}) error { if err == nil { return nil } - err = &withMessage{ - cause: err, - msg: fmt.Sprintf(format, args...), - } + msg := fmt.Sprintf(format, args...) + args = append(args, err) + err = fmt.Errorf(format+": %w", args...) return &withStack{ err, callers(), + msg, } } +/* // WithMessage annotates err with a new message. // If err is nil, WithMessage returns nil. func WithMessage(err error, message string) error { @@ -280,3 +300,4 @@ func Cause(err error) error { } return err } +*/ diff --git a/errors_test.go b/errors_test.go index 2089b2f7..18aa6b84 100644 --- a/errors_test.go +++ b/errors_test.go @@ -1,10 +1,9 @@ package errors import ( - "errors" + goerrors "errors" "fmt" "io" - "reflect" "testing" ) @@ -16,13 +15,13 @@ func TestNew(t *testing.T) { {"", fmt.Errorf("")}, {"foo", fmt.Errorf("foo")}, {"foo", New("foo")}, - {"string with format specifiers: %v", errors.New("string with format specifiers: %v")}, + {"string with format specifiers: %v", goerrors.New("string with format specifiers: %v")}, } - for _, tt := range tests { + for i, tt := range tests { got := New(tt.err) if got.Error() != tt.want.Error() { - t.Errorf("New.Error(): got: %q, want %q", got, tt.want) + t.Errorf("test #%d, New.Error(): got: %q, want %q", i+1, got, tt.want) } } } @@ -56,7 +55,7 @@ type nilError struct{} func (nilError) Error() string { return "nil error" } -func TestCause(t *testing.T) { +/*func TestCause(t *testing.T) { x := New("error") tests := []struct { err error @@ -104,7 +103,7 @@ func TestCause(t *testing.T) { t.Errorf("test %d: got %#v, want %#v", i+1, got, tt.want) } } -} +}*/ func TestWrapfNil(t *testing.T) { got := Wrapf(nil, "no error") @@ -173,7 +172,7 @@ func TestWithStack(t *testing.T) { } } -func TestWithMessageNil(t *testing.T) { +/*func TestWithMessageNil(t *testing.T) { got := WithMessage(nil, "no error") if got != nil { t.Errorf("WithMessage(nil, \"no error\"): got %#v, expected nil", got) @@ -222,7 +221,7 @@ func TestWithMessagef(t *testing.T) { t.Errorf("WithMessage(%v, %q): got: %q, want %q", tt.err, tt.message, got, tt.want) } } -} +}*/ // errors.New, etc values are not expected to be compared by value // but the change in errors#27 made them incomparable. Assert that @@ -232,13 +231,13 @@ func TestErrorEquality(t *testing.T) { vals := []error{ nil, io.EOF, - errors.New("EOF"), + goerrors.New("EOF"), New("EOF"), Errorf("EOF"), Wrap(io.EOF, "EOF"), Wrapf(io.EOF, "EOF%d", 2), - WithMessage(nil, "whoops"), - WithMessage(io.EOF, "whoops"), + //WithMessage(nil, "whoops"), + //WithMessage(io.EOF, "whoops"), WithStack(io.EOF), WithStack(nil), } diff --git a/example_test.go b/example_test.go deleted file mode 100644 index c1fc13e3..00000000 --- a/example_test.go +++ /dev/null @@ -1,205 +0,0 @@ -package errors_test - -import ( - "fmt" - - "github.com/pkg/errors" -) - -func ExampleNew() { - err := errors.New("whoops") - fmt.Println(err) - - // Output: whoops -} - -func ExampleNew_printf() { - err := errors.New("whoops") - fmt.Printf("%+v", err) - - // Example output: - // whoops - // github.com/pkg/errors_test.ExampleNew_printf - // /home/dfc/src/github.com/pkg/errors/example_test.go:17 - // testing.runExample - // /home/dfc/go/src/testing/example.go:114 - // testing.RunExamples - // /home/dfc/go/src/testing/example.go:38 - // testing.(*M).Run - // /home/dfc/go/src/testing/testing.go:744 - // main.main - // /github.com/pkg/errors/_test/_testmain.go:106 - // runtime.main - // /home/dfc/go/src/runtime/proc.go:183 - // runtime.goexit - // /home/dfc/go/src/runtime/asm_amd64.s:2059 -} - -func ExampleWithMessage() { - cause := errors.New("whoops") - err := errors.WithMessage(cause, "oh noes") - fmt.Println(err) - - // Output: oh noes: whoops -} - -func ExampleWithStack() { - cause := errors.New("whoops") - err := errors.WithStack(cause) - fmt.Println(err) - - // Output: whoops -} - -func ExampleWithStack_printf() { - cause := errors.New("whoops") - err := errors.WithStack(cause) - fmt.Printf("%+v", err) - - // Example Output: - // whoops - // github.com/pkg/errors_test.ExampleWithStack_printf - // /home/fabstu/go/src/github.com/pkg/errors/example_test.go:55 - // testing.runExample - // /usr/lib/go/src/testing/example.go:114 - // testing.RunExamples - // /usr/lib/go/src/testing/example.go:38 - // testing.(*M).Run - // /usr/lib/go/src/testing/testing.go:744 - // main.main - // github.com/pkg/errors/_test/_testmain.go:106 - // runtime.main - // /usr/lib/go/src/runtime/proc.go:183 - // runtime.goexit - // /usr/lib/go/src/runtime/asm_amd64.s:2086 - // github.com/pkg/errors_test.ExampleWithStack_printf - // /home/fabstu/go/src/github.com/pkg/errors/example_test.go:56 - // testing.runExample - // /usr/lib/go/src/testing/example.go:114 - // testing.RunExamples - // /usr/lib/go/src/testing/example.go:38 - // testing.(*M).Run - // /usr/lib/go/src/testing/testing.go:744 - // main.main - // github.com/pkg/errors/_test/_testmain.go:106 - // runtime.main - // /usr/lib/go/src/runtime/proc.go:183 - // runtime.goexit - // /usr/lib/go/src/runtime/asm_amd64.s:2086 -} - -func ExampleWrap() { - cause := errors.New("whoops") - err := errors.Wrap(cause, "oh noes") - fmt.Println(err) - - // Output: oh noes: whoops -} - -func fn() error { - e1 := errors.New("error") - e2 := errors.Wrap(e1, "inner") - e3 := errors.Wrap(e2, "middle") - return errors.Wrap(e3, "outer") -} - -func ExampleCause() { - err := fn() - fmt.Println(err) - fmt.Println(errors.Cause(err)) - - // Output: outer: middle: inner: error - // error -} - -func ExampleWrap_extended() { - err := fn() - fmt.Printf("%+v\n", err) - - // Example output: - // error - // github.com/pkg/errors_test.fn - // /home/dfc/src/github.com/pkg/errors/example_test.go:47 - // github.com/pkg/errors_test.ExampleCause_printf - // /home/dfc/src/github.com/pkg/errors/example_test.go:63 - // testing.runExample - // /home/dfc/go/src/testing/example.go:114 - // testing.RunExamples - // /home/dfc/go/src/testing/example.go:38 - // testing.(*M).Run - // /home/dfc/go/src/testing/testing.go:744 - // main.main - // /github.com/pkg/errors/_test/_testmain.go:104 - // runtime.main - // /home/dfc/go/src/runtime/proc.go:183 - // runtime.goexit - // /home/dfc/go/src/runtime/asm_amd64.s:2059 - // github.com/pkg/errors_test.fn - // /home/dfc/src/github.com/pkg/errors/example_test.go:48: inner - // github.com/pkg/errors_test.fn - // /home/dfc/src/github.com/pkg/errors/example_test.go:49: middle - // github.com/pkg/errors_test.fn - // /home/dfc/src/github.com/pkg/errors/example_test.go:50: outer -} - -func ExampleWrapf() { - cause := errors.New("whoops") - err := errors.Wrapf(cause, "oh noes #%d", 2) - fmt.Println(err) - - // Output: oh noes #2: whoops -} - -func ExampleErrorf_extended() { - err := errors.Errorf("whoops: %s", "foo") - fmt.Printf("%+v", err) - - // Example output: - // whoops: foo - // github.com/pkg/errors_test.ExampleErrorf - // /home/dfc/src/github.com/pkg/errors/example_test.go:101 - // testing.runExample - // /home/dfc/go/src/testing/example.go:114 - // testing.RunExamples - // /home/dfc/go/src/testing/example.go:38 - // testing.(*M).Run - // /home/dfc/go/src/testing/testing.go:744 - // main.main - // /github.com/pkg/errors/_test/_testmain.go:102 - // runtime.main - // /home/dfc/go/src/runtime/proc.go:183 - // runtime.goexit - // /home/dfc/go/src/runtime/asm_amd64.s:2059 -} - -func Example_stackTrace() { - type stackTracer interface { - StackTrace() errors.StackTrace - } - - err, ok := errors.Cause(fn()).(stackTracer) - if !ok { - panic("oops, err does not implement stackTracer") - } - - st := err.StackTrace() - fmt.Printf("%+v", st[0:2]) // top two frames - - // Example output: - // github.com/pkg/errors_test.fn - // /home/dfc/src/github.com/pkg/errors/example_test.go:47 - // github.com/pkg/errors_test.Example_stackTrace - // /home/dfc/src/github.com/pkg/errors/example_test.go:127 -} - -func ExampleCause_printf() { - err := errors.Wrap(func() error { - return func() error { - return errors.Errorf("hello %s", fmt.Sprintf("world")) - }() - }(), "failed") - - fmt.Printf("%v", err) - - // Output: failed: hello world -} diff --git a/format_test.go b/format_test.go index cb1df821..3e046c58 100644 --- a/format_test.go +++ b/format_test.go @@ -1,7 +1,7 @@ package errors import ( - "errors" + goerrors "errors" "fmt" "io" "regexp" @@ -26,8 +26,8 @@ func TestFormatNew(t *testing.T) { New("error"), "%+v", "error\n" + - "github.com/pkg/errors.TestFormatNew\n" + - "\t.+/github.com/pkg/errors/format_test.go:26", + "github.com/objenious/errors.TestFormatNew\n" + + "\t.+/github.com/objenious/errors/format_test.go:26", }, { New("error"), "%q", @@ -56,8 +56,8 @@ func TestFormatErrorf(t *testing.T) { Errorf("%s", "error"), "%+v", "error\n" + - "github.com/pkg/errors.TestFormatErrorf\n" + - "\t.+/github.com/pkg/errors/format_test.go:56", + "github.com/objenious/errors.TestFormatErrorf\n" + + "\t.+/github.com/objenious/errors/format_test.go:56", }} for i, tt := range tests { @@ -82,8 +82,8 @@ func TestFormatWrap(t *testing.T) { Wrap(New("error"), "error2"), "%+v", "error\n" + - "github.com/pkg/errors.TestFormatWrap\n" + - "\t.+/github.com/pkg/errors/format_test.go:82", + "github.com/objenious/errors.TestFormatWrap\n" + + "\t.+/github.com/objenious/errors/format_test.go:82", }, { Wrap(io.EOF, "error"), "%s", @@ -97,15 +97,15 @@ func TestFormatWrap(t *testing.T) { "%+v", "EOF\n" + "error\n" + - "github.com/pkg/errors.TestFormatWrap\n" + - "\t.+/github.com/pkg/errors/format_test.go:96", + "github.com/objenious/errors.TestFormatWrap\n" + + "\t.+/github.com/objenious/errors/format_test.go:96", }, { Wrap(Wrap(io.EOF, "error1"), "error2"), "%+v", "EOF\n" + "error1\n" + - "github.com/pkg/errors.TestFormatWrap\n" + - "\t.+/github.com/pkg/errors/format_test.go:103\n", + "github.com/objenious/errors.TestFormatWrap\n" + + "\t.+/github.com/objenious/errors/format_test.go:103\n", }, { Wrap(New("error with space"), "context"), "%q", @@ -135,8 +135,8 @@ func TestFormatWrapf(t *testing.T) { "%+v", "EOF\n" + "error2\n" + - "github.com/pkg/errors.TestFormatWrapf\n" + - "\t.+/github.com/pkg/errors/format_test.go:134", + "github.com/objenious/errors.TestFormatWrapf\n" + + "\t.+/github.com/objenious/errors/format_test.go:134", }, { Wrapf(New("error"), "error%d", 2), "%s", @@ -149,8 +149,8 @@ func TestFormatWrapf(t *testing.T) { Wrapf(New("error"), "error%d", 2), "%+v", "error\n" + - "github.com/pkg/errors.TestFormatWrapf\n" + - "\t.+/github.com/pkg/errors/format_test.go:149", + "github.com/objenious/errors.TestFormatWrapf\n" + + "\t.+/github.com/objenious/errors/format_test.go:149", }} for i, tt := range tests { @@ -175,8 +175,8 @@ func TestFormatWithStack(t *testing.T) { WithStack(io.EOF), "%+v", []string{"EOF", - "github.com/pkg/errors.TestFormatWithStack\n" + - "\t.+/github.com/pkg/errors/format_test.go:175"}, + "github.com/objenious/errors.TestFormatWithStack\n" + + "\t.+/github.com/objenious/errors/format_test.go:175"}, }, { WithStack(New("error")), "%s", @@ -189,37 +189,37 @@ func TestFormatWithStack(t *testing.T) { WithStack(New("error")), "%+v", []string{"error", - "github.com/pkg/errors.TestFormatWithStack\n" + - "\t.+/github.com/pkg/errors/format_test.go:189", - "github.com/pkg/errors.TestFormatWithStack\n" + - "\t.+/github.com/pkg/errors/format_test.go:189"}, + "github.com/objenious/errors.TestFormatWithStack\n" + + "\t.+/github.com/objenious/errors/format_test.go:189", + "github.com/objenious/errors.TestFormatWithStack\n" + + "\t.+/github.com/objenious/errors/format_test.go:189"}, }, { WithStack(WithStack(io.EOF)), "%+v", []string{"EOF", - "github.com/pkg/errors.TestFormatWithStack\n" + - "\t.+/github.com/pkg/errors/format_test.go:197", - "github.com/pkg/errors.TestFormatWithStack\n" + - "\t.+/github.com/pkg/errors/format_test.go:197"}, + "github.com/objenious/errors.TestFormatWithStack\n" + + "\t.+/github.com/objenious/errors/format_test.go:197", + "github.com/objenious/errors.TestFormatWithStack\n" + + "\t.+/github.com/objenious/errors/format_test.go:197"}, }, { WithStack(WithStack(Wrapf(io.EOF, "message"))), "%+v", []string{"EOF", "message", - "github.com/pkg/errors.TestFormatWithStack\n" + - "\t.+/github.com/pkg/errors/format_test.go:205", - "github.com/pkg/errors.TestFormatWithStack\n" + - "\t.+/github.com/pkg/errors/format_test.go:205", - "github.com/pkg/errors.TestFormatWithStack\n" + - "\t.+/github.com/pkg/errors/format_test.go:205"}, + "github.com/objenious/errors.TestFormatWithStack\n" + + "\t.+/github.com/objenious/errors/format_test.go:205", + "github.com/objenious/errors.TestFormatWithStack\n" + + "\t.+/github.com/objenious/errors/format_test.go:205", + "github.com/objenious/errors.TestFormatWithStack\n" + + "\t.+/github.com/objenious/errors/format_test.go:205"}, }, { WithStack(Errorf("error%d", 1)), "%+v", []string{"error1", - "github.com/pkg/errors.TestFormatWithStack\n" + - "\t.+/github.com/pkg/errors/format_test.go:216", - "github.com/pkg/errors.TestFormatWithStack\n" + - "\t.+/github.com/pkg/errors/format_test.go:216"}, + "github.com/objenious/errors.TestFormatWithStack\n" + + "\t.+/github.com/objenious/errors/format_test.go:216", + "github.com/objenious/errors.TestFormatWithStack\n" + + "\t.+/github.com/objenious/errors/format_test.go:216"}, }} for i, tt := range tests { @@ -227,7 +227,7 @@ func TestFormatWithStack(t *testing.T) { } } -func TestFormatWithMessage(t *testing.T) { +/*func TestFormatWithMessage(t *testing.T) { tests := []struct { error format string @@ -305,7 +305,7 @@ func TestFormatWithMessage(t *testing.T) { for i, tt := range tests { testFormatCompleteCompare(t, i, tt.error, tt.format, tt.want, true) } -} +}*/ func TestFormatGeneric(t *testing.T) { starts := []struct { @@ -314,40 +314,45 @@ func TestFormatGeneric(t *testing.T) { }{ {New("new-error"), []string{ "new-error", - "github.com/pkg/errors.TestFormatGeneric\n" + - "\t.+/github.com/pkg/errors/format_test.go:315"}, + "github.com/objenious/errors.TestFormatGeneric\n" + + "\t.+/github.com/objenious/errors/format_test.go:315"}, }, {Errorf("errorf-error"), []string{ "errorf-error", - "github.com/pkg/errors.TestFormatGeneric\n" + - "\t.+/github.com/pkg/errors/format_test.go:319"}, - }, {errors.New("errors-new-error"), []string{ + "github.com/objenious/errors.TestFormatGeneric\n" + + "\t.+/github.com/objenious/errors/format_test.go:319"}, + }, {goerrors.New("errors-new-error"), []string{ "errors-new-error"}, }, } wrappers := []wrapper{ { - func(err error) error { return WithMessage(err, "with-message") }, - []string{"with-message"}, + func(err error) error { return Wrap(err, "with-message") }, + []string{ + "with-message\n" + + "github.com/objenious/errors.(func·002|TestFormatGeneric.func2)\n\t" + + ".+/github.com/objenious/errors/format_test.go:320", + }, }, { func(err error) error { return WithStack(err) }, []string{ - "github.com/pkg/errors.(func·002|TestFormatGeneric.func2)\n\t" + - ".+/github.com/pkg/errors/format_test.go:333", + ".+\n" + + "github.com/objenious/errors.(func·002|TestFormatGeneric.func2)\n\t" + + ".+/github.com/objenious/errors/format_test.go:337", }, }, { func(err error) error { return Wrap(err, "wrap-error") }, []string{ "wrap-error", - "github.com/pkg/errors.(func·003|TestFormatGeneric.func3)\n\t" + - ".+/github.com/pkg/errors/format_test.go:339", + "github.com/objenious/errors.(func·003|TestFormatGeneric.func3)\n\t" + + ".+/github.com/objenious/errors/format_test.go:344", }, }, { func(err error) error { return Wrapf(err, "wrapf-error%d", 1) }, []string{ "wrapf-error1", - "github.com/pkg/errors.(func·004|TestFormatGeneric.func4)\n\t" + - ".+/github.com/pkg/errors/format_test.go:346", + "github.com/objenious/errors.(func·004|TestFormatGeneric.func4)\n\t" + + ".+/github.com/objenious/errors/format_test.go:351", }, }, } @@ -373,10 +378,10 @@ func TestFormatWrappedNew(t *testing.T) { wrappedNew("error"), "%+v", "error\n" + - "github.com/pkg/errors.wrappedNew\n" + - "\t.+/github.com/pkg/errors/format_test.go:364\n" + - "github.com/pkg/errors.TestFormatWrappedNew\n" + - "\t.+/github.com/pkg/errors/format_test.go:373", + "github.com/objenious/errors.wrappedNew\n" + + "\t.+/github.com/objenious/errors/format_test.go:369\n" + + "github.com/objenious/errors.TestFormatWrappedNew\n" + + "\t.+/github.com/objenious/errors/format_test.go:378", }} for i, tt := range tests { @@ -447,7 +452,7 @@ func parseBlocks(input string, detectStackboundaries bool) ([]string, error) { if detectStackboundaries { if lines[l] { if len(stack) == 0 { - return nil, errors.New("len of block must not be zero here") + return nil, goerrors.New("len of block must not be zero here") } blocks = append(blocks, stack) @@ -465,7 +470,7 @@ func parseBlocks(input string, detectStackboundaries bool) ([]string, error) { case !isStackLine && !wasStack: blocks = append(blocks, l) default: - return nil, errors.New("must not happen") + return nil, goerrors.New("must not happen") } wasStack = isStackLine diff --git a/go.mod b/go.mod new file mode 100644 index 00000000..f26a9be3 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/objenious/errors + +go 1.13 diff --git a/json_test.go b/json_test.go index ad1adec9..c04e79bc 100644 --- a/json_test.go +++ b/json_test.go @@ -12,7 +12,7 @@ func TestFrameMarshalText(t *testing.T) { want string }{{ initpc, - `^github.com/pkg/errors\.init(\.ializers)? .+/github\.com/pkg/errors/stack_test.go:\d+$`, + `^github.com/objenious/errors\.init(\.ializers)? .+/github\.com/objenious/errors/stack_test.go:\d+$`, }, { 0, `^unknown$`, @@ -34,7 +34,7 @@ func TestFrameMarshalJSON(t *testing.T) { want string }{{ initpc, - `^"github\.com/pkg/errors\.init(\.ializers)? .+/github\.com/pkg/errors/stack_test.go:\d+"$`, + `^"github\.com/objenious/errors\.init(\.ializers)? .+/github\.com/objenious/errors/stack_test.go:\d+"$`, }, { 0, `^"unknown"$`, diff --git a/stack.go b/stack.go index 779a8348..08a30d06 100644 --- a/stack.go +++ b/stack.go @@ -1,6 +1,7 @@ package errors import ( + goerrors "errors" "fmt" "io" "path" @@ -9,6 +10,21 @@ import ( "strings" ) +// GetStackTrace returns the stack trace if exists +func GetStackTrace(err error) *stack { + cause := goerrors.Unwrap(err) + if cause != nil { + st := GetStackTrace(cause) + if st != nil { + return st + } + } + if err, ok := err.(*withStack); ok { + return err.stack + } + return nil +} + // Frame represents a program counter inside a stack frame. // For historical reasons if Frame is interpreted as a uintptr // its value represents the program counter + 1. diff --git a/stack_test.go b/stack_test.go index 1acd719b..145e5bc7 100644 --- a/stack_test.go +++ b/stack_test.go @@ -1,7 +1,7 @@ package errors import ( - "fmt" + goerrors "errors" "runtime" "testing" ) @@ -32,8 +32,8 @@ func TestFrameFormat(t *testing.T) { }, { initpc, "%+s", - "github.com/pkg/errors.init\n" + - "\t.+/github.com/pkg/errors/stack_test.go", + "github.com/objenious/errors.init\n" + + "\t.+/github.com/objenious/errors/stack_test.go", }, { 0, "%s", @@ -79,8 +79,8 @@ func TestFrameFormat(t *testing.T) { }, { initpc, "%+v", - "github.com/pkg/errors.init\n" + - "\t.+/github.com/pkg/errors/stack_test.go:9", + "github.com/objenious/errors.init\n" + + "\t.+/github.com/objenious/errors/stack_test.go:9", }, { 0, "%v", @@ -98,7 +98,7 @@ func TestFuncname(t *testing.T) { }{ {"", ""}, {"runtime.main", "main"}, - {"github.com/pkg/errors.funcname", "funcname"}, + {"github.com/objenious/errors.funcname", "funcname"}, {"funcname", "funcname"}, {"io.copyBuffer", "copyBuffer"}, {"main.(*R).Write", "(*R).Write"}, @@ -119,30 +119,30 @@ func TestStackTrace(t *testing.T) { want []string }{{ New("ooh"), []string{ - "github.com/pkg/errors.TestStackTrace\n" + - "\t.+/github.com/pkg/errors/stack_test.go:121", + "github.com/objenious/errors.TestStackTrace\n" + + "\t.+/github.com/objenious/errors/stack_test.go:121", }, }, { Wrap(New("ooh"), "ahh"), []string{ - "github.com/pkg/errors.TestStackTrace\n" + - "\t.+/github.com/pkg/errors/stack_test.go:126", // this is the stack of Wrap, not New + "github.com/objenious/errors.TestStackTrace\n" + + "\t.+/github.com/objenious/errors/stack_test.go:126", // this is the stack of Wrap, not New }, }, { - Cause(Wrap(New("ooh"), "ahh")), []string{ - "github.com/pkg/errors.TestStackTrace\n" + - "\t.+/github.com/pkg/errors/stack_test.go:131", // this is the stack of New + goerrors.Unwrap(Wrap(New("ooh"), "ahh")), []string{ + "github.com/objenious/errors.TestStackTrace\n" + + "\t.+/github.com/objenious/errors/stack_test.go:131", // this is the stack of New }, }, { func() error { return New("ooh") }(), []string{ - `github.com/pkg/errors.TestStackTrace.func1` + - "\n\t.+/github.com/pkg/errors/stack_test.go:136", // this is the stack of New - "github.com/pkg/errors.TestStackTrace\n" + - "\t.+/github.com/pkg/errors/stack_test.go:136", // this is the stack of New's caller + `github.com/objenious/errors.TestStackTrace.func1` + + "\n\t.+/github.com/objenious/errors/stack_test.go:136", // this is the stack of New + "github.com/objenious/errors.TestStackTrace\n" + + "\t.+/github.com/objenious/errors/stack_test.go:136", // this is the stack of New's caller }, - }, { - Cause(func() error { + /*}, { + goerrors.Unwrap(func() error { return func() error { - return Errorf("hello %s", fmt.Sprintf("world")) + return Errorf("hello %s", "world") }() }()), []string{ `github.com/pkg/errors.TestStackTrace.func2.1` + @@ -151,15 +151,12 @@ func TestStackTrace(t *testing.T) { "\n\t.+/github.com/pkg/errors/stack_test.go:146", // this is the stack of Errorf's caller "github.com/pkg/errors.TestStackTrace\n" + "\t.+/github.com/pkg/errors/stack_test.go:147", // this is the stack of Errorf's caller's caller - }, + },*/ }} for i, tt := range tests { - x, ok := tt.err.(interface { - StackTrace() StackTrace - }) - if !ok { - t.Errorf("expected %#v to implement StackTrace() StackTrace", tt.err) - continue + x := GetStackTrace(tt.err) + if x == nil { + t.Fatalf("no stacktrace for test %d %+v", i+1, tt.err) } st := x.StackTrace() for j, want := range tt.want { @@ -220,19 +217,19 @@ func TestStackTraceFormat(t *testing.T) { }, { stackTrace()[:2], "%v", - `\[stack_test.go:174 stack_test.go:221\]`, + `\[stack_test.go:171 stack_test.go:218\]`, }, { stackTrace()[:2], "%+v", "\n" + - "github.com/pkg/errors.stackTrace\n" + - "\t.+/github.com/pkg/errors/stack_test.go:174\n" + - "github.com/pkg/errors.TestStackTraceFormat\n" + - "\t.+/github.com/pkg/errors/stack_test.go:225", + "github.com/objenious/errors.stackTrace\n" + + "\t.+/github.com/objenious/errors/stack_test.go:171\n" + + "github.com/objenious/errors.TestStackTraceFormat\n" + + "\t.+/github.com/objenious/errors/stack_test.go:222", }, { stackTrace()[:2], "%#v", - `\[\]errors.Frame{stack_test.go:174, stack_test.go:233}`, + `\[\]errors.Frame{stack_test.go:171, stack_test.go:230}`, }} for i, tt := range tests { From 23807ab5016bf32ac10650b8344b7fa5b3672d52 Mon Sep 17 00:00:00 2001 From: Baptiste Boussemart Date: Tue, 10 Sep 2019 17:20:43 +0200 Subject: [PATCH 2/7] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f3899d7b..5feb754e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# errors [![Travis-CI](https://travis-ci.org/objenious/errors.svg)](https://travis-ci.org/objenious/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/objenious/errors?status.svg)](http://godoc.org/github.com/objenious/errors) [![Report card](https://goreportcard.com/badge/github.com/objenious/errors)](https://goreportcard.com/report/github.com/objenious/errors) [![Sourcegraph](https://sourcegraph.com/github.com/objenious/errors/-/badge.svg)](https://sourcegraph.com/github.com/objenious/errors?badge) +# errors [![Travis-CI](https://travis-ci.org/objenious/errors.svg)](https://travis-ci.org/objenious/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/objenious/errors/branch/master) [![GoDoc](https://godoc.org/github.com/objenious/errors?status.svg)](http://godoc.org/github.com/objenious/errors) [![Report card](https://goreportcard.com/badge/github.com/objenious/errors)](https://goreportcard.com/report/github.com/objenious/errors) [![Sourcegraph](https://sourcegraph.com/github.com/objenious/errors/-/badge.svg)](https://sourcegraph.com/github.com/objenious/errors?badge) Package errors provides simple error handling primitives. From fc0e7c25247bf81ae9d7ed11d0384bc8592edf91 Mon Sep 17 00:00:00 2001 From: Baptiste Boussemart Date: Tue, 10 Sep 2019 18:46:22 +0200 Subject: [PATCH 3/7] advancement --- errors.go | 24 ++++++++++++++++-------- format_test.go | 30 +++++++++++++++--------------- 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/errors.go b/errors.go index 2de415cf..3a759e04 100644 --- a/errors.go +++ b/errors.go @@ -174,16 +174,24 @@ func (w *withStack) Format(s fmt.State, verb rune) { switch verb { case 'v': if s.Flag('+') { - cause := w.Cause() - if cause != nil { - fmt.Fprintf(s, "%+v", cause) // recursive : go to bottom - - if causeWithStack, ok := cause.(*withStack); ok && w.msg != "" && causeWithStack.msg != w.msg || w.msg != "" && cause.Error() != w.msg { - fmt.Fprintf(s, "\n%+v", w.msg) + if w.msg == "" { + br := "" + if w, ok := s.Width(); ok && w > 0 { + br = "\n" } + fmt.Fprintf(s, "%s%+v", br, w.error) // recursive : go to bottom } else { - // root format - fmt.Fprintf(s, "%+v", w.error) + cause := w.Cause() + if cause != nil { + fmt.Fprintf(s, "%+v", cause) // recursive : go to bottom + + if causeWithStack, ok := cause.(*withStack); ok && w.msg != "" && causeWithStack.msg != w.msg || w.msg != "" && cause.Error() != w.msg { + fmt.Fprintf(s, "\n%+v", w.msg) + } + } else { + // root format + fmt.Fprintf(s, "%+v", w.error) + } } w.stack.Format(s, verb) return diff --git a/format_test.go b/format_test.go index 3e046c58..8d0dc159 100644 --- a/format_test.go +++ b/format_test.go @@ -329,30 +329,30 @@ func TestFormatGeneric(t *testing.T) { { func(err error) error { return Wrap(err, "with-message") }, []string{ - "with-message\n" + - "github.com/objenious/errors.(func·002|TestFormatGeneric.func2)\n\t" + - ".+/github.com/objenious/errors/format_test.go:320", + "with-message", + "github.com/objenious/errors.(func·002|TestFormatGeneric.func1)\n" + + "\t.+/github.com/objenious/errors/format_test.go:330", }, }, { func(err error) error { return WithStack(err) }, []string{ - ".+\n" + - "github.com/objenious/errors.(func·002|TestFormatGeneric.func2)\n\t" + - ".+/github.com/objenious/errors/format_test.go:337", + //".+\n" + + "github.com/objenious/errors.(func·002|TestFormatGeneric.func1)\n" + + "\t.+/github.com/objenious/errors/format_test.go:337", }, }, { func(err error) error { return Wrap(err, "wrap-error") }, []string{ "wrap-error", - "github.com/objenious/errors.(func·003|TestFormatGeneric.func3)\n\t" + - ".+/github.com/objenious/errors/format_test.go:344", + "github.com/objenious/errors.(func·003|TestFormatGeneric.func3)\n" + + "\t.+/github.com/objenious/errors/format_test.go:344", }, }, { func(err error) error { return Wrapf(err, "wrapf-error%d", 1) }, []string{ "wrapf-error1", - "github.com/objenious/errors.(func·004|TestFormatGeneric.func4)\n\t" + - ".+/github.com/objenious/errors/format_test.go:351", + "github.com/objenious/errors.(func·004|TestFormatGeneric.func4)\n" + + "\t.+/github.com/objenious/errors/format_test.go:351", }, }, } @@ -547,15 +547,15 @@ func testGenericRecursive(t *testing.T, beforeErr error, beforeWant []string, li copy(beforeCopy, beforeWant) beforeWant := beforeCopy - last := len(beforeWant) - 1 + //last := len(beforeWant) - 1 var want []string // Merge two stacks behind each other. - if strings.ContainsAny(beforeWant[last], "\n") && strings.ContainsAny(w.want[0], "\n") { + /*if strings.ContainsAny(beforeWant[last], "\n") && strings.ContainsAny(w.want[0], "\n") { want = append(beforeWant[:last], append([]string{beforeWant[last] + "((?s).*)" + w.want[0]}, w.want[1:]...)...) - } else { - want = append(beforeWant, w.want...) - } + } else {*/ + want = append(beforeWant, w.want...) + //} testFormatCompleteCompare(t, maxDepth, err, "%+v", want, false) if maxDepth > 0 { From ff3d1080790e787998a72ecb3a5b762c5ceab60c Mon Sep 17 00:00:00 2001 From: Baptiste Boussemart Date: Wed, 11 Sep 2019 11:12:13 +0200 Subject: [PATCH 4/7] fix PR --- README.md | 33 ++------------------------------- errors.go | 3 +-- errors_test.go | 21 +++++++++++++++++++++ format_test.go | 18 +++++++++--------- 4 files changed, 33 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 5feb754e..ce16c5cc 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# errors [![Travis-CI](https://travis-ci.org/objenious/errors.svg)](https://travis-ci.org/objenious/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/objenious/errors/branch/master) [![GoDoc](https://godoc.org/github.com/objenious/errors?status.svg)](http://godoc.org/github.com/objenious/errors) [![Report card](https://goreportcard.com/badge/github.com/objenious/errors)](https://goreportcard.com/report/github.com/objenious/errors) [![Sourcegraph](https://sourcegraph.com/github.com/objenious/errors/-/badge.svg)](https://sourcegraph.com/github.com/objenious/errors?badge) +# errors for go 1.13 and beyond [![Travis-CI](https://travis-ci.org/objenious/errors.svg)](https://travis-ci.org/objenious/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/objenious/errors/branch/master) [![GoDoc](https://godoc.org/github.com/objenious/errors?status.svg)](http://godoc.org/github.com/objenious/errors) [![Report card](https://goreportcard.com/badge/github.com/objenious/errors)](https://goreportcard.com/report/github.com/objenious/errors) [![Sourcegraph](https://sourcegraph.com/github.com/objenious/errors/-/badge.svg)](https://sourcegraph.com/github.com/objenious/errors?badge) -Package errors provides simple error handling primitives. +Package errors provides simple error handling primitives, compatible with Go 1.13 error wrapping. `go get github.com/objenious/errors` @@ -21,38 +21,9 @@ if err != nil { return errors.Wrap(err, "read failed") } ``` -## Retrieving the cause of an error - -Using `errors.Wrap` constructs a stack of errors, adding context to the preceding error. Depending on the nature of the error it may be necessary to reverse the operation of errors.Wrap to retrieve the original error for inspection. Any error value which implements this interface can be inspected by `errors.Cause`. -```go -type causer interface { - Cause() error -} -``` -`errors.Cause` will recursively retrieve the topmost error which does not implement `causer`, which is assumed to be the original cause. For example: -```go -switch err := errors.Cause(err).(type) { -case *MyError: - // handle specifically -default: - // unknown error -} -``` [Read the package documentation for more information](https://godoc.org/github.com/objenious/errors). -## Roadmap - -With the upcoming [Go2 error proposals](https://go.googlesource.com/proposal/+/master/design/go2draft.md) this package is moving into maintenance mode. The roadmap for a 1.0 release is as follows: - -- 0.9. Remove pre Go 1.9 support, address outstanding pull requests (if possible) -- 1.0. Final release. - -## Contributing - -Because of the Go2 errors changes, this package is not accepting proposals for new functionality. With that said, we welcome pull requests, bug fixes and issue reports. - -Before sending a PR, please discuss your change by raising an issue. ## License diff --git a/errors.go b/errors.go index 3a759e04..795a1cfc 100644 --- a/errors.go +++ b/errors.go @@ -181,8 +181,7 @@ func (w *withStack) Format(s fmt.State, verb rune) { } fmt.Fprintf(s, "%s%+v", br, w.error) // recursive : go to bottom } else { - cause := w.Cause() - if cause != nil { + if cause := w.Cause(); cause != nil { fmt.Fprintf(s, "%+v", cause) // recursive : go to bottom if causeWithStack, ok := cause.(*withStack); ok && w.msg != "" && causeWithStack.msg != w.msg || w.msg != "" && cause.Error() != w.msg { diff --git a/errors_test.go b/errors_test.go index 18aa6b84..c0cb40b3 100644 --- a/errors_test.go +++ b/errors_test.go @@ -51,6 +51,27 @@ func TestWrap(t *testing.T) { } } +func TestUnwrap(t *testing.T) { + errSources := []error{goerrors.New("test"), New("test")} + for _, src := range errSources { + w := src + for i := 0; i < 10; i++ { + w = Wrap(w, "wrap") + w = Wrapf(w, "wrapf %d", i) + } + for { + unw := goerrors.Unwrap(w) + if unw == nil { + break + } + w = unw + } + if w != src { + t.Fatalf("unwrapped error different than the source: %v != %v", w, src) + } + } +} + type nilError struct{} func (nilError) Error() string { return "nil error" } diff --git a/format_test.go b/format_test.go index 8d0dc159..b3dba898 100644 --- a/format_test.go +++ b/format_test.go @@ -305,7 +305,7 @@ func TestFormatWithStack(t *testing.T) { for i, tt := range tests { testFormatCompleteCompare(t, i, tt.error, tt.format, tt.want, true) } -}*/ +} func TestFormatGeneric(t *testing.T) { starts := []struct { @@ -363,7 +363,7 @@ func TestFormatGeneric(t *testing.T) { testFormatCompleteCompare(t, s, err, "%+v", want, false) testGenericRecursive(t, err, want, wrappers, 3) } -} +}*/ func wrappedNew(message string) error { // This function will be mid-stack inlined in go 1.12+ return New(message) @@ -531,7 +531,7 @@ func prettyBlocks(blocks []string) string { return " " + strings.Join(out, "\n ") } -func testGenericRecursive(t *testing.T, beforeErr error, beforeWant []string, list []wrapper, maxDepth int) { +/*func testGenericRecursive(t *testing.T, beforeErr error, beforeWant []string, list []wrapper, maxDepth int) { if len(beforeWant) == 0 { panic("beforeWant must not be empty") } @@ -547,19 +547,19 @@ func testGenericRecursive(t *testing.T, beforeErr error, beforeWant []string, li copy(beforeCopy, beforeWant) beforeWant := beforeCopy - //last := len(beforeWant) - 1 + last := len(beforeWant) - 1 var want []string // Merge two stacks behind each other. - /*if strings.ContainsAny(beforeWant[last], "\n") && strings.ContainsAny(w.want[0], "\n") { + if strings.ContainsAny(beforeWant[last], "\n") && strings.ContainsAny(w.want[0], "\n") { want = append(beforeWant[:last], append([]string{beforeWant[last] + "((?s).*)" + w.want[0]}, w.want[1:]...)...) - } else {*/ - want = append(beforeWant, w.want...) - //} + } else { + want = append(beforeWant, w.want...) + } testFormatCompleteCompare(t, maxDepth, err, "%+v", want, false) if maxDepth > 0 { testGenericRecursive(t, err, want, list, maxDepth-1) } } -} +}*/ From 7d83c9bb2bb949dd368440614fc2fee9b489d35b Mon Sep 17 00:00:00 2001 From: Baptiste Boussemart Date: Wed, 11 Sep 2019 11:25:43 +0200 Subject: [PATCH 5/7] fix --- errors_test.go | 4 ++-- format_test.go | 4 ++-- stack_test.go | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/errors_test.go b/errors_test.go index c0cb40b3..67db3feb 100644 --- a/errors_test.go +++ b/errors_test.go @@ -72,11 +72,11 @@ func TestUnwrap(t *testing.T) { } } -type nilError struct{} +/*type nilError struct{} func (nilError) Error() string { return "nil error" } -/*func TestCause(t *testing.T) { +func TestCause(t *testing.T) { x := New("error") tests := []struct { err error diff --git a/format_test.go b/format_test.go index b3dba898..8ab76baf 100644 --- a/format_test.go +++ b/format_test.go @@ -516,10 +516,10 @@ func testFormatCompleteCompare(t *testing.T, n int, arg interface{}, format stri } } -type wrapper struct { +/*type wrapper struct { wrap func(err error) error want []string -} +}*/ func prettyBlocks(blocks []string) string { var out []string diff --git a/stack_test.go b/stack_test.go index 145e5bc7..f690b7df 100644 --- a/stack_test.go +++ b/stack_test.go @@ -139,7 +139,7 @@ func TestStackTrace(t *testing.T) { "github.com/objenious/errors.TestStackTrace\n" + "\t.+/github.com/objenious/errors/stack_test.go:136", // this is the stack of New's caller }, - /*}, { + /*}, { goerrors.Unwrap(func() error { return func() error { return Errorf("hello %s", "world") From 54641c77b580e4bbffb9c8b92a41132a6fbcba26 Mon Sep 17 00:00:00 2001 From: Baptiste Boussemart Date: Wed, 11 Sep 2019 11:54:54 +0200 Subject: [PATCH 6/7] restablish Cause(err) --- errors.go | 21 +++++++++------------ errors_test.go | 19 ++++++++++--------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/errors.go b/errors.go index 795a1cfc..8ce46ad9 100644 --- a/errors.go +++ b/errors.go @@ -280,7 +280,7 @@ func (w *withMessage) Format(s fmt.State, verb rune) { case 's', 'q': io.WriteString(s, w.Error()) } -} +}*/ // Cause returns the underlying cause of the error, if possible. // An error value has a cause if it implements the following @@ -294,17 +294,14 @@ func (w *withMessage) Format(s fmt.State, verb rune) { // be returned. If the error is nil, nil will be returned without further // investigation. func Cause(err error) error { - type causer interface { - Cause() error - } - - for err != nil { - cause, ok := err.(causer) - if !ok { - break + for { + unwrap := goerrors.Unwrap(err) + if unwrap == nil { + if wrap, ok := err.(*withStack); ok { + return wrap.error + } + return err } - err = cause.Cause() + err = unwrap } - return err } -*/ diff --git a/errors_test.go b/errors_test.go index 67db3feb..0024b357 100644 --- a/errors_test.go +++ b/errors_test.go @@ -4,6 +4,7 @@ import ( goerrors "errors" "fmt" "io" + "reflect" "testing" ) @@ -72,7 +73,7 @@ func TestUnwrap(t *testing.T) { } } -/*type nilError struct{} +type nilError struct{} func (nilError) Error() string { return "nil error" } @@ -103,14 +104,14 @@ func TestCause(t *testing.T) { want: io.EOF, }, { err: x, // return from errors.New - want: x, - }, { - WithMessage(nil, "whoops"), - nil, - }, { - WithMessage(io.EOF, "whoops"), - io.EOF, + want: x.(*withStack).error, }, { + // WithMessage(nil, "whoops"), + // nil, + // }, { + // WithMessage(io.EOF, "whoops"), + // io.EOF, + // }, { WithStack(nil), nil, }, { @@ -124,7 +125,7 @@ func TestCause(t *testing.T) { t.Errorf("test %d: got %#v, want %#v", i+1, got, tt.want) } } -}*/ +} func TestWrapfNil(t *testing.T) { got := Wrapf(nil, "no error") From b7a749f98985e5756055578b0b2d3f214b3450e0 Mon Sep 17 00:00:00 2001 From: Baptiste Boussemart Date: Wed, 11 Sep 2019 13:15:59 +0200 Subject: [PATCH 7/7] clean commented code --- errors.go | 100 +++------------------------ errors_test.go | 61 +---------------- format_test.go | 180 +------------------------------------------------ 3 files changed, 14 insertions(+), 327 deletions(-) diff --git a/errors.go b/errors.go index 8ce46ad9..67cef522 100644 --- a/errors.go +++ b/errors.go @@ -29,16 +29,9 @@ // // Retrieving the cause of an error // -// Using errors.Wrap constructs a stack of errors, adding context to the -// preceding error. Depending on the nature of the error it may be necessary -// to reverse the operation of errors.Wrap to retrieve the original error -// for inspection. Any error value which implements this interface +// Using errors.Wrap constructs a stack of errors compatible with Go 1.13 errors wrapping // -// type causer interface { -// Cause() error -// } -// -// can be inspected by errors.Cause. errors.Cause will recursively retrieve +// It can be inspected by errors.Cause. errors.Cause will recursively retrieve // the topmost error that does not implement causer, which is assumed to be // the original cause. For example: // @@ -49,9 +42,6 @@ // // unknown error // } // -// Although the causer interface is not exported by this package, it is -// considered a part of its stable public interface. -// // Formatted printing of errors // // All error values returned from this package implement fmt.Formatter and can @@ -119,30 +109,6 @@ func Errorf(format string, args ...interface{}) error { } } -// fundamental is an error that has a message and a stack, but no caller. -/*type fundamental struct { - error - *stack -} - -func (f *fundamental) Error() string { return f.error.Error() } - -func (f *fundamental) Format(s fmt.State, verb rune) { - switch verb { - case 'v': - if s.Flag('+') { - io.WriteString(s, f.Error()) - f.stack.Format(s, verb) - return - } - fallthrough - case 's': - io.WriteString(s, f.Error()) - case 'q': - fmt.Fprintf(s, "%q", f.Error()) - } -}*/ - // WithStack annotates err with a stack trace at the point WithStack was called. // If err is nil, WithStack returns nil. func WithStack(err error) error { @@ -162,14 +128,17 @@ type withStack struct { msg string } +// Unwrap unwraps one level of this error func (w *withStack) Unwrap() error { return goerrors.Unwrap(w.error) } +// Cause is the same as Unwrap, returns the cause of this error func (w *withStack) Cause() error { return w.Unwrap() } +// Format formats the error with stack trace if available func (w *withStack) Format(s fmt.State, verb rune) { switch verb { case 'v': @@ -179,17 +148,17 @@ func (w *withStack) Format(s fmt.State, verb rune) { if w, ok := s.Width(); ok && w > 0 { br = "\n" } - fmt.Fprintf(s, "%s%+v", br, w.error) // recursive : go to bottom + _, _ = fmt.Fprintf(s, "%s%+v", br, w.error) // recursive : go to bottom } else { if cause := w.Cause(); cause != nil { - fmt.Fprintf(s, "%+v", cause) // recursive : go to bottom + _, _ = fmt.Fprintf(s, "%+v", cause) // recursive : go to bottom if causeWithStack, ok := cause.(*withStack); ok && w.msg != "" && causeWithStack.msg != w.msg || w.msg != "" && cause.Error() != w.msg { - fmt.Fprintf(s, "\n%+v", w.msg) + _, _ = fmt.Fprintf(s, "\n%+v", w.msg) } } else { // root format - fmt.Fprintf(s, "%+v", w.error) + _, _ = fmt.Fprintf(s, "%+v", w.error) } } w.stack.Format(s, verb) @@ -197,9 +166,9 @@ func (w *withStack) Format(s fmt.State, verb rune) { } fallthrough case 's': - io.WriteString(s, w.error.Error()) + _, _ = io.WriteString(s, w.error.Error()) case 'q': - fmt.Fprintf(s, "%q", w.Error()) + _, _ = fmt.Fprintf(s, "%q", w.Error()) } } @@ -235,53 +204,6 @@ func Wrapf(err error, format string, args ...interface{}) error { } } -/* -// WithMessage annotates err with a new message. -// If err is nil, WithMessage returns nil. -func WithMessage(err error, message string) error { - if err == nil { - return nil - } - return &withMessage{ - cause: err, - msg: message, - } -} - -// WithMessagef annotates err with the format specifier. -// If err is nil, WithMessagef returns nil. -func WithMessagef(err error, format string, args ...interface{}) error { - if err == nil { - return nil - } - return &withMessage{ - cause: err, - msg: fmt.Sprintf(format, args...), - } -} - -type withMessage struct { - cause error - msg string -} - -func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() } -func (w *withMessage) Cause() error { return w.cause } - -func (w *withMessage) Format(s fmt.State, verb rune) { - switch verb { - case 'v': - if s.Flag('+') { - fmt.Fprintf(s, "%+v\n", w.Cause()) - io.WriteString(s, w.msg) - return - } - fallthrough - case 's', 'q': - io.WriteString(s, w.Error()) - } -}*/ - // Cause returns the underlying cause of the error, if possible. // An error value has a cause if it implements the following // interface: diff --git a/errors_test.go b/errors_test.go index 0024b357..b42c20ac 100644 --- a/errors_test.go +++ b/errors_test.go @@ -21,7 +21,7 @@ func TestNew(t *testing.T) { for i, tt := range tests { got := New(tt.err) - if got.Error() != tt.want.Error() { + if got == nil || got.Error() != tt.want.Error() { t.Errorf("test #%d, New.Error(): got: %q, want %q", i+1, got, tt.want) } } @@ -106,12 +106,6 @@ func TestCause(t *testing.T) { err: x, // return from errors.New want: x.(*withStack).error, }, { - // WithMessage(nil, "whoops"), - // nil, - // }, { - // WithMessage(io.EOF, "whoops"), - // io.EOF, - // }, { WithStack(nil), nil, }, { @@ -194,57 +188,6 @@ func TestWithStack(t *testing.T) { } } -/*func TestWithMessageNil(t *testing.T) { - got := WithMessage(nil, "no error") - if got != nil { - t.Errorf("WithMessage(nil, \"no error\"): got %#v, expected nil", got) - } -} - -func TestWithMessage(t *testing.T) { - tests := []struct { - err error - message string - want string - }{ - {io.EOF, "read error", "read error: EOF"}, - {WithMessage(io.EOF, "read error"), "client error", "client error: read error: EOF"}, - } - - for _, tt := range tests { - got := WithMessage(tt.err, tt.message).Error() - if got != tt.want { - t.Errorf("WithMessage(%v, %q): got: %q, want %q", tt.err, tt.message, got, tt.want) - } - } -} - -func TestWithMessagefNil(t *testing.T) { - got := WithMessagef(nil, "no error") - if got != nil { - t.Errorf("WithMessage(nil, \"no error\"): got %#v, expected nil", got) - } -} - -func TestWithMessagef(t *testing.T) { - tests := []struct { - err error - message string - want string - }{ - {io.EOF, "read error", "read error: EOF"}, - {WithMessagef(io.EOF, "read error without format specifier"), "client error", "client error: read error without format specifier: EOF"}, - {WithMessagef(io.EOF, "read error with %d format specifier", 1), "client error", "client error: read error with 1 format specifier: EOF"}, - } - - for _, tt := range tests { - got := WithMessagef(tt.err, tt.message).Error() - if got != tt.want { - t.Errorf("WithMessage(%v, %q): got: %q, want %q", tt.err, tt.message, got, tt.want) - } - } -}*/ - // errors.New, etc values are not expected to be compared by value // but the change in errors#27 made them incomparable. Assert that // various kinds of errors have a functional equality operator, even @@ -258,8 +201,6 @@ func TestErrorEquality(t *testing.T) { Errorf("EOF"), Wrap(io.EOF, "EOF"), Wrapf(io.EOF, "EOF%d", 2), - //WithMessage(nil, "whoops"), - //WithMessage(io.EOF, "whoops"), WithStack(io.EOF), WithStack(nil), } diff --git a/format_test.go b/format_test.go index 8ab76baf..a56b718f 100644 --- a/format_test.go +++ b/format_test.go @@ -227,144 +227,6 @@ func TestFormatWithStack(t *testing.T) { } } -/*func TestFormatWithMessage(t *testing.T) { - tests := []struct { - error - format string - want []string - }{{ - WithMessage(New("error"), "error2"), - "%s", - []string{"error2: error"}, - }, { - WithMessage(New("error"), "error2"), - "%v", - []string{"error2: error"}, - }, { - WithMessage(New("error"), "error2"), - "%+v", - []string{ - "error", - "github.com/pkg/errors.TestFormatWithMessage\n" + - "\t.+/github.com/pkg/errors/format_test.go:244", - "error2"}, - }, { - WithMessage(io.EOF, "addition1"), - "%s", - []string{"addition1: EOF"}, - }, { - WithMessage(io.EOF, "addition1"), - "%v", - []string{"addition1: EOF"}, - }, { - WithMessage(io.EOF, "addition1"), - "%+v", - []string{"EOF", "addition1"}, - }, { - WithMessage(WithMessage(io.EOF, "addition1"), "addition2"), - "%v", - []string{"addition2: addition1: EOF"}, - }, { - WithMessage(WithMessage(io.EOF, "addition1"), "addition2"), - "%+v", - []string{"EOF", "addition1", "addition2"}, - }, { - Wrap(WithMessage(io.EOF, "error1"), "error2"), - "%+v", - []string{"EOF", "error1", "error2", - "github.com/pkg/errors.TestFormatWithMessage\n" + - "\t.+/github.com/pkg/errors/format_test.go:272"}, - }, { - WithMessage(Errorf("error%d", 1), "error2"), - "%+v", - []string{"error1", - "github.com/pkg/errors.TestFormatWithMessage\n" + - "\t.+/github.com/pkg/errors/format_test.go:278", - "error2"}, - }, { - WithMessage(WithStack(io.EOF), "error"), - "%+v", - []string{ - "EOF", - "github.com/pkg/errors.TestFormatWithMessage\n" + - "\t.+/github.com/pkg/errors/format_test.go:285", - "error"}, - }, { - WithMessage(Wrap(WithStack(io.EOF), "inside-error"), "outside-error"), - "%+v", - []string{ - "EOF", - "github.com/pkg/errors.TestFormatWithMessage\n" + - "\t.+/github.com/pkg/errors/format_test.go:293", - "inside-error", - "github.com/pkg/errors.TestFormatWithMessage\n" + - "\t.+/github.com/pkg/errors/format_test.go:293", - "outside-error"}, - }} - - for i, tt := range tests { - testFormatCompleteCompare(t, i, tt.error, tt.format, tt.want, true) - } -} - -func TestFormatGeneric(t *testing.T) { - starts := []struct { - err error - want []string - }{ - {New("new-error"), []string{ - "new-error", - "github.com/objenious/errors.TestFormatGeneric\n" + - "\t.+/github.com/objenious/errors/format_test.go:315"}, - }, {Errorf("errorf-error"), []string{ - "errorf-error", - "github.com/objenious/errors.TestFormatGeneric\n" + - "\t.+/github.com/objenious/errors/format_test.go:319"}, - }, {goerrors.New("errors-new-error"), []string{ - "errors-new-error"}, - }, - } - - wrappers := []wrapper{ - { - func(err error) error { return Wrap(err, "with-message") }, - []string{ - "with-message", - "github.com/objenious/errors.(func·002|TestFormatGeneric.func1)\n" + - "\t.+/github.com/objenious/errors/format_test.go:330", - }, - }, { - func(err error) error { return WithStack(err) }, - []string{ - //".+\n" + - "github.com/objenious/errors.(func·002|TestFormatGeneric.func1)\n" + - "\t.+/github.com/objenious/errors/format_test.go:337", - }, - }, { - func(err error) error { return Wrap(err, "wrap-error") }, - []string{ - "wrap-error", - "github.com/objenious/errors.(func·003|TestFormatGeneric.func3)\n" + - "\t.+/github.com/objenious/errors/format_test.go:344", - }, - }, { - func(err error) error { return Wrapf(err, "wrapf-error%d", 1) }, - []string{ - "wrapf-error1", - "github.com/objenious/errors.(func·004|TestFormatGeneric.func4)\n" + - "\t.+/github.com/objenious/errors/format_test.go:351", - }, - }, - } - - for s := range starts { - err := starts[s].err - want := starts[s].want - testFormatCompleteCompare(t, s, err, "%+v", want, false) - testGenericRecursive(t, err, want, wrappers, 3) - } -}*/ - func wrappedNew(message string) error { // This function will be mid-stack inlined in go 1.12+ return New(message) } @@ -379,9 +241,9 @@ func TestFormatWrappedNew(t *testing.T) { "%+v", "error\n" + "github.com/objenious/errors.wrappedNew\n" + - "\t.+/github.com/objenious/errors/format_test.go:369\n" + + "\t.+/github.com/objenious/errors/format_test.go:231\n" + "github.com/objenious/errors.TestFormatWrappedNew\n" + - "\t.+/github.com/objenious/errors/format_test.go:378", + "\t.+/github.com/objenious/errors/format_test.go:240", }} for i, tt := range tests { @@ -516,11 +378,6 @@ func testFormatCompleteCompare(t *testing.T, n int, arg interface{}, format stri } } -/*type wrapper struct { - wrap func(err error) error - want []string -}*/ - func prettyBlocks(blocks []string) string { var out []string @@ -530,36 +387,3 @@ func prettyBlocks(blocks []string) string { return " " + strings.Join(out, "\n ") } - -/*func testGenericRecursive(t *testing.T, beforeErr error, beforeWant []string, list []wrapper, maxDepth int) { - if len(beforeWant) == 0 { - panic("beforeWant must not be empty") - } - for _, w := range list { - if len(w.want) == 0 { - panic("want must not be empty") - } - - err := w.wrap(beforeErr) - - // Copy required cause append(beforeWant, ..) modified beforeWant subtly. - beforeCopy := make([]string, len(beforeWant)) - copy(beforeCopy, beforeWant) - - beforeWant := beforeCopy - last := len(beforeWant) - 1 - var want []string - - // Merge two stacks behind each other. - if strings.ContainsAny(beforeWant[last], "\n") && strings.ContainsAny(w.want[0], "\n") { - want = append(beforeWant[:last], append([]string{beforeWant[last] + "((?s).*)" + w.want[0]}, w.want[1:]...)...) - } else { - want = append(beforeWant, w.want...) - } - - testFormatCompleteCompare(t, maxDepth, err, "%+v", want, false) - if maxDepth > 0 { - testGenericRecursive(t, err, want, list, maxDepth-1) - } - } -}*/