pax_global_header00006660000000000000000000000064134667505750014534gustar00rootroot0000000000000052 comment=fc6e4ce558343e6eab2450e7653502fee61d9ad6 errors-0.11.4/000077500000000000000000000000001346675057500131335ustar00rootroot00000000000000errors-0.11.4/.gitignore000066400000000000000000000004121346675057500151200ustar00rootroot00000000000000# Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof errors-0.11.4/.travis.yml000066400000000000000000000002441346675057500152440ustar00rootroot00000000000000language: go go_import_path: github.com/pkg/errors go: - 1.4.x - 1.5.x - 1.6.x - 1.7.x - 1.8.x - 1.9.x - 1.10.x - tip script: - go test -v ./... errors-0.11.4/LICENSE000066400000000000000000000024401346675057500141400ustar00rootroot00000000000000Copyright (c) 2015, Dave Cheney All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. errors-0.11.4/README.md000066400000000000000000000045031346675057500144140ustar00rootroot00000000000000# 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) Package errors provides simple error handling primitives. `go get github.com/pkg/errors` The traditional error handling idiom in Go is roughly akin to ```go if err != nil { return err } ``` which applied recursively up the call stack results in error reports without context or debugging information. The errors package allows programmers to add context to the failure path in their code in a way that does not destroy the original value of the error. ## Adding context to an error The errors.Wrap function returns a new error that adds context to the original error. For example ```go _, err := ioutil.ReadAll(r) 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/pkg/errors). ## Contributing We welcome pull requests, bug fixes and issue reports. With that said, the bar for adding new symbols to this package is intentionally set high. Before proposing a change, please discuss your change by raising an issue. ## License BSD-2-Clause errors-0.11.4/appveyor.yml000066400000000000000000000011771346675057500155310ustar00rootroot00000000000000version: build-{build}.{branch} clone_folder: C:\gopath\src\github.com\pkg\errors shallow_clone: true # for startup speed environment: GOPATH: C:\gopath platform: - x64 # http://www.appveyor.com/docs/installed-software install: # some helpful output for debugging builds - go version - go env # pre-installed MinGW at C:\MinGW is 32bit only # but MSYS2 at C:\msys64 has mingw64 - set PATH=C:\msys64\mingw64\bin;%PATH% - gcc --version - g++ --version build_script: - go install -v ./... test_script: - set PATH=C:\gopath\bin;%PATH% - go test -v ./... #artifacts: # - path: '%GOPATH%\bin\*.exe' deploy: off errors-0.11.4/bench_test.go000066400000000000000000000036771346675057500156150ustar00rootroot00000000000000// +build go1.7 package errors import ( "fmt" "testing" stderrors "errors" ) func noErrors(at, depth int) error { if at >= depth { return stderrors.New("no error") } return noErrors(at+1, depth) } func yesErrors(at, depth int) error { if at >= depth { return New("ye error") } return yesErrors(at+1, depth) } // GlobalE is an exported global to store the result of benchmark results, // preventing the compiler from optimising the benchmark functions away. var GlobalE interface{} func BenchmarkErrors(b *testing.B) { type run struct { stack int std bool } runs := []run{ {10, false}, {10, true}, {100, false}, {100, true}, {1000, false}, {1000, true}, } for _, r := range runs { part := "pkg/errors" if r.std { part = "errors" } name := fmt.Sprintf("%s-stack-%d", part, r.stack) b.Run(name, func(b *testing.B) { var err error f := yesErrors if r.std { f = noErrors } b.ReportAllocs() for i := 0; i < b.N; i++ { err = f(0, r.stack) } b.StopTimer() GlobalE = err }) } } func BenchmarkStackFormatting(b *testing.B) { type run struct { stack int format string } runs := []run{ {10, "%s"}, {10, "%v"}, {10, "%+v"}, {30, "%s"}, {30, "%v"}, {30, "%+v"}, {60, "%s"}, {60, "%v"}, {60, "%+v"}, } var stackStr string for _, r := range runs { name := fmt.Sprintf("%s-stack-%d", r.format, r.stack) b.Run(name, func(b *testing.B) { err := yesErrors(0, r.stack) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { stackStr = fmt.Sprintf(r.format, err) } b.StopTimer() }) } for _, r := range runs { 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() b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { stackStr = fmt.Sprintf(r.format, st) } b.StopTimer() }) } GlobalE = stackStr } errors-0.11.4/errors.go000066400000000000000000000212651346675057500150040ustar00rootroot00000000000000// Package errors provides simple error handling primitives. // // The traditional error handling idiom in Go is roughly akin to // // if err != nil { // return err // } // // which applied recursively up the call stack results in error reports // without context or debugging information. The errors package allows // programmers to add context to the failure path in their code in a way // that does not destroy the original value of the error. // // Adding context to an error // // The errors.Annotate function returns a new error that adds context to the // original error by recording a stack trace at the point Annotate is called, // and the supplied message. For example // // _, err := ioutil.ReadAll(r) // if err != nil { // return errors.Annotate(err, "read failed") // } // // If additional control is required the errors.AddStack and errors.WithMessage // functions destructure errors.Annotate into its component operations of annotating // an error with a stack trace and an a message, respectively. // // Retrieving the cause of an error // // Using errors.Annotate 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.Annotate to retrieve the original error // for inspection. Any error value which implements this interface // // type causer interface { // Cause() error // } // // can be inspected by errors.Cause. errors.Cause will recursively retrieve // the topmost error which does not implement causer, which is assumed to be // the original cause. For example: // // switch err := errors.Cause(err).(type) { // case *MyError: // // handle specifically // default: // // unknown error // } // // causer interface is not exported by this package, but is considered a part // of stable public API. // errors.Unwrap is also available: this will retrieve the next error in the chain. // // Formatted printing of errors // // All error values returned from this package implement fmt.Formatter and can // be formatted by the fmt package. The following verbs are supported // // %s print the error. If the error has a Cause it will be // printed recursively // %v see %s // %+v extended format. Each Frame of the error's StackTrace will // be printed in detail. // // Retrieving the stack trace of an error or wrapper // // New, Errorf, Annotate, and Annotatef record a stack trace at the point they are invoked. // This information can be retrieved with the StackTracer interface that returns // a StackTrace. Where errors.StackTrace is defined as // // type StackTrace []Frame // // The Frame type represents a call site in the stack trace. Frame supports // the fmt.Formatter interface that can be used for printing information about // the stack trace of this error. For example: // // if stacked := errors.GetStackTracer(err); stacked != nil { // for _, f := range stacked.StackTrace() { // fmt.Printf("%+s:%d", f) // } // } // // See the documentation for Frame.Format for more details. // // errors.Find can be used to search for an error in the error chain. package errors import ( "fmt" "io" ) // 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(), } } // Errorf formats according to a format specifier and returns the string // 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(), } } // StackTraceAware is an optimization to avoid repetitive traversals of an error chain. // HasStack checks for this marker first. // Annotate/Wrap and Annotatef/Wrapf will produce this marker. type StackTraceAware interface { HasStack() bool } // HasStack tells whether a StackTracer exists in the error chain func HasStack(err error) bool { if errWithStack, ok := err.(StackTraceAware); ok { return errWithStack.HasStack() } return GetStackTracer(err) != nil } // fundamental is an error that has a message and a stack, but no caller. type fundamental struct { msg string *stack } func (f *fundamental) Error() string { return f.msg } func (f *fundamental) Format(s fmt.State, verb rune) { switch verb { case 'v': if s.Flag('+') { io.WriteString(s, f.msg) f.stack.Format(s, verb) return } fallthrough case 's': io.WriteString(s, f.msg) case 'q': fmt.Fprintf(s, "%q", f.msg) } } // WithStack annotates err with a stack trace at the point WithStack was called. // If err is nil, WithStack returns nil. // // For most use cases this is deprecated and AddStack should be used (which will ensure just one stack trace). // However, one may want to use this in some situations, for example to create a 2nd trace across a goroutine. func WithStack(err error) error { if err == nil { return nil } return &withStack{ err, callers(), } } // AddStack is similar to WithStack. // However, it will first check with HasStack to see if a stack trace already exists in the causer chain before creating another one. func AddStack(err error) error { if HasStack(err) { return err } return WithStack(err) } type withStack struct { error *stack } func (w *withStack) Cause() error { return w.error } func (w *withStack) Format(s fmt.State, verb rune) { switch verb { case 'v': if s.Flag('+') { fmt.Fprintf(s, "%+v", w.Cause()) w.stack.Format(s, verb) return } fallthrough case 's': io.WriteString(s, w.Error()) case 'q': fmt.Fprintf(s, "%q", w.Error()) } } // Wrap returns an error annotating err with a stack trace // at the point Wrap is called, and the supplied message. // If err is nil, Wrap returns nil. // // For most use cases this is deprecated in favor of Annotate. // Annotate avoids creating duplicate stack traces. func Wrap(err error, message string) error { if err == nil { return nil } hasStack := HasStack(err) err = &withMessage{ cause: err, msg: message, causeHasStack: hasStack, } return &withStack{ err, callers(), } } // Wrapf returns an error annotating err with a stack trace // at the point Wrapf is call, and the format specifier. // If err is nil, Wrapf returns nil. // // For most use cases this is deprecated in favor of Annotatef. // Annotatef avoids creating duplicate stack traces. func Wrapf(err error, format string, args ...interface{}) error { if err == nil { return nil } hasStack := HasStack(err) err = &withMessage{ cause: err, msg: fmt.Sprintf(format, args...), causeHasStack: hasStack, } return &withStack{ err, callers(), } } // 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, causeHasStack: HasStack(err), } } type withMessage struct { cause error msg string causeHasStack bool } func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() } func (w *withMessage) Cause() error { return w.cause } func (w *withMessage) HasStack() bool { return w.causeHasStack } 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: // // type causer interface { // Cause() error // } // // If the error does not implement Cause, the original error will // be returned. If the error is nil, nil will be returned without further // investigation. func Cause(err error) error { cause := Unwrap(err) if cause == nil { return err } return Cause(cause) } // Unwrap uses causer to return the next error in the chain or nil. // This goes one-level deeper, whereas Cause goes as far as possible func Unwrap(err error) error { type causer interface { Cause() error } if unErr, ok := err.(causer); ok { return unErr.Cause() } return nil } // Find an error in the chain that matches a test function. // returns nil if no error is found. func Find(origErr error, test func(error) bool) error { var foundErr error WalkDeep(origErr, func(err error) bool { if test(err) { foundErr = err return true } return false }) return foundErr } errors-0.11.4/errors_test.go000066400000000000000000000200401346675057500160310ustar00rootroot00000000000000package errors import ( "errors" "fmt" "io" "reflect" "strconv" "testing" ) func TestNew(t *testing.T) { tests := []struct { err string want error }{ {"", fmt.Errorf("")}, {"foo", fmt.Errorf("foo")}, {"foo", New("foo")}, {"string with format specifiers: %v", errors.New("string with format specifiers: %v")}, } for _, tt := range tests { got := New(tt.err) if got.Error() != tt.want.Error() { t.Errorf("New.Error(): got: %q, want %q", got, tt.want) } } } func TestWrapNil(t *testing.T) { got := Annotate(nil, "no error") if got != nil { t.Errorf("Wrap(nil, \"no error\"): got %#v, expected nil", got) } } func TestWrap(t *testing.T) { tests := []struct { err error message string want string }{ {io.EOF, "read error", "read error: EOF"}, {Annotate(io.EOF, "read error"), "client error", "client error: read error: EOF"}, } for _, tt := range tests { got := Annotate(tt.err, tt.message).Error() if got != tt.want { t.Errorf("Wrap(%v, %q): got: %v, want %v", tt.err, tt.message, got, tt.want) } } } type nilError struct{} func (nilError) Error() string { return "nil error" } func TestCause(t *testing.T) { x := New("error") tests := []struct { err error want error }{{ // nil error is nil err: nil, want: nil, }, { // explicit nil error is nil err: (error)(nil), want: nil, }, { // typed nil is nil err: (*nilError)(nil), want: (*nilError)(nil), }, { // uncaused error is unaffected err: io.EOF, want: io.EOF, }, { // caused error returns cause err: Annotate(io.EOF, "ignored"), want: io.EOF, }, { err: x, // return from errors.New want: x, }, { WithMessage(nil, "whoops"), nil, }, { WithMessage(io.EOF, "whoops"), io.EOF, }, { WithStack(nil), nil, }, { WithStack(io.EOF), io.EOF, }, { AddStack(nil), nil, }, { AddStack(io.EOF), io.EOF, }} for i, tt := range tests { got := Cause(tt.err) if !reflect.DeepEqual(got, tt.want) { t.Errorf("test %d: got %#v, want %#v", i+1, got, tt.want) } } } func TestWrapfNil(t *testing.T) { got := Annotate(nil, "no error") if got != nil { t.Errorf("Wrapf(nil, \"no error\"): got %#v, expected nil", got) } } func TestWrapf(t *testing.T) { tests := []struct { err error message string want string }{ {io.EOF, "read error", "read error: EOF"}, {Annotatef(io.EOF, "read error without format specifiers"), "client error", "client error: read error without format specifiers: EOF"}, {Annotatef(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 := Annotatef(tt.err, tt.message).Error() if got != tt.want { t.Errorf("Wrapf(%v, %q): got: %v, want %v", tt.err, tt.message, got, tt.want) } } } func TestErrorf(t *testing.T) { tests := []struct { err error want string }{ {Errorf("read error without format specifiers"), "read error without format specifiers"}, {Errorf("read error with %d format specifier", 1), "read error with 1 format specifier"}, } for _, tt := range tests { got := tt.err.Error() if got != tt.want { t.Errorf("Errorf(%v): got: %q, want %q", tt.err, got, tt.want) } } } func TestWithStackNil(t *testing.T) { got := WithStack(nil) if got != nil { t.Errorf("WithStack(nil): got %#v, expected nil", got) } got = AddStack(nil) if got != nil { t.Errorf("AddStack(nil): got %#v, expected nil", got) } } func TestWithStack(t *testing.T) { tests := []struct { err error want string }{ {io.EOF, "EOF"}, {WithStack(io.EOF), "EOF"}, } for _, tt := range tests { got := WithStack(tt.err).Error() if got != tt.want { t.Errorf("WithStack(%v): got: %v, want %v", tt.err, got, tt.want) } } } func TestAddStack(t *testing.T) { tests := []struct { err error want string }{ {io.EOF, "EOF"}, {AddStack(io.EOF), "EOF"}, } for _, tt := range tests { got := AddStack(tt.err).Error() if got != tt.want { t.Errorf("AddStack(%v): got: %v, want %v", tt.err, got, tt.want) } } } func TestGetStackTracer(t *testing.T) { orig := io.EOF if GetStackTracer(orig) != nil { t.Errorf("GetStackTracer: got: %v, want %v", GetStackTracer(orig), nil) } stacked := AddStack(orig) if GetStackTracer(stacked).(error) != stacked { t.Errorf("GetStackTracer(stacked): got: %v, want %v", GetStackTracer(stacked), stacked) } final := AddStack(stacked) if GetStackTracer(final).(error) != stacked { t.Errorf("GetStackTracer(final): got: %v, want %v", GetStackTracer(final), stacked) } } func TestAddStackDedup(t *testing.T) { stacked := WithStack(io.EOF) err := AddStack(stacked) if err != stacked { t.Errorf("AddStack: got: %+v, want %+v", err, stacked) } err = WithStack(stacked) if err == stacked { t.Errorf("WithStack: got: %v, don't want %v", err, stacked) } } 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) } } } // 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 // if the result of that equality is always false. func TestErrorEquality(t *testing.T) { vals := []error{ nil, io.EOF, errors.New("EOF"), New("EOF"), Errorf("EOF"), Annotate(io.EOF, "EOF"), Annotatef(io.EOF, "EOF%d", 2), WithMessage(nil, "whoops"), WithMessage(io.EOF, "whoops"), WithStack(io.EOF), WithStack(nil), AddStack(io.EOF), AddStack(nil), } for i := range vals { for j := range vals { _ = vals[i] == vals[j] // mustn't panic } } } func TestFind(t *testing.T) { eNew := errors.New("error") wrapped := Annotate(nilError{}, "nil") tests := []struct { err error finder func(error) bool found error }{ {io.EOF, func(_ error) bool { return true }, io.EOF}, {io.EOF, func(_ error) bool { return false }, nil}, {io.EOF, func(err error) bool { return err == io.EOF }, io.EOF}, {io.EOF, func(err error) bool { return err != io.EOF }, nil}, {eNew, func(err error) bool { return true }, eNew}, {eNew, func(err error) bool { return false }, nil}, {nilError{}, func(err error) bool { return true }, nilError{}}, {nilError{}, func(err error) bool { return false }, nil}, {nilError{}, func(err error) bool { _, ok := err.(nilError); return ok }, nilError{}}, {wrapped, func(err error) bool { return true }, wrapped}, {wrapped, func(err error) bool { return false }, nil}, {wrapped, func(err error) bool { _, ok := err.(nilError); return ok }, nilError{}}, } for _, tt := range tests { got := Find(tt.err, tt.finder) if got != tt.found { t.Errorf("WithMessage(%v): got: %q, want %q", tt.err, got, tt.found) } } } type errWalkTest struct { cause error sub []error v int } func (e *errWalkTest) Error() string { return strconv.Itoa(e.v) } func (e *errWalkTest) Cause() error { return e.cause } func (e *errWalkTest) Errors() []error { return e.sub } func testFind(err error, v int) bool { return WalkDeep(err, func(err error) bool { e := err.(*errWalkTest) return e.v == v }) } func TestWalkDeep(t *testing.T) { err := &errWalkTest{ sub: []error{ &errWalkTest{ v: 10, cause: &errWalkTest{v: 11}, }, &errWalkTest{ v: 20, cause: &errWalkTest{v: 21, cause: &errWalkTest{v: 22}}, }, &errWalkTest{ v: 30, cause: &errWalkTest{v: 31}, }, }, } if !testFind(err, 11) { t.Errorf("not found in first cause chain") } if !testFind(err, 22) { t.Errorf("not found in siblings") } if testFind(err, 32) { t.Errorf("found not exists") } } errors-0.11.4/example_test.go000066400000000000000000000124751346675057500161650ustar00rootroot00000000000000package 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 } errors-0.11.4/format_test.go000066400000000000000000000307441346675057500160210ustar00rootroot00000000000000package errors import ( "errors" "fmt" "io" "regexp" "strings" "testing" ) func TestFormatNew(t *testing.T) { tests := []struct { error format string want string }{{ New("error"), "%s", "error", }, { New("error"), "%v", "error", }, { New("error"), "%+v", "error\n" + "github.com/pkg/errors.TestFormatNew\n" + "\t.+/github.com/pkg/errors/format_test.go:26", }, { New("error"), "%q", `"error"`, }} for i, tt := range tests { testFormatRegexp(t, i, tt.error, tt.format, tt.want) } } func TestFormatErrorf(t *testing.T) { tests := []struct { error format string want string }{{ Errorf("%s", "error"), "%s", "error", }, { Errorf("%s", "error"), "%v", "error", }, { Errorf("%s", "error"), "%+v", "error\n" + "github.com/pkg/errors.TestFormatErrorf\n" + "\t.+/github.com/pkg/errors/format_test.go:56", }} for i, tt := range tests { testFormatRegexp(t, i, tt.error, tt.format, tt.want) } } func TestFormatWrap(t *testing.T) { tests := []struct { error format string want string }{{ Annotate(New("error"), "error2"), "%s", "error2: error", }, { Annotate(New("error"), "error2"), "%v", "error2: error", }, { Annotate(New("error"), "error2"), "%+v", "error\n" + "github.com/pkg/errors.TestFormatWrap\n" + "\t.+/github.com/pkg/errors/format_test.go:82", }, { Annotate(io.EOF, "error"), "%s", "error: EOF", }, { Annotate(io.EOF, "error"), "%v", "error: EOF", }, { Annotate(io.EOF, "error"), "%+v", "EOF\n" + "error\n" + "github.com/pkg/errors.TestFormatWrap\n" + "\t.+/github.com/pkg/errors/format_test.go:96", }, { Annotate(Annotate(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", }, { Annotate(New("error with space"), "context"), "%q", `context: error with space`, }} for i, tt := range tests { testFormatRegexp(t, i, tt.error, tt.format, tt.want) } } func TestFormatWrapf(t *testing.T) { tests := []struct { error format string want string }{{ Annotatef(io.EOF, "error%d", 2), "%s", "error2: EOF", }, { Annotatef(io.EOF, "error%d", 2), "%v", "error2: EOF", }, { Annotatef(io.EOF, "error%d", 2), "%+v", "EOF\n" + "error2\n" + "github.com/pkg/errors.TestFormatWrapf\n" + "\t.+/github.com/pkg/errors/format_test.go:134", }, { Annotatef(New("error"), "error%d", 2), "%s", "error2: error", }, { Annotatef(New("error"), "error%d", 2), "%v", "error2: error", }, { Annotatef(New("error"), "error%d", 2), "%+v", "error\n" + "github.com/pkg/errors.TestFormatWrapf\n" + "\t.+/github.com/pkg/errors/format_test.go:149", }} for i, tt := range tests { testFormatRegexp(t, i, tt.error, tt.format, tt.want) } } func TestFormatWithStack(t *testing.T) { tests := []struct { error format string want []string }{{ WithStack(io.EOF), "%s", []string{"EOF"}, }, { WithStack(io.EOF), "%v", []string{"EOF"}, }, { WithStack(io.EOF), "%+v", []string{"EOF", "github.com/pkg/errors.TestFormatWithStack\n" + "\t.+/github.com/pkg/errors/format_test.go:175"}, }, { WithStack(New("error")), "%s", []string{"error"}, }, { WithStack(New("error")), "%v", []string{"error"}, }, { 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"}, }, { 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"}, }, { WithStack(WithStack(Annotatef(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"}, }, { 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"}, }} for i, tt := range tests { testFormatCompleteCompare(t, i, tt.error, tt.format, tt.want, true) } } 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"}, }, { Annotate(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(Annotate(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", "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/pkg/errors.TestFormatGeneric\n" + "\t.+/github.com/pkg/errors/format_test.go:313"}, }, {Errorf("errorf-error"), []string{ "errorf-error", "github.com/pkg/errors.TestFormatGeneric\n" + "\t.+/github.com/pkg/errors/format_test.go:317"}, }, {errors.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 WithStack(err) }, []string{ "github.com/pkg/errors.(func·002|TestFormatGeneric.func2)\n\t" + ".+/github.com/pkg/errors/format_test.go:331", }, }, { func(err error) error { return Annotate(err, "wrap-error") }, []string{ "wrap-error", "github.com/pkg/errors.(func·003|TestFormatGeneric.func3)\n\t" + ".+/github.com/pkg/errors/format_test.go:337", }, }, { func(err error) error { return Annotatef(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", }, }, } 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 testFormatRegexp(t *testing.T, n int, arg interface{}, format, want string) { t.Helper() got := fmt.Sprintf(format, arg) gotLines := strings.SplitN(got, "\n", -1) wantLines := strings.SplitN(want, "\n", -1) if len(wantLines) > len(gotLines) { t.Errorf("test %d: wantLines(%d) > gotLines(%d):\n got: %q\nwant: %q", n+1, len(wantLines), len(gotLines), got, want) return } for i, w := range wantLines { match, err := regexp.MatchString(w, gotLines[i]) if err != nil { t.Fatal(err) } if !match { t.Errorf("test %d: line %d: fmt.Sprintf(%q, err):\n got: %q\nwant: %q", n+1, i+1, format, got, want) } } } var stackLineR = regexp.MustCompile(`\.`) // parseBlocks parses input into a slice, where: // - incase entry contains a newline, its a stacktrace // - incase entry contains no newline, its a solo line. // // Detecting stack boundaries only works incase the WithStack-calls are // to be found on the same line, thats why it is optionally here. // // Example use: // // for _, e := range blocks { // if strings.ContainsAny(e, "\n") { // // Match as stack // } else { // // Match as line // } // } // func parseBlocks(input string, detectStackboundaries bool) ([]string, error) { var blocks []string stack := "" wasStack := false lines := map[string]bool{} // already found lines for _, l := range strings.Split(input, "\n") { isStackLine := stackLineR.MatchString(l) switch { case !isStackLine && wasStack: blocks = append(blocks, stack, l) stack = "" lines = map[string]bool{} case isStackLine: if wasStack { // Detecting two stacks after another, possible cause lines match in // our tests due to WithStack(WithStack(io.EOF)) on same line. if detectStackboundaries { if lines[l] { if len(stack) == 0 { return nil, errors.New("len of block must not be zero here") } blocks = append(blocks, stack) stack = l lines = map[string]bool{l: true} continue } } stack = stack + "\n" + l } else { stack = l } lines[l] = true case !isStackLine && !wasStack: blocks = append(blocks, l) default: return nil, errors.New("must not happen") } wasStack = isStackLine } // Use up stack if stack != "" { blocks = append(blocks, stack) } return blocks, nil } func testFormatCompleteCompare(t *testing.T, n int, arg interface{}, format string, want []string, detectStackBoundaries bool) { gotStr := fmt.Sprintf(format, arg) got, err := parseBlocks(gotStr, detectStackBoundaries) if err != nil { t.Fatal(err) } if len(got) != len(want) { t.Fatalf("test %d: fmt.Sprintf(%s, err) -> wrong number of blocks: got(%d) want(%d)\n got: %s\nwant: %s\ngotStr: %q", n+1, format, len(got), len(want), prettyBlocks(got), prettyBlocks(want), gotStr) } for i := range got { if strings.ContainsAny(want[i], "\n") { // Match as stack match, err := regexp.MatchString(want[i], got[i]) if err != nil { t.Fatal(err) } if !match { t.Fatalf("test %d: block %d: fmt.Sprintf(%q, err):\ngot:\n%q\nwant:\n%q\nall-got:\n%s\nall-want:\n%s\n", n+1, i+1, format, got[i], want[i], prettyBlocks(got), prettyBlocks(want)) } } else { // Match as message if got[i] != want[i] { t.Fatalf("test %d: fmt.Sprintf(%s, err) at block %d got != want:\n got: %q\nwant: %q", n+1, format, i+1, got[i], want[i]) } } } } type wrapper struct { wrap func(err error) error want []string } func prettyBlocks(blocks []string) string { var out []string for _, b := range blocks { out = append(out, fmt.Sprintf("%v", b)) } 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) } } } errors-0.11.4/group.go000066400000000000000000000021301346675057500146120ustar00rootroot00000000000000package errors // ErrorGroup is an interface for multiple errors that are not a chain. // This happens for example when executing multiple operations in parallel. type ErrorGroup interface { Errors() []error } // Errors uses the ErrorGroup interface to return a slice of errors. // If the ErrorGroup interface is not implemented it returns an array containing just the given error. func Errors(err error) []error { if eg, ok := err.(ErrorGroup); ok { return eg.Errors() } return []error{err} } // WalkDeep does a depth-first traversal of all errors. // Any ErrorGroup is traversed (after going deep). // The visitor function can return true to end the traversal early // In that case, WalkDeep will return true, otherwise false. func WalkDeep(err error, visitor func(err error) bool) bool { // Go deep unErr := err for unErr != nil { if done := visitor(unErr); done { return true } unErr = Unwrap(unErr) } // Go wide if group, ok := err.(ErrorGroup); ok { for _, err := range group.Errors() { if early := WalkDeep(err, visitor); early { return true } } } return false } errors-0.11.4/juju_adaptor.go000066400000000000000000000063421346675057500161560ustar00rootroot00000000000000package errors import ( "fmt" "strings" ) // ==================== juju adaptor start ======================== // Trace just calls AddStack. func Trace(err error) error { if err == nil { return nil } return AddStack(err) } // Annotate adds a message and ensures there is a stack trace. func Annotate(err error, message string) error { if err == nil { return nil } hasStack := HasStack(err) err = &withMessage{ cause: err, msg: message, causeHasStack: hasStack, } if hasStack { return err } return &withStack{ err, callers(), } } // Annotatef adds a message and ensures there is a stack trace. func Annotatef(err error, format string, args ...interface{}) error { if err == nil { return nil } hasStack := HasStack(err) err = &withMessage{ cause: err, msg: fmt.Sprintf(format, args...), causeHasStack: hasStack, } if hasStack { return err } return &withStack{ err, callers(), } } var emptyStack stack // NewNoStackError creates error without error stack // later duplicate trace will no longer generate Stack too. func NewNoStackError(msg string) error { return &fundamental{ msg: msg, stack: &emptyStack, } } // SuspendStack suspends stack generate for error. func SuspendStack(err error) error { if err == nil { return err } cleared := clearStack(err) if cleared { return err } return &withStack{ err, &emptyStack, } } func clearStack(err error) (cleared bool) { switch typedErr := err.(type) { case *withMessage: return clearStack(typedErr.Cause()) case *fundamental: typedErr.stack = &emptyStack return true case *withStack: typedErr.stack = &emptyStack clearStack(typedErr.Cause()) return true default: return false } } // ErrorStack will format a stack trace if it is available, otherwise it will be Error() // If the error is nil, the empty string is returned // Note that this just calls fmt.Sprintf("%+v", err) func ErrorStack(err error) string { if err == nil { return "" } return fmt.Sprintf("%+v", err) } // IsNotFound reports whether err was not found error. func IsNotFound(err error) bool { return strings.Contains(err.Error(), "not found") } // NotFoundf represents an error with not found message. func NotFoundf(format string, args ...interface{}) error { return Errorf(format+" not found", args...) } // BadRequestf represents an error with bad request message. func BadRequestf(format string, args ...interface{}) error { return Errorf(format+" bad request", args...) } // NotSupportedf represents an error with not supported message. func NotSupportedf(format string, args ...interface{}) error { return Errorf(format+" not supported", args...) } // NotValidf represents an error with not valid message. func NotValidf(format string, args ...interface{}) error { return Errorf(format+" not valid", args...) } // IsAlreadyExists reports whether err was already exists error. func IsAlreadyExists(err error) bool { return strings.Contains(err.Error(), "already exists") } // AlreadyExistsf represents an error with already exists message. func AlreadyExistsf(format string, args ...interface{}) error { return Errorf(format+" already exists", args...) } // ==================== juju adaptor end ======================== errors-0.11.4/stack.go000066400000000000000000000132701346675057500145720ustar00rootroot00000000000000package errors import ( "bytes" "fmt" "io" "path" "runtime" "strconv" "strings" ) // StackTracer retrieves the StackTrace // Generally you would want to use the GetStackTracer function to do that. type StackTracer interface { StackTrace() StackTrace } // GetStackTracer will return the first StackTracer in the causer chain. // This function is used by AddStack to avoid creating redundant stack traces. // // You can also use the StackTracer interface on the returned error to get the stack trace. func GetStackTracer(origErr error) StackTracer { var stacked StackTracer WalkDeep(origErr, func(err error) bool { if stackTracer, ok := err.(StackTracer); ok { stacked = stackTracer return true } return false }) return stacked } // Frame represents a program counter inside a stack frame. type Frame uintptr // pc returns the program counter for this frame; // multiple frames may have the same PC value. func (f Frame) pc() uintptr { return uintptr(f) - 1 } // file returns the full path to the file that contains the // function for this Frame's pc. func (f Frame) file() string { fn := runtime.FuncForPC(f.pc()) if fn == nil { return "unknown" } file, _ := fn.FileLine(f.pc()) return file } // line returns the line number of source code of the // function for this Frame's pc. func (f Frame) line() int { fn := runtime.FuncForPC(f.pc()) if fn == nil { return 0 } _, line := fn.FileLine(f.pc()) return line } // Format formats the frame according to the fmt.Formatter interface. // // %s source file // %d source line // %n function name // %v equivalent to %s:%d // // Format accepts flags that alter the printing of some verbs, as follows: // // %+s function name and path of source file relative to the compile time // GOPATH separated by \n\t (\n\t) // %+v equivalent to %+s:%d func (f Frame) Format(s fmt.State, verb rune) { f.format(s, s, verb) } // format allows stack trace printing calls to be made with a bytes.Buffer. func (f Frame) format(w io.Writer, s fmt.State, verb rune) { switch verb { case 's': switch { case s.Flag('+'): pc := f.pc() fn := runtime.FuncForPC(pc) if fn == nil { io.WriteString(w, "unknown") } else { file, _ := fn.FileLine(pc) io.WriteString(w, fn.Name()) io.WriteString(w, "\n\t") io.WriteString(w, file) } default: io.WriteString(w, path.Base(f.file())) } case 'd': io.WriteString(w, strconv.Itoa(f.line())) case 'n': name := runtime.FuncForPC(f.pc()).Name() io.WriteString(w, funcname(name)) case 'v': f.format(w, s, 's') io.WriteString(w, ":") f.format(w, s, 'd') } } // StackTrace is stack of Frames from innermost (newest) to outermost (oldest). type StackTrace []Frame // Format formats the stack of Frames according to the fmt.Formatter interface. // // %s lists source files for each Frame in the stack // %v lists the source file and line number for each Frame in the stack // // Format accepts flags that alter the printing of some verbs, as follows: // // %+v Prints filename, function, and line number for each Frame in the stack. func (st StackTrace) Format(s fmt.State, verb rune) { var b bytes.Buffer switch verb { case 'v': switch { case s.Flag('+'): b.Grow(len(st) * stackMinLen) for _, fr := range st { b.WriteByte('\n') fr.format(&b, s, verb) } case s.Flag('#'): fmt.Fprintf(&b, "%#v", []Frame(st)) default: st.formatSlice(&b, s, verb) } case 's': st.formatSlice(&b, s, verb) } io.Copy(s, &b) } // formatSlice will format this StackTrace into the given buffer as a slice of // Frame, only valid when called with '%s' or '%v'. func (st StackTrace) formatSlice(b *bytes.Buffer, s fmt.State, verb rune) { b.WriteByte('[') if len(st) == 0 { b.WriteByte(']') return } b.Grow(len(st) * (stackMinLen / 4)) st[0].format(b, s, verb) for _, fr := range st[1:] { b.WriteByte(' ') fr.format(b, s, verb) } b.WriteByte(']') } // stackMinLen is a best-guess at the minimum length of a stack trace. It // doesn't need to be exact, just give a good enough head start for the buffer // to avoid the expensive early growth. const stackMinLen = 96 // stack represents a stack of program counters. type stack []uintptr func (s *stack) Format(st fmt.State, verb rune) { switch verb { case 'v': switch { case st.Flag('+'): var b bytes.Buffer b.Grow(len(*s) * stackMinLen) for _, pc := range *s { f := Frame(pc) b.WriteByte('\n') f.format(&b, st, 'v') } io.Copy(st, &b) } } } func (s *stack) StackTrace() StackTrace { f := make([]Frame, len(*s)) for i := 0; i < len(f); i++ { f[i] = Frame((*s)[i]) } return f } func callers() *stack { return callersSkip(4) } func callersSkip(skip int) *stack { const depth = 32 var pcs [depth]uintptr n := runtime.Callers(skip, pcs[:]) var st stack = pcs[0:n] return &st } // funcname removes the path prefix component of a function's name reported by func.Name(). func funcname(name string) string { i := strings.LastIndex(name, "/") name = name[i+1:] i = strings.Index(name, ".") return name[i+1:] } // NewStack is for library implementers that want to generate a stack trace. // Normally you should insted use AddStack to get an error with a stack trace. // // The result of this function can be turned into a stack trace by calling .StackTrace() // // This function takes an argument for the number of stack frames to skip. // This avoids putting stack generation function calls like this one in the stack trace. // A value of 0 will give you the line that called NewStack(0) // A library author wrapping this in their own function will want to use a value of at least 1. func NewStack(skip int) StackTracer { return callersSkip(skip + 3) } errors-0.11.4/stack_test.go000066400000000000000000000147511346675057500156360ustar00rootroot00000000000000package errors import ( "fmt" "io" "runtime" "testing" ) var initpc, _, _, _ = runtime.Caller(0) func TestFrameLine(t *testing.T) { var tests = []struct { Frame want int }{{ Frame(initpc), 9, }, { func() Frame { var pc, _, _, _ = runtime.Caller(0) return Frame(pc) }(), 20, }, { func() Frame { var pc, _, _, _ = runtime.Caller(1) return Frame(pc) }(), 28, }, { Frame(0), // invalid PC 0, }} for _, tt := range tests { got := tt.Frame.line() want := tt.want if want != got { t.Errorf("Frame(%v): want: %v, got: %v", uintptr(tt.Frame), want, got) } } } type X struct{} func (x X) val() Frame { var pc, _, _, _ = runtime.Caller(0) return Frame(pc) } func (x *X) ptr() Frame { var pc, _, _, _ = runtime.Caller(0) return Frame(pc) } func TestFrameFormat(t *testing.T) { var tests = []struct { Frame format string want string }{{ Frame(initpc), "%s", "stack_test.go", }, { Frame(initpc), "%+s", "github.com/pkg/errors.init\n" + "\t.+/github.com/pkg/errors/stack_test.go", }, { Frame(0), "%s", "unknown", }, { Frame(0), "%+s", "unknown", }, { Frame(initpc), "%d", "9", }, { Frame(0), "%d", "0", }, { Frame(initpc), "%n", "init", }, { func() Frame { var x X return x.ptr() }(), "%n", `\(\*X\).ptr`, }, { func() Frame { var x X return x.val() }(), "%n", "X.val", }, { Frame(0), "%n", "", }, { Frame(initpc), "%v", "stack_test.go:9", }, { Frame(initpc), "%+v", "github.com/pkg/errors.init\n" + "\t.+/github.com/pkg/errors/stack_test.go:9", }, { Frame(0), "%v", "unknown:0", }} for i, tt := range tests { testFormatRegexp(t, i, tt.Frame, tt.format, tt.want) } } func TestFuncname(t *testing.T) { tests := []struct { name, want string }{ {"", ""}, {"runtime.main", "main"}, {"github.com/pkg/errors.funcname", "funcname"}, {"funcname", "funcname"}, {"io.copyBuffer", "copyBuffer"}, {"main.(*R).Write", "(*R).Write"}, } for _, tt := range tests { got := funcname(tt.name) want := tt.want if got != want { t.Errorf("funcname(%q): want: %q, got %q", tt.name, want, got) } } } func TestStackTrace(t *testing.T) { tests := []struct { err error want []string }{{ New("ooh"), []string{ "github.com/pkg/errors.TestStackTrace\n" + "\t.+/github.com/pkg/errors/stack_test.go:154", }, }, { Annotate(New("ooh"), "ahh"), []string{ "github.com/pkg/errors.TestStackTrace\n" + "\t.+/github.com/pkg/errors/stack_test.go:159", // this is the stack of Wrap, not New }, }, { Cause(Annotate(New("ooh"), "ahh")), []string{ "github.com/pkg/errors.TestStackTrace\n" + "\t.+/github.com/pkg/errors/stack_test.go:164", // this is the stack of New }, }, { func() error { return New("ooh") }(), []string{ `github.com/pkg/errors.(func·009|TestStackTrace.func1)` + "\n\t.+/github.com/pkg/errors/stack_test.go:169", // this is the stack of New "github.com/pkg/errors.TestStackTrace\n" + "\t.+/github.com/pkg/errors/stack_test.go:169", // this is the stack of New's caller }, }, { Cause(func() error { return func() error { return Errorf("hello %s", fmt.Sprintf("world")) }() }()), []string{ `github.com/pkg/errors.(func·010|TestStackTrace.func2.1)` + "\n\t.+/github.com/pkg/errors/stack_test.go:178", // this is the stack of Errorf `github.com/pkg/errors.(func·011|TestStackTrace.func2)` + "\n\t.+/github.com/pkg/errors/stack_test.go:179", // this is the stack of Errorf's caller "github.com/pkg/errors.TestStackTrace\n" + "\t.+/github.com/pkg/errors/stack_test.go:180", // this is the stack of Errorf's caller's caller }, }} for i, tt := range tests { ste, ok := tt.err.(interface { StackTrace() StackTrace }) if !ok { ste = tt.err.(interface { Cause() error }).Cause().(interface { StackTrace() StackTrace }) } st := ste.StackTrace() for j, want := range tt.want { testFormatRegexp(t, i, st[j], "%+v", want) } } } // This comment helps to maintain original line numbers // Perhaps this test is too fragile :) func stackTrace() StackTrace { return NewStack(0).StackTrace() // This comment helps to maintain original line numbers // Perhaps this test is too fragile :) } func TestStackTraceFormat(t *testing.T) { tests := []struct { StackTrace format string want string }{{ nil, "%s", `\[\]`, }, { nil, "%v", `\[\]`, }, { nil, "%+v", "", }, { nil, "%#v", `\[\]errors.Frame\(nil\)`, }, { make(StackTrace, 0), "%s", `\[\]`, }, { make(StackTrace, 0), "%v", `\[\]`, }, { make(StackTrace, 0), "%+v", "", }, { make(StackTrace, 0), "%#v", `\[\]errors.Frame{}`, }, { stackTrace()[:2], "%s", `\[stack_test.go stack_test.go\]`, }, { stackTrace()[:2], "%v", `[stack_test.go:207 stack_test.go:254]`, }, { stackTrace()[:2], "%+v", "\n" + "github.com/pkg/errors.stackTrace\n" + "\t.+/github.com/pkg/errors/stack_test.go:210\n" + "github.com/pkg/errors.TestStackTraceFormat\n" + "\t.+/github.com/pkg/errors/stack_test.go:261", }, { stackTrace()[:2], "%#v", `\[\]errors.Frame{stack_test.go:210, stack_test.go:269}`, }} for i, tt := range tests { testFormatRegexp(t, i, tt.StackTrace, tt.format, tt.want) } } func TestNewStack(t *testing.T) { got := NewStack(1).StackTrace() want := NewStack(1).StackTrace() if got[0] != want[0] { t.Errorf("NewStack(remove NewStack): want: %v, got: %v", want, got) } gotFirst := fmt.Sprintf("%+v", got[0])[0:15] if gotFirst != "testing.tRunner" { t.Errorf("NewStack(): want: %v, got: %+v", "testing.tRunner", gotFirst) } } func TestNewNoStackError(t *testing.T) { err := NewNoStackError("test error") err = Trace(err) err = Trace(err) result := fmt.Sprintf("%+v", err) if result != "test error" { t.Errorf("NewNoStackError(): want %s, got %v", "test error", result) } } func TestSuspendError(t *testing.T) { err := io.EOF err = SuspendStack(err) err = Trace(err) result := fmt.Sprintf("%+v", err) if result != "EOF" { t.Errorf("NewNoStackError(): want %s, got %v", "EOF", result) } if io.EOF != Cause(err) { t.Errorf("SuspendStackError can not got back origion error.") } } func TestSuspendTracedWithMessageError(t *testing.T) { tracedErr := Trace(io.EOF) tracedErr = WithStack(tracedErr) tracedErr = WithMessage(tracedErr, "1") tracedErr = SuspendStack(tracedErr) tracedErr = Trace(tracedErr) result := fmt.Sprintf("%+v", tracedErr) if result != "EOF\n1" { t.Errorf("NewNoStackError(): want %s, got %v", "EOF\n1", result) } }