├── .gitignore ├── LICENSE ├── README.md ├── cmd └── whiskey │ └── main.go ├── lock.json ├── manifest.json ├── prefork ├── listener.go ├── listener_test.go ├── logger.go ├── manager.go ├── pipe.go └── worker.go ├── py ├── callbacks.go ├── dict.go ├── dict_test.go ├── error.go ├── int.go ├── iter.go ├── list.go ├── object.go ├── object_test.go ├── python.go ├── python_test.go ├── string.go ├── testdata │ └── hello.py ├── threads.go ├── tuple.go ├── whiskey_py.c └── whiskey_py.h ├── vendor └── github.com │ ├── namsral │ └── flag │ │ ├── LICENSE │ │ ├── README.md │ │ ├── example_test.go │ │ ├── examples │ │ ├── gopher.conf │ │ └── gopher.go │ │ ├── export_test.go │ │ ├── flag.go │ │ ├── flag_test.go │ │ └── testdata │ │ ├── bad_test.conf │ │ └── test.conf │ └── pkg │ └── errors │ ├── .gitignore │ ├── .travis.yml │ ├── LICENSE │ ├── README.md │ ├── appveyor.yml │ ├── bench_test.go │ ├── errors.go │ ├── errors_test.go │ ├── example_test.go │ ├── format_test.go │ ├── stack.go │ └── stack_test.go └── wsgi ├── callbacks.go ├── listen.go ├── pool.go └── wsgi.go /.gitignore: -------------------------------------------------------------------------------- 1 | *.egg-info 2 | *.pyc 3 | whiskey.h 4 | whiskey.so 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2017 Nathan Ostgard 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Whiskey 2 | 3 | Whiskey is a pre-fork Python WSGI server written in Go. It's intended as a 4 | proof-of-concept alternative to something like [gunicorn]. If you had a 5 | standard WSGI application file called hello.py like this one: 6 | 7 | ```python 8 | def application(environ, start_response): 9 | start_response('200 OK', [('Content-Type', 'text/plain')]) 10 | return ['hello', ' ', 'world'] 11 | ``` 12 | 13 | You could run Whiskey like so: 14 | 15 | ``` 16 | whiskey -addr 127.0.0.1:8080 hello:application 17 | ``` 18 | 19 | (Note that hello.py must be importable by Python, so make sure that the 20 | folder containing it has been added to your PYTHONPATH when you run Whiskey.) 21 | 22 | This is currently written for Python 2.7, with plans for Python 3 in the 23 | future at some point. 24 | 25 | ## Caveats 26 | 27 | This is far from complete, and isn't intended for use in anything real. It's 28 | a toy to explore interactions between Go and Python. 29 | 30 | [gunicorn]: http://gunicorn.org -------------------------------------------------------------------------------- /cmd/whiskey/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | _ "net/http/pprof" 8 | 9 | "runtime" 10 | 11 | "os" 12 | 13 | "github.com/namsral/flag" 14 | "github.com/noonat/whiskey/prefork" 15 | "github.com/noonat/whiskey/wsgi" 16 | ) 17 | 18 | func main() { 19 | logger := log.New(os.Stderr, "", log.LstdFlags) 20 | 21 | var ( 22 | addr string 23 | workers int 24 | wsgiConns int 25 | wsgiModule string 26 | ) 27 | flag.StringVar(&addr, "addr", ":8080", "Listen for HTTP connections on this address") 28 | flag.IntVar(&workers, "workers", runtime.NumCPU(), "Number of worker processes.") 29 | flag.StringVar(&wsgiModule, "wsgi-module", "", "Module and function to run for the WSGI application. (e.g. my_wsgi_app:application)") 30 | flag.IntVar(&wsgiConns, "wsgi-conns", 1000, "Number of simultaneous connections per worker.") 31 | flag.Parse() 32 | if wsgiModule == "" { 33 | fmt.Fprintln(os.Stderr, "error: -wsgi-module is required") 34 | flag.Usage() 35 | os.Exit(1) 36 | } 37 | 38 | go http.ListenAndServe(":8181", http.DefaultServeMux) 39 | 40 | w := &wsgi.Worker{Module: wsgiModule, NumConns: wsgiConns} 41 | if err := prefork.Run(w, addr, workers, logger); err != nil { 42 | log.Fatalf("%+v\n", err) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "memo": "20691768f93390d8bae7c29c4ca83783f3f3d01138e9094d42500c197a9a90a3", 3 | "projects": [ 4 | { 5 | "name": "github.com/namsral/flag", 6 | "version": "v1.7.4-pre", 7 | "revision": "71ceffbeb0ba60fccc853971bb3ed4d7d90bfd04", 8 | "packages": [ 9 | "." 10 | ] 11 | }, 12 | { 13 | "name": "github.com/pkg/errors", 14 | "version": "v0.8.0", 15 | "revision": "645ef00459ed84a119197bfb8d8205042c6df63d", 16 | "packages": [ 17 | "." 18 | ] 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "github.com/pkg/errors": { 4 | "version": ">=0.8.0, <1.0.0" 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /prefork/listener.go: -------------------------------------------------------------------------------- 1 | package prefork 2 | 3 | import ( 4 | "net" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | // WorkerListener is a wrapper around a net.Listener that adds helpful things 10 | // that prefork workers will often need. It limits the number of simultaneous 11 | // connections to the specified value, and sets keep alive on the accepted 12 | // conn to the given duration. 13 | // 14 | // Usage of this in workers is completely optional. 15 | func WorkerListener(l net.Listener, numConns int, keepAlivePeriod time.Duration) net.Listener { 16 | return &workerListener{ 17 | TCPListener: l.(*net.TCPListener), 18 | throttle: make(chan struct{}, numConns), 19 | keepAlivePeriod: keepAlivePeriod, 20 | } 21 | } 22 | 23 | type workerListener struct { 24 | *net.TCPListener 25 | throttle chan struct{} 26 | keepAlivePeriod time.Duration 27 | } 28 | 29 | func (wl workerListener) acquire() { 30 | wl.throttle <- struct{}{} 31 | } 32 | 33 | func (wl workerListener) release() { 34 | <-wl.throttle 35 | } 36 | 37 | func (wl workerListener) Accept() (net.Conn, error) { 38 | wl.acquire() 39 | tc, err := wl.TCPListener.AcceptTCP() 40 | if err != nil { 41 | wl.release() 42 | return nil, err 43 | } 44 | tc.SetKeepAlive(true) 45 | tc.SetKeepAlivePeriod(wl.keepAlivePeriod) 46 | return &workerConn{Conn: tc, release: wl.release}, nil 47 | } 48 | 49 | type workerConn struct { 50 | net.Conn 51 | release func() 52 | releaseOnce sync.Once 53 | } 54 | 55 | func (wc *workerConn) Close() error { 56 | err := wc.Conn.Close() 57 | wc.releaseOnce.Do(wc.release) 58 | return err 59 | } 60 | -------------------------------------------------------------------------------- /prefork/listener_test.go: -------------------------------------------------------------------------------- 1 | package prefork 2 | 3 | import ( 4 | "net" 5 | "sync" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func TestWorkerListener(t *testing.T) { 11 | l, err := net.Listen("tcp", ":0") 12 | if err != nil { 13 | t.Fatal(err) 14 | } 15 | defer l.Close() 16 | 17 | wg := &sync.WaitGroup{} 18 | wg.Add(1) 19 | 20 | wl := WorkerListener(l, 2, 30*time.Second) 21 | conns := []net.Conn{} 22 | go func() { 23 | for i := 0; i < 3; i++ { 24 | c, err := wl.Accept() 25 | if err != nil { 26 | t.Error(err) 27 | break 28 | } 29 | conns = append(conns, c) 30 | } 31 | wg.Done() 32 | }() 33 | defer func() { 34 | for _, c := range conns { 35 | c.Close() 36 | } 37 | }() 38 | 39 | connected := make(chan struct{}) 40 | go func() { 41 | for i := 0; i < 3; i++ { 42 | _, err := net.Dial("tcp", wl.Addr().String()) 43 | if err != nil { 44 | t.Error(err) 45 | } 46 | } 47 | close(connected) 48 | }() 49 | 50 | timer := time.NewTimer(time.Second) 51 | select { 52 | case <-timer.C: 53 | t.Fatal("timed out") 54 | case <-connected: 55 | timer.Stop() 56 | } 57 | 58 | if len(conns) != 2 { 59 | t.Fatalf("expected 2 conns, got %d", len(conns)) 60 | } 61 | conns[0].Close() 62 | wg.Wait() 63 | if len(conns) != 3 { 64 | t.Fatalf("expected 3 conns, got %d", len(conns)) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /prefork/logger.go: -------------------------------------------------------------------------------- 1 | package prefork 2 | 3 | // Logger is the interface required for logging performed by this package. 4 | type Logger interface { 5 | Printf(fmt string, args ...interface{}) 6 | Println(args ...interface{}) 7 | SetPrefix(prefix string) 8 | } 9 | -------------------------------------------------------------------------------- /prefork/manager.go: -------------------------------------------------------------------------------- 1 | // Package prefork provides a way to run processes in a prefork server model, 2 | // where a manager process launches a number of subprocesses, all intended to 3 | // communicate in parallel over a shared listener. 4 | // 5 | // This model isn't usually necessary in Go, but it can be helpeful when 6 | // wrapping other software that aren't compatible with Go's concurrency model. 7 | // Python is a good example of this, as it requires a mutex lock across 8 | // threads, and shares global state at the process level. 9 | package prefork 10 | 11 | import ( 12 | "fmt" 13 | "net" 14 | "os" 15 | "os/exec" 16 | "os/signal" 17 | "sync" 18 | 19 | "github.com/pkg/errors" 20 | ) 21 | 22 | func execWorker(wg *sync.WaitGroup, lnf *os.File, env []string, logger Logger) (*Pipe, error) { 23 | mp, wp, err := NewPipes() 24 | if err != nil { 25 | return nil, errors.Wrap(err, "error creating worker pipes") 26 | } 27 | 28 | cmd := exec.Command(os.Args[0], os.Args[1:]...) 29 | cmd.Env = env 30 | cmd.ExtraFiles = []*os.File{lnf, wp.ReadFile, wp.WriteFile} 31 | cmd.Stderr = os.Stderr 32 | cmd.Stdout = os.Stdout 33 | if err := cmd.Start(); err != nil { 34 | mp.Close() 35 | wp.Close() 36 | return nil, errors.Wrap(err, "error starting worker") 37 | } 38 | 39 | // Workers processes should gracefully stop when they receieve an 40 | // interrupt. Ignore the first interrupt we receive, to give them a chance 41 | // to stop. But if we receive a second one, allow this process to stop. 42 | closing := false 43 | interrupt := make(chan os.Signal, 1) 44 | signal.Notify(interrupt, os.Interrupt) 45 | go func() { 46 | <-interrupt 47 | signal.Stop(interrupt) 48 | closing = true 49 | }() 50 | 51 | // This keepalive communication between processes isn't very advanced 52 | // right now. It just writes a byte 1 once a second from the worker to 53 | // the manager to indicate that it's still alive. When it is shutting 54 | // down, it sends a 0. 55 | // 56 | // This pipe will eventually be used to communicate things like stats 57 | // up to the manager, to coordinate shutdown, etc. 58 | go func() { 59 | b := []byte{1} 60 | for { 61 | _, err := mp.Read(b) 62 | if err != nil { 63 | if !closing { 64 | logger.Println("error reading keepalive:", err) 65 | } 66 | break 67 | } 68 | if b[0] == 0 { 69 | break 70 | } 71 | } 72 | }() 73 | 74 | go func(cmd *exec.Cmd) { 75 | pid := cmd.Process.Pid 76 | if err := cmd.Wait(); err != nil { 77 | logger.Printf("worker %d wait returned error: %s", pid, err) 78 | } else { 79 | logger.Printf("worker %d stopped", pid) 80 | } 81 | wg.Done() 82 | wp.Close() 83 | mp.Close() 84 | }(cmd) 85 | 86 | return mp, nil 87 | } 88 | 89 | func runManager(w Worker, addr string, numWorkers int, logger Logger) error { 90 | logger.SetPrefix(fmt.Sprintf("manager\t[%d]\t", os.Getpid())) 91 | 92 | ln, err := net.Listen("tcp", addr) 93 | if err != nil { 94 | return errors.Wrap(err, "error creating listener") 95 | } 96 | logger.Println("listening on", addr) 97 | 98 | if numWorkers == 0 { 99 | // FIXME: it might be better to reuse the code in worker.go, so that 100 | // listener would get closed on sigint. 101 | logger.Println("worker count is 0, running in single process mode") 102 | return w.Serve(ln, logger) 103 | } 104 | 105 | lnf, err := ln.(*net.TCPListener).File() 106 | if err != nil { 107 | return errors.Wrap(err, "error getting file for listener") 108 | } 109 | 110 | // FIXME: The worker management is pretty naive right now. It just starts 111 | // them all up once and assumes they'll keep running. Eventually, the 112 | // manager should kill workers that are running but non-responsive, and 113 | // should ensure that the appropriate number of workers are always running. 114 | // 115 | // It would also be nice if it also supported some of the signals that 116 | // gunicorn does for worker management. 117 | logger.Println("starting", numWorkers, "workers") 118 | wg := &sync.WaitGroup{} 119 | wg.Add(numWorkers) 120 | env := append([]string{}, os.Environ()...) 121 | env = append(env, "PREFORK_WORKER=1") 122 | for i := 0; i < numWorkers; i++ { 123 | if _, err := execWorker(wg, lnf, env, logger); err != nil { 124 | return err 125 | } 126 | } 127 | wg.Wait() 128 | 129 | return nil 130 | } 131 | 132 | // Run starts the process. It handles either starting the manager or starting 133 | // the worker, as appropriate, depending on the execution environment. 134 | // 135 | // The manager will create a listener on the given address, and launch 136 | // subprocesses for the number of workers specified. It runs the workers with 137 | // the same arguments the original process was passed, and also adds a 138 | // PREFORK_WORKER environment variable. It uses the presence of that variable 139 | // to determine that the subprocess should act as a worker. 140 | // 141 | // When this function is invoked in a worker, the worker calls w.Serve(...) 142 | // and passes it a listener. 143 | func Run(w Worker, addr string, numWorkers int, logger Logger) error { 144 | if os.Getenv("PREFORK_WORKER") != "" { 145 | return runWorker(w, logger) 146 | } 147 | return runManager(w, addr, numWorkers, logger) 148 | } 149 | -------------------------------------------------------------------------------- /prefork/pipe.go: -------------------------------------------------------------------------------- 1 | package prefork 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/pkg/errors" 7 | ) 8 | 9 | // Pipe is used for bi-directional communication between processes. 10 | type Pipe struct { 11 | ReadFile *os.File 12 | WriteFile *os.File 13 | } 14 | 15 | // NewPipes creates a pair of pipe objects. Data written to one of the pipes 16 | // will be readable from the other. 17 | func NewPipes() (*Pipe, *Pipe, error) { 18 | pr, cw, err := os.Pipe() 19 | if err != nil { 20 | return nil, nil, errors.Wrap(err, "error creating pipe") 21 | } 22 | cr, pw, err := os.Pipe() 23 | if err != nil { 24 | pr.Close() 25 | cw.Close() 26 | return nil, nil, errors.Wrap(err, "error creating pipe") 27 | } 28 | 29 | pp := &Pipe{ReadFile: pr, WriteFile: pw} 30 | cp := &Pipe{ReadFile: cr, WriteFile: cw} 31 | return pp, cp, nil 32 | } 33 | 34 | // Close the pipe. 35 | func (p *Pipe) Close() error { 36 | rfErr := p.ReadFile.Close() 37 | wfErr := p.WriteFile.Close() 38 | if rfErr != nil { 39 | return errors.Wrap(rfErr, "error closing pipe reader") 40 | } else if wfErr != nil { 41 | return errors.Wrap(wfErr, "error closing pipe writer") 42 | } 43 | return nil 44 | } 45 | 46 | // Read data from the pipe. 47 | func (p *Pipe) Read(b []byte) (n int, err error) { 48 | return p.ReadFile.Read(b) 49 | } 50 | 51 | // Write data to the pipe. 52 | func (p *Pipe) Write(b []byte) (n int, err error) { 53 | return p.WriteFile.Write(b) 54 | } 55 | -------------------------------------------------------------------------------- /prefork/worker.go: -------------------------------------------------------------------------------- 1 | package prefork 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "os" 7 | "os/signal" 8 | "time" 9 | 10 | "github.com/pkg/errors" 11 | ) 12 | 13 | // These file descriptors are passed from the master to the worker. 14 | const ( 15 | listenFD = uintptr(3) 16 | workerPipeReadFD = uintptr(4) 17 | workerPipeWriteFD = uintptr(5) 18 | ) 19 | 20 | // Worker is the interface that workers must implement. The master creates the 21 | // listening TCP socket, and it's passed into each of the workers so they can 22 | // all call Accept() on it. 23 | // 24 | // Serve should block indefinitely and return when the worker should terminate. 25 | type Worker interface { 26 | Serve(ln net.Listener, logger Logger) error 27 | } 28 | 29 | func runWorker(w Worker, logger Logger) error { 30 | logger.SetPrefix(fmt.Sprintf("worker\t[%d]\t", os.Getpid())) 31 | 32 | // Recreate the TCP listener from the inherited files 33 | lnf := os.NewFile(listenFD, "") 34 | ln, err := net.FileListener(lnf) 35 | if err != nil { 36 | return errors.Wrap(err, "error creating net.FileListener") 37 | } 38 | 39 | // Recreate the internal communication pipe from the inherited files 40 | wp := &Pipe{ 41 | ReadFile: os.NewFile(workerPipeReadFD, ""), 42 | WriteFile: os.NewFile(workerPipeWriteFD, ""), 43 | } 44 | defer wp.Close() 45 | 46 | // Shutdown on SIGINT or when the done channel is closed 47 | closed := false 48 | done := make(chan struct{}) 49 | interrupt := make(chan os.Signal, 1) 50 | signal.Notify(interrupt, os.Interrupt) 51 | go func() { 52 | select { 53 | case <-done: 54 | case <-interrupt: 55 | close(done) 56 | } 57 | signal.Stop(interrupt) 58 | closed = true 59 | wp.Write([]byte{0}) 60 | ln.Close() 61 | }() 62 | 63 | // Send a keepalive to the master 64 | t := time.NewTicker(time.Second) 65 | defer t.Stop() 66 | go func() { 67 | b := []byte{1} 68 | var err error 69 | for range t.C { 70 | _, err = wp.Write(b) 71 | if err != nil { 72 | break 73 | } 74 | } 75 | if !closed && err != nil { 76 | logger.Println("error writing keepalive:", err) 77 | } 78 | }() 79 | 80 | logger.Println("started worker") 81 | if err := w.Serve(ln, logger); !closed && err != nil { 82 | return err 83 | } 84 | 85 | return nil 86 | } 87 | -------------------------------------------------------------------------------- /py/callbacks.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | /* 4 | #include "whiskey_py.h" 5 | */ 6 | import "C" 7 | 8 | import "log" 9 | 10 | // CallbackFunc is used with RegisterCallback. 11 | type CallbackFunc func(args Tuple) (Object, error) 12 | 13 | var callbacks = map[string]CallbackFunc{} 14 | 15 | // RegisterCallback adds a callback function to the internal map for the 16 | // Whiskey Python module. This provides a way to easily invoke Go functions 17 | // from Python without needing to create exported cgo functions and Python 18 | // wrappers for each one. 19 | func RegisterCallback(name string, fn CallbackFunc) { 20 | callbacks[name] = fn 21 | } 22 | 23 | //export whiskeyCall 24 | func whiskeyCall(name *C.char, args *C.PyObject) *C.PyObject { 25 | k := C.GoString(name) 26 | fn, ok := callbacks[k] 27 | if !ok { 28 | log.Printf("error: whiskey_call called for unknown callback %q\n", k) 29 | None.IncRef() 30 | return None.PyObject 31 | } 32 | result, err := fn(Tuple{Object{args}}) 33 | if err != nil { 34 | log.Printf("error: whiskey_call for %q failed: %+v\n", k, err) 35 | None.IncRef() 36 | return None.PyObject 37 | } 38 | return result.PyObject 39 | } 40 | -------------------------------------------------------------------------------- /py/dict.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | /* 4 | #include "whiskey_py.h" 5 | */ 6 | import "C" 7 | 8 | import ( 9 | "github.com/pkg/errors" 10 | ) 11 | 12 | // Dict wraps a Python dict. 13 | type Dict struct { 14 | Object 15 | } 16 | 17 | // NewDict creates a new Python dictionary. 18 | func NewDict() (Dict, error) { 19 | var d Dict 20 | d.PyObject = C.PyDict_New() 21 | if d.PyObject == nil { 22 | return d, errors.Wrap(GetError(), "error creating Python dict") 23 | } 24 | return d, nil 25 | } 26 | 27 | // GetItem gets a value from the Python dict by key. 28 | // This calls IncRef on the value before returning it (rather than returning 29 | // a borrowed reference like PyDict_GetItem). 30 | func (d Dict) GetItem(k Object) (Object, error) { 31 | var v Object 32 | v.PyObject = C.PyDict_GetItem(d.PyObject, k.PyObject) 33 | if v.PyObject == nil { 34 | return v, errors.Wrap(GetError(), "error getting dict item") 35 | } 36 | v.IncRef() 37 | return v, nil 38 | } 39 | 40 | // SetItem sets an item in a Python dict using Python Objects for the key 41 | // and the value. 42 | func (d Dict) SetItem(k, v Object) error { 43 | if C.PyDict_SetItem(d.PyObject, k.PyObject, v.PyObject) != 0 { 44 | return errors.Wrap(GetError(), "error setting dict item") 45 | } 46 | return nil 47 | } 48 | 49 | // SetItemInt sets an item in a Python dict using a Python Object for a key 50 | // and a Go int for a value. 51 | func (d Dict) SetItemInt(k Object, i int) error { 52 | v, err := NewInt(i) 53 | if err != nil { 54 | return err 55 | } 56 | return d.SetItem(k, v.Object) 57 | } 58 | 59 | // SetItemString sets an item in a Python dict using a Python Object for a key 60 | // and a Go string for a value. 61 | func (d Dict) SetItemString(k Object, s string) error { 62 | v, err := NewString(s) 63 | if err != nil { 64 | return err 65 | } 66 | return d.SetItem(k, v.Object) 67 | } 68 | -------------------------------------------------------------------------------- /py/dict_test.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | import "testing" 4 | 5 | func TestDictGetAndSetItem(t *testing.T) { 6 | d, err := NewDict() 7 | if err != nil { 8 | t.Fatal(err) 9 | } 10 | k, err := NewString("a") 11 | if err != nil { 12 | t.Fatal(err) 13 | } 14 | 15 | v, err := d.GetItem(k.Object) 16 | if err != nil { 17 | t.Error(err) 18 | } else if v.PyObject != nil { 19 | t.Error("expected nil") 20 | } 21 | 22 | vn, err := NewInt(1) 23 | if err != nil { 24 | t.Fatal(err) 25 | } 26 | err = d.SetItem(k.Object, vn.Object) 27 | if err != nil { 28 | t.Fatal(err) 29 | } 30 | 31 | v, err = d.GetItem(k.Object) 32 | if err != nil { 33 | t.Fatal(err) 34 | } 35 | n, err := v.GoInt() 36 | if err != nil { 37 | t.Fatal(err) 38 | } else if n != 1 { 39 | t.Errorf("expected 1, got %d", n) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /py/error.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | /* 4 | #include "whiskey_py.h" 5 | */ 6 | import "C" 7 | 8 | import "github.com/pkg/errors" 9 | 10 | var gettingError = false 11 | 12 | // GetError returns the currently set exception as a Go error value. 13 | // 14 | // The Python C API doesn't provide a way to convert the current exception to 15 | // a string, so this function imports the traceback module and uses the methods 16 | // from there to convert it into a string. 17 | // 18 | // This clears the Python error as a side effect. 19 | func GetError() error { 20 | if gettingError { 21 | // This is ugly but I haven't put time into figuring out a better way 22 | // yet... if we use the primitives from this package in this function 23 | // and one of them gets an error, they will try to call back into this 24 | // function again. Rather than allowing them to do this potentially 25 | // forever, only allow one level of nested calls here and just print 26 | // out any errors we encounter while in function. 27 | C.PyErr_Print() 28 | return errors.New("(recursive call to GetError, see printed error)") 29 | } 30 | gettingError = true 31 | defer func() { 32 | gettingError = false 33 | }() 34 | 35 | var typ, val, tb Object 36 | C.PyErr_Fetch(&typ.PyObject, &val.PyObject, &tb.PyObject) 37 | if typ.PyObject == nil { 38 | return nil 39 | } 40 | 41 | m, err := ImportModule("traceback") 42 | if err != nil { 43 | return err 44 | } 45 | defer m.DecRef() 46 | 47 | var s string 48 | if tb.PyObject == nil { 49 | s = "format_exception_only" 50 | } else { 51 | s = "format_exception" 52 | } 53 | fn, err := m.GetAttrString(s) 54 | if err != nil { 55 | return err 56 | } 57 | defer fn.DecRef() 58 | 59 | var o Object 60 | if tb.PyObject == nil { 61 | o, err = fn.Call(typ, val) 62 | } else { 63 | o, err = fn.Call(typ, val, tb) 64 | } 65 | if err != nil { 66 | return err 67 | } 68 | defer o.DecRef() 69 | list, err := o.List() 70 | if err != nil { 71 | return err 72 | } 73 | 74 | sep, err := NewString("\n") 75 | if err != nil { 76 | return err 77 | } 78 | defer sep.DecRef() 79 | 80 | ps, err := sep.Join(list) 81 | if err != nil { 82 | return err 83 | } 84 | defer ps.DecRef() 85 | 86 | s, err = ps.GoString() 87 | if err != nil { 88 | return err 89 | } 90 | 91 | return errors.New(s) 92 | } 93 | -------------------------------------------------------------------------------- /py/int.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | /* 4 | #include "whiskey_py.h" 5 | */ 6 | import "C" 7 | 8 | import ( 9 | "github.com/pkg/errors" 10 | ) 11 | 12 | // Int wraps a Python integer. 13 | type Int struct { 14 | Object 15 | } 16 | 17 | // NewInt converts a Go int into a Python Int. 18 | func NewInt(n int) (Int, error) { 19 | var pn Int 20 | pn.PyObject = C.PyInt_FromLong(C.long(n)) 21 | if pn.PyObject == nil { 22 | return pn, errors.Wrap(GetError(), "error converting to Python int") 23 | } 24 | return pn, nil 25 | } 26 | 27 | // GoInt converts the Python int into a Go int. 28 | func (pn Int) GoInt() (int, error) { 29 | n := C.PyInt_AsLong(pn.PyObject) 30 | if n == -1 && C.PyErr_Occurred() != nil { 31 | return 0, errors.Wrap(GetError(), "error converting to Go int") 32 | } 33 | return int(n), nil 34 | } 35 | -------------------------------------------------------------------------------- /py/iter.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | /* 4 | #include "whiskey_py.h" 5 | */ 6 | import "C" 7 | 8 | import ( 9 | "github.com/pkg/errors" 10 | ) 11 | 12 | // Iter wraps a Python iteration object. 13 | type Iter struct { 14 | Object 15 | } 16 | 17 | // Next returns the next item for the iteration, or nil if there aren't 18 | // any more items in the iteration. 19 | func (it Iter) Next() (Object, error) { 20 | var o Object 21 | o.PyObject = C.PyIter_Next(it.PyObject) 22 | if o.PyObject == nil && C.PyErr_Occurred() != nil { 23 | return o, errors.Wrap(GetError(), "error calling iterator next") 24 | } 25 | return o, nil 26 | } 27 | -------------------------------------------------------------------------------- /py/list.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | /* 4 | #include "whiskey_py.h" 5 | */ 6 | import "C" 7 | 8 | import ( 9 | "github.com/pkg/errors" 10 | ) 11 | 12 | // List wraps a Python list object. 13 | type List struct { 14 | Object 15 | } 16 | 17 | // NewList creates a new Python list with the given size. 18 | func NewList(size int) (l List, err error) { 19 | l.PyObject = C.PyList_New(C.Py_ssize_t(size)) 20 | if l.PyObject == nil { 21 | err = errors.Wrapf(GetError(), "error creating Python list of size %d", size) 22 | } 23 | return 24 | } 25 | 26 | // GetItem gets an item from the Python list. 27 | // This calls IncRef on the value before returning it (rather than returning 28 | // a borrowed reference like PyList_GetItem). 29 | func (l List) GetItem(index int) (Object, error) { 30 | var o Object 31 | o.PyObject = C.PyList_GetItem(l.PyObject, C.Py_ssize_t(index)) 32 | if o.PyObject == nil { 33 | return o, errors.Wrapf(GetError(), "error getting list item %d", index) 34 | } 35 | o.IncRef() 36 | return o, nil 37 | } 38 | 39 | // Len returns the size of the Python list. 40 | func (l List) Len() int { 41 | return int(C.PyList_Size(l.PyObject)) 42 | } 43 | 44 | // SetItem sets an item in the Python list using a Python object for the value. 45 | // 46 | // Note that, because C.PyList_SetItem steals references, this function calls 47 | // IncRef on the passed object, to make things more consistent with other 48 | // places in the Python C API. 49 | func (l List) SetItem(index int, v Object) error { 50 | v.IncRef() 51 | if C.PyList_SetItem(l.PyObject, C.Py_ssize_t(index), v.PyObject) != 0 { 52 | v.DecRef() 53 | return errors.Wrapf(GetError(), "error setting tuple item %d", index) 54 | } 55 | return nil 56 | } 57 | -------------------------------------------------------------------------------- /py/object.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | /* 4 | #include "whiskey_py.h" 5 | */ 6 | import "C" 7 | 8 | import ( 9 | "fmt" 10 | "unsafe" 11 | 12 | "github.com/pkg/errors" 13 | ) 14 | 15 | // Object embeds a *C.PyObject, which is the generic object wrapper type 16 | // returned by most of the Python C API. 17 | type Object struct { 18 | PyObject *C.PyObject 19 | } 20 | 21 | // NewModuleString Create a new module with th by compiling src into a string 22 | func NewModuleString(name, src string) (Object, error) { 23 | var o Object 24 | 25 | cs := C.CString(src) 26 | cfn := C.CString(fmt.Sprintf("", name)) 27 | co := C.Py_CompileStringFlags(cs, cfn, C.Py_file_input, nil) 28 | C.free(unsafe.Pointer(cs)) 29 | C.free(unsafe.Pointer(cfn)) 30 | if co == nil { 31 | return o, errors.Errorf("error compiling source for module %s: %s", name, GetError()) 32 | } 33 | defer C.Py_DecRef(co) 34 | 35 | cn := C.CString(name) 36 | o.PyObject = C.PyImport_ExecCodeModule(cn, co) 37 | C.free(unsafe.Pointer(cn)) 38 | if o.PyObject == nil { 39 | return o, errors.Errorf("error executing module %s: %s", name, GetError()) 40 | } 41 | return o, nil 42 | } 43 | 44 | // ImportModule imports a Python module. 45 | func ImportModule(moduleName string) (Object, error) { 46 | var o Object 47 | cs := C.CString(moduleName) 48 | o.PyObject = C.PyImport_ImportModule(cs) 49 | C.free(unsafe.Pointer(cs)) 50 | if o.PyObject == nil { 51 | return o, errors.Errorf("error importing module %s: %s", moduleName, GetError()) 52 | } 53 | return o, nil 54 | } 55 | 56 | // Call invokes the associated Python object as a callable. 57 | // 58 | // This is equivalent to PyObject_CallObject. The passed arguments are packed 59 | // into a tuple and passed to the callable as positional arguments. 60 | func (o Object) Call(args ...Object) (Object, error) { 61 | var result Object 62 | var t Tuple 63 | if len(args) > 0 { 64 | var err error 65 | t, err = NewTupleObjects(args) 66 | if err != nil { 67 | return result, err 68 | } 69 | defer t.DecRef() 70 | } 71 | result.PyObject = C.PyObject_CallObject(o.PyObject, t.PyObject) 72 | if result.PyObject == nil { 73 | return result, errors.Errorf("error calling object: %s", GetError()) 74 | } 75 | return result, nil 76 | } 77 | 78 | // ConvertInto converts the object into a more strict type, and stores the 79 | // result in the given pointer. 80 | // 81 | // This uses a type assertion to figure out what ptr points to, and does any 82 | // necessary validations and conversions. This doesn't support all types, just 83 | // the ones necessary for Whiskey's codebase. 84 | // 85 | // When copying the Object into a pointer to another Object variable, it 86 | // returns a new reference, not a borrowed one. 87 | func (o Object) ConvertInto(ptr interface{}) error { 88 | switch t := ptr.(type) { 89 | case *List: 90 | pl, err := o.List() 91 | if err != nil { 92 | return err 93 | } 94 | pl.IncRef() 95 | *t = pl 96 | case *Object: 97 | o.IncRef() 98 | *t = o 99 | case *String: 100 | ps, err := o.String() 101 | if err != nil { 102 | return err 103 | } 104 | ps.IncRef() 105 | *t = ps 106 | case *int: 107 | n, err := o.GoInt() 108 | if err != nil { 109 | return err 110 | } 111 | *t = n 112 | case *string: 113 | s, err := o.GoString() 114 | if err != nil { 115 | return err 116 | } 117 | *t = s 118 | default: 119 | return errors.New("unsupported type for ptr") 120 | } 121 | 122 | return nil 123 | } 124 | 125 | // DecRef decrements the reference count for the Python object. 126 | func (o Object) DecRef() { 127 | if o.PyObject != nil { 128 | C.Py_DecRef(o.PyObject) 129 | } 130 | } 131 | 132 | // IncRef increments the reference count for the Python object. 133 | func (o Object) IncRef() { 134 | if o.PyObject != nil { 135 | C.Py_IncRef(o.PyObject) 136 | } 137 | } 138 | 139 | // GoInt converts the object into a Go int. 140 | // The underlying type must be a Python int or an error will be returned. 141 | func (o Object) GoInt() (int, error) { 142 | pn, err := o.Int() 143 | if err != nil { 144 | return 0, err 145 | } 146 | return pn.GoInt() 147 | } 148 | 149 | // GoString converts the object into a Go string. 150 | // The underlying type must be a Python string or an error will be returned. 151 | func (o Object) GoString() (string, error) { 152 | ps, err := o.String() 153 | if err != nil { 154 | return "", err 155 | } 156 | return ps.GoString() 157 | } 158 | 159 | // Int wraps the object in an Int struct. 160 | // The underlying type must be a Python int or an error will be returned. 161 | func (o Object) Int() (Int, error) { 162 | n := Int{o} 163 | if C.whiskey_check_int(o.PyObject) == 0 { 164 | return n, errors.New("object is not an integer") 165 | } 166 | return n, nil 167 | } 168 | 169 | // List wraps the object in a List struct. 170 | // The underlying type must be a Python list or an error will be returned. 171 | func (o Object) List() (List, error) { 172 | l := List{o} 173 | if C.whiskey_check_list(o.PyObject) == 0 { 174 | return l, errors.New("object is not a list") 175 | } 176 | return l, nil 177 | } 178 | 179 | // String wraps the object in a String struct. 180 | // The underlying type must be a Python string or an error will be returned. 181 | func (o Object) String() (String, error) { 182 | s := String{o} 183 | if C.whiskey_check_string(o.PyObject) == 0 { 184 | return s, errors.New("object is not a string") 185 | } 186 | return s, nil 187 | } 188 | 189 | // Tuple wraps the object in a Tuple struct. 190 | // The underlying type must be a Python tuple or an error will be returned. 191 | func (o Object) Tuple() (Tuple, error) { 192 | t := Tuple{o} 193 | if C.whiskey_check_tuple(o.PyObject) == 0 { 194 | return t, errors.New("object is not a tuple") 195 | } 196 | return t, nil 197 | } 198 | 199 | // GetAttrString returns the value for the given attribute. 200 | // It's the equivalent of calling getattr(o, attr) in Python. 201 | func (o Object) GetAttrString(attr string) (Object, error) { 202 | var v Object 203 | cs := C.CString(attr) 204 | v.PyObject = C.PyObject_GetAttrString(o.PyObject, cs) 205 | C.free(unsafe.Pointer(cs)) 206 | if v.PyObject == nil { 207 | return v, errors.Errorf("error getting attribute %s: %s", attr, GetError()) 208 | } 209 | return v, nil 210 | } 211 | 212 | // IsCallable returns true if the underlying Python object is callable. 213 | func (o Object) IsCallable() bool { 214 | return C.PyCallable_Check(o.PyObject) != 0 215 | } 216 | 217 | // Iter returns an iterator for the object. 218 | // It's the equivalent of calling iter(o) in Python. 219 | func (o Object) Iter() (Iter, error) { 220 | var it Iter 221 | it.PyObject = C.PyObject_GetIter(o.PyObject) 222 | if it.PyObject == nil { 223 | return it, errors.New("error getting iterator") 224 | } 225 | return it, nil 226 | } 227 | -------------------------------------------------------------------------------- /py/object_test.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | import "testing" 4 | 5 | func mustInt(t *testing.T, n int) Int { 6 | pn, err := NewInt(n) 7 | if err != nil { 8 | t.Fatal(err) 9 | } 10 | return pn 11 | } 12 | 13 | func mustString(t *testing.T, s string) String { 14 | ps, err := NewString(s) 15 | if err != nil { 16 | t.Fatal(err) 17 | } 18 | return ps 19 | } 20 | 21 | func TestImportModule(t *testing.T) { 22 | m, err := ImportModule("this_does_not_exist") 23 | if err == nil { 24 | t.Error("expected error, got nil") 25 | } 26 | 27 | m, err = ImportModule("hello") 28 | if err != nil { 29 | t.Fatal(err) 30 | } 31 | v, err := m.GetAttrString("n") 32 | if err != nil { 33 | t.Fatal(err) 34 | } 35 | n, err := v.GoInt() 36 | if err != nil { 37 | t.Error(err) 38 | } else if n != 1 { 39 | t.Errorf("expected 1, got %d", n) 40 | } 41 | } 42 | 43 | func TestObjectCall(t *testing.T) { 44 | m, err := ImportModule("hello") 45 | if err != nil { 46 | t.Fatal(err) 47 | } 48 | 49 | fn, err := m.GetAttrString("fn") 50 | if err != nil { 51 | t.Fatal(err) 52 | } 53 | 54 | args := []Object{} 55 | for _, s := range []string{"foo", "bar", "baz"} { 56 | ps, err := NewString(s) 57 | if err != nil { 58 | t.Fatal(err) 59 | } 60 | args = append(args, ps.Object) 61 | } 62 | result, err := fn.Call(args...) 63 | if err != nil { 64 | t.Fatal(err) 65 | } 66 | s, err := result.GoString() 67 | if err != nil { 68 | t.Error(err) 69 | } else if s != "foo bar baz" { 70 | t.Errorf(`expected "foo bar baz", got %q`, s) 71 | } 72 | } 73 | 74 | func TestObjectRefs(t *testing.T) { 75 | o := mustString(t, "foo").Object 76 | defer o.DecRef() 77 | 78 | if rc := int(o.PyObject.ob_refcnt); rc != 1 { 79 | t.Errorf("expected 1, got %d", rc) 80 | } 81 | o.IncRef() 82 | if rc := int(o.PyObject.ob_refcnt); rc != 2 { 83 | t.Errorf("expected 2, got %d", rc) 84 | } 85 | o.DecRef() 86 | if rc := int(o.PyObject.ob_refcnt); rc != 1 { 87 | t.Errorf("expected 1, got %d", rc) 88 | } 89 | } 90 | 91 | func TestObjectGoInt(t *testing.T) { 92 | o := mustInt(t, 123).Object 93 | defer o.DecRef() 94 | n, err := o.GoInt() 95 | if err != nil { 96 | t.Error(err) 97 | } else if n != 123 { 98 | t.Errorf("expected 123, got %d", n) 99 | } 100 | 101 | o = mustString(t, "foo").Object 102 | defer o.DecRef() 103 | _, err = o.GoInt() 104 | if err == nil { 105 | t.Error("expected error, got nil") 106 | } 107 | } 108 | 109 | func TestObjectGoString(t *testing.T) { 110 | ps := mustString(t, "foo") 111 | defer ps.DecRef() 112 | s, err := ps.Object.GoString() 113 | if err != nil { 114 | t.Error(err) 115 | } else if s != "foo" { 116 | t.Errorf(`expected "foo", got %q`, s) 117 | } 118 | 119 | pn := mustInt(t, 123) 120 | defer pn.DecRef() 121 | _, err = pn.Object.GoString() 122 | if err == nil { 123 | t.Error("expected error, got nil") 124 | } 125 | } 126 | 127 | func TestObjectInt(t *testing.T) { 128 | pn1 := mustInt(t, 123) 129 | defer pn1.DecRef() 130 | pn2, err := pn1.Object.Int() 131 | if err != nil { 132 | t.Error(err) 133 | } else if pn1 != pn2 { 134 | t.Errorf("pn1 != pn2 (expected %v, got %v)", pn1, pn2) 135 | } 136 | 137 | o := mustString(t, "foo").Object 138 | defer o.DecRef() 139 | _, err = o.Int() 140 | if err == nil { 141 | t.Error("expected error, got nil") 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /py/python.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | // This file contains helper functions for the Python C API. Many of the Python C 4 | // API functions are a bit cumbersome to use, especially from cgo, so these 5 | // helpers make life a little easier. 6 | 7 | /* 8 | #cgo pkg-config: python2 9 | #include "whiskey_py.h" 10 | */ 11 | import "C" 12 | 13 | import ( 14 | "bytes" 15 | "fmt" 16 | "sync" 17 | 18 | "github.com/pkg/errors" 19 | ) 20 | 21 | type manyErrors struct { 22 | err error 23 | errs []error 24 | } 25 | 26 | func (e *manyErrors) Error() string { 27 | buf := &bytes.Buffer{} 28 | fmt.Fprintf(buf, "%+v\n", e.err) 29 | fmt.Fprintf(buf, "%d errors occurred, listed below:\n", len(e.errs)) 30 | for i, err := range e.errs { 31 | fmt.Fprintf(buf, "\n- error %d: %+v\n", i+1, err) 32 | } 33 | return buf.String() 34 | } 35 | 36 | var ( 37 | // None is a wrapper for the Python None value. 38 | None Object 39 | 40 | // True is a wrapper for the Python True value. 41 | True Object 42 | 43 | // False is a wrapper for the Python False value. 44 | False Object 45 | 46 | initialized = false 47 | initializers = []func() error{} 48 | finalizers = []func() error{} 49 | mutex = &sync.Mutex{} 50 | ) 51 | 52 | func AddInitializer(fn func() error) error { 53 | initializers = append(initializers, fn) 54 | return nil 55 | } 56 | 57 | func AddFinalizer(fn func() error) error { 58 | finalizers = append(finalizers, fn) 59 | return nil 60 | } 61 | 62 | func initialize() error { 63 | if C.whiskey_initialize() != 0 { 64 | return errors.New("whiskey_initialize failed") 65 | } 66 | C.PyEval_InitThreads() 67 | 68 | None.PyObject = C.whiskey_none 69 | True.PyObject = C.whiskey_true 70 | False.PyObject = C.whiskey_false 71 | resetStringCache() 72 | 73 | var errs []error 74 | for _, fn := range initializers { 75 | if err := fn(); err != nil { 76 | errs = append(errs, err) 77 | } 78 | } 79 | if len(errs) > 0 { 80 | return &manyErrors{ 81 | err: errors.New("error calling initialization functions"), 82 | errs: errs, 83 | } 84 | } 85 | 86 | return nil 87 | } 88 | 89 | func finalize() error { 90 | errs := []error{} 91 | for _, fn := range finalizers { 92 | if err := fn(); err != nil { 93 | errs = append(errs, err) 94 | } 95 | } 96 | 97 | None.PyObject = nil 98 | True.PyObject = nil 99 | False.PyObject = nil 100 | resetStringCache() 101 | 102 | C.whiskey_finalize() 103 | 104 | if len(errs) > 0 { 105 | return &manyErrors{ 106 | err: errors.New("error calling finalization functions"), 107 | errs: errs, 108 | } 109 | } 110 | 111 | return nil 112 | } 113 | 114 | func Initialize() error { 115 | if err := initialize(); err != nil { 116 | return err 117 | } 118 | return nil 119 | } 120 | -------------------------------------------------------------------------------- /py/python_test.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "path/filepath" 7 | ) 8 | 9 | func init() { 10 | wd, err := os.Getwd() 11 | if err != nil { 12 | log.Fatal(err) 13 | } 14 | os.Setenv("PYTHONPATH", filepath.Join(wd, "testdata")) 15 | Initialize() 16 | } 17 | -------------------------------------------------------------------------------- /py/string.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | /* 4 | #include "whiskey_py.h" 5 | */ 6 | import "C" 7 | 8 | import ( 9 | "sync" 10 | "unsafe" 11 | 12 | "github.com/pkg/errors" 13 | ) 14 | 15 | // String wraps a Python string. 16 | type String struct { 17 | Object 18 | } 19 | 20 | // String converts a Go string to a Python string. 21 | func NewString(s string) (String, error) { 22 | var ps String 23 | cs := C.CString(s) 24 | ps.PyObject = C.PyString_FromString(cs) 25 | C.free(unsafe.Pointer(cs)) 26 | if ps.PyObject == nil { 27 | return ps, errors.Wrap(GetError(), "error converting to Python string") 28 | } 29 | return ps, nil 30 | } 31 | 32 | // GoString converts the Python string into a Go string. 33 | func (s String) GoString() (string, error) { 34 | // NOTE: Don't call C.free(cs) here, because the Python C API says that 35 | // the returned char* shouldn't be modified or freed. 36 | cs := C.PyString_AsString(s.PyObject) 37 | if cs == nil { 38 | return "", errors.Wrap(GetError(), "error converting to C string") 39 | } 40 | return C.GoString(cs), nil 41 | } 42 | 43 | // Join joins all the items in the list by this string. 44 | // It's equivalent to s.join(l) in Python. 45 | func (s String) Join(l List) (String, error) { 46 | var joined String 47 | joined.PyObject = C.PyUnicode_Join(s.PyObject, l.PyObject) 48 | if joined.PyObject == nil { 49 | return joined, errors.Wrap(GetError(), "error joining list") 50 | } 51 | return joined, nil 52 | } 53 | 54 | var ( 55 | stringCache = map[string]String{} 56 | stringCacheMutex = &sync.Mutex{} 57 | ) 58 | 59 | func resetStringCache() { 60 | stringCacheMutex.Lock() 61 | for _, v := range stringCache { 62 | v.DecRef() 63 | } 64 | stringCache = map[string]String{} 65 | stringCacheMutex.Unlock() 66 | } 67 | 68 | // CachedString converts a Go string to a Python string and caches it. 69 | func CachedString(s string) (String, error) { 70 | stringCacheMutex.Lock() 71 | ps, ok := stringCache[s] 72 | if !ok { 73 | var err error 74 | ps, err = NewString(s) 75 | if err != nil { 76 | return ps, err 77 | } 78 | stringCache[s] = ps 79 | } 80 | stringCacheMutex.Unlock() 81 | ps.IncRef() 82 | return ps, nil 83 | } 84 | -------------------------------------------------------------------------------- /py/testdata/hello.py: -------------------------------------------------------------------------------- 1 | n = 1 2 | 3 | 4 | def fn(*strings): 5 | return " ".join(strings) 6 | -------------------------------------------------------------------------------- /py/threads.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | /* 4 | #include "whiskey_py.h" 5 | */ 6 | import "C" 7 | 8 | type ThreadState struct { 9 | PyThreadState *C.PyThreadState 10 | } 11 | 12 | func GetThreadState() *ThreadState { 13 | return &ThreadState{PyThreadState: C.PyThreadState_Get()} 14 | } 15 | 16 | func (ts *ThreadState) New() *ThreadState { 17 | return &ThreadState{C.PyThreadState_New(ts.PyThreadState.interp)} 18 | } 19 | 20 | func (ts *ThreadState) Acquire() { 21 | C.PyEval_RestoreThread(ts.PyThreadState) 22 | } 23 | 24 | func (ts *ThreadState) Release() { 25 | ts.PyThreadState = C.PyEval_SaveThread() 26 | } 27 | -------------------------------------------------------------------------------- /py/tuple.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | /* 4 | #include "whiskey_py.h" 5 | */ 6 | import "C" 7 | 8 | import ( 9 | "fmt" 10 | 11 | "github.com/pkg/errors" 12 | ) 13 | 14 | // Tuple wraps a Python tuple object. 15 | type Tuple struct { 16 | Object 17 | } 18 | 19 | // NewTuple creates a new Python tuple with the given size. 20 | func NewTuple(size int) (t Tuple, err error) { 21 | t.PyObject = C.PyTuple_New(C.Py_ssize_t(size)) 22 | if t.PyObject == nil { 23 | err = errors.Wrapf(GetError(), "error creating Python tuple of size %d", size) 24 | } 25 | return 26 | } 27 | 28 | // NewTupleObjects create a new Python tuple from a slice of Python objects. 29 | func NewTupleObjects(objects []Object) (t Tuple, err error) { 30 | t, err = NewTuple(len(objects)) 31 | if err != nil { 32 | return 33 | } 34 | for i, arg := range objects { 35 | if err = t.SetItem(i, arg); err != nil { 36 | t.DecRef() 37 | return 38 | } 39 | } 40 | return 41 | } 42 | 43 | // GetItem gets an item from the Python tuple. 44 | // This calls IncRef on the value before returning it (rather than returning 45 | // a borrowed reference like PyTuple_GetItem). 46 | func (t Tuple) GetItem(index int) (item Object, err error) { 47 | item.PyObject = C.PyTuple_GetItem(t.PyObject, C.Py_ssize_t(index)) 48 | if item.PyObject == nil { 49 | err = errors.Wrapf(GetError(), "error getting tuple item %d", index) 50 | } 51 | item.IncRef() 52 | return 53 | } 54 | 55 | func (t Tuple) GetItems(ptrs ...interface{}) error { 56 | for i, ptr := range ptrs { 57 | o, err := t.GetItem(i) 58 | if err != nil { 59 | return err 60 | } 61 | defer o.DecRef() 62 | if err := o.ConvertInto(ptr); err != nil { 63 | return errors.WithMessage(err, fmt.Sprintf("(for tuple item %d)", i)) 64 | } 65 | } 66 | return nil 67 | } 68 | 69 | // SetItem sets an item in the Python tuple using a Python object for the value. 70 | // 71 | // Note that, because C.PyTuple_SetItem steals references, this function calls 72 | // IncRef on the passed object, to make things more consistent with other 73 | // places in the Python C API. 74 | func (t Tuple) SetItem(index int, v Object) error { 75 | v.IncRef() 76 | if C.PyTuple_SetItem(t.PyObject, C.Py_ssize_t(index), v.PyObject) != 0 { 77 | v.DecRef() 78 | return errors.Wrapf(GetError(), "error setting tuple item %d", index) 79 | } 80 | return nil 81 | } 82 | 83 | func (t Tuple) SetItemInt(index, n int) error { 84 | pn, err := NewInt(n) 85 | if err != nil { 86 | return err 87 | } 88 | if err := t.SetItem(index, pn.Object); err != nil { 89 | return err 90 | } 91 | pn.DecRef() 92 | return nil 93 | } 94 | 95 | // Len returns the size of the Python tuple. 96 | func (t Tuple) Len() int { 97 | return int(C.PyTuple_Size(t.PyObject)) 98 | } 99 | -------------------------------------------------------------------------------- /py/whiskey_py.c: -------------------------------------------------------------------------------- 1 | #include "whiskey_py.h" 2 | #include "_cgo_export.h" 3 | 4 | static PyObject * _call(PyObject * const self, PyObject * const args) 5 | { 6 | char * callName; 7 | PyObject * callArgs; 8 | if (!PyArg_ParseTuple(args, "sO:call", &callName, &callArgs)) { 9 | return NULL; 10 | } 11 | return whiskeyCall(callName, callArgs); 12 | } 13 | 14 | static PyMethodDef _module_defs[] = { 15 | { 16 | "call", 17 | _call, 18 | METH_VARARGS, 19 | "Call a method in Go." 20 | }, 21 | {NULL, NULL, 0, NULL} 22 | }; 23 | 24 | PyObject * whiskey_none = NULL; 25 | PyObject * whiskey_true = NULL; 26 | PyObject * whiskey_false = NULL; 27 | PyObject * whiskey_module = NULL; 28 | 29 | int whiskey_initialize() { 30 | Py_Initialize(); 31 | Py_InitModule3("_whiskey", _module_defs, "Whiskey WSGI internals."); 32 | 33 | whiskey_none = Py_None; 34 | whiskey_true = Py_True; 35 | whiskey_false = Py_False; 36 | Py_INCREF(whiskey_none); 37 | Py_INCREF(whiskey_true); 38 | Py_INCREF(whiskey_false); 39 | 40 | return 0; 41 | } 42 | 43 | void whiskey_finalize() { 44 | if (whiskey_none != NULL) { 45 | Py_DECREF(whiskey_none); 46 | whiskey_none = NULL; 47 | } 48 | if (whiskey_true != NULL) { 49 | Py_DECREF(whiskey_true); 50 | whiskey_true = NULL; 51 | } 52 | if (whiskey_false != NULL) { 53 | Py_DECREF(whiskey_false); 54 | whiskey_false = NULL; 55 | } 56 | if (whiskey_module != NULL) { 57 | Py_DECREF(whiskey_module); 58 | whiskey_module = NULL; 59 | } 60 | Py_Finalize(); 61 | } 62 | 63 | int whiskey_check_int(PyObject * o) { 64 | return PyInt_Check(o); 65 | } 66 | 67 | int whiskey_check_list(PyObject * o) { 68 | return PyList_Check(o); 69 | } 70 | 71 | int whiskey_check_string(PyObject * o) { 72 | return PyString_Check(o); 73 | } 74 | 75 | int whiskey_check_tuple(PyObject * o) { 76 | return PyTuple_Check(o); 77 | } 78 | -------------------------------------------------------------------------------- /py/whiskey_py.h: -------------------------------------------------------------------------------- 1 | #ifndef __WSGI_H__ 2 | #define __WSGI_H__ 3 | 4 | #include 5 | 6 | extern PyMethodDef whiskey_start_response_def; 7 | extern PyObject * whiskey_none; 8 | extern PyObject * whiskey_true; 9 | extern PyObject * whiskey_false; 10 | extern PyObject * whiskey_module; 11 | 12 | int whiskey_initialize(); 13 | void whiskey_finalize(); 14 | int whiskey_check_int(PyObject * o); 15 | int whiskey_check_list(PyObject * o); 16 | int whiskey_check_string(PyObject * o); 17 | int whiskey_check_tuple(PyObject * o); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /vendor/github.com/namsral/flag/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /vendor/github.com/namsral/flag/README.md: -------------------------------------------------------------------------------- 1 | Flag 2 | === 3 | 4 | Flag is a drop in replacement for Go's flag package with the addition to parse files and environment variables. If you support the [twelve-factor app methodology][], Flag complies with the third factor; "Store config in the environment". 5 | 6 | [twelve-factor app methodology]: http://12factor.net 7 | 8 | An example using a gopher: 9 | 10 | ```go 11 | $ cat > gopher.go 12 | package main 13 | 14 | import ( 15 | "fmt" 16 | "github.com/namsral/flag" 17 | ) 18 | 19 | var age int 20 | 21 | flag.IntVar(&age, "age", 0, "age of gopher") 22 | flag.Parse() 23 | 24 | fmt.Print("age:", age) 25 | 26 | $ go run gopher.go -age 1 27 | age: 1 28 | ``` 29 | 30 | Same code but using an environment variable: 31 | 32 | ```go 33 | $ export AGE=2 34 | $ go run gopher.go 35 | age: 2 36 | ``` 37 | 38 | 39 | Same code but using a configuration file: 40 | 41 | ```go 42 | $ cat > gopher.conf 43 | age 3 44 | 45 | $ go run gopher.go -config gopher.conf 46 | age: 3 47 | ``` 48 | 49 | The following table shows how flags are translated to environment variables and configuration files: 50 | 51 | | Type | Flag | Environment | File | 52 | | ------ | :------------ |:------------ |:------------ | 53 | | int | -age 2 | AGE=2 | age 2 | 54 | | bool | -female | FEMALE=true | female true | 55 | | float | -length 175.5 | LENGTH=175.5 | length 175.5 | 56 | | string | -name Gloria | NAME=Gloria | name Gloria | 57 | 58 | This package is a port of Go's [flag][] package from the standard library with the addition of two functions `ParseEnv` and `ParseFile`. 59 | 60 | [flag]: http://golang.org/src/pkg/flagconfiguration 61 | 62 | 63 | Goals 64 | ----- 65 | 66 | - Compatability with the original `flag` package 67 | - Support the [twelve-factor app methodology][] 68 | - Uniform user experience between the three input methods 69 | 70 | 71 | Why? 72 | --- 73 | 74 | Why not use one of the many INI, JSON or YAML parsers? 75 | 76 | I find it best practice to have simple configuration options to control the behaviour of an applications when it starts up. Use basic types like ints, floats and strings for configuration options and store more complex data structures in the "datastore" layer. 77 | 78 | 79 | Usage 80 | --- 81 | 82 | It's intended for projects which require a simple configuration made available through command-line flags, configuration files and shell environments. It's similar to the original `flag` package. 83 | 84 | Example: 85 | 86 | ```go 87 | import "github.com/namsral/flag" 88 | 89 | flag.String(flag.DefaultConfigFlagname, "", "path to config file") 90 | flag.Int("age", 24, "help message for age") 91 | 92 | flag.Parse() 93 | ``` 94 | 95 | Order of precedence: 96 | 97 | 1. Command line options 98 | 2. Environment variables 99 | 3. Configuration file 100 | 4. Default values 101 | 102 | 103 | #### Parsing Configuration Files 104 | 105 | Create a configuration file: 106 | 107 | ```go 108 | $ cat > ./gopher.conf 109 | # empty newlines and lines beginning with a "#" character are ignored. 110 | name bob 111 | 112 | # keys and values can also be separated by the "=" character 113 | age=20 114 | 115 | # booleans can be empty, set with 0, 1, true, false, etc 116 | hacker 117 | ``` 118 | 119 | Add a "config" flag: 120 | 121 | ```go 122 | flag.String(flag.DefaultConfigFlagname, "", "path to config file") 123 | ``` 124 | 125 | Run the command: 126 | 127 | ```go 128 | $ go run ./gopher.go -config ./gopher.conf 129 | ``` 130 | 131 | The default flag name for the configuration file is "config" and can be changed 132 | by setting `flag.DefaultConfigFlagname`: 133 | 134 | ```go 135 | flag.DefaultConfigFlagname = "conf" 136 | flag.Parse() 137 | ``` 138 | 139 | #### Parsing Environment Variables 140 | 141 | Environment variables are parsed 1-on-1 with defined flags: 142 | 143 | ```go 144 | $ export AGE=44 145 | $ go run ./gopher.go 146 | age=44 147 | ``` 148 | 149 | 150 | You can also parse prefixed environment variables by setting a prefix name when creating a new empty flag set: 151 | 152 | ```go 153 | fs := flag.NewFlagSetWithEnvPrefix(os.Args[0], "GO", 0) 154 | fs.Int("age", 24, "help message for age") 155 | fs.Parse(os.Args[1:]) 156 | ... 157 | $ go export GO_AGE=33 158 | $ go run ./gopher.go 159 | age=33 160 | ``` 161 | 162 | 163 | For more examples see the [examples][] directory in the project repository. 164 | 165 | [examples]: https://github.com/namsral/flag/tree/master/examples 166 | 167 | That's it. 168 | 169 | 170 | License 171 | --- 172 | 173 | 174 | Copyright (c) 2012 The Go Authors. All rights reserved. 175 | 176 | Redistribution and use in source and binary forms, with or without 177 | modification, are permitted provided that the following conditions are 178 | met: 179 | 180 | * Redistributions of source code must retain the above copyright 181 | notice, this list of conditions and the following disclaimer. 182 | * Redistributions in binary form must reproduce the above 183 | copyright notice, this list of conditions and the following disclaimer 184 | in the documentation and/or other materials provided with the 185 | distribution. 186 | * Neither the name of Google Inc. nor the names of its 187 | contributors may be used to endorse or promote products derived from 188 | this software without specific prior written permission. 189 | 190 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 191 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 192 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 193 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 194 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 195 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 196 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 197 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 198 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 199 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 200 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 201 | -------------------------------------------------------------------------------- /vendor/github.com/namsral/flag/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // These examples demonstrate more intricate uses of the flag package. 6 | package flag 7 | 8 | import ( 9 | "errors" 10 | "fmt" 11 | "strings" 12 | "time" 13 | ) 14 | 15 | // Example 1: A single string flag called "species" with default value "gopher". 16 | var species = String("species", "gopher", "the species we are studying") 17 | 18 | // Example 2: Two flags sharing a variable, so we can have a shorthand. 19 | // The order of initialization is undefined, so make sure both use the 20 | // same default value. They must be set up with an init function. 21 | var gopherType string 22 | 23 | func init() { 24 | const ( 25 | defaultGopher = "pocket" 26 | usage = "the variety of gopher" 27 | ) 28 | StringVar(&gopherType, "gopher_type", defaultGopher, usage) 29 | StringVar(&gopherType, "g", defaultGopher, usage+" (shorthand)") 30 | } 31 | 32 | // Example 3: A user-defined flag type, a slice of durations. 33 | type interval []time.Duration 34 | 35 | // String is the method to format the flag's value, part of the Value interface. 36 | // The String method's output will be used in diagnostics. 37 | func (i *interval) String() string { 38 | return fmt.Sprint(*i) 39 | } 40 | 41 | // Set is the method to set the flag value, part of the Value interface. 42 | // Set's argument is a string to be parsed to set the 43 | // It's a comma-separated list, so we split it. 44 | func (i *interval) Set(value string) error { 45 | // If we wanted to allow the flag to be set multiple times, 46 | // accumulating values, we would delete this if statement. 47 | // That would permit usages such as 48 | // -deltaT 10s -deltaT 15s 49 | // and other combinations. 50 | if len(*i) > 0 { 51 | return errors.New("interval flag already set") 52 | } 53 | for _, dt := range strings.Split(value, ",") { 54 | duration, err := time.ParseDuration(dt) 55 | if err != nil { 56 | return err 57 | } 58 | *i = append(*i, duration) 59 | } 60 | return nil 61 | } 62 | 63 | // Define a flag to accumulate durations. Because it has a special type, 64 | // we need to use the Var function and therefore create the flag during 65 | // init. 66 | 67 | var intervalFlag interval 68 | 69 | func init() { 70 | // Tie the command-line flag to the intervalFlag variable and 71 | // set a usage message. 72 | Var(&intervalFlag, "deltaT", "comma-separated list of intervals to use between events") 73 | } 74 | 75 | func Example() { 76 | // All the interesting pieces are with the variables declared above, but 77 | // to enable the flag package to see the flags defined there, one must 78 | // execute, typically at the start of main (not init!): 79 | // flag.Parse() 80 | // We don't run it here because this is not a main function and 81 | // the testing suite has already parsed the flags. 82 | } 83 | -------------------------------------------------------------------------------- /vendor/github.com/namsral/flag/examples/gopher.conf: -------------------------------------------------------------------------------- 1 | # this is a comment followed by an empty line 2 | 3 | length 175.5 4 | age 2 5 | name Gloria -------------------------------------------------------------------------------- /vendor/github.com/namsral/flag/examples/gopher.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/namsral/flag" 5 | "fmt" 6 | ) 7 | 8 | func main() { 9 | var ( 10 | config string 11 | length float64 12 | age int 13 | name string 14 | female bool 15 | ) 16 | 17 | flag.StringVar(&config, "config", "", "help message") 18 | flag.StringVar(&name, "name", "", "help message") 19 | flag.IntVar(&age, "age", 0, "help message") 20 | flag.Float64Var(&length, "length", 0, "help message") 21 | flag.BoolVar(&female, "female", false, "help message") 22 | 23 | flag.Parse() 24 | 25 | fmt.Println("length:", length) 26 | fmt.Println("age:", age) 27 | fmt.Println("name:", name) 28 | fmt.Println("female:", female) 29 | } 30 | -------------------------------------------------------------------------------- /vendor/github.com/namsral/flag/export_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package flag 6 | 7 | import "os" 8 | 9 | // Additional routines compiled into the package only during testing. 10 | 11 | // ResetForTesting clears all flag state and sets the usage function as directed. 12 | // After calling ResetForTesting, parse errors in flag handling will not 13 | // exit the program. 14 | func ResetForTesting(usage func()) { 15 | CommandLine = NewFlagSet(os.Args[0], ContinueOnError) 16 | Usage = usage 17 | } -------------------------------------------------------------------------------- /vendor/github.com/namsral/flag/flag.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /*Package flag implements command-line flag parsing. 6 | okdaokddadok 7 | Usage: 8 | 9 | Define flags using flag.String(), Bool(), Int(), etc. 10 | 11 | This declares an integer flag, -flagname, stored in the pointer ip, with type *int. 12 | import "flag" 13 | var ip = flag.Int("flagname", 1234, "help message for flagname") 14 | If you like, you can bind the flag to a variable using the Var() functions. 15 | var flagvar int 16 | func init() { 17 | flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname") 18 | } 19 | Or you can create custom flags that satisfy the Value interface (with 20 | pointer receivers) and couple them to flag parsing by 21 | flag.Var(&flagVal, "name", "help message for flagname") 22 | For such flags, the default value is just the initial value of the variable. 23 | 24 | After all flags are defined, call 25 | flag.Parse() 26 | to parse the command line into the defined flags. 27 | 28 | Flags may then be used directly. If you're using the flags themselves, 29 | they are all pointers; if you bind to variables, they're values. 30 | fmt.Println("ip has value ", *ip) 31 | fmt.Println("flagvar has value ", flagvar) 32 | 33 | After parsing, the arguments after the flag are available as the 34 | slice flag.Args() or individually as flag.Arg(i). 35 | The arguments are indexed from 0 through flag.NArg()-1. 36 | 37 | Command line flag syntax: 38 | -flag 39 | -flag=x 40 | -flag x // non-boolean flags only 41 | One or two minus signs may be used; they are equivalent. 42 | The last form is not permitted for boolean flags because the 43 | meaning of the command 44 | cmd -x * 45 | will change if there is a file called 0, false, etc. You must 46 | use the -flag=false form to turn off a boolean flag. 47 | 48 | Flag parsing stops just before the first non-flag argument 49 | ("-" is a non-flag argument) or after the terminator "--". 50 | 51 | Integer flags accept 1234, 0664, 0x1234 and may be negative. 52 | Boolean flags may be 1, 0, t, f, true, false, TRUE, FALSE, True, False. 53 | Duration flags accept any input valid for time.ParseDuration. 54 | 55 | The default set of command-line flags is controlled by 56 | top-level functions. The FlagSet type allows one to define 57 | independent sets of flags, such as to implement subcommands 58 | in a command-line interface. The methods of FlagSet are 59 | analogous to the top-level functions for the command-line 60 | flag set. 61 | */ 62 | package flag 63 | 64 | import ( 65 | "bufio" 66 | "errors" 67 | "fmt" 68 | "io" 69 | "os" 70 | "sort" 71 | "strconv" 72 | "strings" 73 | "time" 74 | ) 75 | 76 | // EnvironmentPrefix defines a string that will be implicitely prefixed to a 77 | // flag name before looking it up in the environment variables. 78 | var EnvironmentPrefix = "" 79 | 80 | // DefaultConfigFlagname defines the flag name of the optional config file 81 | // path. Used to lookup and parse the config file when a default is set and 82 | // available on disk. 83 | var DefaultConfigFlagname = "config" 84 | 85 | // ErrHelp is the error returned if the flag -help is invoked but no such flag is defined. 86 | var ErrHelp = errors.New("flag: help requested") 87 | 88 | // -- bool Value 89 | type boolValue bool 90 | 91 | func newBoolValue(val bool, p *bool) *boolValue { 92 | *p = val 93 | return (*boolValue)(p) 94 | } 95 | 96 | func (b *boolValue) Set(s string) error { 97 | v, err := strconv.ParseBool(s) 98 | *b = boolValue(v) 99 | return err 100 | } 101 | 102 | func (b *boolValue) Get() interface{} { return bool(*b) } 103 | 104 | func (b *boolValue) String() string { return fmt.Sprintf("%v", *b) } 105 | 106 | func (b *boolValue) IsBoolFlag() bool { return true } 107 | 108 | // optional interface to indicate boolean flags that can be 109 | // supplied without "=value" text 110 | type boolFlag interface { 111 | Value 112 | IsBoolFlag() bool 113 | } 114 | 115 | // -- int Value 116 | type intValue int 117 | 118 | func newIntValue(val int, p *int) *intValue { 119 | *p = val 120 | return (*intValue)(p) 121 | } 122 | 123 | func (i *intValue) Set(s string) error { 124 | v, err := strconv.ParseInt(s, 0, 64) 125 | *i = intValue(v) 126 | return err 127 | } 128 | 129 | func (i *intValue) Get() interface{} { return int(*i) } 130 | 131 | func (i *intValue) String() string { return fmt.Sprintf("%v", *i) } 132 | 133 | // -- int64 Value 134 | type int64Value int64 135 | 136 | func newInt64Value(val int64, p *int64) *int64Value { 137 | *p = val 138 | return (*int64Value)(p) 139 | } 140 | 141 | func (i *int64Value) Set(s string) error { 142 | v, err := strconv.ParseInt(s, 0, 64) 143 | *i = int64Value(v) 144 | return err 145 | } 146 | 147 | func (i *int64Value) Get() interface{} { return int64(*i) } 148 | 149 | func (i *int64Value) String() string { return fmt.Sprintf("%v", *i) } 150 | 151 | // -- uint Value 152 | type uintValue uint 153 | 154 | func newUintValue(val uint, p *uint) *uintValue { 155 | *p = val 156 | return (*uintValue)(p) 157 | } 158 | 159 | func (i *uintValue) Set(s string) error { 160 | v, err := strconv.ParseUint(s, 0, 64) 161 | *i = uintValue(v) 162 | return err 163 | } 164 | 165 | func (i *uintValue) Get() interface{} { return uint(*i) } 166 | 167 | func (i *uintValue) String() string { return fmt.Sprintf("%v", *i) } 168 | 169 | // -- uint64 Value 170 | type uint64Value uint64 171 | 172 | func newUint64Value(val uint64, p *uint64) *uint64Value { 173 | *p = val 174 | return (*uint64Value)(p) 175 | } 176 | 177 | func (i *uint64Value) Set(s string) error { 178 | v, err := strconv.ParseUint(s, 0, 64) 179 | *i = uint64Value(v) 180 | return err 181 | } 182 | 183 | func (i *uint64Value) Get() interface{} { return uint64(*i) } 184 | 185 | func (i *uint64Value) String() string { return fmt.Sprintf("%v", *i) } 186 | 187 | // -- string Value 188 | type stringValue string 189 | 190 | func newStringValue(val string, p *string) *stringValue { 191 | *p = val 192 | return (*stringValue)(p) 193 | } 194 | 195 | func (s *stringValue) Set(val string) error { 196 | *s = stringValue(val) 197 | return nil 198 | } 199 | 200 | func (s *stringValue) Get() interface{} { return string(*s) } 201 | 202 | func (s *stringValue) String() string { return fmt.Sprintf("%s", *s) } 203 | 204 | // -- float64 Value 205 | type float64Value float64 206 | 207 | func newFloat64Value(val float64, p *float64) *float64Value { 208 | *p = val 209 | return (*float64Value)(p) 210 | } 211 | 212 | func (f *float64Value) Set(s string) error { 213 | v, err := strconv.ParseFloat(s, 64) 214 | *f = float64Value(v) 215 | return err 216 | } 217 | 218 | func (f *float64Value) Get() interface{} { return float64(*f) } 219 | 220 | func (f *float64Value) String() string { return fmt.Sprintf("%v", *f) } 221 | 222 | // -- time.Duration Value 223 | type durationValue time.Duration 224 | 225 | func newDurationValue(val time.Duration, p *time.Duration) *durationValue { 226 | *p = val 227 | return (*durationValue)(p) 228 | } 229 | 230 | func (d *durationValue) Set(s string) error { 231 | v, err := time.ParseDuration(s) 232 | *d = durationValue(v) 233 | return err 234 | } 235 | 236 | func (d *durationValue) Get() interface{} { return time.Duration(*d) } 237 | 238 | func (d *durationValue) String() string { return (*time.Duration)(d).String() } 239 | 240 | // Value is the interface to the dynamic value stored in a flag. 241 | // (The default value is represented as a string.) 242 | // 243 | // If a Value has an IsBoolFlag() bool method returning true, 244 | // the command-line parser makes -name equivalent to -name=true 245 | // rather than using the next command-line argument. 246 | type Value interface { 247 | String() string 248 | Set(string) error 249 | } 250 | 251 | // Getter is an interface that allows the contents of a Value to be retrieved. 252 | // It wraps the Value interface, rather than being part of it, because it 253 | // appeared after Go 1 and its compatibility rules. All Value types provided 254 | // by this package satisfy the Getter interface. 255 | type Getter interface { 256 | Value 257 | Get() interface{} 258 | } 259 | 260 | // ErrorHandling defines how to handle flag parsing errors. 261 | type ErrorHandling int 262 | 263 | const ( 264 | ContinueOnError ErrorHandling = iota 265 | ExitOnError 266 | PanicOnError 267 | ) 268 | 269 | // A FlagSet represents a set of defined flags. The zero value of a FlagSet 270 | // has no name and has ContinueOnError error handling. 271 | type FlagSet struct { 272 | // Usage is the function called when an error occurs while parsing flags. 273 | // The field is a function (not a method) that may be changed to point to 274 | // a custom error handler. 275 | Usage func() 276 | 277 | name string 278 | parsed bool 279 | actual map[string]*Flag 280 | formal map[string]*Flag 281 | envPrefix string // prefix to all env variable names 282 | args []string // arguments after flags 283 | exitOnError bool // does the program exit if there's an error? 284 | errorHandling ErrorHandling 285 | output io.Writer // nil means stderr; use out() accessor 286 | } 287 | 288 | // A Flag represents the state of a flag. 289 | type Flag struct { 290 | Name string // name as it appears on command line 291 | Usage string // help message 292 | Value Value // value as set 293 | DefValue string // default value (as text); for usage message 294 | } 295 | 296 | // sortFlags returns the flags as a slice in lexicographical sorted order. 297 | func sortFlags(flags map[string]*Flag) []*Flag { 298 | list := make(sort.StringSlice, len(flags)) 299 | i := 0 300 | for _, f := range flags { 301 | list[i] = f.Name 302 | i++ 303 | } 304 | list.Sort() 305 | result := make([]*Flag, len(list)) 306 | for i, name := range list { 307 | result[i] = flags[name] 308 | } 309 | return result 310 | } 311 | 312 | func (f *FlagSet) out() io.Writer { 313 | if f.output == nil { 314 | return os.Stderr 315 | } 316 | return f.output 317 | } 318 | 319 | // SetOutput sets the destination for usage and error messages. 320 | // If output is nil, os.Stderr is used. 321 | func (f *FlagSet) SetOutput(output io.Writer) { 322 | f.output = output 323 | } 324 | 325 | // VisitAll visits the flags in lexicographical order, calling fn for each. 326 | // It visits all flags, even those not set. 327 | func (f *FlagSet) VisitAll(fn func(*Flag)) { 328 | for _, flag := range sortFlags(f.formal) { 329 | fn(flag) 330 | } 331 | } 332 | 333 | // VisitAll visits the command-line flags in lexicographical order, calling 334 | // fn for each. It visits all flags, even those not set. 335 | func VisitAll(fn func(*Flag)) { 336 | CommandLine.VisitAll(fn) 337 | } 338 | 339 | // Visit visits the flags in lexicographical order, calling fn for each. 340 | // It visits only those flags that have been set. 341 | func (f *FlagSet) Visit(fn func(*Flag)) { 342 | for _, flag := range sortFlags(f.actual) { 343 | fn(flag) 344 | } 345 | } 346 | 347 | // Visit visits the command-line flags in lexicographical order, calling fn 348 | // for each. It visits only those flags that have been set. 349 | func Visit(fn func(*Flag)) { 350 | CommandLine.Visit(fn) 351 | } 352 | 353 | // Lookup returns the Flag structure of the named flag, returning nil if none exists. 354 | func (f *FlagSet) Lookup(name string) *Flag { 355 | return f.formal[name] 356 | } 357 | 358 | // Lookup returns the Flag structure of the named command-line flag, 359 | // returning nil if none exists. 360 | func Lookup(name string) *Flag { 361 | return CommandLine.formal[name] 362 | } 363 | 364 | // Set sets the value of the named flag. 365 | func (f *FlagSet) Set(name, value string) error { 366 | flag, ok := f.formal[name] 367 | if !ok { 368 | return fmt.Errorf("no such flag -%v", name) 369 | } 370 | err := flag.Value.Set(value) 371 | if err != nil { 372 | return err 373 | } 374 | if f.actual == nil { 375 | f.actual = make(map[string]*Flag) 376 | } 377 | f.actual[name] = flag 378 | return nil 379 | } 380 | 381 | // Set sets the value of the named command-line flag. 382 | func Set(name, value string) error { 383 | return CommandLine.Set(name, value) 384 | } 385 | 386 | // PrintDefaults prints, to standard error unless configured 387 | // otherwise, the default values of all defined flags in the set. 388 | func (f *FlagSet) PrintDefaults() { 389 | f.VisitAll(func(flag *Flag) { 390 | format := " -%s=%s: %s\n" 391 | if _, ok := flag.Value.(*stringValue); ok { 392 | // put quotes on the value 393 | format = " -%s=%q: %s\n" 394 | } 395 | fmt.Fprintf(f.out(), format, flag.Name, flag.DefValue, flag.Usage) 396 | }) 397 | } 398 | 399 | // PrintDefaults prints to standard error the default values of all defined command-line flags. 400 | func PrintDefaults() { 401 | CommandLine.PrintDefaults() 402 | } 403 | 404 | // defaultUsage is the default function to print a usage message. 405 | func defaultUsage(f *FlagSet) { 406 | if f.name == "" { 407 | fmt.Fprintf(f.out(), "Usage:\n") 408 | } else { 409 | fmt.Fprintf(f.out(), "Usage of %s:\n", f.name) 410 | } 411 | f.PrintDefaults() 412 | } 413 | 414 | // NOTE: Usage is not just defaultUsage(CommandLine) 415 | // because it serves (via godoc flag Usage) as the example 416 | // for how to write your own usage function. 417 | 418 | // Usage prints to standard error a usage message documenting all defined command-line flags. 419 | // The function is a variable that may be changed to point to a custom function. 420 | var Usage = func() { 421 | fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) 422 | PrintDefaults() 423 | } 424 | 425 | // NFlag returns the number of flags that have been set. 426 | func (f *FlagSet) NFlag() int { return len(f.actual) } 427 | 428 | // NFlag returns the number of command-line flags that have been set. 429 | func NFlag() int { return len(CommandLine.actual) } 430 | 431 | // Arg returns the i'th argument. Arg(0) is the first remaining argument 432 | // after flags have been processed. 433 | func (f *FlagSet) Arg(i int) string { 434 | if i < 0 || i >= len(f.args) { 435 | return "" 436 | } 437 | return f.args[i] 438 | } 439 | 440 | // Arg returns the i'th command-line argument. Arg(0) is the first remaining argument 441 | // after flags have been processed. 442 | func Arg(i int) string { 443 | return CommandLine.Arg(i) 444 | } 445 | 446 | // NArg is the number of arguments remaining after flags have been processed. 447 | func (f *FlagSet) NArg() int { return len(f.args) } 448 | 449 | // NArg is the number of arguments remaining after flags have been processed. 450 | func NArg() int { return len(CommandLine.args) } 451 | 452 | // Args returns the non-flag arguments. 453 | func (f *FlagSet) Args() []string { return f.args } 454 | 455 | // Args returns the non-flag command-line arguments. 456 | func Args() []string { return CommandLine.args } 457 | 458 | // BoolVar defines a bool flag with specified name, default value, and usage string. 459 | // The argument p points to a bool variable in which to store the value of the flag. 460 | func (f *FlagSet) BoolVar(p *bool, name string, value bool, usage string) { 461 | f.Var(newBoolValue(value, p), name, usage) 462 | } 463 | 464 | // BoolVar defines a bool flag with specified name, default value, and usage string. 465 | // The argument p points to a bool variable in which to store the value of the flag. 466 | func BoolVar(p *bool, name string, value bool, usage string) { 467 | CommandLine.Var(newBoolValue(value, p), name, usage) 468 | } 469 | 470 | // Bool defines a bool flag with specified name, default value, and usage string. 471 | // The return value is the address of a bool variable that stores the value of the flag. 472 | func (f *FlagSet) Bool(name string, value bool, usage string) *bool { 473 | p := new(bool) 474 | f.BoolVar(p, name, value, usage) 475 | return p 476 | } 477 | 478 | // Bool defines a bool flag with specified name, default value, and usage string. 479 | // The return value is the address of a bool variable that stores the value of the flag. 480 | func Bool(name string, value bool, usage string) *bool { 481 | return CommandLine.Bool(name, value, usage) 482 | } 483 | 484 | // IntVar defines an int flag with specified name, default value, and usage string. 485 | // The argument p points to an int variable in which to store the value of the flag. 486 | func (f *FlagSet) IntVar(p *int, name string, value int, usage string) { 487 | f.Var(newIntValue(value, p), name, usage) 488 | } 489 | 490 | // IntVar defines an int flag with specified name, default value, and usage string. 491 | // The argument p points to an int variable in which to store the value of the flag. 492 | func IntVar(p *int, name string, value int, usage string) { 493 | CommandLine.Var(newIntValue(value, p), name, usage) 494 | } 495 | 496 | // Int defines an int flag with specified name, default value, and usage string. 497 | // The return value is the address of an int variable that stores the value of the flag. 498 | func (f *FlagSet) Int(name string, value int, usage string) *int { 499 | p := new(int) 500 | f.IntVar(p, name, value, usage) 501 | return p 502 | } 503 | 504 | // Int defines an int flag with specified name, default value, and usage string. 505 | // The return value is the address of an int variable that stores the value of the flag. 506 | func Int(name string, value int, usage string) *int { 507 | return CommandLine.Int(name, value, usage) 508 | } 509 | 510 | // Int64Var defines an int64 flag with specified name, default value, and usage string. 511 | // The argument p points to an int64 variable in which to store the value of the flag. 512 | func (f *FlagSet) Int64Var(p *int64, name string, value int64, usage string) { 513 | f.Var(newInt64Value(value, p), name, usage) 514 | } 515 | 516 | // Int64Var defines an int64 flag with specified name, default value, and usage string. 517 | // The argument p points to an int64 variable in which to store the value of the flag. 518 | func Int64Var(p *int64, name string, value int64, usage string) { 519 | CommandLine.Var(newInt64Value(value, p), name, usage) 520 | } 521 | 522 | // Int64 defines an int64 flag with specified name, default value, and usage string. 523 | // The return value is the address of an int64 variable that stores the value of the flag. 524 | func (f *FlagSet) Int64(name string, value int64, usage string) *int64 { 525 | p := new(int64) 526 | f.Int64Var(p, name, value, usage) 527 | return p 528 | } 529 | 530 | // Int64 defines an int64 flag with specified name, default value, and usage string. 531 | // The return value is the address of an int64 variable that stores the value of the flag. 532 | func Int64(name string, value int64, usage string) *int64 { 533 | return CommandLine.Int64(name, value, usage) 534 | } 535 | 536 | // UintVar defines a uint flag with specified name, default value, and usage string. 537 | // The argument p points to a uint variable in which to store the value of the flag. 538 | func (f *FlagSet) UintVar(p *uint, name string, value uint, usage string) { 539 | f.Var(newUintValue(value, p), name, usage) 540 | } 541 | 542 | // UintVar defines a uint flag with specified name, default value, and usage string. 543 | // The argument p points to a uint variable in which to store the value of the flag. 544 | func UintVar(p *uint, name string, value uint, usage string) { 545 | CommandLine.Var(newUintValue(value, p), name, usage) 546 | } 547 | 548 | // Uint defines a uint flag with specified name, default value, and usage string. 549 | // The return value is the address of a uint variable that stores the value of the flag. 550 | func (f *FlagSet) Uint(name string, value uint, usage string) *uint { 551 | p := new(uint) 552 | f.UintVar(p, name, value, usage) 553 | return p 554 | } 555 | 556 | // Uint defines a uint flag with specified name, default value, and usage string. 557 | // The return value is the address of a uint variable that stores the value of the flag. 558 | func Uint(name string, value uint, usage string) *uint { 559 | return CommandLine.Uint(name, value, usage) 560 | } 561 | 562 | // Uint64Var defines a uint64 flag with specified name, default value, and usage string. 563 | // The argument p points to a uint64 variable in which to store the value of the flag. 564 | func (f *FlagSet) Uint64Var(p *uint64, name string, value uint64, usage string) { 565 | f.Var(newUint64Value(value, p), name, usage) 566 | } 567 | 568 | // Uint64Var defines a uint64 flag with specified name, default value, and usage string. 569 | // The argument p points to a uint64 variable in which to store the value of the flag. 570 | func Uint64Var(p *uint64, name string, value uint64, usage string) { 571 | CommandLine.Var(newUint64Value(value, p), name, usage) 572 | } 573 | 574 | // Uint64 defines a uint64 flag with specified name, default value, and usage string. 575 | // The return value is the address of a uint64 variable that stores the value of the flag. 576 | func (f *FlagSet) Uint64(name string, value uint64, usage string) *uint64 { 577 | p := new(uint64) 578 | f.Uint64Var(p, name, value, usage) 579 | return p 580 | } 581 | 582 | // Uint64 defines a uint64 flag with specified name, default value, and usage string. 583 | // The return value is the address of a uint64 variable that stores the value of the flag. 584 | func Uint64(name string, value uint64, usage string) *uint64 { 585 | return CommandLine.Uint64(name, value, usage) 586 | } 587 | 588 | // StringVar defines a string flag with specified name, default value, and usage string. 589 | // The argument p points to a string variable in which to store the value of the flag. 590 | func (f *FlagSet) StringVar(p *string, name string, value string, usage string) { 591 | f.Var(newStringValue(value, p), name, usage) 592 | } 593 | 594 | // StringVar defines a string flag with specified name, default value, and usage string. 595 | // The argument p points to a string variable in which to store the value of the flag. 596 | func StringVar(p *string, name string, value string, usage string) { 597 | CommandLine.Var(newStringValue(value, p), name, usage) 598 | } 599 | 600 | // String defines a string flag with specified name, default value, and usage string. 601 | // The return value is the address of a string variable that stores the value of the flag. 602 | func (f *FlagSet) String(name string, value string, usage string) *string { 603 | p := new(string) 604 | f.StringVar(p, name, value, usage) 605 | return p 606 | } 607 | 608 | // String defines a string flag with specified name, default value, and usage string. 609 | // The return value is the address of a string variable that stores the value of the flag. 610 | func String(name string, value string, usage string) *string { 611 | return CommandLine.String(name, value, usage) 612 | } 613 | 614 | // Float64Var defines a float64 flag with specified name, default value, and usage string. 615 | // The argument p points to a float64 variable in which to store the value of the flag. 616 | func (f *FlagSet) Float64Var(p *float64, name string, value float64, usage string) { 617 | f.Var(newFloat64Value(value, p), name, usage) 618 | } 619 | 620 | // Float64Var defines a float64 flag with specified name, default value, and usage string. 621 | // The argument p points to a float64 variable in which to store the value of the flag. 622 | func Float64Var(p *float64, name string, value float64, usage string) { 623 | CommandLine.Var(newFloat64Value(value, p), name, usage) 624 | } 625 | 626 | // Float64 defines a float64 flag with specified name, default value, and usage string. 627 | // The return value is the address of a float64 variable that stores the value of the flag. 628 | func (f *FlagSet) Float64(name string, value float64, usage string) *float64 { 629 | p := new(float64) 630 | f.Float64Var(p, name, value, usage) 631 | return p 632 | } 633 | 634 | // Float64 defines a float64 flag with specified name, default value, and usage string. 635 | // The return value is the address of a float64 variable that stores the value of the flag. 636 | func Float64(name string, value float64, usage string) *float64 { 637 | return CommandLine.Float64(name, value, usage) 638 | } 639 | 640 | // DurationVar defines a time.Duration flag with specified name, default value, and usage string. 641 | // The argument p points to a time.Duration variable in which to store the value of the flag. 642 | func (f *FlagSet) DurationVar(p *time.Duration, name string, value time.Duration, usage string) { 643 | f.Var(newDurationValue(value, p), name, usage) 644 | } 645 | 646 | // DurationVar defines a time.Duration flag with specified name, default value, and usage string. 647 | // The argument p points to a time.Duration variable in which to store the value of the flag. 648 | func DurationVar(p *time.Duration, name string, value time.Duration, usage string) { 649 | CommandLine.Var(newDurationValue(value, p), name, usage) 650 | } 651 | 652 | // Duration defines a time.Duration flag with specified name, default value, and usage string. 653 | // The return value is the address of a time.Duration variable that stores the value of the flag. 654 | func (f *FlagSet) Duration(name string, value time.Duration, usage string) *time.Duration { 655 | p := new(time.Duration) 656 | f.DurationVar(p, name, value, usage) 657 | return p 658 | } 659 | 660 | // Duration defines a time.Duration flag with specified name, default value, and usage string. 661 | // The return value is the address of a time.Duration variable that stores the value of the flag. 662 | func Duration(name string, value time.Duration, usage string) *time.Duration { 663 | return CommandLine.Duration(name, value, usage) 664 | } 665 | 666 | // Var defines a flag with the specified name and usage string. The type and 667 | // value of the flag are represented by the first argument, of type Value, which 668 | // typically holds a user-defined implementation of Value. For instance, the 669 | // caller could create a flag that turns a comma-separated string into a slice 670 | // of strings by giving the slice the methods of Value; in particular, Set would 671 | // decompose the comma-separated string into the slice. 672 | func (f *FlagSet) Var(value Value, name string, usage string) { 673 | // Remember the default value as a string; it won't change. 674 | flag := &Flag{name, usage, value, value.String()} 675 | _, alreadythere := f.formal[name] 676 | if alreadythere { 677 | var msg string 678 | if f.name == "" { 679 | msg = fmt.Sprintf("flag redefined: %s", name) 680 | } else { 681 | msg = fmt.Sprintf("%s flag redefined: %s", f.name, name) 682 | } 683 | fmt.Fprintln(f.out(), msg) 684 | panic(msg) // Happens only if flags are declared with identical names 685 | } 686 | if f.formal == nil { 687 | f.formal = make(map[string]*Flag) 688 | } 689 | f.formal[name] = flag 690 | } 691 | 692 | // Var defines a flag with the specified name and usage string. The type and 693 | // value of the flag are represented by the first argument, of type Value, which 694 | // typically holds a user-defined implementation of Value. For instance, the 695 | // caller could create a flag that turns a comma-separated string into a slice 696 | // of strings by giving the slice the methods of Value; in particular, Set would 697 | // decompose the comma-separated string into the slice. 698 | func Var(value Value, name string, usage string) { 699 | CommandLine.Var(value, name, usage) 700 | } 701 | 702 | // failf prints to standard error a formatted error and usage message and 703 | // returns the error. 704 | func (f *FlagSet) failf(format string, a ...interface{}) error { 705 | err := fmt.Errorf(format, a...) 706 | fmt.Fprintln(f.out(), err) 707 | f.usage() 708 | return err 709 | } 710 | 711 | // usage calls the Usage method for the flag set, or the usage function if 712 | // the flag set is CommandLine. 713 | func (f *FlagSet) usage() { 714 | if f == CommandLine { 715 | Usage() 716 | } else if f.Usage == nil { 717 | defaultUsage(f) 718 | } else { 719 | f.Usage() 720 | } 721 | } 722 | 723 | // parseOne parses one flag. It reports whether a flag was seen. 724 | func (f *FlagSet) parseOne() (bool, error) { 725 | if len(f.args) == 0 { 726 | return false, nil 727 | } 728 | s := f.args[0] 729 | if len(s) == 0 || s[0] != '-' || len(s) == 1 { 730 | return false, nil 731 | } 732 | numMinuses := 1 733 | if s[1] == '-' { 734 | numMinuses++ 735 | if len(s) == 2 { // "--" terminates the flags 736 | f.args = f.args[1:] 737 | return false, nil 738 | } 739 | } 740 | name := s[numMinuses:] 741 | if len(name) == 0 || name[0] == '-' || name[0] == '=' { 742 | return false, f.failf("bad flag syntax: %s", s) 743 | } 744 | 745 | // ignore go test flags 746 | if strings.HasPrefix(name, "test.") { 747 | return false, nil 748 | } 749 | 750 | // it's a flag. does it have an argument? 751 | f.args = f.args[1:] 752 | hasValue := false 753 | value := "" 754 | for i := 1; i < len(name); i++ { // equals cannot be first 755 | if name[i] == '=' { 756 | value = name[i+1:] 757 | hasValue = true 758 | name = name[0:i] 759 | break 760 | } 761 | } 762 | m := f.formal 763 | flag, alreadythere := m[name] // BUG 764 | if !alreadythere { 765 | if name == "help" || name == "h" { // special case for nice help message. 766 | f.usage() 767 | return false, ErrHelp 768 | } 769 | return false, f.failf("flag provided but not defined: -%s", name) 770 | } 771 | if fv, ok := flag.Value.(boolFlag); ok && fv.IsBoolFlag() { // special case: doesn't need an arg 772 | if hasValue { 773 | if err := fv.Set(value); err != nil { 774 | return false, f.failf("invalid boolean value %q for -%s: %v", value, name, err) 775 | } 776 | } else { 777 | fv.Set("true") 778 | } 779 | } else { 780 | // It must have a value, which might be the next argument. 781 | if !hasValue && len(f.args) > 0 { 782 | // value is the next arg 783 | hasValue = true 784 | value, f.args = f.args[0], f.args[1:] 785 | } 786 | if !hasValue { 787 | return false, f.failf("flag needs an argument: -%s", name) 788 | } 789 | if err := flag.Value.Set(value); err != nil { 790 | return false, f.failf("invalid value %q for flag -%s: %v", value, name, err) 791 | } 792 | } 793 | if f.actual == nil { 794 | f.actual = make(map[string]*Flag) 795 | } 796 | f.actual[name] = flag 797 | return true, nil 798 | } 799 | 800 | // Parse parses flag definitions from the argument list, which should not 801 | // include the command name. Must be called after all flags in the FlagSet 802 | // are defined and before flags are accessed by the program. 803 | // The return value will be ErrHelp if -help was set but not defined. 804 | func (f *FlagSet) Parse(arguments []string) error { 805 | f.parsed = true 806 | f.args = arguments 807 | for { 808 | seen, err := f.parseOne() 809 | if seen { 810 | continue 811 | } 812 | if err == nil { 813 | break 814 | } 815 | switch f.errorHandling { 816 | case ContinueOnError: 817 | return err 818 | case ExitOnError: 819 | os.Exit(2) 820 | case PanicOnError: 821 | panic(err) 822 | } 823 | } 824 | 825 | // Parse environment variables 826 | if err := f.ParseEnv(os.Environ()); err != nil { 827 | switch f.errorHandling { 828 | case ContinueOnError: 829 | return err 830 | case ExitOnError: 831 | os.Exit(2) 832 | case PanicOnError: 833 | panic(err) 834 | } 835 | return err 836 | } 837 | 838 | // Parse configuration from file 839 | var cFile string 840 | if cf := f.formal[DefaultConfigFlagname]; cf != nil { 841 | cFile = cf.Value.String() 842 | } 843 | if cf := f.actual[DefaultConfigFlagname]; cf != nil { 844 | cFile = cf.Value.String() 845 | } 846 | if cFile != "" { 847 | if err := f.ParseFile(cFile); err != nil { 848 | switch f.errorHandling { 849 | case ContinueOnError: 850 | return err 851 | case ExitOnError: 852 | os.Exit(2) 853 | case PanicOnError: 854 | panic(err) 855 | } 856 | return err 857 | } 858 | } 859 | 860 | return nil 861 | } 862 | 863 | // ParseEnv parses flags from environment variables. 864 | // Flags already set will be ignored. 865 | func (f *FlagSet) ParseEnv(environ []string) error { 866 | 867 | m := f.formal 868 | 869 | env := make(map[string]string) 870 | for _, s := range environ { 871 | i := strings.Index(s, "=") 872 | if i < 1 { 873 | continue 874 | } 875 | env[s[0:i]] = s[i+1 : len(s)] 876 | } 877 | 878 | for _, flag := range m { 879 | name := flag.Name 880 | _, set := f.actual[name] 881 | if set { 882 | continue 883 | } 884 | 885 | flag, alreadythere := m[name] 886 | if !alreadythere { 887 | if name == "help" || name == "h" { // special case for nice help message. 888 | f.usage() 889 | return ErrHelp 890 | } 891 | return f.failf("environment variable provided but not defined: %s", name) 892 | } 893 | 894 | envKey := strings.ToUpper(flag.Name) 895 | if f.envPrefix != "" { 896 | envKey = f.envPrefix + "_" + envKey 897 | } 898 | envKey = strings.Replace(envKey, "-", "_", -1) 899 | 900 | value, isSet := env[envKey] 901 | if !isSet { 902 | continue 903 | } 904 | 905 | hasValue := false 906 | if len(value) > 0 { 907 | hasValue = true 908 | } 909 | 910 | if fv, ok := flag.Value.(boolFlag); ok && fv.IsBoolFlag() { // special case: doesn't need an arg 911 | if hasValue { 912 | if err := fv.Set(value); err != nil { 913 | return f.failf("invalid boolean value %q for environment variable %s: %v", value, name, err) 914 | } 915 | } else { 916 | // flag without value is regarded a bool 917 | fv.Set("true") 918 | } 919 | } else { 920 | if err := flag.Value.Set(value); err != nil { 921 | return f.failf("invalid value %q for environment variable %s: %v", value, name, err) 922 | } 923 | } 924 | 925 | // update f.actual 926 | if f.actual == nil { 927 | f.actual = make(map[string]*Flag) 928 | } 929 | f.actual[name] = flag 930 | 931 | } 932 | return nil 933 | } 934 | 935 | // ParseFile parses flags from the file in path. 936 | // Same format as commandline argumens, newlines and lines beginning with a 937 | // "#" charater are ignored. Flags already set will be ignored. 938 | func (f *FlagSet) ParseFile(path string) error { 939 | 940 | // Extract arguments from file 941 | fp, err := os.Open(path) 942 | if err != nil { 943 | return err 944 | } 945 | defer fp.Close() 946 | 947 | scanner := bufio.NewScanner(fp) 948 | for scanner.Scan() { 949 | line := scanner.Text() 950 | 951 | // Ignore empty lines 952 | if len(line) == 0 { 953 | continue 954 | } 955 | 956 | // Ignore comments 957 | if line[:1] == "#" { 958 | continue 959 | } 960 | 961 | // Match `key=value` and `key value` 962 | var name, value string 963 | hasValue := false 964 | for i, v := range line { 965 | if v == '=' || v == ' ' { 966 | hasValue = true 967 | name, value = line[:i], line[i+1:] 968 | break 969 | } 970 | } 971 | 972 | if hasValue == false { 973 | name = line 974 | } 975 | 976 | // Ignore flag when already set; arguments have precedence over file 977 | if f.actual[name] != nil { 978 | continue 979 | } 980 | 981 | m := f.formal 982 | flag, alreadythere := m[name] 983 | if !alreadythere { 984 | if name == "help" || name == "h" { // special case for nice help message. 985 | f.usage() 986 | return ErrHelp 987 | } 988 | return f.failf("configuration variable provided but not defined: %s", name) 989 | } 990 | 991 | if fv, ok := flag.Value.(boolFlag); ok && fv.IsBoolFlag() { // special case: doesn't need an arg 992 | if hasValue { 993 | if err := fv.Set(value); err != nil { 994 | return f.failf("invalid boolean value %q for configuration variable %s: %v", value, name, err) 995 | } 996 | } else { 997 | // flag without value is regarded a bool 998 | fv.Set("true") 999 | } 1000 | } else { 1001 | if err := flag.Value.Set(value); err != nil { 1002 | return f.failf("invalid value %q for configuration variable %s: %v", value, name, err) 1003 | } 1004 | } 1005 | 1006 | // update f.actual 1007 | if f.actual == nil { 1008 | f.actual = make(map[string]*Flag) 1009 | } 1010 | f.actual[name] = flag 1011 | } 1012 | 1013 | if err := scanner.Err(); err != nil { 1014 | return err 1015 | } 1016 | 1017 | return nil 1018 | } 1019 | 1020 | // Parsed reports whether f.Parse has been called. 1021 | func (f *FlagSet) Parsed() bool { 1022 | return f.parsed 1023 | } 1024 | 1025 | // Parse parses the command-line flags from os.Args[1:]. Must be called 1026 | // after all flags are defined and before flags are accessed by the program. 1027 | func Parse() { 1028 | // Ignore errors; CommandLine is set for ExitOnError. 1029 | CommandLine.Parse(os.Args[1:]) 1030 | } 1031 | 1032 | // Parsed returns true if the command-line flags have been parsed. 1033 | func Parsed() bool { 1034 | return CommandLine.Parsed() 1035 | } 1036 | 1037 | // CommandLine is the default set of command-line flags, parsed from os.Args. 1038 | // The top-level functions such as BoolVar, Arg, and on are wrappers for the 1039 | // methods of CommandLine. 1040 | var CommandLine = NewFlagSet(os.Args[0], ExitOnError) 1041 | 1042 | // NewFlagSet returns a new, empty flag set with the specified name and 1043 | // error handling property. 1044 | func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet { 1045 | f := &FlagSet{ 1046 | name: name, 1047 | errorHandling: errorHandling, 1048 | } 1049 | return f 1050 | } 1051 | 1052 | // NewFlagSetWithEnvPrefix returns a new empty flag set with the specified name, 1053 | // environment variable prefix, and error handling property. 1054 | func NewFlagSetWithEnvPrefix(name string, prefix string, errorHandling ErrorHandling) *FlagSet { 1055 | f := NewFlagSet(name, errorHandling) 1056 | f.envPrefix = prefix 1057 | return f 1058 | } 1059 | 1060 | // Init sets the name, environment name prefix, and error handling property 1061 | // for a flag set. 1062 | // By default, the zero FlagSet uses an empty name, EnvironmentPrefix, and the 1063 | // ContinueOnError error handling policy. 1064 | func (f *FlagSet) Init(name string, errorHandling ErrorHandling) { 1065 | f.name = name 1066 | f.envPrefix = EnvironmentPrefix 1067 | f.errorHandling = errorHandling 1068 | } 1069 | -------------------------------------------------------------------------------- /vendor/github.com/namsral/flag/flag_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package flag 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "os" 11 | "sort" 12 | "strings" 13 | "syscall" 14 | "testing" 15 | "time" 16 | ) 17 | 18 | func boolString(s string) string { 19 | if s == "0" { 20 | return "false" 21 | } 22 | return "true" 23 | } 24 | 25 | func TestEverything(t *testing.T) { 26 | ResetForTesting(nil) 27 | Bool("test_bool", false, "bool value") 28 | Int("test_int", 0, "int value") 29 | Int64("test_int64", 0, "int64 value") 30 | Uint("test_uint", 0, "uint value") 31 | Uint64("test_uint64", 0, "uint64 value") 32 | String("test_string", "0", "string value") 33 | Float64("test_float64", 0, "float64 value") 34 | Duration("test_duration", 0, "time.Duration value") 35 | 36 | m := make(map[string]*Flag) 37 | desired := "0" 38 | visitor := func(f *Flag) { 39 | if len(f.Name) > 5 && f.Name[0:5] == "test_" { 40 | m[f.Name] = f 41 | ok := false 42 | switch { 43 | case f.Value.String() == desired: 44 | ok = true 45 | case f.Name == "test_bool" && f.Value.String() == boolString(desired): 46 | ok = true 47 | case f.Name == "test_duration" && f.Value.String() == desired+"s": 48 | ok = true 49 | } 50 | if !ok { 51 | t.Error("Visit: bad value", f.Value.String(), "for", f.Name) 52 | } 53 | } 54 | } 55 | VisitAll(visitor) 56 | if len(m) != 8 { 57 | t.Error("VisitAll misses some flags") 58 | for k, v := range m { 59 | t.Log(k, *v) 60 | } 61 | } 62 | m = make(map[string]*Flag) 63 | Visit(visitor) 64 | if len(m) != 0 { 65 | t.Errorf("Visit sees unset flags") 66 | for k, v := range m { 67 | t.Log(k, *v) 68 | } 69 | } 70 | // Now set all flags 71 | Set("test_bool", "true") 72 | Set("test_int", "1") 73 | Set("test_int64", "1") 74 | Set("test_uint", "1") 75 | Set("test_uint64", "1") 76 | Set("test_string", "1") 77 | Set("test_float64", "1") 78 | Set("test_duration", "1s") 79 | desired = "1" 80 | Visit(visitor) 81 | if len(m) != 8 { 82 | t.Error("Visit fails after set") 83 | for k, v := range m { 84 | t.Log(k, *v) 85 | } 86 | } 87 | // Now test they're visited in sort order. 88 | var flagNames []string 89 | Visit(func(f *Flag) { flagNames = append(flagNames, f.Name) }) 90 | if !sort.StringsAreSorted(flagNames) { 91 | t.Errorf("flag names not sorted: %v", flagNames) 92 | } 93 | } 94 | 95 | func TestGet(t *testing.T) { 96 | ResetForTesting(nil) 97 | Bool("test_bool", true, "bool value") 98 | Int("test_int", 1, "int value") 99 | Int64("test_int64", 2, "int64 value") 100 | Uint("test_uint", 3, "uint value") 101 | Uint64("test_uint64", 4, "uint64 value") 102 | String("test_string", "5", "string value") 103 | Float64("test_float64", 6, "float64 value") 104 | Duration("test_duration", 7, "time.Duration value") 105 | 106 | visitor := func(f *Flag) { 107 | if len(f.Name) > 5 && f.Name[0:5] == "test_" { 108 | g, ok := f.Value.(Getter) 109 | if !ok { 110 | t.Errorf("Visit: value does not satisfy Getter: %T", f.Value) 111 | return 112 | } 113 | switch f.Name { 114 | case "test_bool": 115 | ok = g.Get() == true 116 | case "test_int": 117 | ok = g.Get() == int(1) 118 | case "test_int64": 119 | ok = g.Get() == int64(2) 120 | case "test_uint": 121 | ok = g.Get() == uint(3) 122 | case "test_uint64": 123 | ok = g.Get() == uint64(4) 124 | case "test_string": 125 | ok = g.Get() == "5" 126 | case "test_float64": 127 | ok = g.Get() == float64(6) 128 | case "test_duration": 129 | ok = g.Get() == time.Duration(7) 130 | } 131 | if !ok { 132 | t.Errorf("Visit: bad value %T(%v) for %s", g.Get(), g.Get(), f.Name) 133 | } 134 | } 135 | } 136 | VisitAll(visitor) 137 | } 138 | 139 | func TestUsage(t *testing.T) { 140 | called := false 141 | ResetForTesting(func() { called = true }) 142 | if CommandLine.Parse([]string{"-x"}) == nil { 143 | t.Error("parse did not fail for unknown flag") 144 | } 145 | if !called { 146 | t.Error("did not call Usage for unknown flag") 147 | } 148 | } 149 | 150 | func testParse(f *FlagSet, t *testing.T) { 151 | if f.Parsed() { 152 | t.Error("f.Parse() = true before Parse") 153 | } 154 | boolFlag := f.Bool("bool", false, "bool value") 155 | bool2Flag := f.Bool("bool2", false, "bool2 value") 156 | intFlag := f.Int("int", 0, "int value") 157 | int64Flag := f.Int64("int64", 0, "int64 value") 158 | uintFlag := f.Uint("uint", 0, "uint value") 159 | uint64Flag := f.Uint64("uint64", 0, "uint64 value") 160 | stringFlag := f.String("string", "0", "string value") 161 | float64Flag := f.Float64("float64", 0, "float64 value") 162 | durationFlag := f.Duration("duration", 5*time.Second, "time.Duration value") 163 | extra := "one-extra-argument" 164 | args := []string{ 165 | "-bool", 166 | "-bool2=true", 167 | "--int", "22", 168 | "--int64", "0x23", 169 | "-uint", "24", 170 | "--uint64", "25", 171 | "-string", "hello", 172 | "-float64", "2718e28", 173 | "-duration", "2m", 174 | extra, 175 | } 176 | if err := f.Parse(args); err != nil { 177 | t.Fatal(err) 178 | } 179 | if !f.Parsed() { 180 | t.Error("f.Parse() = false after Parse") 181 | } 182 | if *boolFlag != true { 183 | t.Error("bool flag should be true, is ", *boolFlag) 184 | } 185 | if *bool2Flag != true { 186 | t.Error("bool2 flag should be true, is ", *bool2Flag) 187 | } 188 | if *intFlag != 22 { 189 | t.Error("int flag should be 22, is ", *intFlag) 190 | } 191 | if *int64Flag != 0x23 { 192 | t.Error("int64 flag should be 0x23, is ", *int64Flag) 193 | } 194 | if *uintFlag != 24 { 195 | t.Error("uint flag should be 24, is ", *uintFlag) 196 | } 197 | if *uint64Flag != 25 { 198 | t.Error("uint64 flag should be 25, is ", *uint64Flag) 199 | } 200 | if *stringFlag != "hello" { 201 | t.Error("string flag should be `hello`, is ", *stringFlag) 202 | } 203 | if *float64Flag != 2718e28 { 204 | t.Error("float64 flag should be 2718e28, is ", *float64Flag) 205 | } 206 | if *durationFlag != 2*time.Minute { 207 | t.Error("duration flag should be 2m, is ", *durationFlag) 208 | } 209 | if len(f.Args()) != 1 { 210 | t.Error("expected one argument, got", len(f.Args())) 211 | } else if f.Args()[0] != extra { 212 | t.Errorf("expected argument %q got %q", extra, f.Args()[0]) 213 | } 214 | } 215 | 216 | func TestParse(t *testing.T) { 217 | ResetForTesting(func() { t.Error("bad parse") }) 218 | testParse(CommandLine, t) 219 | } 220 | 221 | func TestFlagSetParse(t *testing.T) { 222 | testParse(NewFlagSet("test", ContinueOnError), t) 223 | } 224 | 225 | func TestFlagSetParseErrors(t *testing.T) { 226 | fs := NewFlagSet("test", ContinueOnError) 227 | fs.Int("int", 0, "int value") 228 | 229 | args := []string{"-int", "bad"} 230 | expected := `invalid value "bad" for flag -int: strconv.ParseInt: parsing "bad": invalid syntax` 231 | if err := fs.Parse(args); err == nil || err.Error() != expected { 232 | t.Errorf("expected error %q parsing from args, got: %v", expected, err) 233 | } 234 | 235 | if err := os.Setenv("INT", "bad"); err != nil { 236 | t.Fatalf("error setting env: %s", err.Error()) 237 | } 238 | expected = `invalid value "bad" for environment variable int: strconv.ParseInt: parsing "bad": invalid syntax` 239 | if err := fs.Parse([]string{}); err == nil || err.Error() != expected { 240 | t.Errorf("expected error %q parsing from env, got: %v", expected, err) 241 | } 242 | if err := os.Unsetenv("INT"); err != nil { 243 | t.Fatalf("error unsetting env: %s", err.Error()) 244 | } 245 | 246 | fs.String("config", "", "config filename") 247 | args = []string{"-config", "testdata/bad_test.conf"} 248 | expected = `invalid value "bad" for configuration variable int: strconv.ParseInt: parsing "bad": invalid syntax` 249 | if err := fs.Parse(args); err == nil || err.Error() != expected { 250 | t.Errorf("expected error %q parsing from config, got: %v", expected, err) 251 | } 252 | } 253 | 254 | // Declare a user-defined flag type. 255 | type flagVar []string 256 | 257 | func (f *flagVar) String() string { 258 | return fmt.Sprint([]string(*f)) 259 | } 260 | 261 | func (f *flagVar) Set(value string) error { 262 | *f = append(*f, value) 263 | return nil 264 | } 265 | 266 | func TestUserDefined(t *testing.T) { 267 | var flags FlagSet 268 | flags.Init("test", ContinueOnError) 269 | var v flagVar 270 | flags.Var(&v, "v", "usage") 271 | if err := flags.Parse([]string{"-v", "1", "-v", "2", "-v=3"}); err != nil { 272 | t.Error(err) 273 | } 274 | if len(v) != 3 { 275 | t.Fatal("expected 3 args; got ", len(v)) 276 | } 277 | expect := "[1 2 3]" 278 | if v.String() != expect { 279 | t.Errorf("expected value %q got %q", expect, v.String()) 280 | } 281 | } 282 | 283 | // Declare a user-defined boolean flag type. 284 | type boolFlagVar struct { 285 | count int 286 | } 287 | 288 | func (b *boolFlagVar) String() string { 289 | return fmt.Sprintf("%d", b.count) 290 | } 291 | 292 | func (b *boolFlagVar) Set(value string) error { 293 | if value == "true" { 294 | b.count++ 295 | } 296 | return nil 297 | } 298 | 299 | func (b *boolFlagVar) IsBoolFlag() bool { 300 | return b.count < 4 301 | } 302 | 303 | func TestUserDefinedBool(t *testing.T) { 304 | var flags FlagSet 305 | flags.Init("test", ContinueOnError) 306 | var b boolFlagVar 307 | var err error 308 | flags.Var(&b, "b", "usage") 309 | if err = flags.Parse([]string{"-b", "-b", "-b", "-b=true", "-b=false", "-b", "barg", "-b"}); err != nil { 310 | if b.count < 4 { 311 | t.Error(err) 312 | } 313 | } 314 | 315 | if b.count != 4 { 316 | t.Errorf("want: %d; got: %d", 4, b.count) 317 | } 318 | 319 | if err == nil { 320 | t.Error("expected error; got none") 321 | } 322 | } 323 | 324 | func TestSetOutput(t *testing.T) { 325 | var flags FlagSet 326 | var buf bytes.Buffer 327 | flags.SetOutput(&buf) 328 | flags.Init("test", ContinueOnError) 329 | flags.Parse([]string{"-unknown"}) 330 | if out := buf.String(); !strings.Contains(out, "-unknown") { 331 | t.Logf("expected output mentioning unknown; got %q", out) 332 | } 333 | } 334 | 335 | // This tests that one can reset the flags. This still works but not well, and is 336 | // superseded by FlagSet. 337 | func TestChangingArgs(t *testing.T) { 338 | ResetForTesting(func() { t.Fatal("bad parse") }) 339 | oldArgs := os.Args 340 | defer func() { os.Args = oldArgs }() 341 | os.Args = []string{"cmd", "-before", "subcmd", "-after", "args"} 342 | before := Bool("before", false, "") 343 | if err := CommandLine.Parse(os.Args[1:]); err != nil { 344 | t.Fatal(err) 345 | } 346 | cmd := Arg(0) 347 | os.Args = Args() 348 | after := Bool("after", false, "") 349 | Parse() 350 | args := Args() 351 | 352 | if !*before || cmd != "subcmd" || !*after || len(args) != 1 || args[0] != "args" { 353 | t.Fatalf("expected true subcmd true [args] got %v %v %v %v", *before, cmd, *after, args) 354 | } 355 | } 356 | 357 | // Test that -help invokes the usage message and returns ErrHelp. 358 | func TestHelp(t *testing.T) { 359 | var helpCalled = false 360 | fs := NewFlagSet("help test", ContinueOnError) 361 | fs.Usage = func() { helpCalled = true } 362 | var flag bool 363 | fs.BoolVar(&flag, "flag", false, "regular flag") 364 | // Regular flag invocation should work 365 | err := fs.Parse([]string{"-flag=true"}) 366 | if err != nil { 367 | t.Fatal("expected no error; got ", err) 368 | } 369 | if !flag { 370 | t.Error("flag was not set by -flag") 371 | } 372 | if helpCalled { 373 | t.Error("help called for regular flag") 374 | helpCalled = false // reset for next test 375 | } 376 | // Help flag should work as expected. 377 | err = fs.Parse([]string{"-help"}) 378 | if err == nil { 379 | t.Fatal("error expected") 380 | } 381 | if err != ErrHelp { 382 | t.Fatal("expected ErrHelp; got ", err) 383 | } 384 | if !helpCalled { 385 | t.Fatal("help was not called") 386 | } 387 | // If we define a help flag, that should override. 388 | var help bool 389 | fs.BoolVar(&help, "help", false, "help flag") 390 | helpCalled = false 391 | err = fs.Parse([]string{"-help"}) 392 | if err != nil { 393 | t.Fatal("expected no error for defined -help; got ", err) 394 | } 395 | if helpCalled { 396 | t.Fatal("help was called; should not have been for defined help flag") 397 | } 398 | } 399 | 400 | // Test parsing a environment variables 401 | func TestParseEnv(t *testing.T) { 402 | 403 | syscall.Setenv("BOOL", "") 404 | syscall.Setenv("BOOL2", "true") 405 | syscall.Setenv("INT", "22") 406 | syscall.Setenv("INT64", "0x23") 407 | syscall.Setenv("UINT", "24") 408 | syscall.Setenv("UINT64", "25") 409 | syscall.Setenv("STRING", "hello") 410 | syscall.Setenv("FLOAT64", "2718e28") 411 | syscall.Setenv("DURATION", "2m") 412 | 413 | f := NewFlagSet(os.Args[0], ContinueOnError) 414 | 415 | boolFlag := f.Bool("bool", false, "bool value") 416 | bool2Flag := f.Bool("bool2", false, "bool2 value") 417 | intFlag := f.Int("int", 0, "int value") 418 | int64Flag := f.Int64("int64", 0, "int64 value") 419 | uintFlag := f.Uint("uint", 0, "uint value") 420 | uint64Flag := f.Uint64("uint64", 0, "uint64 value") 421 | stringFlag := f.String("string", "0", "string value") 422 | float64Flag := f.Float64("float64", 0, "float64 value") 423 | durationFlag := f.Duration("duration", 5*time.Second, "time.Duration value") 424 | 425 | err := f.ParseEnv(os.Environ()) 426 | if err != nil { 427 | t.Fatal("expected no error; got ", err) 428 | } 429 | if *boolFlag != true { 430 | t.Error("bool flag should be true, is ", *boolFlag) 431 | } 432 | if *bool2Flag != true { 433 | t.Error("bool2 flag should be true, is ", *bool2Flag) 434 | } 435 | if *intFlag != 22 { 436 | t.Error("int flag should be 22, is ", *intFlag) 437 | } 438 | if *int64Flag != 0x23 { 439 | t.Error("int64 flag should be 0x23, is ", *int64Flag) 440 | } 441 | if *uintFlag != 24 { 442 | t.Error("uint flag should be 24, is ", *uintFlag) 443 | } 444 | if *uint64Flag != 25 { 445 | t.Error("uint64 flag should be 25, is ", *uint64Flag) 446 | } 447 | if *stringFlag != "hello" { 448 | t.Error("string flag should be `hello`, is ", *stringFlag) 449 | } 450 | if *float64Flag != 2718e28 { 451 | t.Error("float64 flag should be 2718e28, is ", *float64Flag) 452 | } 453 | if *durationFlag != 2*time.Minute { 454 | t.Error("duration flag should be 2m, is ", *durationFlag) 455 | } 456 | } 457 | 458 | // Test parsing a configuration file 459 | func TestParseFile(t *testing.T) { 460 | 461 | f := NewFlagSet(os.Args[0], ContinueOnError) 462 | 463 | boolFlag := f.Bool("bool", false, "bool value") 464 | bool2Flag := f.Bool("bool2", false, "bool2 value") 465 | intFlag := f.Int("int", 0, "int value") 466 | int64Flag := f.Int64("int64", 0, "int64 value") 467 | uintFlag := f.Uint("uint", 0, "uint value") 468 | uint64Flag := f.Uint64("uint64", 0, "uint64 value") 469 | stringFlag := f.String("string", "0", "string value") 470 | float64Flag := f.Float64("float64", 0, "float64 value") 471 | durationFlag := f.Duration("duration", 5*time.Second, "time.Duration value") 472 | 473 | err := f.ParseFile("./testdata/test.conf") 474 | if err != nil { 475 | t.Fatal("expected no error; got ", err) 476 | } 477 | if *boolFlag != true { 478 | t.Error("bool flag should be true, is ", *boolFlag) 479 | } 480 | if *bool2Flag != true { 481 | t.Error("bool2 flag should be true, is ", *bool2Flag) 482 | } 483 | if *intFlag != 22 { 484 | t.Error("int flag should be 22, is ", *intFlag) 485 | } 486 | if *int64Flag != 0x23 { 487 | t.Error("int64 flag should be 0x23, is ", *int64Flag) 488 | } 489 | if *uintFlag != 24 { 490 | t.Error("uint flag should be 24, is ", *uintFlag) 491 | } 492 | if *uint64Flag != 25 { 493 | t.Error("uint64 flag should be 25, is ", *uint64Flag) 494 | } 495 | if *stringFlag != "hello" { 496 | t.Error("string flag should be `hello`, is ", *stringFlag) 497 | } 498 | if *float64Flag != 2718e28 { 499 | t.Error("float64 flag should be 2718e28, is ", *float64Flag) 500 | } 501 | if *durationFlag != 2*time.Minute { 502 | t.Error("duration flag should be 2m, is ", *durationFlag) 503 | } 504 | } 505 | 506 | func TestParseFileUnknownFlag(t *testing.T) { 507 | f := NewFlagSet("test", ContinueOnError) 508 | if err := f.ParseFile("./testdata/bad_test.conf"); err == nil { 509 | t.Error("parse did not fail for unknown flag; ", err) 510 | } 511 | } 512 | 513 | func TestTestingPackageFlags(t *testing.T) { 514 | f := NewFlagSet("test", ContinueOnError) 515 | if err := f.Parse([]string{"-test.v", "-test.count", "1"}); err != nil { 516 | t.Error(err) 517 | } 518 | } 519 | 520 | func TestDefaultConfigFlagname(t *testing.T) { 521 | f := NewFlagSet("test", ContinueOnError) 522 | 523 | f.Bool("bool", false, "bool value") 524 | f.Bool("bool2", false, "bool2 value") 525 | f.Int("int", 0, "int value") 526 | f.Int64("int64", 0, "int64 value") 527 | f.Uint("uint", 0, "uint value") 528 | f.Uint64("uint64", 0, "uint64 value") 529 | stringFlag := f.String("string", "0", "string value") 530 | f.Float64("float64", 0, "float64 value") 531 | f.Duration("duration", 5*time.Second, "time.Duration value") 532 | 533 | f.String(DefaultConfigFlagname, "./testdata/test.conf", "config path") 534 | 535 | if err := os.Unsetenv("STRING"); err != nil { 536 | t.Error(err) 537 | } 538 | 539 | if err := f.Parse([]string{}); err != nil { 540 | t.Error("parse failed; ", err) 541 | } 542 | 543 | if *stringFlag != "hello" { 544 | t.Error("string flag should be `hello`, is", *stringFlag) 545 | } 546 | } 547 | 548 | func TestDefaultConfigFlagnameMissingFile(t *testing.T) { 549 | f := NewFlagSet("test", ContinueOnError) 550 | f.String(DefaultConfigFlagname, "./testdata/missing", "config path") 551 | 552 | if err := os.Unsetenv("STRING"); err != nil { 553 | t.Error(err) 554 | } 555 | if err := f.Parse([]string{}); err == nil { 556 | t.Error("expected error of missing config file, got nil") 557 | } 558 | } 559 | -------------------------------------------------------------------------------- /vendor/github.com/namsral/flag/testdata/bad_test.conf: -------------------------------------------------------------------------------- 1 | int bad 2 | -------------------------------------------------------------------------------- /vendor/github.com/namsral/flag/testdata/test.conf: -------------------------------------------------------------------------------- 1 | # this is a comment followed by an empty line 2 | 3 | bool 4 | bool2=true 5 | int 22 6 | int64 0x23 7 | uint 24 8 | uint64 25 9 | string hello 10 | float64 2718e28 11 | duration 2m -------------------------------------------------------------------------------- /vendor/github.com/pkg/errors/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/errors/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go_import_path: github.com/pkg/errors 3 | go: 4 | - 1.4.3 5 | - 1.5.4 6 | - 1.6.2 7 | - 1.7.1 8 | - tip 9 | 10 | script: 11 | - go test -v ./... 12 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/errors/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Dave Cheney 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/errors/README.md: -------------------------------------------------------------------------------- 1 | # 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) 2 | 3 | Package errors provides simple error handling primitives. 4 | 5 | `go get github.com/pkg/errors` 6 | 7 | The traditional error handling idiom in Go is roughly akin to 8 | ```go 9 | if err != nil { 10 | return err 11 | } 12 | ``` 13 | 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. 14 | 15 | ## Adding context to an error 16 | 17 | The errors.Wrap function returns a new error that adds context to the original error. For example 18 | ```go 19 | _, err := ioutil.ReadAll(r) 20 | if err != nil { 21 | return errors.Wrap(err, "read failed") 22 | } 23 | ``` 24 | ## Retrieving the cause of an error 25 | 26 | 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`. 27 | ```go 28 | type causer interface { 29 | Cause() error 30 | } 31 | ``` 32 | `errors.Cause` will recursively retrieve the topmost error which does not implement `causer`, which is assumed to be the original cause. For example: 33 | ```go 34 | switch err := errors.Cause(err).(type) { 35 | case *MyError: 36 | // handle specifically 37 | default: 38 | // unknown error 39 | } 40 | ``` 41 | 42 | [Read the package documentation for more information](https://godoc.org/github.com/pkg/errors). 43 | 44 | ## Contributing 45 | 46 | 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. 47 | 48 | Before proposing a change, please discuss your change by raising an issue. 49 | 50 | ## Licence 51 | 52 | BSD-2-Clause 53 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/errors/appveyor.yml: -------------------------------------------------------------------------------- 1 | version: build-{build}.{branch} 2 | 3 | clone_folder: C:\gopath\src\github.com\pkg\errors 4 | shallow_clone: true # for startup speed 5 | 6 | environment: 7 | GOPATH: C:\gopath 8 | 9 | platform: 10 | - x64 11 | 12 | # http://www.appveyor.com/docs/installed-software 13 | install: 14 | # some helpful output for debugging builds 15 | - go version 16 | - go env 17 | # pre-installed MinGW at C:\MinGW is 32bit only 18 | # but MSYS2 at C:\msys64 has mingw64 19 | - set PATH=C:\msys64\mingw64\bin;%PATH% 20 | - gcc --version 21 | - g++ --version 22 | 23 | build_script: 24 | - go install -v ./... 25 | 26 | test_script: 27 | - set PATH=C:\gopath\bin;%PATH% 28 | - go test -v ./... 29 | 30 | #artifacts: 31 | # - path: '%GOPATH%\bin\*.exe' 32 | deploy: off 33 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/errors/bench_test.go: -------------------------------------------------------------------------------- 1 | // +build go1.7 2 | 3 | package errors 4 | 5 | import ( 6 | "fmt" 7 | "testing" 8 | 9 | stderrors "errors" 10 | ) 11 | 12 | func noErrors(at, depth int) error { 13 | if at >= depth { 14 | return stderrors.New("no error") 15 | } 16 | return noErrors(at+1, depth) 17 | } 18 | func yesErrors(at, depth int) error { 19 | if at >= depth { 20 | return New("ye error") 21 | } 22 | return yesErrors(at+1, depth) 23 | } 24 | 25 | func BenchmarkErrors(b *testing.B) { 26 | var toperr error 27 | type run struct { 28 | stack int 29 | std bool 30 | } 31 | runs := []run{ 32 | {10, false}, 33 | {10, true}, 34 | {100, false}, 35 | {100, true}, 36 | {1000, false}, 37 | {1000, true}, 38 | } 39 | for _, r := range runs { 40 | part := "pkg/errors" 41 | if r.std { 42 | part = "errors" 43 | } 44 | name := fmt.Sprintf("%s-stack-%d", part, r.stack) 45 | b.Run(name, func(b *testing.B) { 46 | var err error 47 | f := yesErrors 48 | if r.std { 49 | f = noErrors 50 | } 51 | b.ReportAllocs() 52 | for i := 0; i < b.N; i++ { 53 | err = f(0, r.stack) 54 | } 55 | b.StopTimer() 56 | toperr = err 57 | }) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/errors/errors.go: -------------------------------------------------------------------------------- 1 | // Package errors provides simple error handling primitives. 2 | // 3 | // The traditional error handling idiom in Go is roughly akin to 4 | // 5 | // if err != nil { 6 | // return err 7 | // } 8 | // 9 | // which applied recursively up the call stack results in error reports 10 | // without context or debugging information. The errors package allows 11 | // programmers to add context to the failure path in their code in a way 12 | // that does not destroy the original value of the error. 13 | // 14 | // Adding context to an error 15 | // 16 | // The errors.Wrap function returns a new error that adds context to the 17 | // original error by recording a stack trace at the point Wrap is called, 18 | // and the supplied message. For example 19 | // 20 | // _, err := ioutil.ReadAll(r) 21 | // if err != nil { 22 | // return errors.Wrap(err, "read failed") 23 | // } 24 | // 25 | // If additional control is required the errors.WithStack and errors.WithMessage 26 | // functions destructure errors.Wrap into its component operations of annotating 27 | // an error with a stack trace and an a message, respectively. 28 | // 29 | // Retrieving the cause of an error 30 | // 31 | // Using errors.Wrap constructs a stack of errors, adding context to the 32 | // preceding error. Depending on the nature of the error it may be necessary 33 | // to reverse the operation of errors.Wrap to retrieve the original error 34 | // for inspection. Any error value which implements this interface 35 | // 36 | // type causer interface { 37 | // Cause() error 38 | // } 39 | // 40 | // can be inspected by errors.Cause. errors.Cause will recursively retrieve 41 | // the topmost error which does not implement causer, which is assumed to be 42 | // the original cause. For example: 43 | // 44 | // switch err := errors.Cause(err).(type) { 45 | // case *MyError: 46 | // // handle specifically 47 | // default: 48 | // // unknown error 49 | // } 50 | // 51 | // causer interface is not exported by this package, but is considered a part 52 | // of stable public API. 53 | // 54 | // Formatted printing of errors 55 | // 56 | // All error values returned from this package implement fmt.Formatter and can 57 | // be formatted by the fmt package. The following verbs are supported 58 | // 59 | // %s print the error. If the error has a Cause it will be 60 | // printed recursively 61 | // %v see %s 62 | // %+v extended format. Each Frame of the error's StackTrace will 63 | // be printed in detail. 64 | // 65 | // Retrieving the stack trace of an error or wrapper 66 | // 67 | // New, Errorf, Wrap, and Wrapf record a stack trace at the point they are 68 | // invoked. This information can be retrieved with the following interface. 69 | // 70 | // type stackTracer interface { 71 | // StackTrace() errors.StackTrace 72 | // } 73 | // 74 | // Where errors.StackTrace is defined as 75 | // 76 | // type StackTrace []Frame 77 | // 78 | // The Frame type represents a call site in the stack trace. Frame supports 79 | // the fmt.Formatter interface that can be used for printing information about 80 | // the stack trace of this error. For example: 81 | // 82 | // if err, ok := err.(stackTracer); ok { 83 | // for _, f := range err.StackTrace() { 84 | // fmt.Printf("%+s:%d", f) 85 | // } 86 | // } 87 | // 88 | // stackTracer interface is not exported by this package, but is considered a part 89 | // of stable public API. 90 | // 91 | // See the documentation for Frame.Format for more details. 92 | package errors 93 | 94 | import ( 95 | "fmt" 96 | "io" 97 | ) 98 | 99 | // New returns an error with the supplied message. 100 | // New also records the stack trace at the point it was called. 101 | func New(message string) error { 102 | return &fundamental{ 103 | msg: message, 104 | stack: callers(), 105 | } 106 | } 107 | 108 | // Errorf formats according to a format specifier and returns the string 109 | // as a value that satisfies error. 110 | // Errorf also records the stack trace at the point it was called. 111 | func Errorf(format string, args ...interface{}) error { 112 | return &fundamental{ 113 | msg: fmt.Sprintf(format, args...), 114 | stack: callers(), 115 | } 116 | } 117 | 118 | // fundamental is an error that has a message and a stack, but no caller. 119 | type fundamental struct { 120 | msg string 121 | *stack 122 | } 123 | 124 | func (f *fundamental) Error() string { return f.msg } 125 | 126 | func (f *fundamental) Format(s fmt.State, verb rune) { 127 | switch verb { 128 | case 'v': 129 | if s.Flag('+') { 130 | io.WriteString(s, f.msg) 131 | f.stack.Format(s, verb) 132 | return 133 | } 134 | fallthrough 135 | case 's': 136 | io.WriteString(s, f.msg) 137 | case 'q': 138 | fmt.Fprintf(s, "%q", f.msg) 139 | } 140 | } 141 | 142 | // WithStack annotates err with a stack trace at the point WithStack was called. 143 | // If err is nil, WithStack returns nil. 144 | func WithStack(err error) error { 145 | if err == nil { 146 | return nil 147 | } 148 | return &withStack{ 149 | err, 150 | callers(), 151 | } 152 | } 153 | 154 | type withStack struct { 155 | error 156 | *stack 157 | } 158 | 159 | func (w *withStack) Cause() error { return w.error } 160 | 161 | func (w *withStack) Format(s fmt.State, verb rune) { 162 | switch verb { 163 | case 'v': 164 | if s.Flag('+') { 165 | fmt.Fprintf(s, "%+v", w.Cause()) 166 | w.stack.Format(s, verb) 167 | return 168 | } 169 | fallthrough 170 | case 's': 171 | io.WriteString(s, w.Error()) 172 | case 'q': 173 | fmt.Fprintf(s, "%q", w.Error()) 174 | } 175 | } 176 | 177 | // Wrap returns an error annotating err with a stack trace 178 | // at the point Wrap is called, and the supplied message. 179 | // If err is nil, Wrap returns nil. 180 | func Wrap(err error, message string) error { 181 | if err == nil { 182 | return nil 183 | } 184 | err = &withMessage{ 185 | cause: err, 186 | msg: message, 187 | } 188 | return &withStack{ 189 | err, 190 | callers(), 191 | } 192 | } 193 | 194 | // Wrapf returns an error annotating err with a stack trace 195 | // at the point Wrapf is call, and the format specifier. 196 | // If err is nil, Wrapf returns nil. 197 | func Wrapf(err error, format string, args ...interface{}) error { 198 | if err == nil { 199 | return nil 200 | } 201 | err = &withMessage{ 202 | cause: err, 203 | msg: fmt.Sprintf(format, args...), 204 | } 205 | return &withStack{ 206 | err, 207 | callers(), 208 | } 209 | } 210 | 211 | // WithMessage annotates err with a new message. 212 | // If err is nil, WithMessage returns nil. 213 | func WithMessage(err error, message string) error { 214 | if err == nil { 215 | return nil 216 | } 217 | return &withMessage{ 218 | cause: err, 219 | msg: message, 220 | } 221 | } 222 | 223 | type withMessage struct { 224 | cause error 225 | msg string 226 | } 227 | 228 | func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() } 229 | func (w *withMessage) Cause() error { return w.cause } 230 | 231 | func (w *withMessage) Format(s fmt.State, verb rune) { 232 | switch verb { 233 | case 'v': 234 | if s.Flag('+') { 235 | fmt.Fprintf(s, "%+v\n", w.Cause()) 236 | io.WriteString(s, w.msg) 237 | return 238 | } 239 | fallthrough 240 | case 's', 'q': 241 | io.WriteString(s, w.Error()) 242 | } 243 | } 244 | 245 | // Cause returns the underlying cause of the error, if possible. 246 | // An error value has a cause if it implements the following 247 | // interface: 248 | // 249 | // type causer interface { 250 | // Cause() error 251 | // } 252 | // 253 | // If the error does not implement Cause, the original error will 254 | // be returned. If the error is nil, nil will be returned without further 255 | // investigation. 256 | func Cause(err error) error { 257 | type causer interface { 258 | Cause() error 259 | } 260 | 261 | for err != nil { 262 | cause, ok := err.(causer) 263 | if !ok { 264 | break 265 | } 266 | err = cause.Cause() 267 | } 268 | return err 269 | } 270 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/errors/errors_test.go: -------------------------------------------------------------------------------- 1 | package errors 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io" 7 | "reflect" 8 | "testing" 9 | ) 10 | 11 | func TestNew(t *testing.T) { 12 | tests := []struct { 13 | err string 14 | want error 15 | }{ 16 | {"", fmt.Errorf("")}, 17 | {"foo", fmt.Errorf("foo")}, 18 | {"foo", New("foo")}, 19 | {"string with format specifiers: %v", errors.New("string with format specifiers: %v")}, 20 | } 21 | 22 | for _, tt := range tests { 23 | got := New(tt.err) 24 | if got.Error() != tt.want.Error() { 25 | t.Errorf("New.Error(): got: %q, want %q", got, tt.want) 26 | } 27 | } 28 | } 29 | 30 | func TestWrapNil(t *testing.T) { 31 | got := Wrap(nil, "no error") 32 | if got != nil { 33 | t.Errorf("Wrap(nil, \"no error\"): got %#v, expected nil", got) 34 | } 35 | } 36 | 37 | func TestWrap(t *testing.T) { 38 | tests := []struct { 39 | err error 40 | message string 41 | want string 42 | }{ 43 | {io.EOF, "read error", "read error: EOF"}, 44 | {Wrap(io.EOF, "read error"), "client error", "client error: read error: EOF"}, 45 | } 46 | 47 | for _, tt := range tests { 48 | got := Wrap(tt.err, tt.message).Error() 49 | if got != tt.want { 50 | t.Errorf("Wrap(%v, %q): got: %v, want %v", tt.err, tt.message, got, tt.want) 51 | } 52 | } 53 | } 54 | 55 | type nilError struct{} 56 | 57 | func (nilError) Error() string { return "nil error" } 58 | 59 | func TestCause(t *testing.T) { 60 | x := New("error") 61 | tests := []struct { 62 | err error 63 | want error 64 | }{{ 65 | // nil error is nil 66 | err: nil, 67 | want: nil, 68 | }, { 69 | // explicit nil error is nil 70 | err: (error)(nil), 71 | want: nil, 72 | }, { 73 | // typed nil is nil 74 | err: (*nilError)(nil), 75 | want: (*nilError)(nil), 76 | }, { 77 | // uncaused error is unaffected 78 | err: io.EOF, 79 | want: io.EOF, 80 | }, { 81 | // caused error returns cause 82 | err: Wrap(io.EOF, "ignored"), 83 | want: io.EOF, 84 | }, { 85 | err: x, // return from errors.New 86 | want: x, 87 | }, { 88 | WithMessage(nil, "whoops"), 89 | nil, 90 | }, { 91 | WithMessage(io.EOF, "whoops"), 92 | io.EOF, 93 | }, { 94 | WithStack(nil), 95 | nil, 96 | }, { 97 | WithStack(io.EOF), 98 | io.EOF, 99 | }} 100 | 101 | for i, tt := range tests { 102 | got := Cause(tt.err) 103 | if !reflect.DeepEqual(got, tt.want) { 104 | t.Errorf("test %d: got %#v, want %#v", i+1, got, tt.want) 105 | } 106 | } 107 | } 108 | 109 | func TestWrapfNil(t *testing.T) { 110 | got := Wrapf(nil, "no error") 111 | if got != nil { 112 | t.Errorf("Wrapf(nil, \"no error\"): got %#v, expected nil", got) 113 | } 114 | } 115 | 116 | func TestWrapf(t *testing.T) { 117 | tests := []struct { 118 | err error 119 | message string 120 | want string 121 | }{ 122 | {io.EOF, "read error", "read error: EOF"}, 123 | {Wrapf(io.EOF, "read error without format specifiers"), "client error", "client error: read error without format specifiers: EOF"}, 124 | {Wrapf(io.EOF, "read error with %d format specifier", 1), "client error", "client error: read error with 1 format specifier: EOF"}, 125 | } 126 | 127 | for _, tt := range tests { 128 | got := Wrapf(tt.err, tt.message).Error() 129 | if got != tt.want { 130 | t.Errorf("Wrapf(%v, %q): got: %v, want %v", tt.err, tt.message, got, tt.want) 131 | } 132 | } 133 | } 134 | 135 | func TestErrorf(t *testing.T) { 136 | tests := []struct { 137 | err error 138 | want string 139 | }{ 140 | {Errorf("read error without format specifiers"), "read error without format specifiers"}, 141 | {Errorf("read error with %d format specifier", 1), "read error with 1 format specifier"}, 142 | } 143 | 144 | for _, tt := range tests { 145 | got := tt.err.Error() 146 | if got != tt.want { 147 | t.Errorf("Errorf(%v): got: %q, want %q", tt.err, got, tt.want) 148 | } 149 | } 150 | } 151 | 152 | func TestWithStackNil(t *testing.T) { 153 | got := WithStack(nil) 154 | if got != nil { 155 | t.Errorf("WithStack(nil): got %#v, expected nil", got) 156 | } 157 | } 158 | 159 | func TestWithStack(t *testing.T) { 160 | tests := []struct { 161 | err error 162 | want string 163 | }{ 164 | {io.EOF, "EOF"}, 165 | {WithStack(io.EOF), "EOF"}, 166 | } 167 | 168 | for _, tt := range tests { 169 | got := WithStack(tt.err).Error() 170 | if got != tt.want { 171 | t.Errorf("WithStack(%v): got: %v, want %v", tt.err, got, tt.want) 172 | } 173 | } 174 | } 175 | 176 | func TestWithMessageNil(t *testing.T) { 177 | got := WithMessage(nil, "no error") 178 | if got != nil { 179 | t.Errorf("WithMessage(nil, \"no error\"): got %#v, expected nil", got) 180 | } 181 | } 182 | 183 | func TestWithMessage(t *testing.T) { 184 | tests := []struct { 185 | err error 186 | message string 187 | want string 188 | }{ 189 | {io.EOF, "read error", "read error: EOF"}, 190 | {WithMessage(io.EOF, "read error"), "client error", "client error: read error: EOF"}, 191 | } 192 | 193 | for _, tt := range tests { 194 | got := WithMessage(tt.err, tt.message).Error() 195 | if got != tt.want { 196 | t.Errorf("WithMessage(%v, %q): got: %q, want %q", tt.err, tt.message, got, tt.want) 197 | } 198 | } 199 | 200 | } 201 | 202 | // errors.New, etc values are not expected to be compared by value 203 | // but the change in errors#27 made them incomparable. Assert that 204 | // various kinds of errors have a functional equality operator, even 205 | // if the result of that equality is always false. 206 | func TestErrorEquality(t *testing.T) { 207 | vals := []error{ 208 | nil, 209 | io.EOF, 210 | errors.New("EOF"), 211 | New("EOF"), 212 | Errorf("EOF"), 213 | Wrap(io.EOF, "EOF"), 214 | Wrapf(io.EOF, "EOF%d", 2), 215 | WithMessage(nil, "whoops"), 216 | WithMessage(io.EOF, "whoops"), 217 | WithStack(io.EOF), 218 | WithStack(nil), 219 | } 220 | 221 | for i := range vals { 222 | for j := range vals { 223 | _ = vals[i] == vals[j] // mustn't panic 224 | } 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/errors/example_test.go: -------------------------------------------------------------------------------- 1 | package errors_test 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/pkg/errors" 7 | ) 8 | 9 | func ExampleNew() { 10 | err := errors.New("whoops") 11 | fmt.Println(err) 12 | 13 | // Output: whoops 14 | } 15 | 16 | func ExampleNew_printf() { 17 | err := errors.New("whoops") 18 | fmt.Printf("%+v", err) 19 | 20 | // Example output: 21 | // whoops 22 | // github.com/pkg/errors_test.ExampleNew_printf 23 | // /home/dfc/src/github.com/pkg/errors/example_test.go:17 24 | // testing.runExample 25 | // /home/dfc/go/src/testing/example.go:114 26 | // testing.RunExamples 27 | // /home/dfc/go/src/testing/example.go:38 28 | // testing.(*M).Run 29 | // /home/dfc/go/src/testing/testing.go:744 30 | // main.main 31 | // /github.com/pkg/errors/_test/_testmain.go:106 32 | // runtime.main 33 | // /home/dfc/go/src/runtime/proc.go:183 34 | // runtime.goexit 35 | // /home/dfc/go/src/runtime/asm_amd64.s:2059 36 | } 37 | 38 | func ExampleWithMessage() { 39 | cause := errors.New("whoops") 40 | err := errors.WithMessage(cause, "oh noes") 41 | fmt.Println(err) 42 | 43 | // Output: oh noes: whoops 44 | } 45 | 46 | func ExampleWithStack() { 47 | cause := errors.New("whoops") 48 | err := errors.WithStack(cause) 49 | fmt.Println(err) 50 | 51 | // Output: whoops 52 | } 53 | 54 | func ExampleWithStack_printf() { 55 | cause := errors.New("whoops") 56 | err := errors.WithStack(cause) 57 | fmt.Printf("%+v", err) 58 | 59 | // Example Output: 60 | // whoops 61 | // github.com/pkg/errors_test.ExampleWithStack_printf 62 | // /home/fabstu/go/src/github.com/pkg/errors/example_test.go:55 63 | // testing.runExample 64 | // /usr/lib/go/src/testing/example.go:114 65 | // testing.RunExamples 66 | // /usr/lib/go/src/testing/example.go:38 67 | // testing.(*M).Run 68 | // /usr/lib/go/src/testing/testing.go:744 69 | // main.main 70 | // github.com/pkg/errors/_test/_testmain.go:106 71 | // runtime.main 72 | // /usr/lib/go/src/runtime/proc.go:183 73 | // runtime.goexit 74 | // /usr/lib/go/src/runtime/asm_amd64.s:2086 75 | // github.com/pkg/errors_test.ExampleWithStack_printf 76 | // /home/fabstu/go/src/github.com/pkg/errors/example_test.go:56 77 | // testing.runExample 78 | // /usr/lib/go/src/testing/example.go:114 79 | // testing.RunExamples 80 | // /usr/lib/go/src/testing/example.go:38 81 | // testing.(*M).Run 82 | // /usr/lib/go/src/testing/testing.go:744 83 | // main.main 84 | // github.com/pkg/errors/_test/_testmain.go:106 85 | // runtime.main 86 | // /usr/lib/go/src/runtime/proc.go:183 87 | // runtime.goexit 88 | // /usr/lib/go/src/runtime/asm_amd64.s:2086 89 | } 90 | 91 | func ExampleWrap() { 92 | cause := errors.New("whoops") 93 | err := errors.Wrap(cause, "oh noes") 94 | fmt.Println(err) 95 | 96 | // Output: oh noes: whoops 97 | } 98 | 99 | func fn() error { 100 | e1 := errors.New("error") 101 | e2 := errors.Wrap(e1, "inner") 102 | e3 := errors.Wrap(e2, "middle") 103 | return errors.Wrap(e3, "outer") 104 | } 105 | 106 | func ExampleCause() { 107 | err := fn() 108 | fmt.Println(err) 109 | fmt.Println(errors.Cause(err)) 110 | 111 | // Output: outer: middle: inner: error 112 | // error 113 | } 114 | 115 | func ExampleWrap_extended() { 116 | err := fn() 117 | fmt.Printf("%+v\n", err) 118 | 119 | // Example output: 120 | // error 121 | // github.com/pkg/errors_test.fn 122 | // /home/dfc/src/github.com/pkg/errors/example_test.go:47 123 | // github.com/pkg/errors_test.ExampleCause_printf 124 | // /home/dfc/src/github.com/pkg/errors/example_test.go:63 125 | // testing.runExample 126 | // /home/dfc/go/src/testing/example.go:114 127 | // testing.RunExamples 128 | // /home/dfc/go/src/testing/example.go:38 129 | // testing.(*M).Run 130 | // /home/dfc/go/src/testing/testing.go:744 131 | // main.main 132 | // /github.com/pkg/errors/_test/_testmain.go:104 133 | // runtime.main 134 | // /home/dfc/go/src/runtime/proc.go:183 135 | // runtime.goexit 136 | // /home/dfc/go/src/runtime/asm_amd64.s:2059 137 | // github.com/pkg/errors_test.fn 138 | // /home/dfc/src/github.com/pkg/errors/example_test.go:48: inner 139 | // github.com/pkg/errors_test.fn 140 | // /home/dfc/src/github.com/pkg/errors/example_test.go:49: middle 141 | // github.com/pkg/errors_test.fn 142 | // /home/dfc/src/github.com/pkg/errors/example_test.go:50: outer 143 | } 144 | 145 | func ExampleWrapf() { 146 | cause := errors.New("whoops") 147 | err := errors.Wrapf(cause, "oh noes #%d", 2) 148 | fmt.Println(err) 149 | 150 | // Output: oh noes #2: whoops 151 | } 152 | 153 | func ExampleErrorf_extended() { 154 | err := errors.Errorf("whoops: %s", "foo") 155 | fmt.Printf("%+v", err) 156 | 157 | // Example output: 158 | // whoops: foo 159 | // github.com/pkg/errors_test.ExampleErrorf 160 | // /home/dfc/src/github.com/pkg/errors/example_test.go:101 161 | // testing.runExample 162 | // /home/dfc/go/src/testing/example.go:114 163 | // testing.RunExamples 164 | // /home/dfc/go/src/testing/example.go:38 165 | // testing.(*M).Run 166 | // /home/dfc/go/src/testing/testing.go:744 167 | // main.main 168 | // /github.com/pkg/errors/_test/_testmain.go:102 169 | // runtime.main 170 | // /home/dfc/go/src/runtime/proc.go:183 171 | // runtime.goexit 172 | // /home/dfc/go/src/runtime/asm_amd64.s:2059 173 | } 174 | 175 | func Example_stackTrace() { 176 | type stackTracer interface { 177 | StackTrace() errors.StackTrace 178 | } 179 | 180 | err, ok := errors.Cause(fn()).(stackTracer) 181 | if !ok { 182 | panic("oops, err does not implement stackTracer") 183 | } 184 | 185 | st := err.StackTrace() 186 | fmt.Printf("%+v", st[0:2]) // top two frames 187 | 188 | // Example output: 189 | // github.com/pkg/errors_test.fn 190 | // /home/dfc/src/github.com/pkg/errors/example_test.go:47 191 | // github.com/pkg/errors_test.Example_stackTrace 192 | // /home/dfc/src/github.com/pkg/errors/example_test.go:127 193 | } 194 | 195 | func ExampleCause_printf() { 196 | err := errors.Wrap(func() error { 197 | return func() error { 198 | return errors.Errorf("hello %s", fmt.Sprintf("world")) 199 | }() 200 | }(), "failed") 201 | 202 | fmt.Printf("%v", err) 203 | 204 | // Output: failed: hello world 205 | } 206 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/errors/format_test.go: -------------------------------------------------------------------------------- 1 | package errors 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io" 7 | "regexp" 8 | "strings" 9 | "testing" 10 | ) 11 | 12 | func TestFormatNew(t *testing.T) { 13 | tests := []struct { 14 | error 15 | format string 16 | want string 17 | }{{ 18 | New("error"), 19 | "%s", 20 | "error", 21 | }, { 22 | New("error"), 23 | "%v", 24 | "error", 25 | }, { 26 | New("error"), 27 | "%+v", 28 | "error\n" + 29 | "github.com/pkg/errors.TestFormatNew\n" + 30 | "\t.+/github.com/pkg/errors/format_test.go:26", 31 | }, { 32 | New("error"), 33 | "%q", 34 | `"error"`, 35 | }} 36 | 37 | for i, tt := range tests { 38 | testFormatRegexp(t, i, tt.error, tt.format, tt.want) 39 | } 40 | } 41 | 42 | func TestFormatErrorf(t *testing.T) { 43 | tests := []struct { 44 | error 45 | format string 46 | want string 47 | }{{ 48 | Errorf("%s", "error"), 49 | "%s", 50 | "error", 51 | }, { 52 | Errorf("%s", "error"), 53 | "%v", 54 | "error", 55 | }, { 56 | Errorf("%s", "error"), 57 | "%+v", 58 | "error\n" + 59 | "github.com/pkg/errors.TestFormatErrorf\n" + 60 | "\t.+/github.com/pkg/errors/format_test.go:56", 61 | }} 62 | 63 | for i, tt := range tests { 64 | testFormatRegexp(t, i, tt.error, tt.format, tt.want) 65 | } 66 | } 67 | 68 | func TestFormatWrap(t *testing.T) { 69 | tests := []struct { 70 | error 71 | format string 72 | want string 73 | }{{ 74 | Wrap(New("error"), "error2"), 75 | "%s", 76 | "error2: error", 77 | }, { 78 | Wrap(New("error"), "error2"), 79 | "%v", 80 | "error2: error", 81 | }, { 82 | Wrap(New("error"), "error2"), 83 | "%+v", 84 | "error\n" + 85 | "github.com/pkg/errors.TestFormatWrap\n" + 86 | "\t.+/github.com/pkg/errors/format_test.go:82", 87 | }, { 88 | Wrap(io.EOF, "error"), 89 | "%s", 90 | "error: EOF", 91 | }, { 92 | Wrap(io.EOF, "error"), 93 | "%v", 94 | "error: EOF", 95 | }, { 96 | Wrap(io.EOF, "error"), 97 | "%+v", 98 | "EOF\n" + 99 | "error\n" + 100 | "github.com/pkg/errors.TestFormatWrap\n" + 101 | "\t.+/github.com/pkg/errors/format_test.go:96", 102 | }, { 103 | Wrap(Wrap(io.EOF, "error1"), "error2"), 104 | "%+v", 105 | "EOF\n" + 106 | "error1\n" + 107 | "github.com/pkg/errors.TestFormatWrap\n" + 108 | "\t.+/github.com/pkg/errors/format_test.go:103\n", 109 | }, { 110 | Wrap(New("error with space"), "context"), 111 | "%q", 112 | `"context: error with space"`, 113 | }} 114 | 115 | for i, tt := range tests { 116 | testFormatRegexp(t, i, tt.error, tt.format, tt.want) 117 | } 118 | } 119 | 120 | func TestFormatWrapf(t *testing.T) { 121 | tests := []struct { 122 | error 123 | format string 124 | want string 125 | }{{ 126 | Wrapf(io.EOF, "error%d", 2), 127 | "%s", 128 | "error2: EOF", 129 | }, { 130 | Wrapf(io.EOF, "error%d", 2), 131 | "%v", 132 | "error2: EOF", 133 | }, { 134 | Wrapf(io.EOF, "error%d", 2), 135 | "%+v", 136 | "EOF\n" + 137 | "error2\n" + 138 | "github.com/pkg/errors.TestFormatWrapf\n" + 139 | "\t.+/github.com/pkg/errors/format_test.go:134", 140 | }, { 141 | Wrapf(New("error"), "error%d", 2), 142 | "%s", 143 | "error2: error", 144 | }, { 145 | Wrapf(New("error"), "error%d", 2), 146 | "%v", 147 | "error2: error", 148 | }, { 149 | Wrapf(New("error"), "error%d", 2), 150 | "%+v", 151 | "error\n" + 152 | "github.com/pkg/errors.TestFormatWrapf\n" + 153 | "\t.+/github.com/pkg/errors/format_test.go:149", 154 | }} 155 | 156 | for i, tt := range tests { 157 | testFormatRegexp(t, i, tt.error, tt.format, tt.want) 158 | } 159 | } 160 | 161 | func TestFormatWithStack(t *testing.T) { 162 | tests := []struct { 163 | error 164 | format string 165 | want []string 166 | }{{ 167 | WithStack(io.EOF), 168 | "%s", 169 | []string{"EOF"}, 170 | }, { 171 | WithStack(io.EOF), 172 | "%v", 173 | []string{"EOF"}, 174 | }, { 175 | WithStack(io.EOF), 176 | "%+v", 177 | []string{"EOF", 178 | "github.com/pkg/errors.TestFormatWithStack\n" + 179 | "\t.+/github.com/pkg/errors/format_test.go:175"}, 180 | }, { 181 | WithStack(New("error")), 182 | "%s", 183 | []string{"error"}, 184 | }, { 185 | WithStack(New("error")), 186 | "%v", 187 | []string{"error"}, 188 | }, { 189 | WithStack(New("error")), 190 | "%+v", 191 | []string{"error", 192 | "github.com/pkg/errors.TestFormatWithStack\n" + 193 | "\t.+/github.com/pkg/errors/format_test.go:189", 194 | "github.com/pkg/errors.TestFormatWithStack\n" + 195 | "\t.+/github.com/pkg/errors/format_test.go:189"}, 196 | }, { 197 | WithStack(WithStack(io.EOF)), 198 | "%+v", 199 | []string{"EOF", 200 | "github.com/pkg/errors.TestFormatWithStack\n" + 201 | "\t.+/github.com/pkg/errors/format_test.go:197", 202 | "github.com/pkg/errors.TestFormatWithStack\n" + 203 | "\t.+/github.com/pkg/errors/format_test.go:197"}, 204 | }, { 205 | WithStack(WithStack(Wrapf(io.EOF, "message"))), 206 | "%+v", 207 | []string{"EOF", 208 | "message", 209 | "github.com/pkg/errors.TestFormatWithStack\n" + 210 | "\t.+/github.com/pkg/errors/format_test.go:205", 211 | "github.com/pkg/errors.TestFormatWithStack\n" + 212 | "\t.+/github.com/pkg/errors/format_test.go:205", 213 | "github.com/pkg/errors.TestFormatWithStack\n" + 214 | "\t.+/github.com/pkg/errors/format_test.go:205"}, 215 | }, { 216 | WithStack(Errorf("error%d", 1)), 217 | "%+v", 218 | []string{"error1", 219 | "github.com/pkg/errors.TestFormatWithStack\n" + 220 | "\t.+/github.com/pkg/errors/format_test.go:216", 221 | "github.com/pkg/errors.TestFormatWithStack\n" + 222 | "\t.+/github.com/pkg/errors/format_test.go:216"}, 223 | }} 224 | 225 | for i, tt := range tests { 226 | testFormatCompleteCompare(t, i, tt.error, tt.format, tt.want, true) 227 | } 228 | } 229 | 230 | func TestFormatWithMessage(t *testing.T) { 231 | tests := []struct { 232 | error 233 | format string 234 | want []string 235 | }{{ 236 | WithMessage(New("error"), "error2"), 237 | "%s", 238 | []string{"error2: error"}, 239 | }, { 240 | WithMessage(New("error"), "error2"), 241 | "%v", 242 | []string{"error2: error"}, 243 | }, { 244 | WithMessage(New("error"), "error2"), 245 | "%+v", 246 | []string{ 247 | "error", 248 | "github.com/pkg/errors.TestFormatWithMessage\n" + 249 | "\t.+/github.com/pkg/errors/format_test.go:244", 250 | "error2"}, 251 | }, { 252 | WithMessage(io.EOF, "addition1"), 253 | "%s", 254 | []string{"addition1: EOF"}, 255 | }, { 256 | WithMessage(io.EOF, "addition1"), 257 | "%v", 258 | []string{"addition1: EOF"}, 259 | }, { 260 | WithMessage(io.EOF, "addition1"), 261 | "%+v", 262 | []string{"EOF", "addition1"}, 263 | }, { 264 | WithMessage(WithMessage(io.EOF, "addition1"), "addition2"), 265 | "%v", 266 | []string{"addition2: addition1: EOF"}, 267 | }, { 268 | WithMessage(WithMessage(io.EOF, "addition1"), "addition2"), 269 | "%+v", 270 | []string{"EOF", "addition1", "addition2"}, 271 | }, { 272 | Wrap(WithMessage(io.EOF, "error1"), "error2"), 273 | "%+v", 274 | []string{"EOF", "error1", "error2", 275 | "github.com/pkg/errors.TestFormatWithMessage\n" + 276 | "\t.+/github.com/pkg/errors/format_test.go:272"}, 277 | }, { 278 | WithMessage(Errorf("error%d", 1), "error2"), 279 | "%+v", 280 | []string{"error1", 281 | "github.com/pkg/errors.TestFormatWithMessage\n" + 282 | "\t.+/github.com/pkg/errors/format_test.go:278", 283 | "error2"}, 284 | }, { 285 | WithMessage(WithStack(io.EOF), "error"), 286 | "%+v", 287 | []string{ 288 | "EOF", 289 | "github.com/pkg/errors.TestFormatWithMessage\n" + 290 | "\t.+/github.com/pkg/errors/format_test.go:285", 291 | "error"}, 292 | }, { 293 | WithMessage(Wrap(WithStack(io.EOF), "inside-error"), "outside-error"), 294 | "%+v", 295 | []string{ 296 | "EOF", 297 | "github.com/pkg/errors.TestFormatWithMessage\n" + 298 | "\t.+/github.com/pkg/errors/format_test.go:293", 299 | "inside-error", 300 | "github.com/pkg/errors.TestFormatWithMessage\n" + 301 | "\t.+/github.com/pkg/errors/format_test.go:293", 302 | "outside-error"}, 303 | }} 304 | 305 | for i, tt := range tests { 306 | testFormatCompleteCompare(t, i, tt.error, tt.format, tt.want, true) 307 | } 308 | } 309 | 310 | func TestFormatGeneric(t *testing.T) { 311 | starts := []struct { 312 | err error 313 | want []string 314 | }{ 315 | {New("new-error"), []string{ 316 | "new-error", 317 | "github.com/pkg/errors.TestFormatGeneric\n" + 318 | "\t.+/github.com/pkg/errors/format_test.go:315"}, 319 | }, {Errorf("errorf-error"), []string{ 320 | "errorf-error", 321 | "github.com/pkg/errors.TestFormatGeneric\n" + 322 | "\t.+/github.com/pkg/errors/format_test.go:319"}, 323 | }, {errors.New("errors-new-error"), []string{ 324 | "errors-new-error"}, 325 | }, 326 | } 327 | 328 | wrappers := []wrapper{ 329 | { 330 | func(err error) error { return WithMessage(err, "with-message") }, 331 | []string{"with-message"}, 332 | }, { 333 | func(err error) error { return WithStack(err) }, 334 | []string{ 335 | "github.com/pkg/errors.(func·002|TestFormatGeneric.func2)\n\t" + 336 | ".+/github.com/pkg/errors/format_test.go:333", 337 | }, 338 | }, { 339 | func(err error) error { return Wrap(err, "wrap-error") }, 340 | []string{ 341 | "wrap-error", 342 | "github.com/pkg/errors.(func·003|TestFormatGeneric.func3)\n\t" + 343 | ".+/github.com/pkg/errors/format_test.go:339", 344 | }, 345 | }, { 346 | func(err error) error { return Wrapf(err, "wrapf-error%d", 1) }, 347 | []string{ 348 | "wrapf-error1", 349 | "github.com/pkg/errors.(func·004|TestFormatGeneric.func4)\n\t" + 350 | ".+/github.com/pkg/errors/format_test.go:346", 351 | }, 352 | }, 353 | } 354 | 355 | for s := range starts { 356 | err := starts[s].err 357 | want := starts[s].want 358 | testFormatCompleteCompare(t, s, err, "%+v", want, false) 359 | testGenericRecursive(t, err, want, wrappers, 3) 360 | } 361 | } 362 | 363 | func testFormatRegexp(t *testing.T, n int, arg interface{}, format, want string) { 364 | got := fmt.Sprintf(format, arg) 365 | gotLines := strings.SplitN(got, "\n", -1) 366 | wantLines := strings.SplitN(want, "\n", -1) 367 | 368 | if len(wantLines) > len(gotLines) { 369 | t.Errorf("test %d: wantLines(%d) > gotLines(%d):\n got: %q\nwant: %q", n+1, len(wantLines), len(gotLines), got, want) 370 | return 371 | } 372 | 373 | for i, w := range wantLines { 374 | match, err := regexp.MatchString(w, gotLines[i]) 375 | if err != nil { 376 | t.Fatal(err) 377 | } 378 | if !match { 379 | t.Errorf("test %d: line %d: fmt.Sprintf(%q, err):\n got: %q\nwant: %q", n+1, i+1, format, got, want) 380 | } 381 | } 382 | } 383 | 384 | var stackLineR = regexp.MustCompile(`\.`) 385 | 386 | // parseBlocks parses input into a slice, where: 387 | // - incase entry contains a newline, its a stacktrace 388 | // - incase entry contains no newline, its a solo line. 389 | // 390 | // Detecting stack boundaries only works incase the WithStack-calls are 391 | // to be found on the same line, thats why it is optionally here. 392 | // 393 | // Example use: 394 | // 395 | // for _, e := range blocks { 396 | // if strings.ContainsAny(e, "\n") { 397 | // // Match as stack 398 | // } else { 399 | // // Match as line 400 | // } 401 | // } 402 | // 403 | func parseBlocks(input string, detectStackboundaries bool) ([]string, error) { 404 | var blocks []string 405 | 406 | stack := "" 407 | wasStack := false 408 | lines := map[string]bool{} // already found lines 409 | 410 | for _, l := range strings.Split(input, "\n") { 411 | isStackLine := stackLineR.MatchString(l) 412 | 413 | switch { 414 | case !isStackLine && wasStack: 415 | blocks = append(blocks, stack, l) 416 | stack = "" 417 | lines = map[string]bool{} 418 | case isStackLine: 419 | if wasStack { 420 | // Detecting two stacks after another, possible cause lines match in 421 | // our tests due to WithStack(WithStack(io.EOF)) on same line. 422 | if detectStackboundaries { 423 | if lines[l] { 424 | if len(stack) == 0 { 425 | return nil, errors.New("len of block must not be zero here") 426 | } 427 | 428 | blocks = append(blocks, stack) 429 | stack = l 430 | lines = map[string]bool{l: true} 431 | continue 432 | } 433 | } 434 | 435 | stack = stack + "\n" + l 436 | } else { 437 | stack = l 438 | } 439 | lines[l] = true 440 | case !isStackLine && !wasStack: 441 | blocks = append(blocks, l) 442 | default: 443 | return nil, errors.New("must not happen") 444 | } 445 | 446 | wasStack = isStackLine 447 | } 448 | 449 | // Use up stack 450 | if stack != "" { 451 | blocks = append(blocks, stack) 452 | } 453 | return blocks, nil 454 | } 455 | 456 | func testFormatCompleteCompare(t *testing.T, n int, arg interface{}, format string, want []string, detectStackBoundaries bool) { 457 | gotStr := fmt.Sprintf(format, arg) 458 | 459 | got, err := parseBlocks(gotStr, detectStackBoundaries) 460 | if err != nil { 461 | t.Fatal(err) 462 | } 463 | 464 | if len(got) != len(want) { 465 | t.Fatalf("test %d: fmt.Sprintf(%s, err) -> wrong number of blocks: got(%d) want(%d)\n got: %s\nwant: %s\ngotStr: %q", 466 | n+1, format, len(got), len(want), prettyBlocks(got), prettyBlocks(want), gotStr) 467 | } 468 | 469 | for i := range got { 470 | if strings.ContainsAny(want[i], "\n") { 471 | // Match as stack 472 | match, err := regexp.MatchString(want[i], got[i]) 473 | if err != nil { 474 | t.Fatal(err) 475 | } 476 | if !match { 477 | 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", 478 | n+1, i+1, format, got[i], want[i], prettyBlocks(got), prettyBlocks(want)) 479 | } 480 | } else { 481 | // Match as message 482 | if got[i] != want[i] { 483 | 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]) 484 | } 485 | } 486 | } 487 | } 488 | 489 | type wrapper struct { 490 | wrap func(err error) error 491 | want []string 492 | } 493 | 494 | func prettyBlocks(blocks []string, prefix ...string) string { 495 | var out []string 496 | 497 | for _, b := range blocks { 498 | out = append(out, fmt.Sprintf("%v", b)) 499 | } 500 | 501 | return " " + strings.Join(out, "\n ") 502 | } 503 | 504 | func testGenericRecursive(t *testing.T, beforeErr error, beforeWant []string, list []wrapper, maxDepth int) { 505 | if len(beforeWant) == 0 { 506 | panic("beforeWant must not be empty") 507 | } 508 | for _, w := range list { 509 | if len(w.want) == 0 { 510 | panic("want must not be empty") 511 | } 512 | 513 | err := w.wrap(beforeErr) 514 | 515 | // Copy required cause append(beforeWant, ..) modified beforeWant subtly. 516 | beforeCopy := make([]string, len(beforeWant)) 517 | copy(beforeCopy, beforeWant) 518 | 519 | beforeWant := beforeCopy 520 | last := len(beforeWant) - 1 521 | var want []string 522 | 523 | // Merge two stacks behind each other. 524 | if strings.ContainsAny(beforeWant[last], "\n") && strings.ContainsAny(w.want[0], "\n") { 525 | want = append(beforeWant[:last], append([]string{beforeWant[last] + "((?s).*)" + w.want[0]}, w.want[1:]...)...) 526 | } else { 527 | want = append(beforeWant, w.want...) 528 | } 529 | 530 | testFormatCompleteCompare(t, maxDepth, err, "%+v", want, false) 531 | if maxDepth > 0 { 532 | testGenericRecursive(t, err, want, list, maxDepth-1) 533 | } 534 | } 535 | } 536 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/errors/stack.go: -------------------------------------------------------------------------------- 1 | package errors 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "path" 7 | "runtime" 8 | "strings" 9 | ) 10 | 11 | // Frame represents a program counter inside a stack frame. 12 | type Frame uintptr 13 | 14 | // pc returns the program counter for this frame; 15 | // multiple frames may have the same PC value. 16 | func (f Frame) pc() uintptr { return uintptr(f) - 1 } 17 | 18 | // file returns the full path to the file that contains the 19 | // function for this Frame's pc. 20 | func (f Frame) file() string { 21 | fn := runtime.FuncForPC(f.pc()) 22 | if fn == nil { 23 | return "unknown" 24 | } 25 | file, _ := fn.FileLine(f.pc()) 26 | return file 27 | } 28 | 29 | // line returns the line number of source code of the 30 | // function for this Frame's pc. 31 | func (f Frame) line() int { 32 | fn := runtime.FuncForPC(f.pc()) 33 | if fn == nil { 34 | return 0 35 | } 36 | _, line := fn.FileLine(f.pc()) 37 | return line 38 | } 39 | 40 | // Format formats the frame according to the fmt.Formatter interface. 41 | // 42 | // %s source file 43 | // %d source line 44 | // %n function name 45 | // %v equivalent to %s:%d 46 | // 47 | // Format accepts flags that alter the printing of some verbs, as follows: 48 | // 49 | // %+s path of source file relative to the compile time GOPATH 50 | // %+v equivalent to %+s:%d 51 | func (f Frame) Format(s fmt.State, verb rune) { 52 | switch verb { 53 | case 's': 54 | switch { 55 | case s.Flag('+'): 56 | pc := f.pc() 57 | fn := runtime.FuncForPC(pc) 58 | if fn == nil { 59 | io.WriteString(s, "unknown") 60 | } else { 61 | file, _ := fn.FileLine(pc) 62 | fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file) 63 | } 64 | default: 65 | io.WriteString(s, path.Base(f.file())) 66 | } 67 | case 'd': 68 | fmt.Fprintf(s, "%d", f.line()) 69 | case 'n': 70 | name := runtime.FuncForPC(f.pc()).Name() 71 | io.WriteString(s, funcname(name)) 72 | case 'v': 73 | f.Format(s, 's') 74 | io.WriteString(s, ":") 75 | f.Format(s, 'd') 76 | } 77 | } 78 | 79 | // StackTrace is stack of Frames from innermost (newest) to outermost (oldest). 80 | type StackTrace []Frame 81 | 82 | func (st StackTrace) Format(s fmt.State, verb rune) { 83 | switch verb { 84 | case 'v': 85 | switch { 86 | case s.Flag('+'): 87 | for _, f := range st { 88 | fmt.Fprintf(s, "\n%+v", f) 89 | } 90 | case s.Flag('#'): 91 | fmt.Fprintf(s, "%#v", []Frame(st)) 92 | default: 93 | fmt.Fprintf(s, "%v", []Frame(st)) 94 | } 95 | case 's': 96 | fmt.Fprintf(s, "%s", []Frame(st)) 97 | } 98 | } 99 | 100 | // stack represents a stack of program counters. 101 | type stack []uintptr 102 | 103 | func (s *stack) Format(st fmt.State, verb rune) { 104 | switch verb { 105 | case 'v': 106 | switch { 107 | case st.Flag('+'): 108 | for _, pc := range *s { 109 | f := Frame(pc) 110 | fmt.Fprintf(st, "\n%+v", f) 111 | } 112 | } 113 | } 114 | } 115 | 116 | func (s *stack) StackTrace() StackTrace { 117 | f := make([]Frame, len(*s)) 118 | for i := 0; i < len(f); i++ { 119 | f[i] = Frame((*s)[i]) 120 | } 121 | return f 122 | } 123 | 124 | func callers() *stack { 125 | const depth = 32 126 | var pcs [depth]uintptr 127 | n := runtime.Callers(3, pcs[:]) 128 | var st stack = pcs[0:n] 129 | return &st 130 | } 131 | 132 | // funcname removes the path prefix component of a function's name reported by func.Name(). 133 | func funcname(name string) string { 134 | i := strings.LastIndex(name, "/") 135 | name = name[i+1:] 136 | i = strings.Index(name, ".") 137 | return name[i+1:] 138 | } 139 | 140 | func trimGOPATH(name, file string) string { 141 | // Here we want to get the source file path relative to the compile time 142 | // GOPATH. As of Go 1.6.x there is no direct way to know the compiled 143 | // GOPATH at runtime, but we can infer the number of path segments in the 144 | // GOPATH. We note that fn.Name() returns the function name qualified by 145 | // the import path, which does not include the GOPATH. Thus we can trim 146 | // segments from the beginning of the file path until the number of path 147 | // separators remaining is one more than the number of path separators in 148 | // the function name. For example, given: 149 | // 150 | // GOPATH /home/user 151 | // file /home/user/src/pkg/sub/file.go 152 | // fn.Name() pkg/sub.Type.Method 153 | // 154 | // We want to produce: 155 | // 156 | // pkg/sub/file.go 157 | // 158 | // From this we can easily see that fn.Name() has one less path separator 159 | // than our desired output. We count separators from the end of the file 160 | // path until it finds two more than in the function name and then move 161 | // one character forward to preserve the initial path segment without a 162 | // leading separator. 163 | const sep = "/" 164 | goal := strings.Count(name, sep) + 2 165 | i := len(file) 166 | for n := 0; n < goal; n++ { 167 | i = strings.LastIndex(file[:i], sep) 168 | if i == -1 { 169 | // not enough separators found, set i so that the slice expression 170 | // below leaves file unmodified 171 | i = -len(sep) 172 | break 173 | } 174 | } 175 | // get back to 0 or trim the leading separator 176 | file = file[i+len(sep):] 177 | return file 178 | } 179 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/errors/stack_test.go: -------------------------------------------------------------------------------- 1 | package errors 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | "testing" 7 | ) 8 | 9 | var initpc, _, _, _ = runtime.Caller(0) 10 | 11 | func TestFrameLine(t *testing.T) { 12 | var tests = []struct { 13 | Frame 14 | want int 15 | }{{ 16 | Frame(initpc), 17 | 9, 18 | }, { 19 | func() Frame { 20 | var pc, _, _, _ = runtime.Caller(0) 21 | return Frame(pc) 22 | }(), 23 | 20, 24 | }, { 25 | func() Frame { 26 | var pc, _, _, _ = runtime.Caller(1) 27 | return Frame(pc) 28 | }(), 29 | 28, 30 | }, { 31 | Frame(0), // invalid PC 32 | 0, 33 | }} 34 | 35 | for _, tt := range tests { 36 | got := tt.Frame.line() 37 | want := tt.want 38 | if want != got { 39 | t.Errorf("Frame(%v): want: %v, got: %v", uintptr(tt.Frame), want, got) 40 | } 41 | } 42 | } 43 | 44 | type X struct{} 45 | 46 | func (x X) val() Frame { 47 | var pc, _, _, _ = runtime.Caller(0) 48 | return Frame(pc) 49 | } 50 | 51 | func (x *X) ptr() Frame { 52 | var pc, _, _, _ = runtime.Caller(0) 53 | return Frame(pc) 54 | } 55 | 56 | func TestFrameFormat(t *testing.T) { 57 | var tests = []struct { 58 | Frame 59 | format string 60 | want string 61 | }{{ 62 | Frame(initpc), 63 | "%s", 64 | "stack_test.go", 65 | }, { 66 | Frame(initpc), 67 | "%+s", 68 | "github.com/pkg/errors.init\n" + 69 | "\t.+/github.com/pkg/errors/stack_test.go", 70 | }, { 71 | Frame(0), 72 | "%s", 73 | "unknown", 74 | }, { 75 | Frame(0), 76 | "%+s", 77 | "unknown", 78 | }, { 79 | Frame(initpc), 80 | "%d", 81 | "9", 82 | }, { 83 | Frame(0), 84 | "%d", 85 | "0", 86 | }, { 87 | Frame(initpc), 88 | "%n", 89 | "init", 90 | }, { 91 | func() Frame { 92 | var x X 93 | return x.ptr() 94 | }(), 95 | "%n", 96 | `\(\*X\).ptr`, 97 | }, { 98 | func() Frame { 99 | var x X 100 | return x.val() 101 | }(), 102 | "%n", 103 | "X.val", 104 | }, { 105 | Frame(0), 106 | "%n", 107 | "", 108 | }, { 109 | Frame(initpc), 110 | "%v", 111 | "stack_test.go:9", 112 | }, { 113 | Frame(initpc), 114 | "%+v", 115 | "github.com/pkg/errors.init\n" + 116 | "\t.+/github.com/pkg/errors/stack_test.go:9", 117 | }, { 118 | Frame(0), 119 | "%v", 120 | "unknown:0", 121 | }} 122 | 123 | for i, tt := range tests { 124 | testFormatRegexp(t, i, tt.Frame, tt.format, tt.want) 125 | } 126 | } 127 | 128 | func TestFuncname(t *testing.T) { 129 | tests := []struct { 130 | name, want string 131 | }{ 132 | {"", ""}, 133 | {"runtime.main", "main"}, 134 | {"github.com/pkg/errors.funcname", "funcname"}, 135 | {"funcname", "funcname"}, 136 | {"io.copyBuffer", "copyBuffer"}, 137 | {"main.(*R).Write", "(*R).Write"}, 138 | } 139 | 140 | for _, tt := range tests { 141 | got := funcname(tt.name) 142 | want := tt.want 143 | if got != want { 144 | t.Errorf("funcname(%q): want: %q, got %q", tt.name, want, got) 145 | } 146 | } 147 | } 148 | 149 | func TestTrimGOPATH(t *testing.T) { 150 | var tests = []struct { 151 | Frame 152 | want string 153 | }{{ 154 | Frame(initpc), 155 | "github.com/pkg/errors/stack_test.go", 156 | }} 157 | 158 | for i, tt := range tests { 159 | pc := tt.Frame.pc() 160 | fn := runtime.FuncForPC(pc) 161 | file, _ := fn.FileLine(pc) 162 | got := trimGOPATH(fn.Name(), file) 163 | testFormatRegexp(t, i, got, "%s", tt.want) 164 | } 165 | } 166 | 167 | func TestStackTrace(t *testing.T) { 168 | tests := []struct { 169 | err error 170 | want []string 171 | }{{ 172 | New("ooh"), []string{ 173 | "github.com/pkg/errors.TestStackTrace\n" + 174 | "\t.+/github.com/pkg/errors/stack_test.go:172", 175 | }, 176 | }, { 177 | Wrap(New("ooh"), "ahh"), []string{ 178 | "github.com/pkg/errors.TestStackTrace\n" + 179 | "\t.+/github.com/pkg/errors/stack_test.go:177", // this is the stack of Wrap, not New 180 | }, 181 | }, { 182 | Cause(Wrap(New("ooh"), "ahh")), []string{ 183 | "github.com/pkg/errors.TestStackTrace\n" + 184 | "\t.+/github.com/pkg/errors/stack_test.go:182", // this is the stack of New 185 | }, 186 | }, { 187 | func() error { return New("ooh") }(), []string{ 188 | `github.com/pkg/errors.(func·009|TestStackTrace.func1)` + 189 | "\n\t.+/github.com/pkg/errors/stack_test.go:187", // this is the stack of New 190 | "github.com/pkg/errors.TestStackTrace\n" + 191 | "\t.+/github.com/pkg/errors/stack_test.go:187", // this is the stack of New's caller 192 | }, 193 | }, { 194 | Cause(func() error { 195 | return func() error { 196 | return Errorf("hello %s", fmt.Sprintf("world")) 197 | }() 198 | }()), []string{ 199 | `github.com/pkg/errors.(func·010|TestStackTrace.func2.1)` + 200 | "\n\t.+/github.com/pkg/errors/stack_test.go:196", // this is the stack of Errorf 201 | `github.com/pkg/errors.(func·011|TestStackTrace.func2)` + 202 | "\n\t.+/github.com/pkg/errors/stack_test.go:197", // this is the stack of Errorf's caller 203 | "github.com/pkg/errors.TestStackTrace\n" + 204 | "\t.+/github.com/pkg/errors/stack_test.go:198", // this is the stack of Errorf's caller's caller 205 | }, 206 | }} 207 | for i, tt := range tests { 208 | x, ok := tt.err.(interface { 209 | StackTrace() StackTrace 210 | }) 211 | if !ok { 212 | t.Errorf("expected %#v to implement StackTrace() StackTrace", tt.err) 213 | continue 214 | } 215 | st := x.StackTrace() 216 | for j, want := range tt.want { 217 | testFormatRegexp(t, i, st[j], "%+v", want) 218 | } 219 | } 220 | } 221 | 222 | func stackTrace() StackTrace { 223 | const depth = 8 224 | var pcs [depth]uintptr 225 | n := runtime.Callers(1, pcs[:]) 226 | var st stack = pcs[0:n] 227 | return st.StackTrace() 228 | } 229 | 230 | func TestStackTraceFormat(t *testing.T) { 231 | tests := []struct { 232 | StackTrace 233 | format string 234 | want string 235 | }{{ 236 | nil, 237 | "%s", 238 | `\[\]`, 239 | }, { 240 | nil, 241 | "%v", 242 | `\[\]`, 243 | }, { 244 | nil, 245 | "%+v", 246 | "", 247 | }, { 248 | nil, 249 | "%#v", 250 | `\[\]errors.Frame\(nil\)`, 251 | }, { 252 | make(StackTrace, 0), 253 | "%s", 254 | `\[\]`, 255 | }, { 256 | make(StackTrace, 0), 257 | "%v", 258 | `\[\]`, 259 | }, { 260 | make(StackTrace, 0), 261 | "%+v", 262 | "", 263 | }, { 264 | make(StackTrace, 0), 265 | "%#v", 266 | `\[\]errors.Frame{}`, 267 | }, { 268 | stackTrace()[:2], 269 | "%s", 270 | `\[stack_test.go stack_test.go\]`, 271 | }, { 272 | stackTrace()[:2], 273 | "%v", 274 | `\[stack_test.go:225 stack_test.go:272\]`, 275 | }, { 276 | stackTrace()[:2], 277 | "%+v", 278 | "\n" + 279 | "github.com/pkg/errors.stackTrace\n" + 280 | "\t.+/github.com/pkg/errors/stack_test.go:225\n" + 281 | "github.com/pkg/errors.TestStackTraceFormat\n" + 282 | "\t.+/github.com/pkg/errors/stack_test.go:276", 283 | }, { 284 | stackTrace()[:2], 285 | "%#v", 286 | `\[\]errors.Frame{stack_test.go:225, stack_test.go:284}`, 287 | }} 288 | 289 | for i, tt := range tests { 290 | testFormatRegexp(t, i, tt.StackTrace, tt.format, tt.want) 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /wsgi/callbacks.go: -------------------------------------------------------------------------------- 1 | package wsgi 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "io/ioutil" 7 | "os" 8 | 9 | "github.com/noonat/whiskey/py" 10 | ) 11 | 12 | // registerCallbacks creates callbacks within Whiskey's Python library to 13 | // invoke functions in this package. 14 | func registerCallbacks() { 15 | py.RegisterCallback("wsgi_errors_flush", wsgiErrorsFlush) 16 | py.RegisterCallback("wsgi_errors_write", wsgiErrorsWrite) 17 | py.RegisterCallback("wsgi_input_read", wsgiInputRead) 18 | py.RegisterCallback("wsgi_input_read_line", wsgiInputReadLine) 19 | py.RegisterCallback("wsgi_start_response", wsgiStartResponse) 20 | } 21 | 22 | func wsgiErrorsFlush(args py.Tuple) (py.Object, error) { 23 | // At the moment this is a no-op, because write is going to stderr, and 24 | // Go doesn't buffer stderr. I'm leaving this hook here, though, to 25 | // allow for the possibility of buffered error streams in the future. 26 | py.None.IncRef() 27 | return py.None, nil 28 | } 29 | 30 | // wsgiErrorsWrite writes a string to stderr. 31 | func wsgiErrorsWrite(args py.Tuple) (py.Object, error) { 32 | var index int 33 | var s string 34 | if err := args.GetItems(&index, &s); err != nil { 35 | return py.Object{}, err 36 | } 37 | fmt.Fprint(os.Stderr, s) 38 | py.None.IncRef() 39 | return py.None, nil 40 | } 41 | 42 | // wsgiInputRead can be called from the application to read data from the 43 | // request body, as a string. It can optionally pass an integer to limit the 44 | // amount of data read. It defaults to reading the entire body. It should 45 | // return an empty string to indicate EOF. 46 | func wsgiInputRead(args py.Tuple) (py.Object, error) { 47 | var index int 48 | var sizeOrNone py.Object 49 | if err := args.GetItems(&index, &sizeOrNone); err != nil { 50 | return py.Object{}, err 51 | } 52 | defer sizeOrNone.DecRef() 53 | 54 | wr := requests[index] 55 | 56 | var b []byte 57 | if sizeOrNone != py.None { 58 | // Try to read exactly size bytes 59 | pn, err := sizeOrNone.Int() 60 | if err != nil { 61 | return py.Object{}, err 62 | } 63 | size, err := pn.GoInt() 64 | if err != nil { 65 | return py.Object{}, err 66 | } 67 | 68 | // FIXME: it might be nice to recycle these byte buffers 69 | b = make([]byte, size) 70 | n, err := io.ReadFull(wr.req.Body, b) 71 | switch err { 72 | case io.EOF, io.ErrUnexpectedEOF, nil: 73 | b = b[:n] 74 | default: 75 | return py.Object{}, err 76 | } 77 | } else { 78 | // Read until the end of the body 79 | var err error 80 | b, err = ioutil.ReadAll(wr.req.Body) 81 | if err != nil { 82 | return py.Object{}, err 83 | } 84 | } 85 | 86 | ps, err := py.NewString(string(b)) 87 | if err != nil { 88 | return py.Object{}, err 89 | } 90 | return ps.Object, err 91 | } 92 | 93 | // wsgiInputReadLine reads a single line from the file. 94 | func wsgiInputReadLine(args py.Tuple) (py.Object, error) { 95 | var index int 96 | if err := args.GetItems(&index); err != nil { 97 | return py.Object{}, err 98 | } 99 | 100 | wr := requests[index] 101 | 102 | line, err := wr.reader.ReadString('\n') 103 | if err != nil && err != io.EOF { 104 | return py.Object{}, err 105 | } 106 | pl, err := py.NewString(line) 107 | if err != nil { 108 | return py.Object{}, err 109 | } 110 | return pl.Object, nil 111 | } 112 | 113 | // wsgiStartResponse is called by the WSGI application to specify the status 114 | // code and headers for the response. It can be called more than once, 115 | // although calls after the first are reserved for setting the excInfo 116 | // parameter (to convert the response into an error response). 117 | func wsgiStartResponse(args py.Tuple) (py.Object, error) { 118 | // FIXME: this doesn't handle excInfo yet 119 | 120 | var index int 121 | var status py.String 122 | var headers py.List 123 | var excInfo py.Object 124 | if err := args.GetItems(&index, &status, &headers, &excInfo); err != nil { 125 | return py.Object{}, err 126 | } 127 | defer status.DecRef() 128 | defer headers.DecRef() 129 | defer excInfo.DecRef() 130 | 131 | c, err := convertStatus(status) 132 | if err != nil { 133 | return py.Object{}, err 134 | } 135 | 136 | h, err := convertHeaders(headers) 137 | if err != nil { 138 | return py.Object{}, err 139 | } 140 | 141 | wr := requests[index] 142 | wr.code = c 143 | wr.headers = h 144 | 145 | py.None.IncRef() 146 | return py.None, nil 147 | } 148 | -------------------------------------------------------------------------------- /wsgi/listen.go: -------------------------------------------------------------------------------- 1 | package wsgi 2 | 3 | import ( 4 | "net" 5 | "net/http" 6 | "strings" 7 | "time" 8 | 9 | "github.com/noonat/whiskey/prefork" 10 | "github.com/noonat/whiskey/py" 11 | "github.com/pkg/errors" 12 | ) 13 | 14 | // Worker serves requests using a Python WSGI application. 15 | type Worker struct { 16 | Module string 17 | NumConns int 18 | } 19 | 20 | var requests []*Request 21 | 22 | // Serve accepts incoming HTTP connections on the listener l, creating a new 23 | // service goroutines for each. The service goroutines invoke the Python WSGI 24 | // application to handle the request. 25 | func (wrk *Worker) Serve(ln net.Listener, logger prefork.Logger) error { 26 | if err := py.Initialize(); err != nil { 27 | return err 28 | } 29 | module := strings.Split(wrk.Module, ":") 30 | application, err := loadApplication(module[0], module[1]) 31 | if err != nil { 32 | return err 33 | } 34 | defer application.DecRef() 35 | 36 | ts := py.GetThreadState() 37 | ts.Release() 38 | 39 | pool := make(chan *Request, wrk.NumConns) 40 | requests = make([]*Request, wrk.NumConns) 41 | for i := 0; i < wrk.NumConns; i++ { 42 | wr, err := NewRequest(i, application, ts.New()) 43 | if err != nil { 44 | return err 45 | } 46 | pool <- wr 47 | requests[i] = wr 48 | } 49 | 50 | http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { 51 | wr := <-pool 52 | wr.Reset(w, req) 53 | wr.ts.Acquire() 54 | defer func() { 55 | wr.ts.Release() 56 | wr.Reset(nil, nil) 57 | pool <- wr 58 | }() 59 | 60 | response, err := callApplication(wr) 61 | if err == nil { 62 | err = writeResponse(wr, response) 63 | } 64 | if err != nil { 65 | logger.Printf("error serving request: %+v\n", err) 66 | } 67 | }) 68 | 69 | srv := &http.Server{} 70 | ln = prefork.WorkerListener(ln, wrk.NumConns, 3*time.Minute) 71 | if err := srv.Serve(ln); err != nil { 72 | return errors.Wrap(err, "error serving in worker") 73 | } 74 | 75 | return nil 76 | } 77 | -------------------------------------------------------------------------------- /wsgi/pool.go: -------------------------------------------------------------------------------- 1 | package wsgi 2 | 3 | import ( 4 | "bufio" 5 | "net/http" 6 | 7 | "github.com/noonat/whiskey/py" 8 | ) 9 | 10 | // Request tracks the state associated with a single WSGI request. This is 11 | // necessary because we need to track this data across Python boundaries, 12 | // where we can't pass the Go pointer data into Python. 13 | type Request struct { 14 | index int 15 | 16 | ts *py.ThreadState 17 | application py.Object 18 | startResponse py.Object 19 | wsgiInput py.Object 20 | wsgiErrors py.Object 21 | 22 | w http.ResponseWriter 23 | req *http.Request 24 | reader *bufio.Reader 25 | 26 | code int 27 | headers http.Header 28 | } 29 | 30 | // NewRequest creates a new Request object for the given index. This also 31 | // generates the associated Python functions and objects required for it to 32 | // interact with WSGI applications. 33 | func NewRequest(index int, application py.Object, ts *py.ThreadState) (*Request, error) { 34 | wr := &Request{ 35 | application: application, 36 | index: index, 37 | reader: bufio.NewReader(nil), 38 | ts: ts, 39 | } 40 | 41 | ts.Acquire() 42 | var err error 43 | wr.startResponse, wr.wsgiInput, wr.wsgiErrors, err = createRequestObjects(wr.index) 44 | ts.Release() 45 | if err != nil { 46 | return nil, err 47 | } 48 | 49 | return wr, nil 50 | } 51 | 52 | // Free releases the Python resoures associated with the Request object. 53 | func (wr *Request) Free() { 54 | if wr.startResponse.PyObject != nil { 55 | wr.startResponse.DecRef() 56 | wr.startResponse.PyObject = nil 57 | } 58 | if wr.wsgiInput.PyObject != nil { 59 | wr.wsgiInput.DecRef() 60 | wr.wsgiInput.PyObject = nil 61 | } 62 | if wr.wsgiErrors.PyObject != nil { 63 | wr.wsgiErrors.DecRef() 64 | wr.wsgiErrors.PyObject = nil 65 | } 66 | } 67 | 68 | // Reset associates the existing Request object with a new HTTP request. 69 | func (wr *Request) Reset(w http.ResponseWriter, req *http.Request) { 70 | wr.w = w 71 | wr.req = req 72 | wr.code = 0 73 | wr.headers = nil 74 | if req != nil { 75 | wr.reader.Reset(wr.req.Body) 76 | } else { 77 | wr.reader.Reset(nil) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /wsgi/wsgi.go: -------------------------------------------------------------------------------- 1 | package wsgi 2 | 3 | import ( 4 | "net/http" 5 | "strconv" 6 | "strings" 7 | 8 | "github.com/noonat/whiskey/py" 9 | "github.com/pkg/errors" 10 | ) 11 | 12 | var ( 13 | pyCreateRequestObjects py.Object 14 | wsgiVersion py.Tuple 15 | 16 | moduleSource = ` 17 | import _whiskey 18 | 19 | 20 | __version__ = '0.1.0' 21 | 22 | 23 | class ErrorsWriter(object): 24 | 25 | def __init__(self, index): 26 | self._index = index 27 | 28 | def flush(self): 29 | _whiskey.call("wsgi_errors_flush", (self._index,)) 30 | 31 | def write(self, string): 32 | _whiskey.call("wsgi_errors_write", (self._index, string)) 33 | 34 | def writelines(self, strings): 35 | for string in strings: 36 | _whiskey.call("wsgi_errors_write", (self._index, string)) 37 | 38 | 39 | class InputReader(object): 40 | 41 | def __init__(self, index): 42 | self._index = index 43 | 44 | def __iter__(self): 45 | return self 46 | 47 | def next(self): 48 | line = _whiskey.call("wsgi_input_read_line", (self._index, None)) 49 | if line == '': 50 | raise StopIteration() 51 | return line 52 | 53 | def read(self, size=None): 54 | return _whiskey.call("wsgi_input_read", (self._index, size)) 55 | 56 | def readline(self, size=None): 57 | return _whiskey.call("wsgi_input_read_line", (self._index, size)) 58 | 59 | def readlines(self, sizehint=None): 60 | return list(self) 61 | 62 | 63 | def create_request_objects(index): 64 | def start_response(status, headers, exc_info=None): 65 | return _whiskey.call("wsgi_start_response", (index, status, headers, 66 | exc_info)) 67 | return start_response, InputReader(index), ErrorsWriter(index) 68 | ` 69 | ) 70 | 71 | func init() { 72 | py.AddInitializer(func() error { 73 | registerCallbacks() 74 | m, err := py.NewModuleString("whiskey", moduleSource) 75 | if err != nil { 76 | return err 77 | } 78 | defer m.DecRef() 79 | pyCreateRequestObjects, err = m.GetAttrString("create_request_objects") 80 | if err != nil { 81 | return err 82 | } 83 | return nil 84 | }) 85 | py.AddFinalizer(func() error { 86 | if pyCreateRequestObjects.PyObject != nil { 87 | pyCreateRequestObjects.DecRef() 88 | pyCreateRequestObjects.PyObject = nil 89 | } 90 | return nil 91 | }) 92 | } 93 | 94 | func loadApplication(moduleName, applicationName string) (py.Object, error) { 95 | m, err := py.ImportModule(moduleName) 96 | if err != nil { 97 | return py.Object{}, err 98 | } 99 | defer m.DecRef() 100 | 101 | application, err := m.GetAttrString(applicationName) 102 | if err != nil { 103 | return py.Object{}, err 104 | } else if !application.IsCallable() { 105 | application.DecRef() 106 | return py.Object{}, errors.New("application is not callable") 107 | } 108 | 109 | return application, nil 110 | } 111 | 112 | // callApplication runs the Python WSGI application function. 113 | // 114 | // The application function receives a start_response function as one of its 115 | // arguments. It can later call this function to give the WSGI server a status 116 | // code and headers. Because we may have multiple requests in progress at any 117 | // given time, we need to associate the start_response function that we pass 118 | // to the application with the WSGIRequest for this request. We do that by 119 | // generating a start_response function per request and binding the WSGIRequest 120 | // index as the "self" object associated with the function, so we can look it 121 | // up again later. 122 | func callApplication(wr *Request) (py.Iter, error) { 123 | environ, err := createEnviron(wr) 124 | if err != nil { 125 | return py.Iter{}, err 126 | } 127 | response, err := wr.application.Call(environ.Object, wr.startResponse) 128 | if err != nil { 129 | return py.Iter{}, err 130 | } 131 | return response.Iter() 132 | } 133 | 134 | // writeResponse iterates over the value returned by the WSGI application 135 | // function, and writes each chunk out to the http.ResponseWriter. 136 | func writeResponse(wr *Request, iter py.Iter) error { 137 | wroteHeaders := false 138 | for { 139 | value, err := iter.Next() 140 | if err != nil { 141 | return err 142 | } else if value.PyObject == nil { 143 | break 144 | } 145 | s, err := value.GoString() 146 | value.DecRef() 147 | if err != nil { 148 | return err 149 | } 150 | 151 | b := []byte(s) 152 | if len(b) == 0 { 153 | continue 154 | } 155 | if !wroteHeaders { 156 | for k, vs := range wr.headers { 157 | for _, v := range vs { 158 | wr.w.Header().Add(k, v) 159 | } 160 | } 161 | wr.w.WriteHeader(wr.code) 162 | wroteHeaders = true 163 | } 164 | wr.w.Write(b) 165 | } 166 | 167 | return nil 168 | } 169 | 170 | // convertHeaders converts the WSGI header list into an http.Header object. 171 | // 172 | // WSGI specifies that headers must be a list of tuples, where each tuple is a 173 | // of header name and value. Iterate over them and convert them to the Go 174 | // http.Header type. 175 | func convertHeaders(wsgiHeaders py.List) (http.Header, error) { 176 | headers := http.Header{} 177 | 178 | length := wsgiHeaders.Len() 179 | for i := 0; i < length; i++ { 180 | h, err := wsgiHeaders.GetItem(i) 181 | if err != nil { 182 | return headers, err 183 | } 184 | ht, err := h.Tuple() 185 | if err != nil { 186 | return headers, err 187 | } 188 | var k, v string 189 | if err := ht.GetItems(&k, &v); err != nil { 190 | return headers, err 191 | } 192 | headers.Add(k, v) 193 | } 194 | 195 | return headers, nil 196 | } 197 | 198 | // convertStatus converts the WSGI status string into an integer code. 199 | // 200 | // WSGI specifies that status must be a string of the form "200 OK". We only 201 | // care about the code itself, so convert that part to an integer that we can 202 | // send to WriteHeader later. 203 | func convertStatus(status py.String) (int, error) { 204 | s, err := status.GoString() 205 | if err != nil { 206 | return 500, err 207 | } 208 | i := strings.IndexRune(s, ' ') 209 | if i == -1 { 210 | return 500, errors.New("invalid status string, couldn't find space character") 211 | } 212 | code, err := strconv.Atoi(s[:i]) 213 | if err != nil { 214 | return 500, errors.Wrap(err, "error converting status to integer") 215 | } 216 | return code, nil 217 | } 218 | 219 | func createRequestObjects(index int) (startResponse, wsgiInput, wsgiErrors py.Object, err error) { 220 | pi, err := py.NewInt(index) 221 | if err != nil { 222 | return 223 | } 224 | defer pi.DecRef() 225 | 226 | r, err := pyCreateRequestObjects.Call(pi.Object) 227 | if err != nil { 228 | return 229 | } 230 | defer r.DecRef() 231 | rt, err := r.Tuple() 232 | if err != nil { 233 | return 234 | } 235 | 236 | items := [3]py.Object{} 237 | for i := 0; i < 3; i++ { 238 | items[i], err = rt.GetItem(i) 239 | if err != nil { 240 | return 241 | } 242 | } 243 | startResponse, wsgiInput, wsgiErrors = items[0], items[1], items[2] 244 | return 245 | } 246 | 247 | func sicscs(d py.Dict, k, v string) error { 248 | pk, err := py.CachedString(k) 249 | if err != nil { 250 | return err 251 | } 252 | pv, err := py.CachedString(v) 253 | if err != nil { 254 | return err 255 | } 256 | return d.SetItem(pk.Object, pv.Object) 257 | } 258 | 259 | func sicss(d py.Dict, k, v string) error { 260 | pk, err := py.CachedString(k) 261 | if err != nil { 262 | return err 263 | } 264 | return d.SetItemString(pk.Object, v) 265 | } 266 | 267 | func sicsi(d py.Dict, k string, v py.Object) error { 268 | pk, err := py.CachedString(k) 269 | if err != nil { 270 | return err 271 | } 272 | return d.SetItem(pk.Object, v) 273 | } 274 | 275 | // createEnviron returns a WSGI environ dict for the given request. 276 | func createEnviron(wr *Request) (py.Dict, error) { 277 | // The comments in this function come from the descriptions for keys in 278 | // PEP-3333. (https://www.python.org/dev/peps/pep-3333/) 279 | 280 | var d py.Dict 281 | 282 | if wsgiVersion.PyObject == nil { 283 | wv, err := py.NewTuple(2) 284 | if err != nil { 285 | return d, err 286 | } else if err := wv.SetItemInt(0, 1); err != nil { 287 | wv.DecRef() 288 | return d, err 289 | } else if err := wv.SetItemInt(1, 0); err != nil { 290 | wv.DecRef() 291 | return d, err 292 | } 293 | wsgiVersion = wv 294 | } 295 | 296 | d, err := py.NewDict() 297 | if err != nil { 298 | return d, err 299 | } 300 | 301 | // The HTTP request method, such as "GET" or "POST" . This cannot ever be 302 | // an empty string, and so is always required. 303 | sicscs(d, "REQUEST_METHOD", wr.req.Method) 304 | 305 | // The initial portion of the request URL's "path" that corresponds to the 306 | // application object, so that the application knows its virtual 307 | // "location". This may be an empty string, if the application corresponds 308 | // to the "root" of the server. 309 | sicscs(d, "SCRIPT_NAME", "") 310 | 311 | // The remainder of the request URL's "path", designating the virtual 312 | // "location" of the request's target within the application. This may be 313 | // an empty string, if the request URL targets the application root and 314 | // does not have a trailing slash. 315 | sicss(d, "PATH_INFO", wr.req.URL.Path) 316 | 317 | // The portion of the request URL that follows the "?", if any. May be 318 | // empty or absent. 319 | sicss(d, "QUERY_STRING", wr.req.URL.RawQuery) 320 | 321 | // The contents of any Content-Type fields in the HTTP request. May be 322 | // empty or absent. 323 | sicss(d, "CONTENT_TYPE", wr.req.Header.Get("Content-Type")) 324 | 325 | // The contents of any Content-Length fields in the HTTP request. May be 326 | // empty or absent. 327 | sicss(d, "CONTENT_LENGTH", wr.req.Header.Get("Content-Length")) 328 | 329 | // When combined with SCRIPT_NAME and PATH_INFO, these variables can be 330 | // used to complete the URL. Note, however, that HTTP_HOST, if present, 331 | // should be used in preference to SERVER_NAME for reconstructing the 332 | // request URL. SERVER_NAME and SERVER_PORT can never be empty strings, and 333 | // so are always required. 334 | sicss(d, "SERVER_NAME", "127.0.0.1") 335 | sicss(d, "SERVER_PORT", "8080") 336 | 337 | // The version of the protocol the client used to send the request. 338 | // Typically this will be something like "HTTP/1.0" or "HTTP/1.1" and may 339 | // be used by the application to determine how to treat any HTTP request 340 | // headers. (This variable should probably be called REQUEST_PROTOCOL, 341 | // since it denotes the protocol used in the request, and is not 342 | // necessarily the protocol that will be used in the server's response. 343 | // However, for compatibility with CGI we have to keep the existing name.) 344 | sicss(d, "SERVER_PROTOCOL", wr.req.Proto) 345 | 346 | // The tuple (1, 0), representing WSGI version 1.0. 347 | sicsi(d, "wsgi.version", wsgiVersion.Object) 348 | 349 | // A string representing the "scheme" portion of the URL at which the 350 | // application is being invoked. Normally, this will have the value "http" 351 | // or "https", as appropriate. 352 | var scheme string 353 | if wr.req.TLS != nil { 354 | scheme = "https" 355 | } else { 356 | scheme = "http" 357 | } 358 | sicscs(d, "wsgi.url_scheme", scheme) 359 | 360 | // An input stream (file-like object) from which the HTTP request body can 361 | // be read. (The server or gateway may perform reads on-demand as requested 362 | // by the application, or it may pre-read the client's request body and 363 | // buffer it in-memory or on disk, or use any other technique for providing 364 | // such an input stream, according to its preference.) 365 | sicsi(d, "wsgi.input", wr.wsgiInput) 366 | 367 | // An output stream (file-like object) to which error output can be written, 368 | // for the purpose of recording program or other errors in a standardized 369 | // and possibly centralized location. This should be a "text mode" stream; 370 | // i.e., applications should use "\n" as a line ending, and assume that it 371 | // will be converted to the correct line ending by the server/gateway. 372 | // 373 | // For many servers, wsgi.errors will be the server's main error log. 374 | // Alternatively, this may be sys.stderr, or a log file of some sort. The 375 | // server's documentation should include an explanation of how to configure 376 | // this or where to find the recorded output. A server or gateway may 377 | // supply different error streams to different applications, if this is 378 | // desired. 379 | sicsi(d, "wsgi.errors", wr.wsgiErrors) 380 | 381 | // This value should evaluate true if the application object may be 382 | // simultaneously invoked by another thread in the same process, and should 383 | // evaluate false otherwise. 384 | sicsi(d, "wsgi.multithread", py.True) 385 | 386 | // This value should evaluate true if an equivalent application object may 387 | // be simultaneously invoked by another process, and should evaluate false 388 | // otherwise. 389 | sicsi(d, "wsgi.multiprocess", py.True) 390 | 391 | // This value should evaluate true if the server or gateway expects (but 392 | // does not guarantee!) that the application will only be invoked this one 393 | // time during the life of its containing process. Normally, this will only 394 | // be true for a gateway based on CGI (or something similar). 395 | sicsi(d, "wsgi.run_once", py.False) 396 | 397 | return d, nil 398 | } 399 | --------------------------------------------------------------------------------