diff --git a/.gitignore b/.gitignore index 79b007c..90c97b4 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ certcheck +promcertcheck diff --git a/Gopkg.lock b/Gopkg.lock index d24da23..a0357ad 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -14,10 +14,10 @@ revision = "4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9" [[projects]] + branch = "master" name = "github.com/flosch/pongo2" packages = ["."] - revision = "5e81b817a0c48c1c57cdf1a9056cf76bdee02ca9" - version = "v3.0" + revision = "1f4be1efe3b3529b7e58861f75d70120a9567dc4" [[projects]] branch = "master" @@ -37,6 +37,12 @@ revision = "24fca303ac6da784b9e8269f724ddeb0b2eea5e7" version = "v1.5.0" +[[projects]] + branch = "master" + name = "github.com/juju/errors" + packages = ["."] + revision = "c7d06af17c68cd34c835053720b21f6549d9b0ee" + [[projects]] name = "github.com/matttproud/golang_protobuf_extensions" packages = ["pbutil"] @@ -112,6 +118,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "d0a90698569f49d4e7ef58586b857a1c7c60e48eaca07f1585d02e7ce91ca092" + inputs-digest = "f88f786b9ff5e99b5589aea171923fd8d9dcef123a4d6be68d7659b923e8dc1e" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index d4dd19b..62974f7 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -27,6 +27,7 @@ [[constraint]] name = "github.com/flosch/pongo2" + branch = "master" [[constraint]] name = "github.com/gorilla/mux" diff --git a/promcertcheck b/promcertcheck deleted file mode 100755 index 5f64e3e..0000000 Binary files a/promcertcheck and /dev/null differ diff --git a/vendor/github.com/flosch/pongo2/.gitignore b/vendor/github.com/flosch/pongo2/.gitignore index 37eaf44..1346be5 100644 --- a/vendor/github.com/flosch/pongo2/.gitignore +++ b/vendor/github.com/flosch/pongo2/.gitignore @@ -7,6 +7,7 @@ _obj _test .idea +.vscode # Architecture specific extensions/prefixes *.[568vq] diff --git a/vendor/github.com/flosch/pongo2/.travis.yml b/vendor/github.com/flosch/pongo2/.travis.yml index a22ad21..0445a45 100644 --- a/vendor/github.com/flosch/pongo2/.travis.yml +++ b/vendor/github.com/flosch/pongo2/.travis.yml @@ -1,12 +1,13 @@ language: go go: - - 1.3 + - 1.7 - tip install: - - go get code.google.com/p/go.tools/cmd/cover + - go get golang.org/x/tools/cmd/cover - go get github.com/mattn/goveralls - go get gopkg.in/check.v1 + - go get github.com/juju/errors script: - go test -v -covermode=count -coverprofile=coverage.out -bench . -cpu 1,4 - '[ "${TRAVIS_PULL_REQUEST}" = "false" ] && $HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci -repotoken $COVERALLS_TOKEN || true' diff --git a/vendor/github.com/flosch/pongo2/README.md b/vendor/github.com/flosch/pongo2/README.md index 7c61e9e..33def30 100644 --- a/vendor/github.com/flosch/pongo2/README.md +++ b/vendor/github.com/flosch/pongo2/README.md @@ -1,8 +1,9 @@ # [pongo](https://en.wikipedia.org/wiki/Pongo_%28genus%29)2 -[![GoDoc](https://godoc.org/github.com/flosch/pongo2?status.png)](https://godoc.org/github.com/flosch/pongo2) +[![Join the chat at https://gitter.im/flosch/pongo2](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/flosch/pongo2) +[![GoDoc](https://godoc.org/github.com/flosch/pongo2?status.svg)](https://godoc.org/github.com/flosch/pongo2) [![Build Status](https://travis-ci.org/flosch/pongo2.svg?branch=master)](https://travis-ci.org/flosch/pongo2) -[![Coverage Status](https://coveralls.io/repos/flosch/pongo2/badge.png?branch=master)](https://coveralls.io/r/flosch/pongo2?branch=master) +[![Coverage Status](https://coveralls.io/repos/flosch/pongo2/badge.svg?branch=master)](https://coveralls.io/r/flosch/pongo2?branch=master) [![gratipay](http://img.shields.io/badge/gratipay-support%20pongo-brightgreen.svg)](https://gratipay.com/flosch/) [![Bountysource](https://www.bountysource.com/badge/tracker?tracker_id=3654947)](https://www.bountysource.com/trackers/3654947-pongo2?utm_source=3654947&utm_medium=shield&utm_campaign=TRACKER_BADGE) @@ -95,6 +96,7 @@ Please also have a look on the [caveats](https://github.com/flosch/pongo2#caveat If you're using the `master`-branch of pongo2, you might be interested in this section. Since pongo2 is still in development (even though there is a first stable release!), there could be (backwards-incompatible) API changes over time. To keep track of these and therefore make it painless for you to adapt your codebase, I'll list them here. + * Function signature for tag execution changed: not taking a `bytes.Buffer` anymore; instead `Execute()`-functions are now taking a `TemplateWriter` interface. * Function signature for tag and filter parsing/execution changed (`error` return type changed to `*Error`). * `INodeEvaluator` has been removed and got replaced by `IEvaluator`. You can change your existing tags/filters by simply replacing the interface. * Two new helper functions: [`RenderTemplateFile()`](https://godoc.org/github.com/flosch/pongo2#RenderTemplateFile) and [`RenderTemplateString()`](https://godoc.org/github.com/flosch/pongo2#RenderTemplateString). @@ -104,7 +106,7 @@ If you're using the `master`-branch of pongo2, you might be interested in this s ## How you can help * Write [filters](https://github.com/flosch/pongo2/blob/master/filters_builtin.go#L3) / [tags](https://github.com/flosch/pongo2/blob/master/tags.go#L4) (see [tutorial](https://www.florian-schlachter.de/post/pongo2/)) by forking pongo2 and sending pull requests - * Write/improve code tests (use the following command to see what tests are missing: `go test -v -cover -covermode=count -coverprofile=cover.out && go tool cover -html=cover.out`) + * Write/improve code tests (use the following command to see what tests are missing: `go test -v -cover -covermode=count -coverprofile=cover.out && go tool cover -html=cover.out` or have a look on [gocover.io/github.com/flosch/pongo2](http://gocover.io/github.com/flosch/pongo2)) * Write/improve template tests (see the `template_tests/` directory) * Write middleware, libraries and websites using pongo2. :-) @@ -115,7 +117,8 @@ For a documentation on how the templating language works you can [head over to t You can access pongo2's API documentation on [godoc](https://godoc.org/github.com/flosch/pongo2). ## Blog post series - + + * [pongo2 v3 released](https://www.florian-schlachter.de/post/pongo2-v3/) * [pongo2 v2 released](https://www.florian-schlachter.de/post/pongo2-v2/) * [pongo2 1.0 released](https://www.florian-schlachter.de/post/pongo2-10/) [August 8th 2014] * [pongo2 playground](https://www.florian-schlachter.de/post/pongo2-playground/) [August 1st 2014] @@ -154,8 +157,12 @@ You can access pongo2's API documentation on [godoc](https://godoc.org/github.co * [beego-pongo2.v2](https://github.com/ipfans/beego-pongo2.v2) - Same as `beego-pongo2`, but for pongo2 v2. * [macaron-pongo2](https://github.com/macaron-contrib/pongo2) - pongo2 support for [Macaron](https://github.com/Unknwon/macaron), a modular web framework. * [ginpongo2](https://github.com/ngerakines/ginpongo2) - middleware for [gin](github.com/gin-gonic/gin) to use pongo2 templates - * [pongo2-trans](https://github.com/fromYukki/pongo2trans) - `trans`-tag implementation for internationalization - + * [Build'n support for Iris' template engine](https://github.com/kataras/iris) + * [pongo2gin](https://github.com/robvdl/pongo2gin) - alternative renderer for [gin](github.com/gin-gonic/gin) to use pongo2 templates + * [pongo2-trans](https://github.com/digitalcrab/pongo2trans) - `trans`-tag implementation for internationalization + * [tpongo2](https://github.com/tango-contrib/tpongo2) - pongo2 support for [Tango](https://github.com/lunny/tango), a micro-kernel & pluggable web framework. + * [p2cli](https://github.com/wrouesnel/p2cli) - command line templating utility based on pongo2 + Please add your project to this list and send me a pull request when you've developed something nice for pongo2. # API-usage examples diff --git a/vendor/github.com/flosch/pongo2/context.go b/vendor/github.com/flosch/pongo2/context.go index df587c8..6e3c166 100644 --- a/vendor/github.com/flosch/pongo2/context.go +++ b/vendor/github.com/flosch/pongo2/context.go @@ -1,13 +1,14 @@ package pongo2 import ( - "fmt" "regexp" + + "github.com/juju/errors" ) var reIdentifiers = regexp.MustCompile("^[a-zA-Z0-9_]+$") -// Use this Context type to provide constants, variables, instances or functions to your template. +// A Context type provides constants, variables, instances or functions to a template. // // pongo2 automatically provides meta-information or functions through the "pongo2"-key. // Currently, context["pongo2"] contains the following keys: @@ -24,14 +25,15 @@ func (c Context) checkForValidIdentifiers() *Error { for k, v := range c { if !reIdentifiers.MatchString(k) { return &Error{ - Sender: "checkForValidIdentifiers", - ErrorMsg: fmt.Sprintf("Context-key '%s' (value: '%+v') is not a valid identifier.", k, v), + Sender: "checkForValidIdentifiers", + OrigError: errors.Errorf("context-key '%s' (value: '%+v') is not a valid identifier", k, v), } } } return nil } +// Update updates this context with the key/value-pairs from another context. func (c Context) Update(other Context) Context { for k, v := range other { c[k] = v @@ -39,6 +41,8 @@ func (c Context) Update(other Context) Context { return c } +// ExecutionContext contains all data important for the current rendering state. +// // If you're writing a custom tag, your tag's Execute()-function will // have access to the ExecutionContext. This struct stores anything // about the current rendering process's Context including @@ -97,6 +101,10 @@ func NewChildExecutionContext(parent *ExecutionContext) *ExecutionContext { } func (ctx *ExecutionContext) Error(msg string, token *Token) *Error { + return ctx.OrigError(errors.New(msg), token) +} + +func (ctx *ExecutionContext) OrigError(err error, token *Token) *Error { filename := ctx.template.name var line, col int if token != nil { @@ -107,13 +115,13 @@ func (ctx *ExecutionContext) Error(msg string, token *Token) *Error { col = token.Col } return &Error{ - Template: ctx.template, - Filename: filename, - Line: line, - Column: col, - Token: token, - Sender: "execution", - ErrorMsg: msg, + Template: ctx.template, + Filename: filename, + Line: line, + Column: col, + Token: token, + Sender: "execution", + OrigError: err, } } diff --git a/vendor/github.com/flosch/pongo2/error.go b/vendor/github.com/flosch/pongo2/error.go index c1ee86e..8aec8c1 100644 --- a/vendor/github.com/flosch/pongo2/error.go +++ b/vendor/github.com/flosch/pongo2/error.go @@ -6,20 +6,20 @@ import ( "os" ) -// This Error type is being used to address an error during lexing, parsing or +// The Error type is being used to address an error during lexing, parsing or // execution. If you want to return an error object (for example in your own // tag or filter) fill this object with as much information as you have. // Make sure "Sender" is always given (if you're returning an error within // a filter, make Sender equals 'filter:yourfilter'; same goes for tags: 'tag:mytag'). // It's okay if you only fill in ErrorMsg if you don't have any other details at hand. type Error struct { - Template *Template - Filename string - Line int - Column int - Token *Token - Sender string - ErrorMsg string + Template *Template + Filename string + Line int + Column int + Token *Token + Sender string + OrigError error } func (e *Error) updateFromTokenIfNeeded(template *Template, t *Token) *Error { @@ -54,14 +54,14 @@ func (e *Error) Error() string { } } s += "] " - s += e.ErrorMsg + s += e.OrigError.Error() return s } -// Returns the affected line from the original template, if available. -func (e *Error) RawLine() (line string, available bool) { +// RawLine returns the affected line from the original template, if available. +func (e *Error) RawLine() (line string, available bool, outErr error) { if e.Line <= 0 || e.Filename == "" { - return "", false + return "", false, nil } filename := e.Filename @@ -70,17 +70,22 @@ func (e *Error) RawLine() (line string, available bool) { } file, err := os.Open(filename) if err != nil { - panic(err) + return "", false, err } - defer file.Close() + defer func() { + err := file.Close() + if err != nil && outErr == nil { + outErr = err + } + }() scanner := bufio.NewScanner(file) l := 0 for scanner.Scan() { l++ if l == e.Line { - return scanner.Text(), true + return scanner.Text(), true, nil } } - return "", false + return "", false, nil } diff --git a/vendor/github.com/flosch/pongo2/filters.go b/vendor/github.com/flosch/pongo2/filters.go index 229f7fe..1092705 100644 --- a/vendor/github.com/flosch/pongo2/filters.go +++ b/vendor/github.com/flosch/pongo2/filters.go @@ -2,8 +2,11 @@ package pongo2 import ( "fmt" + + "github.com/juju/errors" ) +// FilterFunction is the type filter functions must fulfil type FilterFunction func(in *Value, param *Value) (out *Value, err *Error) var filters map[string]FilterFunction @@ -12,32 +15,38 @@ func init() { filters = make(map[string]FilterFunction) } -// Registers a new filter. If there's already a filter with the same +// FilterExists returns true if the given filter is already registered +func FilterExists(name string) bool { + _, existing := filters[name] + return existing +} + +// RegisterFilter registers a new filter. If there's already a filter with the same // name, RegisterFilter will panic. You usually want to call this // function in the filter's init() function: // http://golang.org/doc/effective_go.html#init // // See http://www.florian-schlachter.de/post/pongo2/ for more about // writing filters and tags. -func RegisterFilter(name string, fn FilterFunction) { - _, existing := filters[name] - if existing { - panic(fmt.Sprintf("Filter with name '%s' is already registered.", name)) +func RegisterFilter(name string, fn FilterFunction) error { + if FilterExists(name) { + return errors.Errorf("filter with name '%s' is already registered", name) } filters[name] = fn + return nil } -// Replaces an already registered filter with a new implementation. Use this +// ReplaceFilter replaces an already registered filter with a new implementation. Use this // function with caution since it allows you to change existing filter behaviour. -func ReplaceFilter(name string, fn FilterFunction) { - _, existing := filters[name] - if !existing { - panic(fmt.Sprintf("Filter with name '%s' does not exist (therefore cannot be overridden).", name)) +func ReplaceFilter(name string, fn FilterFunction) error { + if !FilterExists(name) { + return errors.Errorf("filter with name '%s' does not exist (therefore cannot be overridden)", name) } filters[name] = fn + return nil } -// Like ApplyFilter, but panics on an error +// MustApplyFilter behaves like ApplyFilter, but panics on an error. func MustApplyFilter(name string, value *Value, param *Value) *Value { val, err := ApplyFilter(name, value, param) if err != nil { @@ -46,13 +55,14 @@ func MustApplyFilter(name string, value *Value, param *Value) *Value { return val } -// Applies a filter to a given value using the given parameters. Returns a *pongo2.Value or an error. +// ApplyFilter applies a filter to a given value using the given parameters. +// Returns a *pongo2.Value or an error. func ApplyFilter(name string, value *Value, param *Value) (*Value, *Error) { fn, existing := filters[name] if !existing { return nil, &Error{ - Sender: "applyfilter", - ErrorMsg: fmt.Sprintf("Filter with name '%s' not found.", name), + Sender: "applyfilter", + OrigError: errors.Errorf("Filter with name '%s' not found.", name), } } @@ -86,31 +96,31 @@ func (fc *filterCall) Execute(v *Value, ctx *ExecutionContext) (*Value, *Error) param = AsValue(nil) } - filtered_value, err := fc.filterFunc(v, param) + filteredValue, err := fc.filterFunc(v, param) if err != nil { return nil, err.updateFromTokenIfNeeded(ctx.template, fc.token) } - return filtered_value, nil + return filteredValue, nil } // Filter = IDENT | IDENT ":" FilterArg | IDENT "|" Filter func (p *Parser) parseFilter() (*filterCall, *Error) { - ident_token := p.MatchType(TokenIdentifier) + identToken := p.MatchType(TokenIdentifier) // Check filter ident - if ident_token == nil { + if identToken == nil { return nil, p.Error("Filter name must be an identifier.", nil) } filter := &filterCall{ - token: ident_token, - name: ident_token.Val, + token: identToken, + name: identToken.Val, } // Get the appropriate filter function and bind it - filterFn, exists := filters[ident_token.Val] + filterFn, exists := filters[identToken.Val] if !exists { - return nil, p.Error(fmt.Sprintf("Filter '%s' does not exist.", ident_token.Val), ident_token) + return nil, p.Error(fmt.Sprintf("Filter '%s' does not exist.", identToken.Val), identToken) } filter.filterFunc = filterFn diff --git a/vendor/github.com/flosch/pongo2/filters_builtin.go b/vendor/github.com/flosch/pongo2/filters_builtin.go index aaa68b1..f02b491 100644 --- a/vendor/github.com/flosch/pongo2/filters_builtin.go +++ b/vendor/github.com/flosch/pongo2/filters_builtin.go @@ -35,6 +35,8 @@ import ( "strings" "time" "unicode/utf8" + + "github.com/juju/errors" ) func init() { @@ -73,14 +75,15 @@ func init() { RegisterFilter("removetags", filterRemovetags) RegisterFilter("rjust", filterRjust) RegisterFilter("slice", filterSlice) + RegisterFilter("split", filterSplit) RegisterFilter("stringformat", filterStringformat) RegisterFilter("striptags", filterStriptags) RegisterFilter("time", filterDate) // time uses filterDate (same golang-format) RegisterFilter("title", filterTitle) RegisterFilter("truncatechars", filterTruncatechars) - RegisterFilter("truncatechars_html", filterTruncatecharsHtml) + RegisterFilter("truncatechars_html", filterTruncatecharsHTML) RegisterFilter("truncatewords", filterTruncatewords) - RegisterFilter("truncatewords_html", filterTruncatewordsHtml) + RegisterFilter("truncatewords_html", filterTruncatewordsHTML) RegisterFilter("upper", filterUpper) RegisterFilter("urlencode", filterUrlencode) RegisterFilter("urlize", filterUrlize) @@ -105,9 +108,9 @@ func filterTruncatecharsHelper(s string, newLen int) string { return string(runes) } -func filterTruncateHtmlHelper(value string, new_output *bytes.Buffer, cond func() bool, fn func(c rune, s int, idx int) int, finalize func()) { +func filterTruncateHTMLHelper(value string, newOutput *bytes.Buffer, cond func() bool, fn func(c rune, s int, idx int) int, finalize func()) { vLen := len(value) - tag_stack := make([]string, 0) + var tagStack []string idx := 0 for idx < vLen && !cond() { @@ -118,17 +121,17 @@ func filterTruncateHtmlHelper(value string, new_output *bytes.Buffer, cond func( } if c == '<' { - new_output.WriteRune(c) + newOutput.WriteRune(c) idx += s // consume "<" if idx+1 < vLen { if value[idx] == '/' { // Close tag - new_output.WriteString("/") + newOutput.WriteString("/") tag := "" - idx += 1 // consume "/" + idx++ // consume "/" for idx < vLen { c2, size2 := utf8.DecodeRuneInString(value[idx:]) @@ -146,21 +149,21 @@ func filterTruncateHtmlHelper(value string, new_output *bytes.Buffer, cond func( idx += size2 } - if len(tag_stack) > 0 { + if len(tagStack) > 0 { // Ideally, the close tag is TOP of tag stack // In malformed HTML, it must not be, so iterate through the stack and remove the tag - for i := len(tag_stack) - 1; i >= 0; i-- { - if tag_stack[i] == tag { + for i := len(tagStack) - 1; i >= 0; i-- { + if tagStack[i] == tag { // Found the tag - tag_stack[i] = tag_stack[len(tag_stack)-1] - tag_stack = tag_stack[:len(tag_stack)-1] + tagStack[i] = tagStack[len(tagStack)-1] + tagStack = tagStack[:len(tagStack)-1] break } } } - new_output.WriteString(tag) - new_output.WriteString(">") + newOutput.WriteString(tag) + newOutput.WriteString(">") } else { // Open tag @@ -174,7 +177,7 @@ func filterTruncateHtmlHelper(value string, new_output *bytes.Buffer, cond func( continue } - new_output.WriteRune(c2) + newOutput.WriteRune(c2) // End of tag found if c2 == '>' { @@ -194,7 +197,7 @@ func filterTruncateHtmlHelper(value string, new_output *bytes.Buffer, cond func( } // Add tag to stack - tag_stack = append(tag_stack, tag) + tagStack = append(tagStack, tag) } } } else { @@ -204,10 +207,10 @@ func filterTruncateHtmlHelper(value string, new_output *bytes.Buffer, cond func( finalize() - for i := len(tag_stack) - 1; i >= 0; i-- { - tag := tag_stack[i] + for i := len(tagStack) - 1; i >= 0; i-- { + tag := tagStack[i] // Close everything from the regular tag stack - new_output.WriteString(fmt.Sprintf("", tag)) + newOutput.WriteString(fmt.Sprintf("", tag)) } } @@ -217,28 +220,28 @@ func filterTruncatechars(in *Value, param *Value) (*Value, *Error) { return AsValue(filterTruncatecharsHelper(s, newLen)), nil } -func filterTruncatecharsHtml(in *Value, param *Value) (*Value, *Error) { +func filterTruncatecharsHTML(in *Value, param *Value) (*Value, *Error) { value := in.String() newLen := max(param.Integer()-3, 0) - new_output := bytes.NewBuffer(nil) + newOutput := bytes.NewBuffer(nil) textcounter := 0 - filterTruncateHtmlHelper(value, new_output, func() bool { + filterTruncateHTMLHelper(value, newOutput, func() bool { return textcounter >= newLen }, func(c rune, s int, idx int) int { textcounter++ - new_output.WriteRune(c) + newOutput.WriteRune(c) return idx + s }, func() { if textcounter >= newLen && textcounter < len(value) { - new_output.WriteString("...") + newOutput.WriteString("...") } }) - return AsSafeValue(new_output.String()), nil + return AsSafeValue(newOutput.String()), nil } func filterTruncatewords(in *Value, param *Value) (*Value, *Error) { @@ -260,19 +263,19 @@ func filterTruncatewords(in *Value, param *Value) (*Value, *Error) { return AsValue(strings.Join(out, " ")), nil } -func filterTruncatewordsHtml(in *Value, param *Value) (*Value, *Error) { +func filterTruncatewordsHTML(in *Value, param *Value) (*Value, *Error) { value := in.String() newLen := max(param.Integer(), 0) - new_output := bytes.NewBuffer(nil) + newOutput := bytes.NewBuffer(nil) wordcounter := 0 - filterTruncateHtmlHelper(value, new_output, func() bool { + filterTruncateHTMLHelper(value, newOutput, func() bool { return wordcounter >= newLen }, func(_ rune, _ int, idx int) int { // Get next word - word_found := false + wordFound := false for idx < len(value) { c2, size2 := utf8.DecodeRuneInString(value[idx:]) @@ -286,29 +289,29 @@ func filterTruncatewordsHtml(in *Value, param *Value) (*Value, *Error) { return idx } - new_output.WriteRune(c2) + newOutput.WriteRune(c2) idx += size2 if c2 == ' ' || c2 == '.' || c2 == ',' || c2 == ';' { // Word ends here, stop capturing it now break } else { - word_found = true + wordFound = true } } - if word_found { + if wordFound { wordcounter++ } return idx }, func() { if wordcounter >= newLen { - new_output.WriteString("...") + newOutput.WriteString("...") } }) - return AsSafeValue(new_output.String()), nil + return AsSafeValue(newOutput.String()), nil } func filterEscape(in *Value, param *Value) (*Value, *Error) { @@ -377,12 +380,11 @@ func filterAdd(in *Value, param *Value) (*Value, *Error) { if in.IsNumber() && param.IsNumber() { if in.IsFloat() || param.IsFloat() { return AsValue(in.Float() + param.Float()), nil - } else { - return AsValue(in.Integer() + param.Integer()), nil } + return AsValue(in.Integer() + param.Integer()), nil } // If in/param is not a number, we're relying on the - // Value's String() convertion and just add them both together + // Value's String() conversion and just add them both together return AsValue(in.String() + param.String()), nil } @@ -550,11 +552,11 @@ func filterCenter(in *Value, param *Value) (*Value, *Error) { } func filterDate(in *Value, param *Value) (*Value, *Error) { - t, is_time := in.Interface().(time.Time) - if !is_time { + t, isTime := in.Interface().(time.Time) + if !isTime { return nil, &Error{ - Sender: "filter:date", - ErrorMsg: "Filter input argument must be of type 'time.Time'.", + Sender: "filter:date", + OrigError: errors.New("filter input argument must be of type 'time.Time'"), } } return AsValue(t.Format(param.String())), nil @@ -612,6 +614,12 @@ func filterLinebreaks(in *Value, param *Value) (*Value, *Error) { return AsValue(b.String()), nil } +func filterSplit(in *Value, param *Value) (*Value, *Error) { + chunks := strings.Split(in.String(), param.String()) + + return AsValue(chunks), nil +} + func filterLinebreaksbr(in *Value, param *Value) (*Value, *Error) { return AsValue(strings.Replace(in.String(), "\n", "
", -1)), nil } @@ -641,7 +649,8 @@ func filterUrlencode(in *Value, param *Value) (*Value, *Error) { var filterUrlizeURLRegexp = regexp.MustCompile(`((((http|https)://)|www\.|((^|[ ])[0-9A-Za-z_\-]+(\.com|\.net|\.org|\.info|\.biz|\.de))))(?U:.*)([ ]+|$)`) var filterUrlizeEmailRegexp = regexp.MustCompile(`(\w+@\w+\.\w{2,4})`) -func filterUrlizeHelper(input string, autoescape bool, trunc int) string { +func filterUrlizeHelper(input string, autoescape bool, trunc int) (string, error) { + var soutErr error sout := filterUrlizeURLRegexp.ReplaceAllStringFunc(input, func(raw_url string) string { var prefix string var suffix string @@ -656,7 +665,8 @@ func filterUrlizeHelper(input string, autoescape bool, trunc int) string { t, err := ApplyFilter("iriencode", AsValue(raw_url), nil) if err != nil { - panic(err) + soutErr = err + return "" } url := t.String() @@ -673,16 +683,19 @@ func filterUrlizeHelper(input string, autoescape bool, trunc int) string { if autoescape { t, err := ApplyFilter("escape", AsValue(title), nil) if err != nil { - panic(err) + soutErr = err + return "" } title = t.String() } return fmt.Sprintf(`%s%s%s`, prefix, url, title, suffix) }) + if soutErr != nil { + return "", soutErr + } sout = filterUrlizeEmailRegexp.ReplaceAllStringFunc(sout, func(mail string) string { - title := mail if trunc > 3 && len(title) > trunc { @@ -692,7 +705,7 @@ func filterUrlizeHelper(input string, autoescape bool, trunc int) string { return fmt.Sprintf(`%s`, mail, title) }) - return sout + return sout, nil } func filterUrlize(in *Value, param *Value) (*Value, *Error) { @@ -701,24 +714,36 @@ func filterUrlize(in *Value, param *Value) (*Value, *Error) { autoescape = param.Bool() } - return AsValue(filterUrlizeHelper(in.String(), autoescape, -1)), nil + s, err := filterUrlizeHelper(in.String(), autoescape, -1) + if err != nil { + + } + + return AsValue(s), nil } func filterUrlizetrunc(in *Value, param *Value) (*Value, *Error) { - return AsValue(filterUrlizeHelper(in.String(), true, param.Integer())), nil + s, err := filterUrlizeHelper(in.String(), true, param.Integer()) + if err != nil { + return nil, &Error{ + Sender: "filter:urlizetrunc", + OrigError: errors.New("you cannot pass more than 2 arguments to filter 'pluralize'"), + } + } + return AsValue(s), nil } func filterStringformat(in *Value, param *Value) (*Value, *Error) { return AsValue(fmt.Sprintf(param.String(), in.Interface())), nil } -var re_striptags = regexp.MustCompile("<[^>]*?>") +var reStriptags = regexp.MustCompile("<[^>]*?>") func filterStriptags(in *Value, param *Value) (*Value, *Error) { s := in.String() // Strip all tags - s = re_striptags.ReplaceAllString(s, "") + s = reStriptags.ReplaceAllString(s, "") return AsValue(strings.TrimSpace(s)), nil } @@ -746,8 +771,8 @@ func filterPluralize(in *Value, param *Value) (*Value, *Error) { endings := strings.Split(param.String(), ",") if len(endings) > 2 { return nil, &Error{ - Sender: "filter:pluralize", - ErrorMsg: "You cannot pass more than 2 arguments to filter 'pluralize'.", + Sender: "filter:pluralize", + OrigError: errors.New("you cannot pass more than 2 arguments to filter 'pluralize'"), } } if len(endings) == 1 { @@ -770,11 +795,10 @@ func filterPluralize(in *Value, param *Value) (*Value, *Error) { } return AsValue(""), nil - } else { - return nil, &Error{ - Sender: "filter:pluralize", - ErrorMsg: "Filter 'pluralize' does only work on numbers.", - } + } + return nil, &Error{ + Sender: "filter:pluralize", + OrigError: errors.New("filter 'pluralize' does only work on numbers"), } } @@ -807,8 +831,8 @@ func filterSlice(in *Value, param *Value) (*Value, *Error) { comp := strings.Split(param.String(), ":") if len(comp) != 2 { return nil, &Error{ - Sender: "filter:slice", - ErrorMsg: "Slice string must have the format 'from:to' [from/to can be omitted, but the ':' is required]", + Sender: "filter:slice", + OrigError: errors.New("Slice string must have the format 'from:to' [from/to can be omitted, but the ':' is required]"), } } @@ -844,16 +868,16 @@ func filterWordcount(in *Value, param *Value) (*Value, *Error) { func filterWordwrap(in *Value, param *Value) (*Value, *Error) { words := strings.Fields(in.String()) - words_len := len(words) - wrap_at := param.Integer() - if wrap_at <= 0 { + wordsLen := len(words) + wrapAt := param.Integer() + if wrapAt <= 0 { return in, nil } - linecount := words_len/wrap_at + words_len%wrap_at + linecount := wordsLen/wrapAt + wordsLen%wrapAt lines := make([]string, 0, linecount) for i := 0; i < linecount; i++ { - lines = append(lines, strings.Join(words[wrap_at*i:min(wrap_at*(i+1), words_len)], " ")) + lines = append(lines, strings.Join(words[wrapAt*i:min(wrapAt*(i+1), wordsLen)], " ")) } return AsValue(strings.Join(lines, "\n")), nil } @@ -864,27 +888,27 @@ func filterYesno(in *Value, param *Value) (*Value, *Error) { 1: "no", 2: "maybe", } - param_string := param.String() - custom_choices := strings.Split(param_string, ",") - if len(param_string) > 0 { - if len(custom_choices) > 3 { + paramString := param.String() + customChoices := strings.Split(paramString, ",") + if len(paramString) > 0 { + if len(customChoices) > 3 { return nil, &Error{ - Sender: "filter:yesno", - ErrorMsg: fmt.Sprintf("You cannot pass more than 3 options to the 'yesno'-filter (got: '%s').", param_string), + Sender: "filter:yesno", + OrigError: errors.Errorf("You cannot pass more than 3 options to the 'yesno'-filter (got: '%s').", paramString), } } - if len(custom_choices) < 2 { + if len(customChoices) < 2 { return nil, &Error{ - Sender: "filter:yesno", - ErrorMsg: fmt.Sprintf("You must pass either no or at least 2 arguments to the 'yesno'-filter (got: '%s').", param_string), + Sender: "filter:yesno", + OrigError: errors.Errorf("You must pass either no or at least 2 arguments to the 'yesno'-filter (got: '%s').", paramString), } } // Map to the options now - choices[0] = custom_choices[0] - choices[1] = custom_choices[1] - if len(custom_choices) == 3 { - choices[2] = custom_choices[2] + choices[0] = customChoices[0] + choices[1] = customChoices[1] + if len(customChoices) == 3 { + choices[2] = customChoices[2] } } diff --git a/vendor/github.com/flosch/pongo2/lexer.go b/vendor/github.com/flosch/pongo2/lexer.go index 8956f9c..36280de 100644 --- a/vendor/github.com/flosch/pongo2/lexer.go +++ b/vendor/github.com/flosch/pongo2/lexer.go @@ -4,6 +4,8 @@ import ( "fmt" "strings" "unicode/utf8" + + "github.com/juju/errors" ) const ( @@ -63,8 +65,8 @@ type lexer struct { line int col int - in_verbatim bool - verbatim_name string + inVerbatim bool + verbatimName string } func (t *Token) String() string { @@ -111,11 +113,11 @@ func lex(name string, input string) ([]*Token, *Error) { if l.errored { errtoken := l.tokens[len(l.tokens)-1] return nil, &Error{ - Filename: name, - Line: errtoken.Line, - Column: errtoken.Col, - Sender: "lexer", - ErrorMsg: errtoken.Val, + Filename: name, + Line: errtoken.Line, + Column: errtoken.Col, + Sender: "lexer", + OrigError: errors.New(errtoken.Val), } } return l.tokens, nil @@ -216,8 +218,8 @@ func (l *lexer) run() { for { // TODO: Support verbatim tag names // https://docs.djangoproject.com/en/dev/ref/templates/builtins/#verbatim - if l.in_verbatim { - name := l.verbatim_name + if l.inVerbatim { + name := l.verbatimName if name != "" { name += " " } @@ -229,20 +231,20 @@ func (l *lexer) run() { l.pos += w l.col += w l.ignore() - l.in_verbatim = false + l.inVerbatim = false } } else if strings.HasPrefix(l.input[l.pos:], "{% verbatim %}") { // tag if l.pos > l.start { l.emit(TokenHTML) } - l.in_verbatim = true + l.inVerbatim = true w := len("{% verbatim %}") l.pos += w l.col += w l.ignore() } - if !l.in_verbatim { + if !l.inVerbatim { // Ignore single-line comments {# ... #} if strings.HasPrefix(l.input[l.pos:], "{#") { if l.pos > l.start { @@ -303,7 +305,7 @@ func (l *lexer) run() { l.emit(TokenHTML) } - if l.in_verbatim { + if l.inVerbatim { l.errorf("verbatim-tag not closed, got EOF.") } } @@ -328,7 +330,7 @@ outer_loop: return l.stateIdentifier case l.accept(tokenDigits): return l.stateNumber - case l.accept(`"`): + case l.accept(`"'`): return l.stateString } @@ -348,10 +350,6 @@ outer_loop: } } - if l.pos < len(l.input) { - return l.errorf("Unknown character: %q (%d)", l.peek(), l.peek()) - } - break } @@ -374,6 +372,11 @@ func (l *lexer) stateIdentifier() lexerStateFn { func (l *lexer) stateNumber() lexerStateFn { l.acceptRun(tokenDigits) + if l.accept(tokenIdentifierCharsWithDigits) { + // This seems to be an identifier starting with a number. + // See https://github.com/flosch/pongo2/issues/151 + return l.stateIdentifier() + } /* Maybe context-sensitive number lexing? * comments.0.Text // first comment @@ -393,9 +396,10 @@ func (l *lexer) stateNumber() lexerStateFn { } func (l *lexer) stateString() lexerStateFn { + quotationMark := l.value() l.ignore() - l.startcol -= 1 // we're starting the position at the first " - for !l.accept(`"`) { + l.startcol-- // we're starting the position at the first " + for !l.accept(quotationMark) { switch l.next() { case '\\': // escape sequence diff --git a/vendor/github.com/flosch/pongo2/nodes.go b/vendor/github.com/flosch/pongo2/nodes.go index 5fd5a6c..5b039cd 100644 --- a/vendor/github.com/flosch/pongo2/nodes.go +++ b/vendor/github.com/flosch/pongo2/nodes.go @@ -1,17 +1,13 @@ package pongo2 -import ( - "bytes" -) - // The root document type nodeDocument struct { Nodes []INode } -func (doc *nodeDocument) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { +func (doc *nodeDocument) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { for _, n := range doc.Nodes { - err := n.Execute(ctx, buffer) + err := n.Execute(ctx, writer) if err != nil { return err } diff --git a/vendor/github.com/flosch/pongo2/nodes_html.go b/vendor/github.com/flosch/pongo2/nodes_html.go index 9aa630c..9680285 100644 --- a/vendor/github.com/flosch/pongo2/nodes_html.go +++ b/vendor/github.com/flosch/pongo2/nodes_html.go @@ -1,14 +1,10 @@ package pongo2 -import ( - "bytes" -) - type nodeHTML struct { token *Token } -func (n *nodeHTML) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { - buffer.WriteString(n.token.Val) +func (n *nodeHTML) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { + writer.WriteString(n.token.Val) return nil } diff --git a/vendor/github.com/flosch/pongo2/nodes_wrapper.go b/vendor/github.com/flosch/pongo2/nodes_wrapper.go index 9180dc7..d1bcb8d 100644 --- a/vendor/github.com/flosch/pongo2/nodes_wrapper.go +++ b/vendor/github.com/flosch/pongo2/nodes_wrapper.go @@ -1,17 +1,13 @@ package pongo2 -import ( - "bytes" -) - type NodeWrapper struct { Endtag string nodes []INode } -func (wrapper *NodeWrapper) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { +func (wrapper *NodeWrapper) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { for _, n := range wrapper.nodes { - err := n.Execute(ctx, buffer) + err := n.Execute(ctx, writer) if err != nil { return err } diff --git a/vendor/github.com/flosch/pongo2/parser.go b/vendor/github.com/flosch/pongo2/parser.go index c85e2d2..d0e08b5 100644 --- a/vendor/github.com/flosch/pongo2/parser.go +++ b/vendor/github.com/flosch/pongo2/parser.go @@ -1,13 +1,14 @@ package pongo2 import ( - "bytes" "fmt" "strings" + + "github.com/juju/errors" ) type INode interface { - Execute(*ExecutionContext, *bytes.Buffer) *Error + Execute(*ExecutionContext, TemplateWriter) *Error } type IEvaluator interface { @@ -27,10 +28,10 @@ type IEvaluator interface { // // (See Token's documentation for more about tokens) type Parser struct { - name string - idx int - tokens []*Token - last_token *Token + name string + idx int + tokens []*Token + lastToken *Token // if the parser parses a template document, here will be // a reference to it (needed to access the template through Tags) @@ -47,7 +48,7 @@ func newParser(name string, tokens []*Token, template *Template) *Parser { template: template, } if len(tokens) > 0 { - p.last_token = tokens[len(tokens)-1] + p.lastToken = tokens[len(tokens)-1] } return p } @@ -175,7 +176,7 @@ func (p *Parser) GetR(shift int) *Token { return p.Get(i) } -// Produces a nice error message and returns an error-object. +// Error produces a nice error message and returns an error-object. // The 'token'-argument is optional. If provided, it will take // the token's position information. If not provided, it will // automatically use the CURRENT token's position information. @@ -196,13 +197,13 @@ func (p *Parser) Error(msg string, token *Token) *Error { col = token.Col } return &Error{ - Template: p.template, - Filename: p.name, - Sender: "parser", - Line: line, - Column: col, - Token: token, - ErrorMsg: msg, + Template: p.template, + Filename: p.name, + Sender: "parser", + Line: line, + Column: col, + Token: token, + OrigError: errors.New(msg), } } @@ -212,19 +213,19 @@ func (p *Parser) Error(msg string, token *Token) *Error { func (p *Parser) WrapUntilTag(names ...string) (*NodeWrapper, *Parser, *Error) { wrapper := &NodeWrapper{} - tagArgs := make([]*Token, 0) + var tagArgs []*Token for p.Remaining() > 0 { // New tag, check whether we have to stop wrapping here if p.Peek(TokenSymbol, "{%") != nil { - tag_ident := p.PeekTypeN(1, TokenIdentifier) + tagIdent := p.PeekTypeN(1, TokenIdentifier) - if tag_ident != nil { + if tagIdent != nil { // We've found a (!) end-tag found := false for _, n := range names { - if tag_ident.Val == n { + if tagIdent.Val == n { found = true break } @@ -238,16 +239,15 @@ func (p *Parser) WrapUntilTag(names ...string) (*NodeWrapper, *Parser, *Error) { for { if p.Match(TokenSymbol, "%}") != nil { // Okay, end the wrapping here - wrapper.Endtag = tag_ident.Val + wrapper.Endtag = tagIdent.Val return wrapper, newParser(p.template.name, tagArgs, p.template), nil - } else { - t := p.Current() - p.Consume() - if t == nil { - return nil, nil, p.Error("Unexpected EOF.", p.last_token) - } - tagArgs = append(tagArgs, t) } + t := p.Current() + p.Consume() + if t == nil { + return nil, nil, p.Error("Unexpected EOF.", p.lastToken) + } + tagArgs = append(tagArgs, t) } } } @@ -263,5 +263,47 @@ func (p *Parser) WrapUntilTag(names ...string) (*NodeWrapper, *Parser, *Error) { } return nil, nil, p.Error(fmt.Sprintf("Unexpected EOF, expected tag %s.", strings.Join(names, " or ")), - p.last_token) + p.lastToken) +} + +// Skips all nodes between starting tag and "{% endtag %}" +func (p *Parser) SkipUntilTag(names ...string) *Error { + for p.Remaining() > 0 { + // New tag, check whether we have to stop wrapping here + if p.Peek(TokenSymbol, "{%") != nil { + tagIdent := p.PeekTypeN(1, TokenIdentifier) + + if tagIdent != nil { + // We've found a (!) end-tag + + found := false + for _, n := range names { + if tagIdent.Val == n { + found = true + break + } + } + + // We only process the tag if we've found an end tag + if found { + // Okay, endtag found. + p.ConsumeN(2) // '{%' tagname + + for { + if p.Match(TokenSymbol, "%}") != nil { + // Done skipping, exit. + return nil + } + } + } + } + } + t := p.Current() + p.Consume() + if t == nil { + return p.Error("Unexpected EOF.", p.lastToken) + } + } + + return p.Error(fmt.Sprintf("Unexpected EOF, expected tag %s.", strings.Join(names, " or ")), p.lastToken) } diff --git a/vendor/github.com/flosch/pongo2/parser_expression.go b/vendor/github.com/flosch/pongo2/parser_expression.go index c1002de..988468e 100644 --- a/vendor/github.com/flosch/pongo2/parser_expression.go +++ b/vendor/github.com/flosch/pongo2/parser_expression.go @@ -1,38 +1,37 @@ package pongo2 import ( - "bytes" "fmt" "math" ) type Expression struct { // TODO: Add location token? - expr1 IEvaluator - expr2 IEvaluator - op_token *Token + expr1 IEvaluator + expr2 IEvaluator + opToken *Token } type relationalExpression struct { // TODO: Add location token? - expr1 IEvaluator - expr2 IEvaluator - op_token *Token + expr1 IEvaluator + expr2 IEvaluator + opToken *Token } type simpleExpression struct { - negate bool - negative_sign bool - term1 IEvaluator - term2 IEvaluator - op_token *Token + negate bool + negativeSign bool + term1 IEvaluator + term2 IEvaluator + opToken *Token } type term struct { // TODO: Add location token? - factor1 IEvaluator - factor2 IEvaluator - op_token *Token + factor1 IEvaluator + factor2 IEvaluator + opToken *Token } type power struct { @@ -56,14 +55,14 @@ func (expr *simpleExpression) FilterApplied(name string) bool { (expr.term2 != nil && expr.term2.FilterApplied(name))) } -func (t *term) FilterApplied(name string) bool { - return t.factor1.FilterApplied(name) && (t.factor2 == nil || - (t.factor2 != nil && t.factor2.FilterApplied(name))) +func (expr *term) FilterApplied(name string) bool { + return expr.factor1.FilterApplied(name) && (expr.factor2 == nil || + (expr.factor2 != nil && expr.factor2.FilterApplied(name))) } -func (p *power) FilterApplied(name string) bool { - return p.power1.FilterApplied(name) && (p.power2 == nil || - (p.power2 != nil && p.power2.FilterApplied(name))) +func (expr *power) FilterApplied(name string) bool { + return expr.power1.FilterApplied(name) && (expr.power2 == nil || + (expr.power2 != nil && expr.power2.FilterApplied(name))) } func (expr *Expression) GetPositionToken() *Token { @@ -86,48 +85,48 @@ func (expr *power) GetPositionToken() *Token { return expr.power1.GetPositionToken() } -func (expr *Expression) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { +func (expr *Expression) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { value, err := expr.Evaluate(ctx) if err != nil { return err } - buffer.WriteString(value.String()) + writer.WriteString(value.String()) return nil } -func (expr *relationalExpression) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { +func (expr *relationalExpression) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { value, err := expr.Evaluate(ctx) if err != nil { return err } - buffer.WriteString(value.String()) + writer.WriteString(value.String()) return nil } -func (expr *simpleExpression) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { +func (expr *simpleExpression) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { value, err := expr.Evaluate(ctx) if err != nil { return err } - buffer.WriteString(value.String()) + writer.WriteString(value.String()) return nil } -func (expr *term) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { +func (expr *term) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { value, err := expr.Evaluate(ctx) if err != nil { return err } - buffer.WriteString(value.String()) + writer.WriteString(value.String()) return nil } -func (expr *power) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { +func (expr *power) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { value, err := expr.Evaluate(ctx) if err != nil { return err } - buffer.WriteString(value.String()) + writer.WriteString(value.String()) return nil } @@ -141,13 +140,13 @@ func (expr *Expression) Evaluate(ctx *ExecutionContext) (*Value, *Error) { if err != nil { return nil, err } - switch expr.op_token.Val { + switch expr.opToken.Val { case "and", "&&": return AsValue(v1.IsTrue() && v2.IsTrue()), nil case "or", "||": return AsValue(v1.IsTrue() || v2.IsTrue()), nil default: - panic(fmt.Sprintf("unimplemented: %s", expr.op_token.Val)) + return nil, ctx.Error(fmt.Sprintf("unimplemented: %s", expr.opToken.Val), expr.opToken) } } else { return v1, nil @@ -164,39 +163,35 @@ func (expr *relationalExpression) Evaluate(ctx *ExecutionContext) (*Value, *Erro if err != nil { return nil, err } - switch expr.op_token.Val { + switch expr.opToken.Val { case "<=": if v1.IsFloat() || v2.IsFloat() { return AsValue(v1.Float() <= v2.Float()), nil - } else { - return AsValue(v1.Integer() <= v2.Integer()), nil } + return AsValue(v1.Integer() <= v2.Integer()), nil case ">=": if v1.IsFloat() || v2.IsFloat() { return AsValue(v1.Float() >= v2.Float()), nil - } else { - return AsValue(v1.Integer() >= v2.Integer()), nil } + return AsValue(v1.Integer() >= v2.Integer()), nil case "==": return AsValue(v1.EqualValueTo(v2)), nil case ">": if v1.IsFloat() || v2.IsFloat() { return AsValue(v1.Float() > v2.Float()), nil - } else { - return AsValue(v1.Integer() > v2.Integer()), nil } + return AsValue(v1.Integer() > v2.Integer()), nil case "<": if v1.IsFloat() || v2.IsFloat() { return AsValue(v1.Float() < v2.Float()), nil - } else { - return AsValue(v1.Integer() < v2.Integer()), nil } + return AsValue(v1.Integer() < v2.Integer()), nil case "!=", "<>": return AsValue(!v1.EqualValueTo(v2)), nil case "in": return AsValue(v2.Contains(v1)), nil default: - panic(fmt.Sprintf("unimplemented: %s", expr.op_token.Val)) + return nil, ctx.Error(fmt.Sprintf("unimplemented: %s", expr.opToken.Val), expr.opToken) } } else { return v1, nil @@ -214,7 +209,7 @@ func (expr *simpleExpression) Evaluate(ctx *ExecutionContext) (*Value, *Error) { result = result.Negate() } - if expr.negative_sign { + if expr.negativeSign { if result.IsNumber() { switch { case result.IsFloat(): @@ -222,7 +217,7 @@ func (expr *simpleExpression) Evaluate(ctx *ExecutionContext) (*Value, *Error) { case result.IsInteger(): result = AsValue(-1 * result.Integer()) default: - panic("not possible") + return nil, ctx.Error("Operation between a number and a non-(float/integer) is not possible", nil) } } else { return nil, ctx.Error("Negative sign on a non-number expression", expr.GetPositionToken()) @@ -234,42 +229,40 @@ func (expr *simpleExpression) Evaluate(ctx *ExecutionContext) (*Value, *Error) { if err != nil { return nil, err } - switch expr.op_token.Val { + switch expr.opToken.Val { case "+": if result.IsFloat() || t2.IsFloat() { // Result will be a float return AsValue(result.Float() + t2.Float()), nil - } else { - // Result will be an integer - return AsValue(result.Integer() + t2.Integer()), nil } + // Result will be an integer + return AsValue(result.Integer() + t2.Integer()), nil case "-": if result.IsFloat() || t2.IsFloat() { // Result will be a float return AsValue(result.Float() - t2.Float()), nil - } else { - // Result will be an integer - return AsValue(result.Integer() - t2.Integer()), nil } + // Result will be an integer + return AsValue(result.Integer() - t2.Integer()), nil default: - panic("unimplemented") + return nil, ctx.Error("Unimplemented", expr.GetPositionToken()) } } return result, nil } -func (t *term) Evaluate(ctx *ExecutionContext) (*Value, *Error) { - f1, err := t.factor1.Evaluate(ctx) +func (expr *term) Evaluate(ctx *ExecutionContext) (*Value, *Error) { + f1, err := expr.factor1.Evaluate(ctx) if err != nil { return nil, err } - if t.factor2 != nil { - f2, err := t.factor2.Evaluate(ctx) + if expr.factor2 != nil { + f2, err := expr.factor2.Evaluate(ctx) if err != nil { return nil, err } - switch t.op_token.Val { + switch expr.opToken.Val { case "*": if f1.IsFloat() || f2.IsFloat() { // Result will be float @@ -288,27 +281,26 @@ func (t *term) Evaluate(ctx *ExecutionContext) (*Value, *Error) { // Result will be int return AsValue(f1.Integer() % f2.Integer()), nil default: - panic("unimplemented") + return nil, ctx.Error("unimplemented", expr.opToken) } } else { return f1, nil } } -func (pw *power) Evaluate(ctx *ExecutionContext) (*Value, *Error) { - p1, err := pw.power1.Evaluate(ctx) +func (expr *power) Evaluate(ctx *ExecutionContext) (*Value, *Error) { + p1, err := expr.power1.Evaluate(ctx) if err != nil { return nil, err } - if pw.power2 != nil { - p2, err := pw.power2.Evaluate(ctx) + if expr.power2 != nil { + p2, err := expr.power2.Evaluate(ctx) if err != nil { return nil, err } return AsValue(math.Pow(p1.Float(), p2.Float())), nil - } else { - return p1, nil } + return p1, nil } func (p *Parser) parseFactor() (IEvaluator, *Error) { @@ -352,19 +344,19 @@ func (p *Parser) parsePower() (IEvaluator, *Error) { } func (p *Parser) parseTerm() (IEvaluator, *Error) { - return_term := new(term) + returnTerm := new(term) factor1, err := p.parsePower() if err != nil { return nil, err } - return_term.factor1 = factor1 + returnTerm.factor1 = factor1 for p.PeekOne(TokenSymbol, "*", "/", "%") != nil { - if return_term.op_token != nil { + if returnTerm.opToken != nil { // Create new sub-term - return_term = &term{ - factor1: return_term, + returnTerm = &term{ + factor1: returnTerm, } } @@ -376,16 +368,16 @@ func (p *Parser) parseTerm() (IEvaluator, *Error) { return nil, err } - return_term.op_token = op - return_term.factor2 = factor2 + returnTerm.opToken = op + returnTerm.factor2 = factor2 } - if return_term.op_token == nil { + if returnTerm.opToken == nil { // Shortcut for faster evaluation - return return_term.factor1, nil + return returnTerm.factor1, nil } - return return_term, nil + return returnTerm, nil } func (p *Parser) parseSimpleExpression() (IEvaluator, *Error) { @@ -393,7 +385,7 @@ func (p *Parser) parseSimpleExpression() (IEvaluator, *Error) { if sign := p.MatchOne(TokenSymbol, "+", "-"); sign != nil { if sign.Val == "-" { - expr.negative_sign = true + expr.negativeSign = true } } @@ -408,7 +400,7 @@ func (p *Parser) parseSimpleExpression() (IEvaluator, *Error) { expr.term1 = term1 for p.PeekOne(TokenSymbol, "+", "-") != nil { - if expr.op_token != nil { + if expr.opToken != nil { // New sub expr expr = &simpleExpression{ term1: expr, @@ -424,10 +416,10 @@ func (p *Parser) parseSimpleExpression() (IEvaluator, *Error) { } expr.term2 = term2 - expr.op_token = op + expr.opToken = op } - if expr.negate == false && expr.negative_sign == false && expr.term2 == nil { + if expr.negate == false && expr.negativeSign == false && expr.term2 == nil { // Shortcut for faster evaluation return expr.term1, nil } @@ -450,14 +442,14 @@ func (p *Parser) parseRelationalExpression() (IEvaluator, *Error) { if err != nil { return nil, err } - expr.op_token = t + expr.opToken = t expr.expr2 = expr2 } else if t := p.MatchOne(TokenKeyword, "in"); t != nil { expr2, err := p.parseSimpleExpression() if err != nil { return nil, err } - expr.op_token = t + expr.opToken = t expr.expr2 = expr2 } @@ -487,7 +479,7 @@ func (p *Parser) ParseExpression() (IEvaluator, *Error) { return nil, err } exp.expr2 = expr2 - exp.op_token = op + exp.opToken = op } if exp.expr2 == nil { diff --git a/vendor/github.com/flosch/pongo2/pongo2.go b/vendor/github.com/flosch/pongo2/pongo2.go index e61faa4..eda3aa0 100644 --- a/vendor/github.com/flosch/pongo2/pongo2.go +++ b/vendor/github.com/flosch/pongo2/pongo2.go @@ -1,10 +1,10 @@ package pongo2 // Version string -const Version = "v3" +const Version = "dev" -// Helper function which panics, if a Template couldn't -// successfully parsed. This is how you would use it: +// Must panics, if a Template couldn't successfully parsed. This is how you +// would use it: // var baseTemplate = pongo2.Must(pongo2.FromFile("templates/base.html")) func Must(tpl *Template, err error) *Template { if err != nil { diff --git a/vendor/github.com/flosch/pongo2/pongo2_issues_test.go b/vendor/github.com/flosch/pongo2/pongo2_issues_test.go index 731a290..725ab41 100644 --- a/vendor/github.com/flosch/pongo2/pongo2_issues_test.go +++ b/vendor/github.com/flosch/pongo2/pongo2_issues_test.go @@ -1,20 +1,29 @@ -package pongo2 +package pongo2_test import ( "testing" - . "gopkg.in/check.v1" + "github.com/flosch/pongo2" ) -// Hook up gocheck into the "go test" runner. +func TestIssue151(t *testing.T) { + tpl, err := pongo2.FromString("{{ mydict.51232_3 }}{{ 12345_123}}{{ 995189baz }}") + if err != nil { + t.Fatal(err) + } -func TestIssues(t *testing.T) { TestingT(t) } + str, err := tpl.Execute(pongo2.Context{ + "mydict": map[string]string{ + "51232_3": "foo", + }, + "12345_123": "bar", + "995189baz": "baz", + }) + if err != nil { + t.Fatal(err) + } -type IssueTestSuite struct{} - -var _ = Suite(&IssueTestSuite{}) - -func (s *TestSuite) TestIssues(c *C) { - // Add a test for any issue - c.Check(42, Equals, 42) + if str != "foobarbaz" { + t.Fatalf("Expected output 'foobarbaz', but got '%s'.", str) + } } diff --git a/vendor/github.com/flosch/pongo2/pongo2_template_test.go b/vendor/github.com/flosch/pongo2/pongo2_template_test.go index b6dc8fa..4b7d8fa 100644 --- a/vendor/github.com/flosch/pongo2/pongo2_template_test.go +++ b/vendor/github.com/flosch/pongo2/pongo2_template_test.go @@ -1,4 +1,4 @@ -package pongo2 +package pongo2_test import ( "bytes" @@ -9,9 +9,11 @@ import ( "strings" "testing" "time" + + "github.com/flosch/pongo2" ) -var admin_list = []string{"user2"} +var adminList = []string{"user2"} var time1 = time.Date(2014, 06, 10, 15, 30, 15, 0, time.UTC) var time2 = time.Date(2011, 03, 21, 8, 37, 56, 12, time.UTC) @@ -32,8 +34,8 @@ type comment struct { Text string } -func is_admin(u *user) bool { - for _, a := range admin_list { +func isAdmin(u *user) bool { + for _, a := range adminList { if a == u.Name { return true } @@ -41,12 +43,12 @@ func is_admin(u *user) bool { return false } -func (u *user) Is_admin() *Value { - return AsValue(is_admin(u)) +func (u *user) Is_admin() *pongo2.Value { + return pongo2.AsValue(isAdmin(u)) } func (u *user) Is_admin2() bool { - return is_admin(u) + return isAdmin(u) } func (p *post) String() string { @@ -60,74 +62,53 @@ func (p *post) String() string { type tagSandboxDemoTag struct { } -func (node *tagSandboxDemoTag) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { - buffer.WriteString("hello") +func (node *tagSandboxDemoTag) Execute(ctx *pongo2.ExecutionContext, writer pongo2.TemplateWriter) *pongo2.Error { + writer.WriteString("hello") return nil } -func tagSandboxDemoTagParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { +func tagSandboxDemoTagParser(doc *pongo2.Parser, start *pongo2.Token, arguments *pongo2.Parser) (pongo2.INodeTag, *pongo2.Error) { return &tagSandboxDemoTag{}, nil } -func BannedFilterFn(in *Value, params *Value) (*Value, *Error) { +func BannedFilterFn(in *pongo2.Value, params *pongo2.Value) (*pongo2.Value, *pongo2.Error) { return in, nil } func init() { - DefaultSet.Debug = true + pongo2.DefaultSet.Debug = true - RegisterFilter("banned_filter", BannedFilterFn) - RegisterFilter("unbanned_filter", BannedFilterFn) - RegisterTag("banned_tag", tagSandboxDemoTagParser) - RegisterTag("unbanned_tag", tagSandboxDemoTagParser) + pongo2.RegisterFilter("banned_filter", BannedFilterFn) + pongo2.RegisterFilter("unbanned_filter", BannedFilterFn) + pongo2.RegisterTag("banned_tag", tagSandboxDemoTagParser) + pongo2.RegisterTag("unbanned_tag", tagSandboxDemoTagParser) - DefaultSet.BanFilter("banned_filter") - DefaultSet.BanTag("banned_tag") - - // Allow different kind of levels inside template_tests/ - abs_path, err := filepath.Abs("./template_tests/*") - if err != nil { - panic(err) - } - DefaultSet.SandboxDirectories = append(DefaultSet.SandboxDirectories, abs_path) - - abs_path, err = filepath.Abs("./template_tests/*/*") - if err != nil { - panic(err) - } - DefaultSet.SandboxDirectories = append(DefaultSet.SandboxDirectories, abs_path) - - abs_path, err = filepath.Abs("./template_tests/*/*/*") - if err != nil { - panic(err) - } - DefaultSet.SandboxDirectories = append(DefaultSet.SandboxDirectories, abs_path) - - // Allow pongo2 temp files - DefaultSet.SandboxDirectories = append(DefaultSet.SandboxDirectories, "/tmp/pongo2_*") + pongo2.DefaultSet.BanFilter("banned_filter") + pongo2.DefaultSet.BanTag("banned_tag") f, err := ioutil.TempFile("/tmp/", "pongo2_") if err != nil { panic("cannot write to /tmp/") } f.Write([]byte("Hello from pongo2")) - DefaultSet.Globals["temp_file"] = f.Name() + pongo2.DefaultSet.Globals["temp_file"] = f.Name() } /* * End setup sandbox */ -var tplContext = Context{ +var tplContext = pongo2.Context{ "number": 11, "simple": map[string]interface{}{ - "number": 42, - "name": "john doe", - "included_file": "INCLUDES.helper", - "nil": nil, - "uint": uint(8), - "float": float64(3.1415), - "str": "string", + "number": 42, + "name": "john doe", + "included_file": "INCLUDES.helper", + "included_file_not_exists": "INCLUDES.helper.not_exists", + "nil": nil, + "uint": uint(8), + "float": float64(3.1415), + "str": "string", "chinese_hello_world": "你好世界", "bool_true": true, "bool_false": false, @@ -142,13 +123,23 @@ Yep!`, "escape_js_test": `escape sequences \r\n\'\" special chars "?!=$<>`, "one_item_list": []int{99}, "multiple_item_list": []int{1, 1, 2, 3, 5, 8, 13, 21, 34, 55}, + "unsorted_int_list": []int{192, 581, 22, 1, 249, 9999, 1828591, 8271}, + "fixed_item_list": [...]int{1, 2, 3, 4}, "misc_list": []interface{}{"Hello", 99, 3.14, "good"}, "escape_text": "This is \\a Test. \"Yep\". 'Yep'.", "xss": "", "intmap": map[int]string{ 1: "one", - 2: "two", 5: "five", + 2: "two", + }, + "strmap": map[string]string{ + "abc": "def", + "bcd": "efg", + "zab": "cde", + "gh": "kqm", + "ukq": "qqa", + "aab": "aba", }, "func_add": func(a, b int) int { return a + b @@ -167,17 +158,17 @@ Yep!`, } return s }, - "func_variadic_sum_int2": func(args ...*Value) *Value { + "func_variadic_sum_int2": func(args ...*pongo2.Value) *pongo2.Value { // Create a sum s := 0 for _, i := range args { s += i.Integer() } - return AsValue(s) + return pongo2.AsValue(s) }, }, "complex": map[string]interface{}{ - "is_admin": is_admin, + "is_admin": isAdmin, "post": post{ Text: "

Hello!

Welcome to my new blog page. I'm using pongo2 which supports {{ variables }} and {% tags %}.

", Created: time2, @@ -238,10 +229,8 @@ Yep!`, } func TestTemplates(t *testing.T) { - debug = true - // Add a global to the default set - Globals["this_is_a_global_variable"] = "this is a global text" + pongo2.Globals["this_is_a_global_variable"] = "this is a global text" matches, err := filepath.Glob("./template_tests/*.tpl") if err != nil { @@ -249,34 +238,34 @@ func TestTemplates(t *testing.T) { } for idx, match := range matches { t.Logf("[Template %3d] Testing '%s'", idx+1, match) - tpl, err := FromFile(match) + tpl, err := pongo2.FromFile(match) if err != nil { t.Fatalf("Error on FromFile('%s'): %s", match, err.Error()) } - test_filename := fmt.Sprintf("%s.out", match) - test_out, rerr := ioutil.ReadFile(test_filename) + testFilename := fmt.Sprintf("%s.out", match) + testOut, rerr := ioutil.ReadFile(testFilename) if rerr != nil { - t.Fatalf("Error on ReadFile('%s'): %s", test_filename, rerr.Error()) + t.Fatalf("Error on ReadFile('%s'): %s", testFilename, rerr.Error()) } - tpl_out, err := tpl.ExecuteBytes(tplContext) + tplOut, err := tpl.ExecuteBytes(tplContext) if err != nil { t.Fatalf("Error on Execute('%s'): %s", match, err.Error()) } - if bytes.Compare(test_out, tpl_out) != 0 { - t.Logf("Template (rendered) '%s': '%s'", match, tpl_out) - err_filename := filepath.Base(fmt.Sprintf("%s.error", match)) - err := ioutil.WriteFile(err_filename, []byte(tpl_out), 0600) + if bytes.Compare(testOut, tplOut) != 0 { + t.Logf("Template (rendered) '%s': '%s'", match, tplOut) + errFilename := filepath.Base(fmt.Sprintf("%s.error", match)) + err := ioutil.WriteFile(errFilename, []byte(tplOut), 0600) if err != nil { t.Fatalf(err.Error()) } - t.Logf("get a complete diff with command: 'diff -ya %s %s'", test_filename, err_filename) + t.Logf("get a complete diff with command: 'diff -ya %s %s'", testFilename, errFilename) t.Errorf("Failed: test_out != tpl_out for %s", match) } } } func TestExecutionErrors(t *testing.T) { - debug = true + //debug = true matches, err := filepath.Glob("./template_tests/*-execution.err") if err != nil { @@ -285,15 +274,15 @@ func TestExecutionErrors(t *testing.T) { for idx, match := range matches { t.Logf("[Errors %3d] Testing '%s'", idx+1, match) - test_data, err := ioutil.ReadFile(match) - tests := strings.Split(string(test_data), "\n") + testData, err := ioutil.ReadFile(match) + tests := strings.Split(string(testData), "\n") - check_filename := fmt.Sprintf("%s.out", match) - check_data, err := ioutil.ReadFile(check_filename) + checkFilename := fmt.Sprintf("%s.out", match) + checkData, err := ioutil.ReadFile(checkFilename) if err != nil { - t.Fatalf("Error on ReadFile('%s'): %s", check_filename, err.Error()) + t.Fatalf("Error on ReadFile('%s'): %s", checkFilename, err.Error()) } - checks := strings.Split(string(check_data), "\n") + checks := strings.Split(string(checkData), "\n") if len(checks) != len(tests) { t.Fatal("Template lines != Checks lines") @@ -308,11 +297,16 @@ func TestExecutionErrors(t *testing.T) { match, idx+1) } - tpl, err := FromString(test) + tpl, err := pongo2.FromString(test) if err != nil { t.Fatalf("Error on FromString('%s'): %s", test, err.Error()) } + tpl, err = pongo2.FromBytes([]byte(test)) + if err != nil { + t.Fatalf("Error on FromBytes('%s'): %s", test, err.Error()) + } + _, err = tpl.ExecuteBytes(tplContext) if err == nil { t.Fatalf("[%s Line %d] Expected error for (got none): %s", @@ -329,7 +323,7 @@ func TestExecutionErrors(t *testing.T) { } func TestCompilationErrors(t *testing.T) { - debug = true + //debug = true matches, err := filepath.Glob("./template_tests/*-compilation.err") if err != nil { @@ -338,15 +332,15 @@ func TestCompilationErrors(t *testing.T) { for idx, match := range matches { t.Logf("[Errors %3d] Testing '%s'", idx+1, match) - test_data, err := ioutil.ReadFile(match) - tests := strings.Split(string(test_data), "\n") + testData, err := ioutil.ReadFile(match) + tests := strings.Split(string(testData), "\n") - check_filename := fmt.Sprintf("%s.out", match) - check_data, err := ioutil.ReadFile(check_filename) + checkFilename := fmt.Sprintf("%s.out", match) + checkData, err := ioutil.ReadFile(checkFilename) if err != nil { - t.Fatalf("Error on ReadFile('%s'): %s", check_filename, err.Error()) + t.Fatalf("Error on ReadFile('%s'): %s", checkFilename, err.Error()) } - checks := strings.Split(string(check_data), "\n") + checks := strings.Split(string(checkData), "\n") if len(checks) != len(tests) { t.Fatal("Template lines != Checks lines") @@ -361,7 +355,7 @@ func TestCompilationErrors(t *testing.T) { match, idx+1) } - _, err = FromString(test) + _, err = pongo2.FromString(test) if err == nil { t.Fatalf("[%s | Line %d] Expected error for (got none): %s", match, idx+1, tests[idx]) } @@ -377,9 +371,10 @@ func TestCompilationErrors(t *testing.T) { func TestBaseDirectory(t *testing.T) { mustStr := "Hello from template_tests/base_dir_test/" - s := NewSet("test set with base directory") + fs := pongo2.MustNewLocalFileSystemLoader("") + s := pongo2.NewSet("test set with base directory", fs) s.Globals["base_directory"] = "template_tests/base_dir_test/" - if err := s.SetBaseDirectory(s.Globals["base_directory"].(string)); err != nil { + if err := fs.SetBaseDir(s.Globals["base_directory"].(string)); err != nil { t.Fatal(err) } @@ -405,13 +400,13 @@ func TestBaseDirectory(t *testing.T) { } func BenchmarkCache(b *testing.B) { - cache_set := NewSet("cache set") + cacheSet := pongo2.NewSet("cache set", pongo2.MustNewLocalFileSystemLoader("")) for i := 0; i < b.N; i++ { - tpl, err := cache_set.FromCache("template_tests/complex.tpl") + tpl, err := cacheSet.FromCache("template_tests/complex.tpl") if err != nil { b.Fatal(err) } - _, err = tpl.ExecuteBytes(tplContext) + err = tpl.ExecuteWriterUnbuffered(tplContext, ioutil.Discard) if err != nil { b.Fatal(err) } @@ -419,14 +414,14 @@ func BenchmarkCache(b *testing.B) { } func BenchmarkCacheDebugOn(b *testing.B) { - cache_debug_set := NewSet("cache set") - cache_debug_set.Debug = true + cacheDebugSet := pongo2.NewSet("cache set", pongo2.MustNewLocalFileSystemLoader("")) + cacheDebugSet.Debug = true for i := 0; i < b.N; i++ { - tpl, err := cache_debug_set.FromFile("template_tests/complex.tpl") + tpl, err := cacheDebugSet.FromFile("template_tests/complex.tpl") if err != nil { b.Fatal(err) } - _, err = tpl.ExecuteBytes(tplContext) + err = tpl.ExecuteWriterUnbuffered(tplContext, ioutil.Discard) if err != nil { b.Fatal(err) } @@ -434,13 +429,13 @@ func BenchmarkCacheDebugOn(b *testing.B) { } func BenchmarkExecuteComplexWithSandboxActive(b *testing.B) { - tpl, err := FromFile("template_tests/complex.tpl") + tpl, err := pongo2.FromFile("template_tests/complex.tpl") if err != nil { b.Fatal(err) } b.ResetTimer() for i := 0; i < b.N; i++ { - _, err = tpl.ExecuteBytes(tplContext) + err = tpl.ExecuteWriterUnbuffered(tplContext, ioutil.Discard) if err != nil { b.Fatal(err) } @@ -455,12 +450,12 @@ func BenchmarkCompileAndExecuteComplexWithSandboxActive(b *testing.B) { preloadedTpl := string(buf) b.ResetTimer() for i := 0; i < b.N; i++ { - tpl, err := FromString(preloadedTpl) + tpl, err := pongo2.FromString(preloadedTpl) if err != nil { b.Fatal(err) } - _, err = tpl.ExecuteBytes(tplContext) + err = tpl.ExecuteWriterUnbuffered(tplContext, ioutil.Discard) if err != nil { b.Fatal(err) } @@ -468,14 +463,14 @@ func BenchmarkCompileAndExecuteComplexWithSandboxActive(b *testing.B) { } func BenchmarkParallelExecuteComplexWithSandboxActive(b *testing.B) { - tpl, err := FromFile("template_tests/complex.tpl") + tpl, err := pongo2.FromFile("template_tests/complex.tpl") if err != nil { b.Fatal(err) } b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - _, err := tpl.ExecuteBytes(tplContext) + err := tpl.ExecuteWriterUnbuffered(tplContext, ioutil.Discard) if err != nil { b.Fatal(err) } @@ -484,14 +479,14 @@ func BenchmarkParallelExecuteComplexWithSandboxActive(b *testing.B) { } func BenchmarkExecuteComplexWithoutSandbox(b *testing.B) { - s := NewSet("set without sandbox") + s := pongo2.NewSet("set without sandbox", pongo2.MustNewLocalFileSystemLoader("")) tpl, err := s.FromFile("template_tests/complex.tpl") if err != nil { b.Fatal(err) } b.ResetTimer() for i := 0; i < b.N; i++ { - _, err = tpl.ExecuteBytes(tplContext) + err = tpl.ExecuteWriterUnbuffered(tplContext, ioutil.Discard) if err != nil { b.Fatal(err) } @@ -505,7 +500,7 @@ func BenchmarkCompileAndExecuteComplexWithoutSandbox(b *testing.B) { } preloadedTpl := string(buf) - s := NewSet("set without sandbox") + s := pongo2.NewSet("set without sandbox", pongo2.MustNewLocalFileSystemLoader("")) b.ResetTimer() for i := 0; i < b.N; i++ { @@ -514,7 +509,7 @@ func BenchmarkCompileAndExecuteComplexWithoutSandbox(b *testing.B) { b.Fatal(err) } - _, err = tpl.ExecuteBytes(tplContext) + err = tpl.ExecuteWriterUnbuffered(tplContext, ioutil.Discard) if err != nil { b.Fatal(err) } @@ -522,7 +517,7 @@ func BenchmarkCompileAndExecuteComplexWithoutSandbox(b *testing.B) { } func BenchmarkParallelExecuteComplexWithoutSandbox(b *testing.B) { - s := NewSet("set without sandbox") + s := pongo2.NewSet("set without sandbox", pongo2.MustNewLocalFileSystemLoader("")) tpl, err := s.FromFile("template_tests/complex.tpl") if err != nil { b.Fatal(err) @@ -530,7 +525,7 @@ func BenchmarkParallelExecuteComplexWithoutSandbox(b *testing.B) { b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - _, err := tpl.ExecuteBytes(tplContext) + err := tpl.ExecuteWriterUnbuffered(tplContext, ioutil.Discard) if err != nil { b.Fatal(err) } diff --git a/vendor/github.com/flosch/pongo2/pongo2_test.go b/vendor/github.com/flosch/pongo2/pongo2_test.go index 5f54584..3a4f6b7 100644 --- a/vendor/github.com/flosch/pongo2/pongo2_test.go +++ b/vendor/github.com/flosch/pongo2/pongo2_test.go @@ -1,26 +1,26 @@ -package pongo2 +package pongo2_test import ( "testing" + "github.com/flosch/pongo2" . "gopkg.in/check.v1" ) // Hook up gocheck into the "go test" runner. - func Test(t *testing.T) { TestingT(t) } type TestSuite struct { - tpl *Template + tpl *pongo2.Template } var ( - _ = Suite(&TestSuite{}) - test_suite2 = NewSet("test suite 2") + _ = Suite(&TestSuite{}) + testSuite2 = pongo2.NewSet("test suite 2", pongo2.MustNewLocalFileSystemLoader("")) ) -func parseTemplate(s string, c Context) string { - t, err := test_suite2.FromString(s) +func parseTemplate(s string, c pongo2.Context) string { + t, err := testSuite2.FromString(s) if err != nil { panic(err) } @@ -31,7 +31,7 @@ func parseTemplate(s string, c Context) string { return out } -func parseTemplateFn(s string, c Context) func() { +func parseTemplateFn(s string, c pongo2.Context) func() { return func() { parseTemplate(s, c) } @@ -40,27 +40,64 @@ func parseTemplateFn(s string, c Context) func() { func (s *TestSuite) TestMisc(c *C) { // Must // TODO: Add better error message (see issue #18) - c.Check(func() { Must(test_suite2.FromFile("template_tests/inheritance/base2.tpl")) }, + c.Check( + func() { pongo2.Must(testSuite2.FromFile("template_tests/inheritance/base2.tpl")) }, PanicMatches, - `\[Error \(where: fromfile\) in template_tests/inheritance/doesnotexist.tpl | Line 1 Col 12 near 'doesnotexist.tpl'\] open template_tests/inheritance/doesnotexist.tpl: no such file or directory`) + `\[Error \(where: fromfile\) in .*template_tests/inheritance/doesnotexist.tpl | Line 1 Col 12 near 'doesnotexist.tpl'\] open .*template_tests/inheritance/doesnotexist.tpl: no such file or directory`, + ) // Context - c.Check(parseTemplateFn("", Context{"'illegal": nil}), PanicMatches, ".*not a valid identifier.*") + c.Check(parseTemplateFn("", pongo2.Context{"'illegal": nil}), PanicMatches, ".*not a valid identifier.*") // Registers - c.Check(func() { RegisterFilter("escape", nil) }, PanicMatches, ".*is already registered.*") - c.Check(func() { RegisterTag("for", nil) }, PanicMatches, ".*is already registered.*") + c.Check(pongo2.RegisterFilter("escape", nil).Error(), Matches, ".*is already registered") + c.Check(pongo2.RegisterTag("for", nil).Error(), Matches, ".*is already registered") // ApplyFilter - v, err := ApplyFilter("title", AsValue("this is a title"), nil) + v, err := pongo2.ApplyFilter("title", pongo2.AsValue("this is a title"), nil) if err != nil { c.Fatal(err) } c.Check(v.String(), Equals, "This Is A Title") c.Check(func() { - _, err := ApplyFilter("doesnotexist", nil, nil) + _, err := pongo2.ApplyFilter("doesnotexist", nil, nil) if err != nil { panic(err) } }, PanicMatches, `\[Error \(where: applyfilter\)\] Filter with name 'doesnotexist' not found.`) } + +func (s *TestSuite) TestImplicitExecCtx(c *C) { + tpl, err := pongo2.FromString("{{ ImplicitExec }}") + if err != nil { + c.Fatalf("Error in FromString: %v", err) + } + + val := "a stringy thing" + + res, err := tpl.Execute(pongo2.Context{ + "Value": val, + "ImplicitExec": func(ctx *pongo2.ExecutionContext) string { + return ctx.Public["Value"].(string) + }, + }) + + if err != nil { + c.Fatalf("Error executing template: %v", err) + } + + c.Check(res, Equals, val) + + // The implicit ctx should not be persisted from call-to-call + res, err = tpl.Execute(pongo2.Context{ + "ImplicitExec": func() string { + return val + }, + }) + + if err != nil { + c.Fatalf("Error executing template: %v", err) + } + + c.Check(res, Equals, val) +} diff --git a/vendor/github.com/flosch/pongo2/tags.go b/vendor/github.com/flosch/pongo2/tags.go index 292c30d..3668b06 100644 --- a/vendor/github.com/flosch/pongo2/tags.go +++ b/vendor/github.com/flosch/pongo2/tags.go @@ -21,6 +21,8 @@ package pongo2 import ( "fmt" + + "github.com/juju/errors" ) type INodeTag interface { @@ -53,80 +55,81 @@ func init() { tags = make(map[string]*tag) } -// Registers a new tag. If there's already a tag with the same -// name, RegisterTag will panic. You usually want to call this +// Registers a new tag. You usually want to call this // function in the tag's init() function: // http://golang.org/doc/effective_go.html#init // // See http://www.florian-schlachter.de/post/pongo2/ for more about // writing filters and tags. -func RegisterTag(name string, parserFn TagParser) { +func RegisterTag(name string, parserFn TagParser) error { _, existing := tags[name] if existing { - panic(fmt.Sprintf("Tag with name '%s' is already registered.", name)) + return errors.Errorf("tag with name '%s' is already registered", name) } tags[name] = &tag{ name: name, parser: parserFn, } + return nil } // Replaces an already registered tag with a new implementation. Use this // function with caution since it allows you to change existing tag behaviour. -func ReplaceTag(name string, parserFn TagParser) { +func ReplaceTag(name string, parserFn TagParser) error { _, existing := tags[name] if !existing { - panic(fmt.Sprintf("Tag with name '%s' does not exist (therefore cannot be overridden).", name)) + return errors.Errorf("tag with name '%s' does not exist (therefore cannot be overridden)", name) } tags[name] = &tag{ name: name, parser: parserFn, } + return nil } // Tag = "{%" IDENT ARGS "%}" func (p *Parser) parseTagElement() (INodeTag, *Error) { p.Consume() // consume "{%" - token_name := p.MatchType(TokenIdentifier) + tokenName := p.MatchType(TokenIdentifier) // Check for identifier - if token_name == nil { + if tokenName == nil { return nil, p.Error("Tag name must be an identifier.", nil) } // Check for the existing tag - tag, exists := tags[token_name.Val] + tag, exists := tags[tokenName.Val] if !exists { // Does not exists - return nil, p.Error(fmt.Sprintf("Tag '%s' not found (or beginning tag not provided)", token_name.Val), token_name) + return nil, p.Error(fmt.Sprintf("Tag '%s' not found (or beginning tag not provided)", tokenName.Val), tokenName) } // Check sandbox tag restriction - if _, is_banned := p.template.set.bannedTags[token_name.Val]; is_banned { - return nil, p.Error(fmt.Sprintf("Usage of tag '%s' is not allowed (sandbox restriction active).", token_name.Val), token_name) + if _, isBanned := p.template.set.bannedTags[tokenName.Val]; isBanned { + return nil, p.Error(fmt.Sprintf("Usage of tag '%s' is not allowed (sandbox restriction active).", tokenName.Val), tokenName) } - args_token := make([]*Token, 0) + var argsToken []*Token for p.Peek(TokenSymbol, "%}") == nil && p.Remaining() > 0 { // Add token to args - args_token = append(args_token, p.Current()) + argsToken = append(argsToken, p.Current()) p.Consume() // next token } // EOF? if p.Remaining() == 0 { - return nil, p.Error("Unexpectedly reached EOF, no tag end found.", p.last_token) + return nil, p.Error("Unexpectedly reached EOF, no tag end found.", p.lastToken) } p.Match(TokenSymbol, "%}") - arg_parser := newParser(p.name, args_token, p.template) - if len(args_token) == 0 { + argParser := newParser(p.name, argsToken, p.template) + if len(argsToken) == 0 { // This is done to have nice EOF error messages - arg_parser.last_token = token_name + argParser.lastToken = tokenName } p.template.level++ defer func() { p.template.level-- }() - return tag.parser(p, token_name, arg_parser) + return tag.parser(p, tokenName, argParser) } diff --git a/vendor/github.com/flosch/pongo2/tags_autoescape.go b/vendor/github.com/flosch/pongo2/tags_autoescape.go index ec30438..590a1db 100644 --- a/vendor/github.com/flosch/pongo2/tags_autoescape.go +++ b/vendor/github.com/flosch/pongo2/tags_autoescape.go @@ -1,19 +1,15 @@ package pongo2 -import ( - "bytes" -) - type tagAutoescapeNode struct { wrapper *NodeWrapper autoescape bool } -func (node *tagAutoescapeNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { +func (node *tagAutoescapeNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { old := ctx.Autoescape ctx.Autoescape = node.autoescape - err := node.wrapper.Execute(ctx, buffer) + err := node.wrapper.Execute(ctx, writer) if err != nil { return err } @@ -24,22 +20,22 @@ func (node *tagAutoescapeNode) Execute(ctx *ExecutionContext, buffer *bytes.Buff } func tagAutoescapeParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { - autoescape_node := &tagAutoescapeNode{} + autoescapeNode := &tagAutoescapeNode{} wrapper, _, err := doc.WrapUntilTag("endautoescape") if err != nil { return nil, err } - autoescape_node.wrapper = wrapper + autoescapeNode.wrapper = wrapper - mode_token := arguments.MatchType(TokenIdentifier) - if mode_token == nil { + modeToken := arguments.MatchType(TokenIdentifier) + if modeToken == nil { return nil, arguments.Error("A mode is required for autoescape-tag.", nil) } - if mode_token.Val == "on" { - autoescape_node.autoescape = true - } else if mode_token.Val == "off" { - autoescape_node.autoescape = false + if modeToken.Val == "on" { + autoescapeNode.autoescape = true + } else if modeToken.Val == "off" { + autoescapeNode.autoescape = false } else { return nil, arguments.Error("Only 'on' or 'off' is valid as an autoescape-mode.", nil) } @@ -48,7 +44,7 @@ func tagAutoescapeParser(doc *Parser, start *Token, arguments *Parser) (INodeTag return nil, arguments.Error("Malformed autoescape-tag arguments.", nil) } - return autoescape_node, nil + return autoescapeNode, nil } func init() { diff --git a/vendor/github.com/flosch/pongo2/tags_block.go b/vendor/github.com/flosch/pongo2/tags_block.go index 30e205a..86145f3 100644 --- a/vendor/github.com/flosch/pongo2/tags_block.go +++ b/vendor/github.com/flosch/pongo2/tags_block.go @@ -9,47 +9,82 @@ type tagBlockNode struct { name string } -func (node *tagBlockNode) getBlockWrapperByName(tpl *Template) *NodeWrapper { +func (node *tagBlockNode) getBlockWrappers(tpl *Template) []*NodeWrapper { + nodeWrappers := make([]*NodeWrapper, 0) var t *NodeWrapper - if tpl.child != nil { - // First ask the child for the block - t = node.getBlockWrapperByName(tpl.child) - } - if t == nil { - // Child has no block, lets look up here at parent + + for tpl != nil { t = tpl.blocks[node.name] + if t != nil { + nodeWrappers = append(nodeWrappers, t) + } + tpl = tpl.child } - return t + + return nodeWrappers } -func (node *tagBlockNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { +func (node *tagBlockNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { tpl := ctx.template if tpl == nil { panic("internal error: tpl == nil") } + // Determine the block to execute - block_wrapper := node.getBlockWrapperByName(tpl) - if block_wrapper == nil { - // fmt.Printf("could not find: %s\n", node.name) - return ctx.Error("internal error: block_wrapper == nil in tagBlockNode.Execute()", nil) + blockWrappers := node.getBlockWrappers(tpl) + lenBlockWrappers := len(blockWrappers) + + if lenBlockWrappers == 0 { + return ctx.Error("internal error: len(block_wrappers) == 0 in tagBlockNode.Execute()", nil) } - err := block_wrapper.Execute(ctx, buffer) + + blockWrapper := blockWrappers[lenBlockWrappers-1] + ctx.Private["block"] = tagBlockInformation{ + ctx: ctx, + wrappers: blockWrappers[0 : lenBlockWrappers-1], + } + err := blockWrapper.Execute(ctx, writer) if err != nil { return err } - // TODO: Add support for {{ block.super }} - return nil } +type tagBlockInformation struct { + ctx *ExecutionContext + wrappers []*NodeWrapper +} + +func (t tagBlockInformation) Super() string { + lenWrappers := len(t.wrappers) + + if lenWrappers == 0 { + return "" + } + + superCtx := NewChildExecutionContext(t.ctx) + superCtx.Private["block"] = tagBlockInformation{ + ctx: t.ctx, + wrappers: t.wrappers[0 : lenWrappers-1], + } + + blockWrapper := t.wrappers[lenWrappers-1] + buf := bytes.NewBufferString("") + err := blockWrapper.Execute(superCtx, &templateWriter{buf}) + if err != nil { + return "" + } + return buf.String() +} + func tagBlockParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { if arguments.Count() == 0 { return nil, arguments.Error("Tag 'block' requires an identifier.", nil) } - name_token := arguments.MatchType(TokenIdentifier) - if name_token == nil { + nameToken := arguments.MatchType(TokenIdentifier) + if nameToken == nil { return nil, arguments.Error("First argument for tag 'block' must be an identifier.", nil) } @@ -62,15 +97,15 @@ func tagBlockParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Er return nil, err } if endtagargs.Remaining() > 0 { - endtagname_token := endtagargs.MatchType(TokenIdentifier) - if endtagname_token != nil { - if endtagname_token.Val != name_token.Val { + endtagnameToken := endtagargs.MatchType(TokenIdentifier) + if endtagnameToken != nil { + if endtagnameToken.Val != nameToken.Val { return nil, endtagargs.Error(fmt.Sprintf("Name for 'endblock' must equal to 'block'-tag's name ('%s' != '%s').", - name_token.Val, endtagname_token.Val), nil) + nameToken.Val, endtagnameToken.Val), nil) } } - if endtagname_token == nil || endtagargs.Remaining() > 0 { + if endtagnameToken == nil || endtagargs.Remaining() > 0 { return nil, endtagargs.Error("Either no or only one argument (identifier) allowed for 'endblock'.", nil) } } @@ -79,14 +114,14 @@ func tagBlockParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Er if tpl == nil { panic("internal error: tpl == nil") } - _, has_block := tpl.blocks[name_token.Val] - if !has_block { - tpl.blocks[name_token.Val] = wrapper + _, hasBlock := tpl.blocks[nameToken.Val] + if !hasBlock { + tpl.blocks[nameToken.Val] = wrapper } else { - return nil, arguments.Error(fmt.Sprintf("Block named '%s' already defined", name_token.Val), nil) + return nil, arguments.Error(fmt.Sprintf("Block named '%s' already defined", nameToken.Val), nil) } - return &tagBlockNode{name: name_token.Val}, nil + return &tagBlockNode{name: nameToken.Val}, nil } func init() { diff --git a/vendor/github.com/flosch/pongo2/tags_comment.go b/vendor/github.com/flosch/pongo2/tags_comment.go index 8c22496..56a02ed 100644 --- a/vendor/github.com/flosch/pongo2/tags_comment.go +++ b/vendor/github.com/flosch/pongo2/tags_comment.go @@ -1,20 +1,16 @@ package pongo2 -import ( - "bytes" -) - type tagCommentNode struct{} -func (node *tagCommentNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { +func (node *tagCommentNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { return nil } func tagCommentParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { - comment_node := &tagCommentNode{} + commentNode := &tagCommentNode{} // TODO: Process the endtag's arguments (see django 'comment'-tag documentation) - _, _, err := doc.WrapUntilTag("endcomment") + err := doc.SkipUntilTag("endcomment") if err != nil { return nil, err } @@ -23,7 +19,7 @@ func tagCommentParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, * return nil, arguments.Error("Tag 'comment' does not take any argument.", nil) } - return comment_node, nil + return commentNode, nil } func init() { diff --git a/vendor/github.com/flosch/pongo2/tags_cycle.go b/vendor/github.com/flosch/pongo2/tags_cycle.go index 6a6830e..ffbd254 100644 --- a/vendor/github.com/flosch/pongo2/tags_cycle.go +++ b/vendor/github.com/flosch/pongo2/tags_cycle.go @@ -1,9 +1,5 @@ package pongo2 -import ( - "bytes" -) - type tagCycleValue struct { node *tagCycleNode value *Value @@ -13,7 +9,7 @@ type tagCycleNode struct { position *Token args []IEvaluator idx int - as_name string + asName string silent bool } @@ -21,7 +17,7 @@ func (cv *tagCycleValue) String() string { return cv.value.String() } -func (node *tagCycleNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { +func (node *tagCycleNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { item := node.args[node.idx%len(node.args)] node.idx++ @@ -46,30 +42,30 @@ func (node *tagCycleNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) * t.value = val if !t.node.silent { - buffer.WriteString(val.String()) + writer.WriteString(val.String()) } } else { // Regular call - cycle_value := &tagCycleValue{ + cycleValue := &tagCycleValue{ node: node, value: val, } - if node.as_name != "" { - ctx.Private[node.as_name] = cycle_value + if node.asName != "" { + ctx.Private[node.asName] = cycleValue } if !node.silent { - buffer.WriteString(val.String()) + writer.WriteString(val.String()) } } return nil } -// HINT: We're not supporting the old comma-seperated list of expresions argument-style +// HINT: We're not supporting the old comma-separated list of expressions argument-style func tagCycleParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { - cycle_node := &tagCycleNode{ + cycleNode := &tagCycleNode{ position: start, } @@ -78,19 +74,19 @@ func tagCycleParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Er if err != nil { return nil, err } - cycle_node.args = append(cycle_node.args, node) + cycleNode.args = append(cycleNode.args, node) if arguments.MatchOne(TokenKeyword, "as") != nil { // as - name_token := arguments.MatchType(TokenIdentifier) - if name_token == nil { + nameToken := arguments.MatchType(TokenIdentifier) + if nameToken == nil { return nil, arguments.Error("Name (identifier) expected after 'as'.", nil) } - cycle_node.as_name = name_token.Val + cycleNode.asName = nameToken.Val if arguments.MatchOne(TokenIdentifier, "silent") != nil { - cycle_node.silent = true + cycleNode.silent = true } // Now we're finished @@ -102,7 +98,7 @@ func tagCycleParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Er return nil, arguments.Error("Malformed cycle-tag.", nil) } - return cycle_node, nil + return cycleNode, nil } func init() { diff --git a/vendor/github.com/flosch/pongo2/tags_extends.go b/vendor/github.com/flosch/pongo2/tags_extends.go index 6abbb6b..5771020 100644 --- a/vendor/github.com/flosch/pongo2/tags_extends.go +++ b/vendor/github.com/flosch/pongo2/tags_extends.go @@ -1,19 +1,15 @@ package pongo2 -import ( - "bytes" -) - type tagExtendsNode struct { filename string } -func (node *tagExtendsNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { +func (node *tagExtendsNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { return nil } func tagExtendsParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { - extends_node := &tagExtendsNode{} + extendsNode := &tagExtendsNode{} if doc.template.level > 1 { return nil, arguments.Error("The 'extends' tag can only defined on root level.", start) @@ -24,22 +20,22 @@ func tagExtendsParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, * return nil, arguments.Error("This template has already one parent.", start) } - if filename_token := arguments.MatchType(TokenString); filename_token != nil { + if filenameToken := arguments.MatchType(TokenString); filenameToken != nil { // prepared, static template // Get parent's filename - parent_filename := doc.template.set.resolveFilename(doc.template, filename_token.Val) + parentFilename := doc.template.set.resolveFilename(doc.template, filenameToken.Val) // Parse the parent - parent_template, err := doc.template.set.FromFile(parent_filename) + parentTemplate, err := doc.template.set.FromFile(parentFilename) if err != nil { return nil, err.(*Error) } // Keep track of things - parent_template.child = doc.template - doc.template.parent = parent_template - extends_node.filename = parent_filename + parentTemplate.child = doc.template + doc.template.parent = parentTemplate + extendsNode.filename = parentFilename } else { return nil, arguments.Error("Tag 'extends' requires a template filename as string.", nil) } @@ -48,7 +44,7 @@ func tagExtendsParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, * return nil, arguments.Error("Tag 'extends' does only take 1 argument.", nil) } - return extends_node, nil + return extendsNode, nil } func init() { diff --git a/vendor/github.com/flosch/pongo2/tags_filter.go b/vendor/github.com/flosch/pongo2/tags_filter.go index f421e5a..b38fd92 100644 --- a/vendor/github.com/flosch/pongo2/tags_filter.go +++ b/vendor/github.com/flosch/pongo2/tags_filter.go @@ -5,8 +5,8 @@ import ( ) type nodeFilterCall struct { - name string - param_expr IEvaluator + name string + paramExpr IEvaluator } type tagFilterNode struct { @@ -15,7 +15,7 @@ type tagFilterNode struct { filterChain []*nodeFilterCall } -func (node *tagFilterNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { +func (node *tagFilterNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { temp := bytes.NewBuffer(make([]byte, 0, 1024)) // 1 KiB size err := node.bodyWrapper.Execute(ctx, temp) @@ -27,8 +27,8 @@ func (node *tagFilterNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) for _, call := range node.filterChain { var param *Value - if call.param_expr != nil { - param, err = call.param_expr.Evaluate(ctx) + if call.paramExpr != nil { + param, err = call.paramExpr.Evaluate(ctx) if err != nil { return err } @@ -41,13 +41,13 @@ func (node *tagFilterNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) } } - buffer.WriteString(value.String()) + writer.WriteString(value.String()) return nil } func tagFilterParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { - filter_node := &tagFilterNode{ + filterNode := &tagFilterNode{ position: start, } @@ -55,16 +55,16 @@ func tagFilterParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *E if err != nil { return nil, err } - filter_node.bodyWrapper = wrapper + filterNode.bodyWrapper = wrapper for arguments.Remaining() > 0 { filterCall := &nodeFilterCall{} - name_token := arguments.MatchType(TokenIdentifier) - if name_token == nil { + nameToken := arguments.MatchType(TokenIdentifier) + if nameToken == nil { return nil, arguments.Error("Expected a filter name (identifier).", nil) } - filterCall.name = name_token.Val + filterCall.name = nameToken.Val if arguments.MatchOne(TokenSymbol, ":") != nil { // Filter parameter @@ -73,10 +73,10 @@ func tagFilterParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *E if err != nil { return nil, err } - filterCall.param_expr = expr + filterCall.paramExpr = expr } - filter_node.filterChain = append(filter_node.filterChain, filterCall) + filterNode.filterChain = append(filterNode.filterChain, filterCall) if arguments.MatchOne(TokenSymbol, "|") == nil { break @@ -87,7 +87,7 @@ func tagFilterParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *E return nil, arguments.Error("Malformed filter-tag arguments.", nil) } - return filter_node, nil + return filterNode, nil } func init() { diff --git a/vendor/github.com/flosch/pongo2/tags_firstof.go b/vendor/github.com/flosch/pongo2/tags_firstof.go index b677979..5b2888e 100644 --- a/vendor/github.com/flosch/pongo2/tags_firstof.go +++ b/vendor/github.com/flosch/pongo2/tags_firstof.go @@ -1,15 +1,11 @@ package pongo2 -import ( - "bytes" -) - type tagFirstofNode struct { position *Token args []IEvaluator } -func (node *tagFirstofNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { +func (node *tagFirstofNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { for _, arg := range node.args { val, err := arg.Evaluate(ctx) if err != nil { @@ -24,7 +20,7 @@ func (node *tagFirstofNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) } } - buffer.WriteString(val.String()) + writer.WriteString(val.String()) return nil } } @@ -33,7 +29,7 @@ func (node *tagFirstofNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) } func tagFirstofParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { - firstof_node := &tagFirstofNode{ + firstofNode := &tagFirstofNode{ position: start, } @@ -42,10 +38,10 @@ func tagFirstofParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, * if err != nil { return nil, err } - firstof_node.args = append(firstof_node.args, node) + firstofNode.args = append(firstofNode.args, node) } - return firstof_node, nil + return firstofNode, nil } func init() { diff --git a/vendor/github.com/flosch/pongo2/tags_for.go b/vendor/github.com/flosch/pongo2/tags_for.go index de56699..5b0b555 100644 --- a/vendor/github.com/flosch/pongo2/tags_for.go +++ b/vendor/github.com/flosch/pongo2/tags_for.go @@ -1,14 +1,11 @@ package pongo2 -import ( - "bytes" -) - type tagForNode struct { - key string - value string // only for maps: for key, value in map - object_evaluator IEvaluator - reversed bool + key string + value string // only for maps: for key, value in map + objectEvaluator IEvaluator + reversed bool + sorted bool bodyWrapper *NodeWrapper emptyWrapper *NodeWrapper @@ -24,7 +21,7 @@ type tagForLoopInformation struct { Parentloop *tagForLoopInformation } -func (node *tagForNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) (forError *Error) { +func (node *tagForNode) Execute(ctx *ExecutionContext, writer TemplateWriter) (forError *Error) { // Backup forloop (as parentloop in public context), key-name and value-name forCtx := NewChildExecutionContext(ctx) parentloop := forCtx.Private["forloop"] @@ -42,7 +39,7 @@ func (node *tagForNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) (fo // Register loopInfo in public context forCtx.Private["forloop"] = loopInfo - obj, err := node.object_evaluator.Evaluate(forCtx) + obj, err := node.objectEvaluator.Evaluate(forCtx) if err != nil { return err } @@ -67,7 +64,7 @@ func (node *tagForNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) (fo loopInfo.Revcounter0 = count - (idx + 1) // TODO: Not sure about this, have to look it up // Render elements with updated context - err := node.bodyWrapper.Execute(forCtx, buffer) + err := node.bodyWrapper.Execute(forCtx, writer) if err != nil { forError = err return false @@ -76,30 +73,30 @@ func (node *tagForNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) (fo }, func() { // Nothing to iterate over (maybe wrong type or no items) if node.emptyWrapper != nil { - err := node.emptyWrapper.Execute(forCtx, buffer) + err := node.emptyWrapper.Execute(forCtx, writer) if err != nil { forError = err } } - }, node.reversed) + }, node.reversed, node.sorted) - return nil + return forError } func tagForParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { - for_node := &tagForNode{} + forNode := &tagForNode{} // Arguments parsing - var value_token *Token - key_token := arguments.MatchType(TokenIdentifier) - if key_token == nil { + var valueToken *Token + keyToken := arguments.MatchType(TokenIdentifier) + if keyToken == nil { return nil, arguments.Error("Expected an key identifier as first argument for 'for'-tag", nil) } if arguments.Match(TokenSymbol, ",") != nil { // Value name is provided - value_token = arguments.MatchType(TokenIdentifier) - if value_token == nil { + valueToken = arguments.MatchType(TokenIdentifier) + if valueToken == nil { return nil, arguments.Error("Value name must be an identifier.", nil) } } @@ -108,18 +105,22 @@ func tagForParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Erro return nil, arguments.Error("Expected keyword 'in'.", nil) } - object_evaluator, err := arguments.ParseExpression() + objectEvaluator, err := arguments.ParseExpression() if err != nil { return nil, err } - for_node.object_evaluator = object_evaluator - for_node.key = key_token.Val - if value_token != nil { - for_node.value = value_token.Val + forNode.objectEvaluator = objectEvaluator + forNode.key = keyToken.Val + if valueToken != nil { + forNode.value = valueToken.Val } if arguments.MatchOne(TokenIdentifier, "reversed") != nil { - for_node.reversed = true + forNode.reversed = true + } + + if arguments.MatchOne(TokenIdentifier, "sorted") != nil { + forNode.sorted = true } if arguments.Remaining() > 0 { @@ -131,7 +132,7 @@ func tagForParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Erro if err != nil { return nil, err } - for_node.bodyWrapper = wrapper + forNode.bodyWrapper = wrapper if endargs.Count() > 0 { return nil, endargs.Error("Arguments not allowed here.", nil) @@ -143,14 +144,14 @@ func tagForParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Erro if err != nil { return nil, err } - for_node.emptyWrapper = wrapper + forNode.emptyWrapper = wrapper if endargs.Count() > 0 { return nil, endargs.Error("Arguments not allowed here.", nil) } } - return for_node, nil + return forNode, nil } func init() { diff --git a/vendor/github.com/flosch/pongo2/tags_if.go b/vendor/github.com/flosch/pongo2/tags_if.go index 2515c44..3eeaf3b 100644 --- a/vendor/github.com/flosch/pongo2/tags_if.go +++ b/vendor/github.com/flosch/pongo2/tags_if.go @@ -1,15 +1,11 @@ package pongo2 -import ( - "bytes" -) - type tagIfNode struct { conditions []IEvaluator wrappers []*NodeWrapper } -func (node *tagIfNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { +func (node *tagIfNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { for i, condition := range node.conditions { result, err := condition.Evaluate(ctx) if err != nil { @@ -17,26 +13,25 @@ func (node *tagIfNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Err } if result.IsTrue() { - return node.wrappers[i].Execute(ctx, buffer) - } else { - // Last condition? - if len(node.conditions) == i+1 && len(node.wrappers) > i+1 { - return node.wrappers[i+1].Execute(ctx, buffer) - } + return node.wrappers[i].Execute(ctx, writer) + } + // Last condition? + if len(node.conditions) == i+1 && len(node.wrappers) > i+1 { + return node.wrappers[i+1].Execute(ctx, writer) } } return nil } func tagIfParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { - if_node := &tagIfNode{} + ifNode := &tagIfNode{} // Parse first and main IF condition condition, err := arguments.ParseExpression() if err != nil { return nil, err } - if_node.conditions = append(if_node.conditions, condition) + ifNode.conditions = append(ifNode.conditions, condition) if arguments.Remaining() > 0 { return nil, arguments.Error("If-condition is malformed.", nil) @@ -44,27 +39,27 @@ func tagIfParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error // Check the rest for { - wrapper, tag_args, err := doc.WrapUntilTag("elif", "else", "endif") + wrapper, tagArgs, err := doc.WrapUntilTag("elif", "else", "endif") if err != nil { return nil, err } - if_node.wrappers = append(if_node.wrappers, wrapper) + ifNode.wrappers = append(ifNode.wrappers, wrapper) if wrapper.Endtag == "elif" { // elif can take a condition - condition, err := tag_args.ParseExpression() + condition, err = tagArgs.ParseExpression() if err != nil { return nil, err } - if_node.conditions = append(if_node.conditions, condition) + ifNode.conditions = append(ifNode.conditions, condition) - if tag_args.Remaining() > 0 { - return nil, tag_args.Error("Elif-condition is malformed.", nil) + if tagArgs.Remaining() > 0 { + return nil, tagArgs.Error("Elif-condition is malformed.", nil) } } else { - if tag_args.Count() > 0 { + if tagArgs.Count() > 0 { // else/endif can't take any conditions - return nil, tag_args.Error("Arguments not allowed here.", nil) + return nil, tagArgs.Error("Arguments not allowed here.", nil) } } @@ -73,7 +68,7 @@ func tagIfParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error } } - return if_node, nil + return ifNode, nil } func init() { diff --git a/vendor/github.com/flosch/pongo2/tags_ifchanged.go b/vendor/github.com/flosch/pongo2/tags_ifchanged.go index 4412ace..45296a0 100644 --- a/vendor/github.com/flosch/pongo2/tags_ifchanged.go +++ b/vendor/github.com/flosch/pongo2/tags_ifchanged.go @@ -5,16 +5,15 @@ import ( ) type tagIfchangedNode struct { - watched_expr []IEvaluator - last_values []*Value - last_content []byte - thenWrapper *NodeWrapper - elseWrapper *NodeWrapper + watchedExpr []IEvaluator + lastValues []*Value + lastContent []byte + thenWrapper *NodeWrapper + elseWrapper *NodeWrapper } -func (node *tagIfchangedNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { - - if len(node.watched_expr) == 0 { +func (node *tagIfchangedNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { + if len(node.watchedExpr) == 0 { // Check against own rendered body buf := bytes.NewBuffer(make([]byte, 0, 1024)) // 1 KiB @@ -23,43 +22,43 @@ func (node *tagIfchangedNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffe return err } - buf_bytes := buf.Bytes() - if !bytes.Equal(node.last_content, buf_bytes) { + bufBytes := buf.Bytes() + if !bytes.Equal(node.lastContent, bufBytes) { // Rendered content changed, output it - buffer.Write(buf_bytes) - node.last_content = buf_bytes + writer.Write(bufBytes) + node.lastContent = bufBytes } } else { - now_values := make([]*Value, 0, len(node.watched_expr)) - for _, expr := range node.watched_expr { + nowValues := make([]*Value, 0, len(node.watchedExpr)) + for _, expr := range node.watchedExpr { val, err := expr.Evaluate(ctx) if err != nil { return err } - now_values = append(now_values, val) + nowValues = append(nowValues, val) } // Compare old to new values now - changed := len(node.last_values) == 0 + changed := len(node.lastValues) == 0 - for idx, old_val := range node.last_values { - if !old_val.EqualValueTo(now_values[idx]) { + for idx, oldVal := range node.lastValues { + if !oldVal.EqualValueTo(nowValues[idx]) { changed = true break // we can stop here because ONE value changed } } - node.last_values = now_values + node.lastValues = nowValues if changed { // Render thenWrapper - err := node.thenWrapper.Execute(ctx, buffer) + err := node.thenWrapper.Execute(ctx, writer) if err != nil { return err } } else { // Render elseWrapper - err := node.elseWrapper.Execute(ctx, buffer) + err := node.elseWrapper.Execute(ctx, writer) if err != nil { return err } @@ -70,7 +69,7 @@ func (node *tagIfchangedNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffe } func tagIfchangedParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { - ifchanged_node := &tagIfchangedNode{} + ifchangedNode := &tagIfchangedNode{} for arguments.Remaining() > 0 { // Parse condition @@ -78,7 +77,7 @@ func tagIfchangedParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, if err != nil { return nil, err } - ifchanged_node.watched_expr = append(ifchanged_node.watched_expr, expr) + ifchangedNode.watchedExpr = append(ifchangedNode.watchedExpr, expr) } if arguments.Remaining() > 0 { @@ -90,7 +89,7 @@ func tagIfchangedParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, if err != nil { return nil, err } - ifchanged_node.thenWrapper = wrapper + ifchangedNode.thenWrapper = wrapper if endargs.Count() > 0 { return nil, endargs.Error("Arguments not allowed here.", nil) @@ -102,14 +101,14 @@ func tagIfchangedParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, if err != nil { return nil, err } - ifchanged_node.elseWrapper = wrapper + ifchangedNode.elseWrapper = wrapper if endargs.Count() > 0 { return nil, endargs.Error("Arguments not allowed here.", nil) } } - return ifchanged_node, nil + return ifchangedNode, nil } func init() { diff --git a/vendor/github.com/flosch/pongo2/tags_ifequal.go b/vendor/github.com/flosch/pongo2/tags_ifequal.go index 035b8fd..103f1c7 100644 --- a/vendor/github.com/flosch/pongo2/tags_ifequal.go +++ b/vendor/github.com/flosch/pongo2/tags_ifequal.go @@ -1,16 +1,12 @@ package pongo2 -import ( - "bytes" -) - type tagIfEqualNode struct { var1, var2 IEvaluator thenWrapper *NodeWrapper elseWrapper *NodeWrapper } -func (node *tagIfEqualNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { +func (node *tagIfEqualNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { r1, err := node.var1.Evaluate(ctx) if err != nil { return err @@ -23,17 +19,16 @@ func (node *tagIfEqualNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) result := r1.EqualValueTo(r2) if result { - return node.thenWrapper.Execute(ctx, buffer) - } else { - if node.elseWrapper != nil { - return node.elseWrapper.Execute(ctx, buffer) - } + return node.thenWrapper.Execute(ctx, writer) + } + if node.elseWrapper != nil { + return node.elseWrapper.Execute(ctx, writer) } return nil } func tagIfEqualParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { - ifequal_node := &tagIfEqualNode{} + ifequalNode := &tagIfEqualNode{} // Parse two expressions var1, err := arguments.ParseExpression() @@ -44,8 +39,8 @@ func tagIfEqualParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, * if err != nil { return nil, err } - ifequal_node.var1 = var1 - ifequal_node.var2 = var2 + ifequalNode.var1 = var1 + ifequalNode.var2 = var2 if arguments.Remaining() > 0 { return nil, arguments.Error("ifequal only takes 2 arguments.", nil) @@ -56,7 +51,7 @@ func tagIfEqualParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, * if err != nil { return nil, err } - ifequal_node.thenWrapper = wrapper + ifequalNode.thenWrapper = wrapper if endargs.Count() > 0 { return nil, endargs.Error("Arguments not allowed here.", nil) @@ -68,14 +63,14 @@ func tagIfEqualParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, * if err != nil { return nil, err } - ifequal_node.elseWrapper = wrapper + ifequalNode.elseWrapper = wrapper if endargs.Count() > 0 { return nil, endargs.Error("Arguments not allowed here.", nil) } } - return ifequal_node, nil + return ifequalNode, nil } func init() { diff --git a/vendor/github.com/flosch/pongo2/tags_ifnotequal.go b/vendor/github.com/flosch/pongo2/tags_ifnotequal.go index 1c1ba53..0d287d3 100644 --- a/vendor/github.com/flosch/pongo2/tags_ifnotequal.go +++ b/vendor/github.com/flosch/pongo2/tags_ifnotequal.go @@ -1,16 +1,12 @@ package pongo2 -import ( - "bytes" -) - type tagIfNotEqualNode struct { var1, var2 IEvaluator thenWrapper *NodeWrapper elseWrapper *NodeWrapper } -func (node *tagIfNotEqualNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { +func (node *tagIfNotEqualNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { r1, err := node.var1.Evaluate(ctx) if err != nil { return err @@ -23,17 +19,16 @@ func (node *tagIfNotEqualNode) Execute(ctx *ExecutionContext, buffer *bytes.Buff result := !r1.EqualValueTo(r2) if result { - return node.thenWrapper.Execute(ctx, buffer) - } else { - if node.elseWrapper != nil { - return node.elseWrapper.Execute(ctx, buffer) - } + return node.thenWrapper.Execute(ctx, writer) + } + if node.elseWrapper != nil { + return node.elseWrapper.Execute(ctx, writer) } return nil } func tagIfNotEqualParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { - ifnotequal_node := &tagIfNotEqualNode{} + ifnotequalNode := &tagIfNotEqualNode{} // Parse two expressions var1, err := arguments.ParseExpression() @@ -44,19 +39,19 @@ func tagIfNotEqualParser(doc *Parser, start *Token, arguments *Parser) (INodeTag if err != nil { return nil, err } - ifnotequal_node.var1 = var1 - ifnotequal_node.var2 = var2 + ifnotequalNode.var1 = var1 + ifnotequalNode.var2 = var2 if arguments.Remaining() > 0 { return nil, arguments.Error("ifequal only takes 2 arguments.", nil) } // Wrap then/else-blocks - wrapper, endargs, err := doc.WrapUntilTag("else", "endifequal") + wrapper, endargs, err := doc.WrapUntilTag("else", "endifnotequal") if err != nil { return nil, err } - ifnotequal_node.thenWrapper = wrapper + ifnotequalNode.thenWrapper = wrapper if endargs.Count() > 0 { return nil, endargs.Error("Arguments not allowed here.", nil) @@ -64,18 +59,18 @@ func tagIfNotEqualParser(doc *Parser, start *Token, arguments *Parser) (INodeTag if wrapper.Endtag == "else" { // if there's an else in the if-statement, we need the else-Block as well - wrapper, endargs, err = doc.WrapUntilTag("endifequal") + wrapper, endargs, err = doc.WrapUntilTag("endifnotequal") if err != nil { return nil, err } - ifnotequal_node.elseWrapper = wrapper + ifnotequalNode.elseWrapper = wrapper if endargs.Count() > 0 { return nil, endargs.Error("Arguments not allowed here.", nil) } } - return ifnotequal_node, nil + return ifnotequalNode, nil } func init() { diff --git a/vendor/github.com/flosch/pongo2/tags_import.go b/vendor/github.com/flosch/pongo2/tags_import.go index 2abeccd..7e0d6a0 100644 --- a/vendor/github.com/flosch/pongo2/tags_import.go +++ b/vendor/github.com/flosch/pongo2/tags_import.go @@ -1,18 +1,16 @@ package pongo2 import ( - "bytes" "fmt" ) type tagImportNode struct { position *Token filename string - template *Template macros map[string]*tagMacroNode // alias/name -> macro instance } -func (node *tagImportNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { +func (node *tagImportNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { for name, macro := range node.macros { func(name string, macro *tagMacroNode) { ctx.Private[name] = func(args ...*Value) *Value { @@ -24,50 +22,50 @@ func (node *tagImportNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) } func tagImportParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { - import_node := &tagImportNode{ + importNode := &tagImportNode{ position: start, macros: make(map[string]*tagMacroNode), } - filename_token := arguments.MatchType(TokenString) - if filename_token == nil { + filenameToken := arguments.MatchType(TokenString) + if filenameToken == nil { return nil, arguments.Error("Import-tag needs a filename as string.", nil) } - import_node.filename = doc.template.set.resolveFilename(doc.template, filename_token.Val) + importNode.filename = doc.template.set.resolveFilename(doc.template, filenameToken.Val) if arguments.Remaining() == 0 { return nil, arguments.Error("You must at least specify one macro to import.", nil) } // Compile the given template - tpl, err := doc.template.set.FromFile(import_node.filename) + tpl, err := doc.template.set.FromFile(importNode.filename) if err != nil { return nil, err.(*Error).updateFromTokenIfNeeded(doc.template, start) } for arguments.Remaining() > 0 { - macro_name_token := arguments.MatchType(TokenIdentifier) - if macro_name_token == nil { + macroNameToken := arguments.MatchType(TokenIdentifier) + if macroNameToken == nil { return nil, arguments.Error("Expected macro name (identifier).", nil) } - as_name := macro_name_token.Val + asName := macroNameToken.Val if arguments.Match(TokenKeyword, "as") != nil { - alias_token := arguments.MatchType(TokenIdentifier) - if alias_token == nil { + aliasToken := arguments.MatchType(TokenIdentifier) + if aliasToken == nil { return nil, arguments.Error("Expected macro alias name (identifier).", nil) } - as_name = alias_token.Val + asName = aliasToken.Val } - macro_instance, has := tpl.exported_macros[macro_name_token.Val] + macroInstance, has := tpl.exportedMacros[macroNameToken.Val] if !has { - return nil, arguments.Error(fmt.Sprintf("Macro '%s' not found (or not exported) in '%s'.", macro_name_token.Val, - import_node.filename), macro_name_token) + return nil, arguments.Error(fmt.Sprintf("Macro '%s' not found (or not exported) in '%s'.", macroNameToken.Val, + importNode.filename), macroNameToken) } - import_node.macros[as_name] = macro_instance + importNode.macros[asName] = macroInstance if arguments.Remaining() == 0 { break @@ -78,7 +76,7 @@ func tagImportParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *E } } - return import_node, nil + return importNode, nil } func init() { diff --git a/vendor/github.com/flosch/pongo2/tags_include.go b/vendor/github.com/flosch/pongo2/tags_include.go index 7a7cce2..6d619fd 100644 --- a/vendor/github.com/flosch/pongo2/tags_include.go +++ b/vendor/github.com/flosch/pongo2/tags_include.go @@ -1,41 +1,38 @@ package pongo2 -import ( - "bytes" -) - type tagIncludeNode struct { - tpl *Template - filename_evaluator IEvaluator - lazy bool - only bool - filename string - with_pairs map[string]IEvaluator + tpl *Template + filenameEvaluator IEvaluator + lazy bool + only bool + filename string + withPairs map[string]IEvaluator + ifExists bool } -func (node *tagIncludeNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { +func (node *tagIncludeNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { // Building the context for the template - include_ctx := make(Context) + includeCtx := make(Context) // Fill the context with all data from the parent if !node.only { - include_ctx.Update(ctx.Public) - include_ctx.Update(ctx.Private) + includeCtx.Update(ctx.Public) + includeCtx.Update(ctx.Private) } // Put all custom with-pairs into the context - for key, value := range node.with_pairs { + for key, value := range node.withPairs { val, err := value.Evaluate(ctx) if err != nil { return err } - include_ctx[key] = val + includeCtx[key] = val } // Execute the template if node.lazy { // Evaluate the filename - filename, err := node.filename_evaluator.Evaluate(ctx) + filename, err := node.filenameEvaluator.Evaluate(ctx) if err != nil { return err } @@ -45,76 +42,93 @@ func (node *tagIncludeNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) } // Get include-filename - included_filename := ctx.template.set.resolveFilename(ctx.template, filename.String()) + includedFilename := ctx.template.set.resolveFilename(ctx.template, filename.String()) - included_tpl, err2 := ctx.template.set.FromFile(included_filename) + includedTpl, err2 := ctx.template.set.FromFile(includedFilename) if err2 != nil { + // if this is ReadFile error, and "if_exists" flag is enabled + if node.ifExists && err2.(*Error).Sender == "fromfile" { + return nil + } return err2.(*Error) } - err2 = included_tpl.ExecuteWriter(include_ctx, buffer) + err2 = includedTpl.ExecuteWriter(includeCtx, writer) if err2 != nil { return err2.(*Error) } return nil - } else { - // Template is already parsed with static filename - err := node.tpl.ExecuteWriter(include_ctx, buffer) - if err != nil { - return err.(*Error) - } - return nil } + // Template is already parsed with static filename + err := node.tpl.ExecuteWriter(includeCtx, writer) + if err != nil { + return err.(*Error) + } + return nil +} + +type tagIncludeEmptyNode struct{} + +func (node *tagIncludeEmptyNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { + return nil } func tagIncludeParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { - include_node := &tagIncludeNode{ - with_pairs: make(map[string]IEvaluator), + includeNode := &tagIncludeNode{ + withPairs: make(map[string]IEvaluator), } - if filename_token := arguments.MatchType(TokenString); filename_token != nil { + if filenameToken := arguments.MatchType(TokenString); filenameToken != nil { // prepared, static template + // "if_exists" flag + ifExists := arguments.Match(TokenIdentifier, "if_exists") != nil + // Get include-filename - included_filename := doc.template.set.resolveFilename(doc.template, filename_token.Val) + includedFilename := doc.template.set.resolveFilename(doc.template, filenameToken.Val) // Parse the parent - include_node.filename = included_filename - included_tpl, err := doc.template.set.FromFile(included_filename) + includeNode.filename = includedFilename + includedTpl, err := doc.template.set.FromFile(includedFilename) if err != nil { - return nil, err.(*Error).updateFromTokenIfNeeded(doc.template, filename_token) + // if this is ReadFile error, and "if_exists" token presents we should create and empty node + if err.(*Error).Sender == "fromfile" && ifExists { + return &tagIncludeEmptyNode{}, nil + } + return nil, err.(*Error).updateFromTokenIfNeeded(doc.template, filenameToken) } - include_node.tpl = included_tpl + includeNode.tpl = includedTpl } else { // No String, then the user wants to use lazy-evaluation (slower, but possible) - filename_evaluator, err := arguments.ParseExpression() + filenameEvaluator, err := arguments.ParseExpression() if err != nil { - return nil, err.updateFromTokenIfNeeded(doc.template, filename_token) + return nil, err.updateFromTokenIfNeeded(doc.template, filenameToken) } - include_node.filename_evaluator = filename_evaluator - include_node.lazy = true + includeNode.filenameEvaluator = filenameEvaluator + includeNode.lazy = true + includeNode.ifExists = arguments.Match(TokenIdentifier, "if_exists") != nil // "if_exists" flag } // After having parsed the filename we're gonna parse the with+only options if arguments.Match(TokenIdentifier, "with") != nil { for arguments.Remaining() > 0 { // We have at least one key=expr pair (because of starting "with") - key_token := arguments.MatchType(TokenIdentifier) - if key_token == nil { + keyToken := arguments.MatchType(TokenIdentifier) + if keyToken == nil { return nil, arguments.Error("Expected an identifier", nil) } if arguments.Match(TokenSymbol, "=") == nil { return nil, arguments.Error("Expected '='.", nil) } - value_expr, err := arguments.ParseExpression() + valueExpr, err := arguments.ParseExpression() if err != nil { - return nil, err.updateFromTokenIfNeeded(doc.template, key_token) + return nil, err.updateFromTokenIfNeeded(doc.template, keyToken) } - include_node.with_pairs[key_token.Val] = value_expr + includeNode.withPairs[keyToken.Val] = valueExpr // Only? if arguments.Match(TokenIdentifier, "only") != nil { - include_node.only = true + includeNode.only = true break // stop parsing arguments because it's the last option } } @@ -124,7 +138,7 @@ func tagIncludeParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, * return nil, arguments.Error("Malformed 'include'-tag arguments.", nil) } - return include_node, nil + return includeNode, nil } func init() { diff --git a/vendor/github.com/flosch/pongo2/tags_lorem.go b/vendor/github.com/flosch/pongo2/tags_lorem.go index 16b018c..1d353f2 100644 --- a/vendor/github.com/flosch/pongo2/tags_lorem.go +++ b/vendor/github.com/flosch/pongo2/tags_lorem.go @@ -1,10 +1,11 @@ package pongo2 import ( - "bytes" "math/rand" "strings" "time" + + "github.com/juju/errors" ) var ( @@ -19,102 +20,102 @@ type tagLoremNode struct { random bool // does not use the default paragraph "Lorem ipsum dolor sit amet, ..." } -func (node *tagLoremNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { +func (node *tagLoremNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { switch node.method { case "b": if node.random { for i := 0; i < node.count; i++ { if i > 0 { - buffer.WriteString("\n") + writer.WriteString("\n") } par := tagLoremParagraphs[rand.Intn(len(tagLoremParagraphs))] - buffer.WriteString(par) + writer.WriteString(par) } } else { for i := 0; i < node.count; i++ { if i > 0 { - buffer.WriteString("\n") + writer.WriteString("\n") } par := tagLoremParagraphs[i%len(tagLoremParagraphs)] - buffer.WriteString(par) + writer.WriteString(par) } } case "w": if node.random { for i := 0; i < node.count; i++ { if i > 0 { - buffer.WriteString(" ") + writer.WriteString(" ") } word := tagLoremWords[rand.Intn(len(tagLoremWords))] - buffer.WriteString(word) + writer.WriteString(word) } } else { for i := 0; i < node.count; i++ { if i > 0 { - buffer.WriteString(" ") + writer.WriteString(" ") } word := tagLoremWords[i%len(tagLoremWords)] - buffer.WriteString(word) + writer.WriteString(word) } } case "p": if node.random { for i := 0; i < node.count; i++ { if i > 0 { - buffer.WriteString("\n") + writer.WriteString("\n") } - buffer.WriteString("

") + writer.WriteString("

") par := tagLoremParagraphs[rand.Intn(len(tagLoremParagraphs))] - buffer.WriteString(par) - buffer.WriteString("

") + writer.WriteString(par) + writer.WriteString("

") } } else { for i := 0; i < node.count; i++ { if i > 0 { - buffer.WriteString("\n") + writer.WriteString("\n") } - buffer.WriteString("

") + writer.WriteString("

") par := tagLoremParagraphs[i%len(tagLoremParagraphs)] - buffer.WriteString(par) - buffer.WriteString("

") + writer.WriteString(par) + writer.WriteString("

") } } default: - panic("unsupported method") + return ctx.OrigError(errors.Errorf("unsupported method: %s", node.method), nil) } return nil } func tagLoremParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { - lorem_node := &tagLoremNode{ + loremNode := &tagLoremNode{ position: start, count: 1, method: "b", } - if count_token := arguments.MatchType(TokenNumber); count_token != nil { - lorem_node.count = AsValue(count_token.Val).Integer() + if countToken := arguments.MatchType(TokenNumber); countToken != nil { + loremNode.count = AsValue(countToken.Val).Integer() } - if method_token := arguments.MatchType(TokenIdentifier); method_token != nil { - if method_token.Val != "w" && method_token.Val != "p" && method_token.Val != "b" { + if methodToken := arguments.MatchType(TokenIdentifier); methodToken != nil { + if methodToken.Val != "w" && methodToken.Val != "p" && methodToken.Val != "b" { return nil, arguments.Error("lorem-method must be either 'w', 'p' or 'b'.", nil) } - lorem_node.method = method_token.Val + loremNode.method = methodToken.Val } if arguments.MatchOne(TokenIdentifier, "random") != nil { - lorem_node.random = true + loremNode.random = true } if arguments.Remaining() > 0 { return nil, arguments.Error("Malformed lorem-tag arguments.", nil) } - return lorem_node, nil + return loremNode, nil } func init() { diff --git a/vendor/github.com/flosch/pongo2/tags_macro.go b/vendor/github.com/flosch/pongo2/tags_macro.go index 41cba99..dd3e0bf 100644 --- a/vendor/github.com/flosch/pongo2/tags_macro.go +++ b/vendor/github.com/flosch/pongo2/tags_macro.go @@ -6,16 +6,16 @@ import ( ) type tagMacroNode struct { - position *Token - name string - args_order []string - args map[string]IEvaluator - exported bool + position *Token + name string + argsOrder []string + args map[string]IEvaluator + exported bool wrapper *NodeWrapper } -func (node *tagMacroNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { +func (node *tagMacroNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { ctx.Private[node.name] = func(args ...*Value) *Value { return node.call(ctx, args...) } @@ -24,28 +24,28 @@ func (node *tagMacroNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) * } func (node *tagMacroNode) call(ctx *ExecutionContext, args ...*Value) *Value { - args_ctx := make(Context) + argsCtx := make(Context) for k, v := range node.args { if v == nil { // User did not provided a default value - args_ctx[k] = nil + argsCtx[k] = nil } else { // Evaluate the default value - value_expr, err := v.Evaluate(ctx) + valueExpr, err := v.Evaluate(ctx) if err != nil { ctx.Logf(err.Error()) return AsSafeValue(err.Error()) } - args_ctx[k] = value_expr + argsCtx[k] = valueExpr } } - if len(args) > len(node.args_order) { + if len(args) > len(node.argsOrder) { // Too many arguments, we're ignoring them and just logging into debug mode. err := ctx.Error(fmt.Sprintf("Macro '%s' called with too many arguments (%d instead of %d).", - node.name, len(args), len(node.args_order)), nil).updateFromTokenIfNeeded(ctx.template, node.position) + node.name, len(args), len(node.argsOrder)), nil).updateFromTokenIfNeeded(ctx.template, node.position) ctx.Logf(err.Error()) // TODO: This is a workaround, because the error is not returned yet to the Execution()-methods return AsSafeValue(err.Error()) @@ -55,10 +55,10 @@ func (node *tagMacroNode) call(ctx *ExecutionContext, args ...*Value) *Value { macroCtx := NewChildExecutionContext(ctx) // Register all arguments in the private context - macroCtx.Private.Update(args_ctx) + macroCtx.Private.Update(argsCtx) - for idx, arg_value := range args { - macroCtx.Private[node.args_order[idx]] = arg_value.Interface() + for idx, argValue := range args { + macroCtx.Private[node.argsOrder[idx]] = argValue.Interface() } var b bytes.Buffer @@ -71,38 +71,38 @@ func (node *tagMacroNode) call(ctx *ExecutionContext, args ...*Value) *Value { } func tagMacroParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { - macro_node := &tagMacroNode{ + macroNode := &tagMacroNode{ position: start, args: make(map[string]IEvaluator), } - name_token := arguments.MatchType(TokenIdentifier) - if name_token == nil { + nameToken := arguments.MatchType(TokenIdentifier) + if nameToken == nil { return nil, arguments.Error("Macro-tag needs at least an identifier as name.", nil) } - macro_node.name = name_token.Val + macroNode.name = nameToken.Val if arguments.MatchOne(TokenSymbol, "(") == nil { return nil, arguments.Error("Expected '('.", nil) } for arguments.Match(TokenSymbol, ")") == nil { - arg_name_token := arguments.MatchType(TokenIdentifier) - if arg_name_token == nil { + argNameToken := arguments.MatchType(TokenIdentifier) + if argNameToken == nil { return nil, arguments.Error("Expected argument name as identifier.", nil) } - macro_node.args_order = append(macro_node.args_order, arg_name_token.Val) + macroNode.argsOrder = append(macroNode.argsOrder, argNameToken.Val) if arguments.Match(TokenSymbol, "=") != nil { // Default expression follows - arg_default_expr, err := arguments.ParseExpression() + argDefaultExpr, err := arguments.ParseExpression() if err != nil { return nil, err } - macro_node.args[arg_name_token.Val] = arg_default_expr + macroNode.args[argNameToken.Val] = argDefaultExpr } else { // No default expression - macro_node.args[arg_name_token.Val] = nil + macroNode.args[argNameToken.Val] = nil } if arguments.Match(TokenSymbol, ")") != nil { @@ -114,7 +114,7 @@ func tagMacroParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Er } if arguments.Match(TokenKeyword, "export") != nil { - macro_node.exported = true + macroNode.exported = true } if arguments.Remaining() > 0 { @@ -126,22 +126,22 @@ func tagMacroParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Er if err != nil { return nil, err } - macro_node.wrapper = wrapper + macroNode.wrapper = wrapper if endargs.Count() > 0 { return nil, endargs.Error("Arguments not allowed here.", nil) } - if macro_node.exported { + if macroNode.exported { // Now register the macro if it wants to be exported - _, has := doc.template.exported_macros[macro_node.name] + _, has := doc.template.exportedMacros[macroNode.name] if has { - return nil, doc.Error(fmt.Sprintf("Another macro with name '%s' already exported.", macro_node.name), start) + return nil, doc.Error(fmt.Sprintf("another macro with name '%s' already exported", macroNode.name), start) } - doc.template.exported_macros[macro_node.name] = macro_node + doc.template.exportedMacros[macroNode.name] = macroNode } - return macro_node, nil + return macroNode, nil } func init() { diff --git a/vendor/github.com/flosch/pongo2/tags_now.go b/vendor/github.com/flosch/pongo2/tags_now.go index 0f4320f..d9fa4a3 100644 --- a/vendor/github.com/flosch/pongo2/tags_now.go +++ b/vendor/github.com/flosch/pongo2/tags_now.go @@ -1,7 +1,6 @@ package pongo2 import ( - "bytes" "time" ) @@ -11,7 +10,7 @@ type tagNowNode struct { fake bool } -func (node *tagNowNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { +func (node *tagNowNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { var t time.Time if node.fake { t = time.Date(2014, time.February, 05, 18, 31, 45, 00, time.UTC) @@ -19,31 +18,31 @@ func (node *tagNowNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Er t = time.Now() } - buffer.WriteString(t.Format(node.format)) + writer.WriteString(t.Format(node.format)) return nil } func tagNowParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { - now_node := &tagNowNode{ + nowNode := &tagNowNode{ position: start, } - format_token := arguments.MatchType(TokenString) - if format_token == nil { + formatToken := arguments.MatchType(TokenString) + if formatToken == nil { return nil, arguments.Error("Expected a format string.", nil) } - now_node.format = format_token.Val + nowNode.format = formatToken.Val if arguments.MatchOne(TokenIdentifier, "fake") != nil { - now_node.fake = true + nowNode.fake = true } if arguments.Remaining() > 0 { return nil, arguments.Error("Malformed now-tag arguments.", nil) } - return now_node, nil + return nowNode, nil } func init() { diff --git a/vendor/github.com/flosch/pongo2/tags_set.go b/vendor/github.com/flosch/pongo2/tags_set.go index 2729f44..be121c1 100644 --- a/vendor/github.com/flosch/pongo2/tags_set.go +++ b/vendor/github.com/flosch/pongo2/tags_set.go @@ -1,13 +1,11 @@ package pongo2 -import "bytes" - type tagSetNode struct { name string expression IEvaluator } -func (node *tagSetNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { +func (node *tagSetNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { // Evaluate expression value, err := node.expression.Evaluate(ctx) if err != nil { diff --git a/vendor/github.com/flosch/pongo2/tags_spaceless.go b/vendor/github.com/flosch/pongo2/tags_spaceless.go index a4b3003..4fa851b 100644 --- a/vendor/github.com/flosch/pongo2/tags_spaceless.go +++ b/vendor/github.com/flosch/pongo2/tags_spaceless.go @@ -11,7 +11,7 @@ type tagSpacelessNode struct { var tagSpacelessRegexp = regexp.MustCompile(`(?U:(<.*>))([\t\n\v\f\r ]+)(?U:(<.*>))`) -func (node *tagSpacelessNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { +func (node *tagSpacelessNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { b := bytes.NewBuffer(make([]byte, 0, 1024)) // 1 KiB err := node.wrapper.Execute(ctx, b) @@ -28,25 +28,25 @@ func (node *tagSpacelessNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffe s = s2 } - buffer.WriteString(s) + writer.WriteString(s) return nil } func tagSpacelessParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { - spaceless_node := &tagSpacelessNode{} + spacelessNode := &tagSpacelessNode{} wrapper, _, err := doc.WrapUntilTag("endspaceless") if err != nil { return nil, err } - spaceless_node.wrapper = wrapper + spacelessNode.wrapper = wrapper if arguments.Remaining() > 0 { return nil, arguments.Error("Malformed spaceless-tag arguments.", nil) } - return spaceless_node, nil + return spacelessNode, nil } func init() { diff --git a/vendor/github.com/flosch/pongo2/tags_ssi.go b/vendor/github.com/flosch/pongo2/tags_ssi.go index 3c3894f..c33858d 100644 --- a/vendor/github.com/flosch/pongo2/tags_ssi.go +++ b/vendor/github.com/flosch/pongo2/tags_ssi.go @@ -1,7 +1,6 @@ package pongo2 import ( - "bytes" "io/ioutil" ) @@ -11,47 +10,47 @@ type tagSSINode struct { template *Template } -func (node *tagSSINode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { +func (node *tagSSINode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { if node.template != nil { // Execute the template within the current context includeCtx := make(Context) includeCtx.Update(ctx.Public) includeCtx.Update(ctx.Private) - err := node.template.ExecuteWriter(includeCtx, buffer) + err := node.template.execute(includeCtx, writer) if err != nil { return err.(*Error) } } else { // Just print out the content - buffer.WriteString(node.content) + writer.WriteString(node.content) } return nil } func tagSSIParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { - ssi_node := &tagSSINode{} + SSINode := &tagSSINode{} - if file_token := arguments.MatchType(TokenString); file_token != nil { - ssi_node.filename = file_token.Val + if fileToken := arguments.MatchType(TokenString); fileToken != nil { + SSINode.filename = fileToken.Val if arguments.Match(TokenIdentifier, "parsed") != nil { // parsed - temporary_tpl, err := doc.template.set.FromFile(doc.template.set.resolveFilename(doc.template, file_token.Val)) + temporaryTpl, err := doc.template.set.FromFile(doc.template.set.resolveFilename(doc.template, fileToken.Val)) if err != nil { - return nil, err.(*Error).updateFromTokenIfNeeded(doc.template, file_token) + return nil, err.(*Error).updateFromTokenIfNeeded(doc.template, fileToken) } - ssi_node.template = temporary_tpl + SSINode.template = temporaryTpl } else { // plaintext - buf, err := ioutil.ReadFile(doc.template.set.resolveFilename(doc.template, file_token.Val)) + buf, err := ioutil.ReadFile(doc.template.set.resolveFilename(doc.template, fileToken.Val)) if err != nil { return nil, (&Error{ - Sender: "tag:ssi", - ErrorMsg: err.Error(), - }).updateFromTokenIfNeeded(doc.template, file_token) + Sender: "tag:ssi", + OrigError: err, + }).updateFromTokenIfNeeded(doc.template, fileToken) } - ssi_node.content = string(buf) + SSINode.content = string(buf) } } else { return nil, arguments.Error("First argument must be a string.", nil) @@ -61,7 +60,7 @@ func tagSSIParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Erro return nil, arguments.Error("Malformed SSI-tag argument.", nil) } - return ssi_node, nil + return SSINode, nil } func init() { diff --git a/vendor/github.com/flosch/pongo2/tags_templatetag.go b/vendor/github.com/flosch/pongo2/tags_templatetag.go index ffd3d9d..164b4dc 100644 --- a/vendor/github.com/flosch/pongo2/tags_templatetag.go +++ b/vendor/github.com/flosch/pongo2/tags_templatetag.go @@ -1,9 +1,5 @@ package pongo2 -import ( - "bytes" -) - type tagTemplateTagNode struct { content string } @@ -19,20 +15,20 @@ var templateTagMapping = map[string]string{ "closecomment": "#}", } -func (node *tagTemplateTagNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { - buffer.WriteString(node.content) +func (node *tagTemplateTagNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { + writer.WriteString(node.content) return nil } func tagTemplateTagParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { - tt_node := &tagTemplateTagNode{} + ttNode := &tagTemplateTagNode{} - if arg_token := arguments.MatchType(TokenIdentifier); arg_token != nil { - output, found := templateTagMapping[arg_token.Val] + if argToken := arguments.MatchType(TokenIdentifier); argToken != nil { + output, found := templateTagMapping[argToken.Val] if !found { - return nil, arguments.Error("Argument not found", arg_token) + return nil, arguments.Error("Argument not found", argToken) } - tt_node.content = output + ttNode.content = output } else { return nil, arguments.Error("Identifier expected.", nil) } @@ -41,7 +37,7 @@ func tagTemplateTagParser(doc *Parser, start *Token, arguments *Parser) (INodeTa return nil, arguments.Error("Malformed templatetag-tag argument.", nil) } - return tt_node, nil + return ttNode, nil } func init() { diff --git a/vendor/github.com/flosch/pongo2/tags_widthratio.go b/vendor/github.com/flosch/pongo2/tags_widthratio.go index d7d7141..70c9c3e 100644 --- a/vendor/github.com/flosch/pongo2/tags_widthratio.go +++ b/vendor/github.com/flosch/pongo2/tags_widthratio.go @@ -1,7 +1,6 @@ package pongo2 import ( - "bytes" "fmt" "math" ) @@ -10,10 +9,10 @@ type tagWidthratioNode struct { position *Token current, max IEvaluator width IEvaluator - ctx_name string + ctxName string } -func (node *tagWidthratioNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { +func (node *tagWidthratioNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { current, err := node.current.Evaluate(ctx) if err != nil { return err @@ -31,17 +30,17 @@ func (node *tagWidthratioNode) Execute(ctx *ExecutionContext, buffer *bytes.Buff value := int(math.Ceil(current.Float()/max.Float()*width.Float() + 0.5)) - if node.ctx_name == "" { - buffer.WriteString(fmt.Sprintf("%d", value)) + if node.ctxName == "" { + writer.WriteString(fmt.Sprintf("%d", value)) } else { - ctx.Private[node.ctx_name] = value + ctx.Private[node.ctxName] = value } return nil } func tagWidthratioParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { - widthratio_node := &tagWidthratioNode{ + widthratioNode := &tagWidthratioNode{ position: start, } @@ -49,34 +48,34 @@ func tagWidthratioParser(doc *Parser, start *Token, arguments *Parser) (INodeTag if err != nil { return nil, err } - widthratio_node.current = current + widthratioNode.current = current max, err := arguments.ParseExpression() if err != nil { return nil, err } - widthratio_node.max = max + widthratioNode.max = max width, err := arguments.ParseExpression() if err != nil { return nil, err } - widthratio_node.width = width + widthratioNode.width = width if arguments.MatchOne(TokenKeyword, "as") != nil { // Name follows - name_token := arguments.MatchType(TokenIdentifier) - if name_token == nil { + nameToken := arguments.MatchType(TokenIdentifier) + if nameToken == nil { return nil, arguments.Error("Expected name (identifier).", nil) } - widthratio_node.ctx_name = name_token.Val + widthratioNode.ctxName = nameToken.Val } if arguments.Remaining() > 0 { return nil, arguments.Error("Malformed widthratio-tag arguments.", nil) } - return widthratio_node, nil + return widthratioNode, nil } func init() { diff --git a/vendor/github.com/flosch/pongo2/tags_with.go b/vendor/github.com/flosch/pongo2/tags_with.go index 5bf4af0..32b3c1c 100644 --- a/vendor/github.com/flosch/pongo2/tags_with.go +++ b/vendor/github.com/flosch/pongo2/tags_with.go @@ -1,20 +1,16 @@ package pongo2 -import ( - "bytes" -) - type tagWithNode struct { - with_pairs map[string]IEvaluator - wrapper *NodeWrapper + withPairs map[string]IEvaluator + wrapper *NodeWrapper } -func (node *tagWithNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { +func (node *tagWithNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { //new context for block withctx := NewChildExecutionContext(ctx) // Put all custom with-pairs into the context - for key, value := range node.with_pairs { + for key, value := range node.withPairs { val, err := value.Evaluate(ctx) if err != nil { return err @@ -22,12 +18,12 @@ func (node *tagWithNode) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *E withctx.Private[key] = val } - return node.wrapper.Execute(withctx, buffer) + return node.wrapper.Execute(withctx, writer) } func tagWithParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { - with_node := &tagWithNode{ - with_pairs: make(map[string]IEvaluator), + withNode := &tagWithNode{ + withPairs: make(map[string]IEvaluator), } if arguments.Count() == 0 { @@ -38,7 +34,7 @@ func tagWithParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Err if err != nil { return nil, err } - with_node.wrapper = wrapper + withNode.wrapper = wrapper if endargs.Count() > 0 { return nil, endargs.Error("Arguments not allowed here.", nil) @@ -46,45 +42,45 @@ func tagWithParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Err // Scan through all arguments to see which style the user uses (old or new style). // If we find any "as" keyword we will enforce old style; otherwise we will use new style. - old_style := false // by default we're using the new_style + oldStyle := false // by default we're using the new_style for i := 0; i < arguments.Count(); i++ { if arguments.PeekN(i, TokenKeyword, "as") != nil { - old_style = true + oldStyle = true break } } for arguments.Remaining() > 0 { - if old_style { - value_expr, err := arguments.ParseExpression() + if oldStyle { + valueExpr, err := arguments.ParseExpression() if err != nil { return nil, err } if arguments.Match(TokenKeyword, "as") == nil { return nil, arguments.Error("Expected 'as' keyword.", nil) } - key_token := arguments.MatchType(TokenIdentifier) - if key_token == nil { + keyToken := arguments.MatchType(TokenIdentifier) + if keyToken == nil { return nil, arguments.Error("Expected an identifier", nil) } - with_node.with_pairs[key_token.Val] = value_expr + withNode.withPairs[keyToken.Val] = valueExpr } else { - key_token := arguments.MatchType(TokenIdentifier) - if key_token == nil { + keyToken := arguments.MatchType(TokenIdentifier) + if keyToken == nil { return nil, arguments.Error("Expected an identifier", nil) } if arguments.Match(TokenSymbol, "=") == nil { return nil, arguments.Error("Expected '='.", nil) } - value_expr, err := arguments.ParseExpression() + valueExpr, err := arguments.ParseExpression() if err != nil { return nil, err } - with_node.with_pairs[key_token.Val] = value_expr + withNode.withPairs[keyToken.Val] = valueExpr } } - return with_node, nil + return withNode, nil } func init() { diff --git a/vendor/github.com/flosch/pongo2/template.go b/vendor/github.com/flosch/pongo2/template.go index c7fe98b..869adce 100644 --- a/vendor/github.com/flosch/pongo2/template.go +++ b/vendor/github.com/flosch/pongo2/template.go @@ -2,52 +2,72 @@ package pongo2 import ( "bytes" - "fmt" "io" + + "github.com/juju/errors" ) +type TemplateWriter interface { + io.Writer + WriteString(string) (int, error) +} + +type templateWriter struct { + w io.Writer +} + +func (tw *templateWriter) WriteString(s string) (int, error) { + return tw.w.Write([]byte(s)) +} + +func (tw *templateWriter) Write(b []byte) (int, error) { + return tw.w.Write(b) +} + type Template struct { set *TemplateSet // Input - is_tpl_string bool - name string - tpl string - size int + isTplString bool + name string + tpl string + size int // Calculation tokens []*Token parser *Parser // first come, first serve (it's important to not override existing entries in here) - level int - parent *Template - child *Template - blocks map[string]*NodeWrapper - exported_macros map[string]*tagMacroNode + level int + parent *Template + child *Template + blocks map[string]*NodeWrapper + exportedMacros map[string]*tagMacroNode // Output root *nodeDocument } -func newTemplateString(set *TemplateSet, tpl string) (*Template, error) { +func newTemplateString(set *TemplateSet, tpl []byte) (*Template, error) { return newTemplate(set, "", true, tpl) } -func newTemplate(set *TemplateSet, name string, is_tpl_string bool, tpl string) (*Template, error) { +func newTemplate(set *TemplateSet, name string, isTplString bool, tpl []byte) (*Template, error) { + strTpl := string(tpl) + // Create the template t := &Template{ - set: set, - is_tpl_string: is_tpl_string, - name: name, - tpl: tpl, - size: len(tpl), - blocks: make(map[string]*NodeWrapper), - exported_macros: make(map[string]*tagMacroNode), + set: set, + isTplString: isTplString, + name: name, + tpl: strTpl, + size: len(strTpl), + blocks: make(map[string]*NodeWrapper), + exportedMacros: make(map[string]*tagMacroNode), } // Tokenize it - tokens, err := lex(name, tpl) + tokens, err := lex(name, strTpl) if err != nil { return nil, err } @@ -67,11 +87,7 @@ func newTemplate(set *TemplateSet, name string, is_tpl_string bool, tpl string) return t, nil } -func (tpl *Template) execute(context Context) (*bytes.Buffer, error) { - // Create output buffer - // We assume that the rendered template will be 30% larger - buffer := bytes.NewBuffer(make([]byte, 0, int(float64(tpl.size)*1.3))) - +func (tpl *Template) execute(context Context, writer TemplateWriter) error { // Determine the parent to be executed (for template inheritance) parent := tpl for parent.parent != nil { @@ -89,17 +105,17 @@ func (tpl *Template) execute(context Context) (*bytes.Buffer, error) { // Check for context name syntax err := newContext.checkForValidIdentifiers() if err != nil { - return nil, err + return err } // Check for clashes with macro names - for k, _ := range newContext { - _, has := tpl.exported_macros[k] + for k := range newContext { + _, has := tpl.exportedMacros[k] if has { - return nil, &Error{ - Filename: tpl.name, - Sender: "execution", - ErrorMsg: fmt.Sprintf("Context key name '%s' clashes with macro '%s'.", k, k), + return &Error{ + Filename: tpl.name, + Sender: "execution", + OrigError: errors.Errorf("context key name '%s' clashes with macro '%s'", k, k), } } } @@ -110,8 +126,22 @@ func (tpl *Template) execute(context Context) (*bytes.Buffer, error) { ctx := newExecutionContext(parent, newContext) // Run the selected document - err := parent.root.Execute(ctx, buffer) - if err != nil { + if err := parent.root.Execute(ctx, writer); err != nil { + return err + } + + return nil +} + +func (tpl *Template) newTemplateWriterAndExecute(context Context, writer io.Writer) error { + return tpl.execute(context, &templateWriter{w: writer}) +} + +func (tpl *Template) newBufferAndExecute(context Context) (*bytes.Buffer, error) { + // Create output buffer + // We assume that the rendered template will be 30% larger + buffer := bytes.NewBuffer(make([]byte, 0, int(float64(tpl.size)*1.3))) + if err := tpl.execute(context, buffer); err != nil { return nil, err } return buffer, nil @@ -121,30 +151,30 @@ func (tpl *Template) execute(context Context) (*bytes.Buffer, error) { // on success. Context can be nil. Nothing is written on error; instead the error // is being returned. func (tpl *Template) ExecuteWriter(context Context, writer io.Writer) error { - buffer, err := tpl.execute(context) + buf, err := tpl.newBufferAndExecute(context) if err != nil { return err } - - l := buffer.Len() - n, werr := buffer.WriteTo(writer) - if int(n) != l { - panic(fmt.Sprintf("error on writing template: n(%d) != buffer.Len(%d)", n, l)) - } - if werr != nil { - return &Error{ - Filename: tpl.name, - Sender: "execution", - ErrorMsg: werr.Error(), - } + _, err = buf.WriteTo(writer) + if err != nil { + return err } return nil } +// Same as ExecuteWriter. The only difference between both functions is that +// this function might already have written parts of the generated template in the +// case of an execution error because there's no intermediate buffer involved for +// performance reasons. This is handy if you need high performance template +// generation or if you want to manage your own pool of buffers. +func (tpl *Template) ExecuteWriterUnbuffered(context Context, writer io.Writer) error { + return tpl.newTemplateWriterAndExecute(context, writer) +} + // Executes the template and returns the rendered template as a []byte func (tpl *Template) ExecuteBytes(context Context) ([]byte, error) { // Execute template - buffer, err := tpl.execute(context) + buffer, err := tpl.newBufferAndExecute(context) if err != nil { return nil, err } @@ -154,7 +184,7 @@ func (tpl *Template) ExecuteBytes(context Context) ([]byte, error) { // Executes the template and returns the rendered template as a string func (tpl *Template) Execute(context Context) (string, error) { // Execute template - buffer, err := tpl.execute(context) + buffer, err := tpl.newBufferAndExecute(context) if err != nil { return "", err } diff --git a/vendor/github.com/flosch/pongo2/template_loader.go b/vendor/github.com/flosch/pongo2/template_loader.go new file mode 100644 index 0000000..bc80f4a --- /dev/null +++ b/vendor/github.com/flosch/pongo2/template_loader.go @@ -0,0 +1,157 @@ +package pongo2 + +import ( + "bytes" + "io" + "io/ioutil" + "log" + "os" + "path/filepath" + + "github.com/juju/errors" +) + +// LocalFilesystemLoader represents a local filesystem loader with basic +// BaseDirectory capabilities. The access to the local filesystem is unrestricted. +type LocalFilesystemLoader struct { + baseDir string +} + +// MustNewLocalFileSystemLoader creates a new LocalFilesystemLoader instance +// and panics if there's any error during instantiation. The parameters +// are the same like NewLocalFileSystemLoader. +func MustNewLocalFileSystemLoader(baseDir string) *LocalFilesystemLoader { + fs, err := NewLocalFileSystemLoader(baseDir) + if err != nil { + log.Panic(err) + } + return fs +} + +// NewLocalFileSystemLoader creates a new LocalFilesystemLoader and allows +// templatesto be loaded from disk (unrestricted). If any base directory +// is given (or being set using SetBaseDir), this base directory is being used +// for path calculation in template inclusions/imports. Otherwise the path +// is calculated based relatively to the including template's path. +func NewLocalFileSystemLoader(baseDir string) (*LocalFilesystemLoader, error) { + fs := &LocalFilesystemLoader{} + if baseDir != "" { + if err := fs.SetBaseDir(baseDir); err != nil { + return nil, err + } + } + return fs, nil +} + +// SetBaseDir sets the template's base directory. This directory will +// be used for any relative path in filters, tags and From*-functions to determine +// your template. See the comment for NewLocalFileSystemLoader as well. +func (fs *LocalFilesystemLoader) SetBaseDir(path string) error { + // Make the path absolute + if !filepath.IsAbs(path) { + abs, err := filepath.Abs(path) + if err != nil { + return err + } + path = abs + } + + // Check for existence + fi, err := os.Stat(path) + if err != nil { + return err + } + if !fi.IsDir() { + return errors.Errorf("The given path '%s' is not a directory.", path) + } + + fs.baseDir = path + return nil +} + +// Get reads the path's content from your local filesystem. +func (fs *LocalFilesystemLoader) Get(path string) (io.Reader, error) { + buf, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + return bytes.NewReader(buf), nil +} + +// Abs resolves a filename relative to the base directory. Absolute paths are allowed. +// When there's no base dir set, the absolute path to the filename +// will be calculated based on either the provided base directory (which +// might be a path of a template which includes another template) or +// the current working directory. +func (fs *LocalFilesystemLoader) Abs(base, name string) string { + if filepath.IsAbs(name) { + return name + } + + // Our own base dir has always priority; if there's none + // we use the path provided in base. + var err error + if fs.baseDir == "" { + if base == "" { + base, err = os.Getwd() + if err != nil { + panic(err) + } + return filepath.Join(base, name) + } + + return filepath.Join(filepath.Dir(base), name) + } + + return filepath.Join(fs.baseDir, name) +} + +// SandboxedFilesystemLoader is still WIP. +type SandboxedFilesystemLoader struct { + *LocalFilesystemLoader +} + +// NewSandboxedFilesystemLoader creates a new sandboxed local file system instance. +func NewSandboxedFilesystemLoader(baseDir string) (*SandboxedFilesystemLoader, error) { + fs, err := NewLocalFileSystemLoader(baseDir) + if err != nil { + return nil, err + } + return &SandboxedFilesystemLoader{ + LocalFilesystemLoader: fs, + }, nil +} + +// Move sandbox to a virtual fs + +/* +if len(set.SandboxDirectories) > 0 { + defer func() { + // Remove any ".." or other crap + resolvedPath = filepath.Clean(resolvedPath) + + // Make the path absolute + absPath, err := filepath.Abs(resolvedPath) + if err != nil { + panic(err) + } + resolvedPath = absPath + + // Check against the sandbox directories (once one pattern matches, we're done and can allow it) + for _, pattern := range set.SandboxDirectories { + matched, err := filepath.Match(pattern, resolvedPath) + if err != nil { + panic("Wrong sandbox directory match pattern (see http://golang.org/pkg/path/filepath/#Match).") + } + if matched { + // OK! + return + } + } + + // No pattern matched, we have to log+deny the request + set.logf("Access attempt outside of the sandbox directories (blocked): '%s'", resolvedPath) + resolvedPath = "" + }() +} +*/ diff --git a/vendor/github.com/flosch/pongo2/template_sets.go b/vendor/github.com/flosch/pongo2/template_sets.go index c582c5d..6b4533c 100644 --- a/vendor/github.com/flosch/pongo2/template_sets.go +++ b/vendor/github.com/flosch/pongo2/template_sets.go @@ -2,48 +2,49 @@ package pongo2 import ( "fmt" + "io" "io/ioutil" "log" "os" - "path/filepath" "sync" + + "github.com/juju/errors" ) -// A template set allows you to create your own group of templates with their own global context (which is shared -// among all members of the set), their own configuration (like a specific base directory) and their own sandbox. -// It's useful for a separation of different kind of templates (e. g. web templates vs. mail templates). +// TemplateLoader allows to implement a virtual file system. +type TemplateLoader interface { + // Abs calculates the path to a given template. Whenever a path must be resolved + // due to an import from another template, the base equals the parent template's path. + Abs(base, name string) string + + // Get returns an io.Reader where the template's content can be read from. + Get(path string) (io.Reader, error) +} + +// TemplateSet allows you to create your own group of templates with their own +// global context (which is shared among all members of the set) and their own +// configuration. +// It's useful for a separation of different kind of templates +// (e. g. web templates vs. mail templates). type TemplateSet struct { - name string + name string + loader TemplateLoader // Globals will be provided to all templates created within this template set Globals Context - // If debug is true (default false), ExecutionContext.Logf() will work and output to STDOUT. Furthermore, - // FromCache() won't cache the templates. Make sure to synchronize the access to it in case you're changing this + // If debug is true (default false), ExecutionContext.Logf() will work and output + // to STDOUT. Furthermore, FromCache() won't cache the templates. + // Make sure to synchronize the access to it in case you're changing this // variable during program execution (and template compilation/execution). Debug bool - // Base directory: If you set the base directory (string is non-empty), all filename lookups in tags/filters are - // relative to this directory. If it's empty, all lookups are relative to the current filename which is importing. - baseDirectory string - // Sandbox features - // - Limit access to directories (using SandboxDirectories) // - Disallow access to specific tags and/or filters (using BanTag() and BanFilter()) // - // You can limit file accesses (for all tags/filters which are using pongo2's file resolver technique) - // to these sandbox directories. All default pongo2 filters/tags are respecting these restrictions. - // For example, if you only have your base directory in the list, a {% ssi "/etc/passwd" %} will not work. - // No items in SandboxDirectories means no restrictions at all. - // - // For efficiency reasons you can ban tags/filters only *before* you have added your first - // template to the set (restrictions are statically checked). After you added one, it's not possible anymore - // (for your personal security). - // - // SandboxDirectories can be changed at runtime. Please synchronize the access to it if you need to change it - // after you've added your first template to the set. You *must* use this match pattern for your directories: - // http://golang.org/pkg/path/filepath/#Match - SandboxDirectories []string + // For efficiency reasons you can ban tags/filters only *before* you have + // added your first template to the set (restrictions are statically checked). + // After you added one, it's not possible anymore (for your personal security). firstTemplateCreated bool bannedTags map[string]bool bannedFilters map[string]bool @@ -53,11 +54,13 @@ type TemplateSet struct { templateCacheMutex sync.Mutex } -// Create your own template sets to separate different kind of templates (e. g. web from mail templates) with -// different globals or other configurations (like base directories). -func NewSet(name string) *TemplateSet { +// NewSet can be used to create sets with different kind of templates +// (e. g. web from mail templates), with different globals or +// other configurations. +func NewSet(name string, loader TemplateLoader) *TemplateSet { return &TemplateSet{ name: name, + loader: loader, Globals: make(Context), bannedTags: make(map[string]bool), bannedFilters: make(map[string]bool), @@ -65,151 +68,157 @@ func NewSet(name string) *TemplateSet { } } -// Use this function to set your template set's base directory. This directory will be used for any relative -// path in filters, tags and From*-functions to determine your template. -func (set *TemplateSet) SetBaseDirectory(name string) error { - // Make the path absolute - if !filepath.IsAbs(name) { - abs, err := filepath.Abs(name) - if err != nil { - return err - } - name = abs +func (set *TemplateSet) resolveFilename(tpl *Template, path string) string { + name := "" + if tpl != nil && tpl.isTplString { + return path } - - // Check for existence - fi, err := os.Stat(name) - if err != nil { - return err + if tpl != nil { + name = tpl.name } - if !fi.IsDir() { - return fmt.Errorf("The given path '%s' is not a directory.") - } - - set.baseDirectory = name - return nil + return set.loader.Abs(name, path) } -func (set *TemplateSet) BaseDirectory() string { - return set.baseDirectory -} - -// Ban a specific tag for this template set. See more in the documentation for TemplateSet. -func (set *TemplateSet) BanTag(name string) { +// BanTag bans a specific tag for this template set. See more in the documentation for TemplateSet. +func (set *TemplateSet) BanTag(name string) error { _, has := tags[name] if !has { - panic(fmt.Sprintf("Tag '%s' not found.", name)) + return errors.Errorf("tag '%s' not found", name) } if set.firstTemplateCreated { - panic("You cannot ban any tags after you've added your first template to your template set.") + return errors.New("you cannot ban any tags after you've added your first template to your template set") } _, has = set.bannedTags[name] if has { - panic(fmt.Sprintf("Tag '%s' is already banned.", name)) + return errors.Errorf("tag '%s' is already banned", name) } set.bannedTags[name] = true + + return nil } -// Ban a specific filter for this template set. See more in the documentation for TemplateSet. -func (set *TemplateSet) BanFilter(name string) { +// BanFilter bans a specific filter for this template set. See more in the documentation for TemplateSet. +func (set *TemplateSet) BanFilter(name string) error { _, has := filters[name] if !has { - panic(fmt.Sprintf("Filter '%s' not found.", name)) + return errors.Errorf("filter '%s' not found", name) } if set.firstTemplateCreated { - panic("You cannot ban any filters after you've added your first template to your template set.") + return errors.New("you cannot ban any filters after you've added your first template to your template set") } _, has = set.bannedFilters[name] if has { - panic(fmt.Sprintf("Filter '%s' is already banned.", name)) + return errors.Errorf("filter '%s' is already banned", name) } set.bannedFilters[name] = true + + return nil } -// FromCache() is a convenient method to cache templates. It is thread-safe +// FromCache is a convenient method to cache templates. It is thread-safe // and will only compile the template associated with a filename once. // If TemplateSet.Debug is true (for example during development phase), // FromCache() will not cache the template and instead recompile it on any // call (to make changes to a template live instantaneously). -// Like FromFile(), FromCache() takes a relative path to a set base directory. -// Sandbox restrictions apply (if given). func (set *TemplateSet) FromCache(filename string) (*Template, error) { if set.Debug { // Recompile on any request return set.FromFile(filename) - } else { - // Cache the template - cleaned_filename := set.resolveFilename(nil, filename) + } + // Cache the template + cleanedFilename := set.resolveFilename(nil, filename) - set.templateCacheMutex.Lock() - defer set.templateCacheMutex.Unlock() + set.templateCacheMutex.Lock() + defer set.templateCacheMutex.Unlock() - tpl, has := set.templateCache[cleaned_filename] + tpl, has := set.templateCache[cleanedFilename] - // Cache miss - if !has { - tpl, err := set.FromFile(cleaned_filename) - if err != nil { - return nil, err - } - set.templateCache[cleaned_filename] = tpl - return tpl, nil + // Cache miss + if !has { + tpl, err := set.FromFile(cleanedFilename) + if err != nil { + return nil, err } - - // Cache hit + set.templateCache[cleanedFilename] = tpl return tpl, nil } + + // Cache hit + return tpl, nil } -// Loads a template from string and returns a Template instance. +// FromString loads a template from string and returns a Template instance. func (set *TemplateSet) FromString(tpl string) (*Template, error) { set.firstTemplateCreated = true + return newTemplateString(set, []byte(tpl)) +} + +// FromBytes loads a template from bytes and returns a Template instance. +func (set *TemplateSet) FromBytes(tpl []byte) (*Template, error) { + set.firstTemplateCreated = true + return newTemplateString(set, tpl) } -// Loads a template from a filename and returns a Template instance. -// If a base directory is set, the filename must be either relative to it -// or be an absolute path. Sandbox restrictions (SandboxDirectories) apply -// if given. +// FromFile loads a template from a filename and returns a Template instance. func (set *TemplateSet) FromFile(filename string) (*Template, error) { set.firstTemplateCreated = true - buf, err := ioutil.ReadFile(set.resolveFilename(nil, filename)) + fd, err := set.loader.Get(set.resolveFilename(nil, filename)) if err != nil { return nil, &Error{ - Filename: filename, - Sender: "fromfile", - ErrorMsg: err.Error(), + Filename: filename, + Sender: "fromfile", + OrigError: err, } } - return newTemplate(set, filename, false, string(buf)) + buf, err := ioutil.ReadAll(fd) + if err != nil { + return nil, &Error{ + Filename: filename, + Sender: "fromfile", + OrigError: err, + } + } + + return newTemplate(set, filename, false, buf) } -// Shortcut; renders a template string directly. Panics when providing a -// malformed template or an error occurs during execution. -func (set *TemplateSet) RenderTemplateString(s string, ctx Context) string { +// RenderTemplateString is a shortcut and renders a template string directly. +func (set *TemplateSet) RenderTemplateString(s string, ctx Context) (string, error) { set.firstTemplateCreated = true tpl := Must(set.FromString(s)) result, err := tpl.Execute(ctx) if err != nil { - panic(err) + return "", err } - return result + return result, nil } -// Shortcut; renders a template file directly. Panics when providing a -// malformed template or an error occurs during execution. -func (set *TemplateSet) RenderTemplateFile(fn string, ctx Context) string { +// RenderTemplateBytes is a shortcut and renders template bytes directly. +func (set *TemplateSet) RenderTemplateBytes(b []byte, ctx Context) (string, error) { + set.firstTemplateCreated = true + + tpl := Must(set.FromBytes(b)) + result, err := tpl.Execute(ctx) + if err != nil { + return "", err + } + return result, nil +} + +// RenderTemplateFile is a shortcut and renders a template file directly. +func (set *TemplateSet) RenderTemplateFile(fn string, ctx Context) (string, error) { set.firstTemplateCreated = true tpl := Must(set.FromFile(fn)) result, err := tpl.Execute(ctx) if err != nil { - panic(err) + return "", err } - return result + return result, nil } func (set *TemplateSet) logf(format string, args ...interface{}) { @@ -218,58 +227,6 @@ func (set *TemplateSet) logf(format string, args ...interface{}) { } } -// Resolves a filename relative to the base directory. Absolute paths are allowed. -// If sandbox restrictions are given (SandboxDirectories), they will be respected and checked. -// On sandbox restriction violation, resolveFilename() panics. -func (set *TemplateSet) resolveFilename(tpl *Template, filename string) (resolved_path string) { - if len(set.SandboxDirectories) > 0 { - defer func() { - // Remove any ".." or other crap - resolved_path = filepath.Clean(resolved_path) - - // Make the path absolute - abs_path, err := filepath.Abs(resolved_path) - if err != nil { - panic(err) - } - resolved_path = abs_path - - // Check against the sandbox directories (once one pattern matches, we're done and can allow it) - for _, pattern := range set.SandboxDirectories { - matched, err := filepath.Match(pattern, resolved_path) - if err != nil { - panic("Wrong sandbox directory match pattern (see http://golang.org/pkg/path/filepath/#Match).") - } - if matched { - // OK! - return - } - } - - // No pattern matched, we have to log+deny the request - set.logf("Access attempt outside of the sandbox directories (blocked): '%s'", resolved_path) - resolved_path = "" - }() - } - - if filepath.IsAbs(filename) { - return filename - } - - if set.baseDirectory == "" { - if tpl != nil { - if tpl.is_tpl_string { - return filename - } - base := filepath.Dir(tpl.name) - return filepath.Join(base, filename) - } - return filename - } else { - return filepath.Join(set.baseDirectory, filename) - } -} - // Logging function (internally used) func logf(format string, items ...interface{}) { if debug { @@ -279,13 +236,18 @@ func logf(format string, items ...interface{}) { var ( debug bool // internal debugging - logger = log.New(os.Stdout, "[pongo2] ", log.LstdFlags) + logger = log.New(os.Stdout, "[pongo2] ", log.LstdFlags|log.Lshortfile) - // Creating a default set - DefaultSet = NewSet("default") + // DefaultLoader allows the default un-sandboxed access to the local file + // system and is being used by the DefaultSet. + DefaultLoader = MustNewLocalFileSystemLoader("") + + // DefaultSet is a set created for you for convinience reasons. + DefaultSet = NewSet("default", DefaultLoader) // Methods on the default set FromString = DefaultSet.FromString + FromBytes = DefaultSet.FromBytes FromFile = DefaultSet.FromFile FromCache = DefaultSet.FromCache RenderTemplateString = DefaultSet.RenderTemplateString diff --git a/vendor/github.com/flosch/pongo2/template_tests/comment.tpl b/vendor/github.com/flosch/pongo2/template_tests/comment.tpl new file mode 100644 index 0000000..2dac952 --- /dev/null +++ b/vendor/github.com/flosch/pongo2/template_tests/comment.tpl @@ -0,0 +1,50 @@ +empty single line comment +{# #} + +filled single line comment +{# testing single line comment #} + +filled single line comment with valid tags +{# testing single line comment {% if thing %}{% endif %} #} + +filled single line comment with invalid tags +{# testing single line comment {% if thing %} #} + +filled single line comment with invalid syntax +{# testing single line comment {% if thing('') %}wow{% endif %} #} + +empty block comment +{% comment %}{% endcomment %} + +filled text single line block comment +{% comment %}filled block comment {% endcomment %} + +empty multi line block comment +{% comment %} + + +{% endcomment %} + +block comment with other tags inside of it +{% comment %} + {{ thing_goes_here }} + {% if stuff %}do stuff{% endif %} +{% endcomment %} + +block comment with invalid tags inside of it +{% comment %} + {% if thing %} +{% endcomment %} + +block comment with invalid syntax inside of it +{% comment %} + {% thing('') %} +{% endcomment %} + +Regular tags between comments to verify it doesn't break in the lexer +{% if hello %} +{% endif %} +after if +{% comment %}All done{% endcomment %} + +end of file diff --git a/vendor/github.com/flosch/pongo2/template_tests/comment.tpl.out b/vendor/github.com/flosch/pongo2/template_tests/comment.tpl.out new file mode 100644 index 0000000..d7acd9d --- /dev/null +++ b/vendor/github.com/flosch/pongo2/template_tests/comment.tpl.out @@ -0,0 +1,39 @@ +empty single line comment + + +filled single line comment + + +filled single line comment with valid tags + + +filled single line comment with invalid tags + + +filled single line comment with invalid syntax + + +empty block comment + + +filled text single line block comment + + +empty multi line block comment + + +block comment with other tags inside of it + + +block comment with invalid tags inside of it + + +block comment with invalid syntax inside of it + + +Regular tags between comments to verify it doesn't break in the lexer + +after if + + +end of file diff --git a/vendor/github.com/flosch/pongo2/template_tests/expressions.tpl b/vendor/github.com/flosch/pongo2/template_tests/expressions.tpl index 40a158a..caada14 100644 --- a/vendor/github.com/flosch/pongo2/template_tests/expressions.tpl +++ b/vendor/github.com/flosch/pongo2/template_tests/expressions.tpl @@ -46,8 +46,24 @@ in/not in {{ 7 in simple.intmap }} {{ !(5 in simple.intmap) }} {{ not(7 in simple.intmap) }} +{{ 1 in simple.multiple_item_list }} +{{ 4 in simple.multiple_item_list }} +{{ !(4 in simple.multiple_item_list) }} +{{ "Hello" in simple.misc_list }} +{{ "Hello2" in simple.misc_list }} +{{ 99 in simple.misc_list }} +{{ False in simple.misc_list }} issue #48 (associativity for infix operators) {{ 34/3*3 }} {{ 10 + 24 / 6 / 2 }} -{{ 6 - 4 - 2 }} \ No newline at end of file +{{ 6 - 4 - 2 }} + +issue #64 (uint comparison with int const) +{{ simple.uint }} +{{ simple.uint == 8 }} +{{ simple.uint == 9 }} +{{ simple.uint >= 8 }} +{{ simple.uint <= 8 }} +{{ simple.uint < 8 }} +{{ simple.uint > 8 }} \ No newline at end of file diff --git a/vendor/github.com/flosch/pongo2/template_tests/expressions.tpl.out b/vendor/github.com/flosch/pongo2/template_tests/expressions.tpl.out index be51073..d710fc8 100644 --- a/vendor/github.com/flosch/pongo2/template_tests/expressions.tpl.out +++ b/vendor/github.com/flosch/pongo2/template_tests/expressions.tpl.out @@ -46,8 +46,24 @@ True False False True +True +False +True +True +False +True +False issue #48 (associativity for infix operators) 33 12 -0 \ No newline at end of file +0 + +issue #64 (uint comparison with int const) +8 +True +False +True +True +False +False \ No newline at end of file diff --git a/vendor/github.com/flosch/pongo2/template_tests/extends_super.tpl b/vendor/github.com/flosch/pongo2/template_tests/extends_super.tpl new file mode 100644 index 0000000..54154bd --- /dev/null +++ b/vendor/github.com/flosch/pongo2/template_tests/extends_super.tpl @@ -0,0 +1,3 @@ +{% extends "inheritance/base.tpl" %} + +{% block content %}{{ block.Super }}extends-level-1{% endblock %} \ No newline at end of file diff --git a/vendor/github.com/flosch/pongo2/template_tests/extends_super.tpl.out b/vendor/github.com/flosch/pongo2/template_tests/extends_super.tpl.out new file mode 100644 index 0000000..7b4e8d2 --- /dev/null +++ b/vendor/github.com/flosch/pongo2/template_tests/extends_super.tpl.out @@ -0,0 +1 @@ +Start#This is base's bodyDefault contentextends-level-1#End \ No newline at end of file diff --git a/vendor/github.com/flosch/pongo2/template_tests/extends_super2.tpl b/vendor/github.com/flosch/pongo2/template_tests/extends_super2.tpl new file mode 100644 index 0000000..ebac477 --- /dev/null +++ b/vendor/github.com/flosch/pongo2/template_tests/extends_super2.tpl @@ -0,0 +1,3 @@ +{% extends "extends_super.tpl" %} + +{% block content %}{{ block.Super }}extends-level-2{% endblock %} \ No newline at end of file diff --git a/vendor/github.com/flosch/pongo2/template_tests/extends_super2.tpl.out b/vendor/github.com/flosch/pongo2/template_tests/extends_super2.tpl.out new file mode 100644 index 0000000..2284485 --- /dev/null +++ b/vendor/github.com/flosch/pongo2/template_tests/extends_super2.tpl.out @@ -0,0 +1 @@ +Start#This is base's bodyDefault contentextends-level-1extends-level-2#End \ No newline at end of file diff --git a/vendor/github.com/flosch/pongo2/template_tests/filters-execution.err b/vendor/github.com/flosch/pongo2/template_tests/filters-execution.err index da761b2..1fc53f2 100644 --- a/vendor/github.com/flosch/pongo2/template_tests/filters-execution.err +++ b/vendor/github.com/flosch/pongo2/template_tests/filters-execution.err @@ -1,3 +1,4 @@ {{ -(true || false) }} {{ simple.func_add("test", 5) }} -{{ simple.func_variadic_sum_int("foo") }} \ No newline at end of file +{% for item in simple.multiple_item_list %} {{ simple.func_add("test", 5) }} {% endfor %} +{{ simple.func_variadic_sum_int("foo") }} diff --git a/vendor/github.com/flosch/pongo2/template_tests/filters-execution.err.out b/vendor/github.com/flosch/pongo2/template_tests/filters-execution.err.out index e83ae01..a960607 100644 --- a/vendor/github.com/flosch/pongo2/template_tests/filters-execution.err.out +++ b/vendor/github.com/flosch/pongo2/template_tests/filters-execution.err.out @@ -1,3 +1,4 @@ .*where: execution.*Negative sign on a non\-number expression .*Function input argument 0 of 'simple.func_add' must be of type int or \*pongo2.Value \(not string\). -.*Function variadic input argument of 'simple.func_variadic_sum_int' must be of type int or \*pongo2.Value \(not string\). \ No newline at end of file +.*Function input argument 0 of 'simple.func_add' must be of type int or \*pongo2.Value \(not string\). +.*Function variadic input argument of 'simple.func_variadic_sum_int' must be of type int or \*pongo2.Value \(not string\). diff --git a/vendor/github.com/flosch/pongo2/template_tests/filters.tpl b/vendor/github.com/flosch/pongo2/template_tests/filters.tpl index c15468a..9cace95 100644 --- a/vendor/github.com/flosch/pongo2/template_tests/filters.tpl +++ b/vendor/github.com/flosch/pongo2/template_tests/filters.tpl @@ -176,6 +176,9 @@ floatformat join {{ simple.misc_list|join:", " }} +split +{{ "Hello, 99, 3.140000, good"|split:", "|join:", " }} + stringformat {{ simple.float|stringformat:"%.2f" }} {{ simple.uint|stringformat:"Test: %d" }} diff --git a/vendor/github.com/flosch/pongo2/template_tests/filters.tpl.out b/vendor/github.com/flosch/pongo2/template_tests/filters.tpl.out index 1044857..5f2df13 100644 --- a/vendor/github.com/flosch/pongo2/template_tests/filters.tpl.out +++ b/vendor/github.com/flosch/pongo2/template_tests/filters.tpl.out @@ -105,7 +105,7 @@ h first T - + @@ -113,7 +113,7 @@ T last t - + @@ -176,6 +176,9 @@ floatformat join Hello, 99, 3.140000, good +split +Hello, 99, 3.140000, good + stringformat 3.14 Test: 8 diff --git a/vendor/github.com/flosch/pongo2/template_tests/for.tpl b/vendor/github.com/flosch/pongo2/template_tests/for.tpl index 51e144c..d14e632 100644 --- a/vendor/github.com/flosch/pongo2/template_tests/for.tpl +++ b/vendor/github.com/flosch/pongo2/template_tests/for.tpl @@ -6,4 +6,22 @@ {% endfor %} reversed -'{% for item in simple.multiple_item_list reversed %}{{ item }} {% endfor %}' \ No newline at end of file +'{% for item in simple.multiple_item_list reversed %}{{ item }} {% endfor %}' + +sorted string map +'{% for key in simple.strmap sorted %}{{ key }} {% endfor %}' + +sorted int map +'{% for key in simple.intmap sorted %}{{ key }} {% endfor %}' + +sorted int list +'{% for key in simple.unsorted_int_list sorted %}{{ key }} {% endfor %}' + +reversed sorted int list +'{% for key in simple.unsorted_int_list reversed sorted %}{{ key }} {% endfor %}' + +reversed sorted string map +'{% for key in simple.strmap reversed sorted %}{{ key }} {% endfor %}' + +reversed sorted int map +'{% for key in simple.intmap reversed sorted %}{{ key }} {% endfor %}' diff --git a/vendor/github.com/flosch/pongo2/template_tests/for.tpl.out b/vendor/github.com/flosch/pongo2/template_tests/for.tpl.out index 18258a2..27c7120 100644 --- a/vendor/github.com/flosch/pongo2/template_tests/for.tpl.out +++ b/vendor/github.com/flosch/pongo2/template_tests/for.tpl.out @@ -16,4 +16,22 @@ reversed -'55 34 21 13 8 5 3 2 1 1 ' \ No newline at end of file +'55 34 21 13 8 5 3 2 1 1 ' + +sorted string map +'aab abc bcd gh ukq zab ' + +sorted int map +'1 2 5 ' + +sorted int list +'1 22 192 249 581 8271 9999 1828591 ' + +reversed sorted int list +'1828591 9999 8271 581 249 192 22 1 ' + +reversed sorted string map +'zab ukq gh bcd abc aab ' + +reversed sorted int map +'5 2 1 ' diff --git a/vendor/github.com/flosch/pongo2/template_tests/if.tpl b/vendor/github.com/flosch/pongo2/template_tests/if.tpl index c434c3f..29fe936 100644 --- a/vendor/github.com/flosch/pongo2/template_tests/if.tpl +++ b/vendor/github.com/flosch/pongo2/template_tests/if.tpl @@ -9,6 +9,7 @@ {% if 5 in simple.intmap %}5 in simple.intmap{% endif %} {% if !0.0 %}!0.0{% endif %} {% if !0 %}!0{% endif %} +{% if not complex.post %}true{% else %}false{% endif %} {% if simple.number == 43 %}no{% else %}42{% endif %} {% if simple.number < 42 %}false{% elif simple.number > 42 %}no{% elif simple.number >= 42 %}yes{% else %}no{% endif %} {% if simple.number < 42 %}false{% elif simple.number > 42 %}no{% elif simple.number != 42 %}no{% else %}yes{% endif %} diff --git a/vendor/github.com/flosch/pongo2/template_tests/if.tpl.out b/vendor/github.com/flosch/pongo2/template_tests/if.tpl.out index bf931be..d4733ec 100644 --- a/vendor/github.com/flosch/pongo2/template_tests/if.tpl.out +++ b/vendor/github.com/flosch/pongo2/template_tests/if.tpl.out @@ -9,6 +9,7 @@ text field in complex.post 5 in simple.intmap !0.0 !0 +false 42 yes yes diff --git a/vendor/github.com/flosch/pongo2/template_tests/includes.tpl b/vendor/github.com/flosch/pongo2/template_tests/includes.tpl index f2b0253..2394ee9 100644 --- a/vendor/github.com/flosch/pongo2/template_tests/includes.tpl +++ b/vendor/github.com/flosch/pongo2/template_tests/includes.tpl @@ -1,4 +1,7 @@ Start '{% include "includes.helper" %}' End +Start '{% include "includes.helper" if_exists %}' End Start '{% include "includes.helper" with what_am_i=simple.name only %}' End Start '{% include "includes.helper" with what_am_i=simple.name %}' End -Start '{% include simple.included_file|lower with number=7 what_am_i="guest" %}' End \ No newline at end of file +Start '{% include simple.included_file|lower with number=7 what_am_i="guest" %}' End +Start '{% include "includes.helper.not_exists" if_exists %}' End +Start '{% include simple.included_file_not_exists if_exists with number=7 what_am_i="guest" %}' End \ No newline at end of file diff --git a/vendor/github.com/flosch/pongo2/template_tests/includes.tpl.out b/vendor/github.com/flosch/pongo2/template_tests/includes.tpl.out index 90f7726..61d9318 100644 --- a/vendor/github.com/flosch/pongo2/template_tests/includes.tpl.out +++ b/vendor/github.com/flosch/pongo2/template_tests/includes.tpl.out @@ -1,4 +1,7 @@ Start 'I'm 11' End +Start 'I'm 11' End Start 'I'm john doe' End Start 'I'm john doe11' End -Start 'I'm guest7' End \ No newline at end of file +Start 'I'm guest7' End +Start '' End +Start '' End \ No newline at end of file diff --git a/vendor/github.com/flosch/pongo2/template_tests/macro-compilation.err.out b/vendor/github.com/flosch/pongo2/template_tests/macro-compilation.err.out index a9ad8f1..0c46fba 100644 --- a/vendor/github.com/flosch/pongo2/template_tests/macro-compilation.err.out +++ b/vendor/github.com/flosch/pongo2/template_tests/macro-compilation.err.out @@ -1 +1 @@ -.*Another macro with name 'test_override' already exported. \ No newline at end of file +.*another macro with name 'test_override' already exported \ No newline at end of file diff --git a/vendor/github.com/flosch/pongo2/template_tests/macro-execution.err.out b/vendor/github.com/flosch/pongo2/template_tests/macro-execution.err.out index 4d4067b..32cf3fb 100644 --- a/vendor/github.com/flosch/pongo2/template_tests/macro-execution.err.out +++ b/vendor/github.com/flosch/pongo2/template_tests/macro-execution.err.out @@ -1 +1 @@ -.*Context key name 'number' clashes with macro 'number'. \ No newline at end of file +.*context key name 'number' clashes with macro 'number' \ No newline at end of file diff --git a/vendor/github.com/flosch/pongo2/template_tests/pongo2ctx.tpl.out b/vendor/github.com/flosch/pongo2/template_tests/pongo2ctx.tpl.out index 04d0d54..9001211 100644 --- a/vendor/github.com/flosch/pongo2/template_tests/pongo2ctx.tpl.out +++ b/vendor/github.com/flosch/pongo2/template_tests/pongo2ctx.tpl.out @@ -1 +1 @@ -v3 \ No newline at end of file +dev \ No newline at end of file diff --git a/vendor/github.com/flosch/pongo2/template_tests/quotes.tpl b/vendor/github.com/flosch/pongo2/template_tests/quotes.tpl new file mode 100644 index 0000000..cb89310 --- /dev/null +++ b/vendor/github.com/flosch/pongo2/template_tests/quotes.tpl @@ -0,0 +1,15 @@ +Variables +{{ "hello" }} +{{ 'hello' }} +{{ "hell'o" }} + +Filters +{{ 'Test'|slice:'1:3' }} +{{ '
  • This is a long test which will be cutted after some chars.

'|truncatechars_html:25 }} +{{ '

This is a long test which will be cutted after some chars.

'|truncatechars_html:25 }} + +Tags +{% if 'Text' in complex.post %}text field in complex.post{% endif %} + +Functions +{{ simple.func_variadic('hello') }} diff --git a/vendor/github.com/flosch/pongo2/template_tests/quotes.tpl.out b/vendor/github.com/flosch/pongo2/template_tests/quotes.tpl.out new file mode 100644 index 0000000..4245893 --- /dev/null +++ b/vendor/github.com/flosch/pongo2/template_tests/quotes.tpl.out @@ -0,0 +1,15 @@ +Variables +hello +hello +hell'o + +Filters +es +
  • This is a long test wh...

+

This is a long test wh...

+ +Tags +text field in complex.post + +Functions +hello diff --git a/vendor/github.com/flosch/pongo2/template_tests/set.tpl b/vendor/github.com/flosch/pongo2/template_tests/set.tpl index 09e7b2d..3ace3be 100644 --- a/vendor/github.com/flosch/pongo2/template_tests/set.tpl +++ b/vendor/github.com/flosch/pongo2/template_tests/set.tpl @@ -2,4 +2,5 @@ {% block content %}{% set new_var = "world" %}{{ new_var }}{% endblock %} {{ new_var }}{% for item in simple.misc_list %} {% set new_var = item %}{{ new_var }}{% endfor %} -{{ new_var }} \ No newline at end of file +{{ new_var }} +{% set car=someUndefinedVar %}{{ car.Drive }}No Panic \ No newline at end of file diff --git a/vendor/github.com/flosch/pongo2/template_tests/set.tpl.out b/vendor/github.com/flosch/pongo2/template_tests/set.tpl.out index bede53d..9d8d348 100644 --- a/vendor/github.com/flosch/pongo2/template_tests/set.tpl.out +++ b/vendor/github.com/flosch/pongo2/template_tests/set.tpl.out @@ -5,4 +5,5 @@ Hello 99 3.140000 good -world \ No newline at end of file +world +No Panic \ No newline at end of file diff --git a/vendor/github.com/flosch/pongo2/template_tests/variables.tpl b/vendor/github.com/flosch/pongo2/template_tests/variables.tpl index 2f78cae..7a0b08d 100644 --- a/vendor/github.com/flosch/pongo2/template_tests/variables.tpl +++ b/vendor/github.com/flosch/pongo2/template_tests/variables.tpl @@ -10,4 +10,6 @@ {{ simple.bool_true }} {{ simple.uint }} {{ simple.uint|integer }} -{{ simple.uint|float }} \ No newline at end of file +{{ simple.uint|float }} +{{ simple.multiple_item_list.10 }} +{{ simple.multiple_item_list.4 }} diff --git a/vendor/github.com/flosch/pongo2/template_tests/variables.tpl.out b/vendor/github.com/flosch/pongo2/template_tests/variables.tpl.out index bdc9569..c795f10 100644 --- a/vendor/github.com/flosch/pongo2/template_tests/variables.tpl.out +++ b/vendor/github.com/flosch/pongo2/template_tests/variables.tpl.out @@ -10,4 +10,6 @@ False True 8 8 -8.000000 \ No newline at end of file +8.000000 + +5 diff --git a/vendor/github.com/flosch/pongo2/template_tests/with.tpl.out b/vendor/github.com/flosch/pongo2/template_tests/with.tpl.out index 41e3829..46686bd 100644 --- a/vendor/github.com/flosch/pongo2/template_tests/with.tpl.out +++ b/vendor/github.com/flosch/pongo2/template_tests/with.tpl.out @@ -11,6 +11,6 @@ Start 'I'm guest7' End Start 'Hi number 10! Will not be overridden inside the block. I'm john doe, 50 years old.I have 10 children.' End more with tests - + user1 user3 \ No newline at end of file diff --git a/vendor/github.com/flosch/pongo2/value.go b/vendor/github.com/flosch/pongo2/value.go index 8cf8552..df70bbc 100644 --- a/vendor/github.com/flosch/pongo2/value.go +++ b/vendor/github.com/flosch/pongo2/value.go @@ -3,6 +3,7 @@ package pongo2 import ( "fmt" "reflect" + "sort" "strconv" "strings" ) @@ -12,7 +13,7 @@ type Value struct { safe bool // used to indicate whether a Value needs explicit escaping in the template } -// Converts any given value to a pongo2.Value +// AsValue converts any given value to a pongo2.Value // Usually being used within own functions passed to a template // through a Context or within filter functions. // @@ -24,7 +25,7 @@ func AsValue(i interface{}) *Value { } } -// Like AsValue, but does not apply the 'escape' filter. +// AsSafeValue works like AsValue, but does not apply the 'escape' filter. func AsSafeValue(i interface{}) *Value { return &Value{ val: reflect.ValueOf(i), @@ -39,23 +40,23 @@ func (v *Value) getResolvedValue() reflect.Value { return v.val } -// Checks whether the underlying value is a string +// IsString checks whether the underlying value is a string func (v *Value) IsString() bool { return v.getResolvedValue().Kind() == reflect.String } -// Checks whether the underlying value is a bool +// IsBool checks whether the underlying value is a bool func (v *Value) IsBool() bool { return v.getResolvedValue().Kind() == reflect.Bool } -// Checks whether the underlying value is a float +// IsFloat checks whether the underlying value is a float func (v *Value) IsFloat() bool { return v.getResolvedValue().Kind() == reflect.Float32 || v.getResolvedValue().Kind() == reflect.Float64 } -// Checks whether the underlying value is an integer +// IsInteger checks whether the underlying value is an integer func (v *Value) IsInteger() bool { return v.getResolvedValue().Kind() == reflect.Int || v.getResolvedValue().Kind() == reflect.Int8 || @@ -69,19 +70,19 @@ func (v *Value) IsInteger() bool { v.getResolvedValue().Kind() == reflect.Uint64 } -// Checks whether the underlying value is either an integer +// IsNumber checks whether the underlying value is either an integer // or a float. func (v *Value) IsNumber() bool { return v.IsInteger() || v.IsFloat() } -// Checks whether the underlying value is NIL +// IsNil checks whether the underlying value is NIL func (v *Value) IsNil() bool { //fmt.Printf("%+v\n", v.getResolvedValue().Type().String()) return !v.getResolvedValue().IsValid() } -// Returns a string for the underlying value. If this value is not +// String returns a string for the underlying value. If this value is not // of type string, pongo2 tries to convert it. Currently the following // types for underlying values are supported: // @@ -111,9 +112,8 @@ func (v *Value) String() string { case reflect.Bool: if v.Bool() { return "True" - } else { - return "False" } + return "False" case reflect.Struct: if t, ok := v.Interface().(fmt.Stringer); ok { return t.String() @@ -124,7 +124,7 @@ func (v *Value) String() string { return v.getResolvedValue().String() } -// Returns the underlying value as an integer (converts the underlying +// Integer returns the underlying value as an integer (converts the underlying // value, if necessary). If it's not possible to convert the underlying value, // it will return 0. func (v *Value) Integer() int { @@ -148,7 +148,7 @@ func (v *Value) Integer() int { } } -// Returns the underlying value as a float (converts the underlying +// Float returns the underlying value as a float (converts the underlying // value, if necessary). If it's not possible to convert the underlying value, // it will return 0.0. func (v *Value) Float() float64 { @@ -172,7 +172,7 @@ func (v *Value) Float() float64 { } } -// Returns the underlying value as bool. If the value is not bool, false +// Bool returns the underlying value as bool. If the value is not bool, false // will always be returned. If you're looking for true/false-evaluation of the // underlying value, have a look on the IsTrue()-function. func (v *Value) Bool() bool { @@ -185,7 +185,7 @@ func (v *Value) Bool() bool { } } -// Tries to evaluate the underlying value the Pythonic-way: +// IsTrue tries to evaluate the underlying value the Pythonic-way: // // Returns TRUE in one the following cases: // @@ -217,7 +217,7 @@ func (v *Value) IsTrue() bool { } } -// Tries to negate the underlying value. It's mainly used for +// Negate tries to negate the underlying value. It's mainly used for // the NOT-operator and in conjunction with a call to // return_value.IsTrue() afterwards. // @@ -229,26 +229,26 @@ func (v *Value) Negate() *Value { reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: if v.Integer() != 0 { return AsValue(0) - } else { - return AsValue(1) } + return AsValue(1) case reflect.Float32, reflect.Float64: if v.Float() != 0.0 { return AsValue(float64(0.0)) - } else { - return AsValue(float64(1.1)) } + return AsValue(float64(1.1)) case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String: return AsValue(v.getResolvedValue().Len() == 0) case reflect.Bool: return AsValue(!v.getResolvedValue().Bool()) + case reflect.Struct: + return AsValue(false) default: logf("Value.IsTrue() not available for type: %s\n", v.getResolvedValue().Kind().String()) return AsValue(true) } } -// Returns the length for an array, chan, map, slice or string. +// Len returns the length for an array, chan, map, slice or string. // Otherwise it will return 0. func (v *Value) Len() int { switch v.getResolvedValue().Kind() { @@ -263,7 +263,7 @@ func (v *Value) Len() int { } } -// Slices an array, slice or string. Otherwise it will +// Slice slices an array, slice or string. Otherwise it will // return an empty []int. func (v *Value) Slice(i, j int) *Value { switch v.getResolvedValue().Kind() { @@ -278,7 +278,7 @@ func (v *Value) Slice(i, j int) *Value { } } -// Get the i-th item of an array, slice or string. Otherwise +// Index gets the i-th item of an array, slice or string. Otherwise // it will return NIL. func (v *Value) Index(i int) *Value { switch v.getResolvedValue().Kind() { @@ -301,7 +301,7 @@ func (v *Value) Index(i int) *Value { } } -// Checks whether the underlying value (which must be of type struct, map, +// Contains checks whether the underlying value (which must be of type struct, map, // string, array or slice) contains of another Value (e. g. used to check // whether a struct contains of a specific field or a map contains a specific key). // @@ -310,25 +310,32 @@ func (v *Value) Index(i int) *Value { func (v *Value) Contains(other *Value) bool { switch v.getResolvedValue().Kind() { case reflect.Struct: - field_value := v.getResolvedValue().FieldByName(other.String()) - return field_value.IsValid() + fieldValue := v.getResolvedValue().FieldByName(other.String()) + return fieldValue.IsValid() case reflect.Map: - var map_value reflect.Value + var mapValue reflect.Value switch other.Interface().(type) { case int: - map_value = v.getResolvedValue().MapIndex(other.getResolvedValue()) + mapValue = v.getResolvedValue().MapIndex(other.getResolvedValue()) case string: - map_value = v.getResolvedValue().MapIndex(other.getResolvedValue()) + mapValue = v.getResolvedValue().MapIndex(other.getResolvedValue()) default: logf("Value.Contains() does not support lookup type '%s'\n", other.getResolvedValue().Kind().String()) return false } - return map_value.IsValid() + return mapValue.IsValid() case reflect.String: return strings.Contains(v.getResolvedValue().String(), other.String()) - // TODO: reflect.Array, reflect.Slice + case reflect.Slice, reflect.Array: + for i := 0; i < v.getResolvedValue().Len(); i++ { + item := v.getResolvedValue().Index(i) + if other.Interface() == item.Interface() { + return true + } + } + return false default: logf("Value.Contains() not available for type: %s\n", v.getResolvedValue().Kind().String()) @@ -336,7 +343,7 @@ func (v *Value) Contains(other *Value) bool { } } -// Checks whether the underlying value is of type array, slice or string. +// CanSlice checks whether the underlying value is of type array, slice or string. // You normally would use CanSlice() before using the Slice() operation. func (v *Value) CanSlice() bool { switch v.getResolvedValue().Kind() { @@ -346,7 +353,7 @@ func (v *Value) CanSlice() bool { return false } -// Iterates over a map, array, slice or a string. It calls the +// Iterate iterates over a map, array, slice or a string. It calls the // function's first argument for every value with the following arguments: // // idx current 0-index @@ -357,16 +364,23 @@ func (v *Value) CanSlice() bool { // If the underlying value has no items or is not one of the types above, // the empty function (function's second argument) will be called. func (v *Value) Iterate(fn func(idx, count int, key, value *Value) bool, empty func()) { - v.IterateOrder(fn, empty, false) + v.IterateOrder(fn, empty, false, false) } -// Like Value.Iterate, but can iterate through an array/slice/string in reverse. Does +// IterateOrder behaves like Value.Iterate, but can iterate through an array/slice/string in reverse. Does // not affect the iteration through a map because maps don't have any particular order. -func (v *Value) IterateOrder(fn func(idx, count int, key, value *Value) bool, empty func(), reverse bool) { +// However, you can force an order using the `sorted` keyword (and even use `reversed sorted`). +func (v *Value) IterateOrder(fn func(idx, count int, key, value *Value) bool, empty func(), reverse bool, sorted bool) { switch v.getResolvedValue().Kind() { case reflect.Map: - // Reverse not needed for maps, since they are not ordered - keys := v.getResolvedValue().MapKeys() + keys := sortedKeys(v.getResolvedValue().MapKeys()) + if sorted { + if reverse { + sort.Sort(sort.Reverse(keys)) + } else { + sort.Sort(keys) + } + } keyLen := len(keys) for idx, key := range keys { value := v.getResolvedValue().MapIndex(key) @@ -379,19 +393,31 @@ func (v *Value) IterateOrder(fn func(idx, count int, key, value *Value) bool, em } return // done case reflect.Array, reflect.Slice: + var items valuesList + itemCount := v.getResolvedValue().Len() - if itemCount > 0 { + for i := 0; i < itemCount; i++ { + items = append(items, &Value{val: v.getResolvedValue().Index(i)}) + } + + if sorted { if reverse { - for i := itemCount - 1; i >= 0; i-- { - if !fn(i, itemCount, &Value{val: v.getResolvedValue().Index(i)}, nil) { - return - } - } + sort.Sort(sort.Reverse(items)) } else { - for i := 0; i < itemCount; i++ { - if !fn(i, itemCount, &Value{val: v.getResolvedValue().Index(i)}, nil) { - return - } + sort.Sort(items) + } + } else { + if reverse { + for i := 0; i < itemCount/2; i++ { + items[i], items[itemCount-1-i] = items[itemCount-1-i], items[i] + } + } + } + + if len(items) > 0 { + for idx, item := range items { + if !fn(idx, itemCount, item, nil) { + return } } } else { @@ -399,7 +425,12 @@ func (v *Value) IterateOrder(fn func(idx, count int, key, value *Value) bool, em } return // done case reflect.String: - // TODO: Not utf8-compatible (utf8-decoding neccessary) + if sorted { + // TODO(flosch): Handle sorted + panic("TODO: handle sort for type string") + } + + // TODO(flosch): Not utf8-compatible (utf8-decoding necessary) charCount := v.getResolvedValue().Len() if charCount > 0 { if reverse { @@ -425,7 +456,7 @@ func (v *Value) IterateOrder(fn func(idx, count int, key, value *Value) bool, em empty() } -// Gives you access to the underlying value. +// Interface gives you access to the underlying value. func (v *Value) Interface() interface{} { if v.val.IsValid() { return v.val.Interface() @@ -433,7 +464,57 @@ func (v *Value) Interface() interface{} { return nil } -// Checks whether two values are containing the same value or object. +// EqualValueTo checks whether two values are containing the same value or object. func (v *Value) EqualValueTo(other *Value) bool { + // comparison of uint with int fails using .Interface()-comparison (see issue #64) + if v.IsInteger() && other.IsInteger() { + return v.Integer() == other.Integer() + } return v.Interface() == other.Interface() } + +type sortedKeys []reflect.Value + +func (sk sortedKeys) Len() int { + return len(sk) +} + +func (sk sortedKeys) Less(i, j int) bool { + vi := &Value{val: sk[i]} + vj := &Value{val: sk[j]} + switch { + case vi.IsInteger() && vj.IsInteger(): + return vi.Integer() < vj.Integer() + case vi.IsFloat() && vj.IsFloat(): + return vi.Float() < vj.Float() + default: + return vi.String() < vj.String() + } +} + +func (sk sortedKeys) Swap(i, j int) { + sk[i], sk[j] = sk[j], sk[i] +} + +type valuesList []*Value + +func (vl valuesList) Len() int { + return len(vl) +} + +func (vl valuesList) Less(i, j int) bool { + vi := vl[i] + vj := vl[j] + switch { + case vi.IsInteger() && vj.IsInteger(): + return vi.Integer() < vj.Integer() + case vi.IsFloat() && vj.IsFloat(): + return vi.Float() < vj.Float() + default: + return vi.String() < vj.String() + } +} + +func (vl valuesList) Swap(i, j int) { + vl[i], vl[j] = vl[j], vl[i] +} diff --git a/vendor/github.com/flosch/pongo2/variable.go b/vendor/github.com/flosch/pongo2/variable.go index 9ec6a59..4a1ee69 100644 --- a/vendor/github.com/flosch/pongo2/variable.go +++ b/vendor/github.com/flosch/pongo2/variable.go @@ -1,11 +1,12 @@ package pongo2 import ( - "bytes" "fmt" "reflect" "strconv" "strings" + + "github.com/juju/errors" ) const ( @@ -13,13 +14,18 @@ const ( varTypeIdent ) +var ( + typeOfValuePtr = reflect.TypeOf(new(Value)) + typeOfExecCtxPtr = reflect.TypeOf(new(ExecutionContext)) +) + type variablePart struct { typ int s string i int - is_function_call bool - calling_args []functionCallArgument // needed for a function call, represents all argument nodes (INode supports nested function calls) + isFunctionCall bool + callingArgs []functionCallArgument // needed for a function call, represents all argument nodes (INode supports nested function calls) } type functionCallArgument interface { @@ -28,119 +34,121 @@ type functionCallArgument interface { // TODO: Add location tokens type stringResolver struct { - location_token *Token - val string + locationToken *Token + val string } type intResolver struct { - location_token *Token - val int + locationToken *Token + val int } type floatResolver struct { - location_token *Token - val float64 + locationToken *Token + val float64 } type boolResolver struct { - location_token *Token - val bool + locationToken *Token + val bool } type variableResolver struct { - location_token *Token + locationToken *Token parts []*variablePart } type nodeFilteredVariable struct { - location_token *Token + locationToken *Token resolver IEvaluator filterChain []*filterCall } type nodeVariable struct { - location_token *Token - expr IEvaluator + locationToken *Token + expr IEvaluator } -func (expr *nodeFilteredVariable) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { - value, err := expr.Evaluate(ctx) +type executionCtxEval struct{} + +func (v *nodeFilteredVariable) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { + value, err := v.Evaluate(ctx) if err != nil { return err } - buffer.WriteString(value.String()) + writer.WriteString(value.String()) return nil } -func (expr *variableResolver) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { - value, err := expr.Evaluate(ctx) +func (vr *variableResolver) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { + value, err := vr.Evaluate(ctx) if err != nil { return err } - buffer.WriteString(value.String()) + writer.WriteString(value.String()) return nil } -func (expr *stringResolver) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { - value, err := expr.Evaluate(ctx) +func (s *stringResolver) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { + value, err := s.Evaluate(ctx) if err != nil { return err } - buffer.WriteString(value.String()) + writer.WriteString(value.String()) return nil } -func (expr *intResolver) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { - value, err := expr.Evaluate(ctx) +func (i *intResolver) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { + value, err := i.Evaluate(ctx) if err != nil { return err } - buffer.WriteString(value.String()) + writer.WriteString(value.String()) return nil } -func (expr *floatResolver) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { - value, err := expr.Evaluate(ctx) +func (f *floatResolver) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { + value, err := f.Evaluate(ctx) if err != nil { return err } - buffer.WriteString(value.String()) + writer.WriteString(value.String()) return nil } -func (expr *boolResolver) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { - value, err := expr.Evaluate(ctx) +func (b *boolResolver) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { + value, err := b.Evaluate(ctx) if err != nil { return err } - buffer.WriteString(value.String()) + writer.WriteString(value.String()) return nil } func (v *nodeFilteredVariable) GetPositionToken() *Token { - return v.location_token + return v.locationToken } -func (v *variableResolver) GetPositionToken() *Token { - return v.location_token +func (vr *variableResolver) GetPositionToken() *Token { + return vr.locationToken } -func (v *stringResolver) GetPositionToken() *Token { - return v.location_token +func (s *stringResolver) GetPositionToken() *Token { + return s.locationToken } -func (v *intResolver) GetPositionToken() *Token { - return v.location_token +func (i *intResolver) GetPositionToken() *Token { + return i.locationToken } -func (v *floatResolver) GetPositionToken() *Token { - return v.location_token +func (f *floatResolver) GetPositionToken() *Token { + return f.locationToken } -func (v *boolResolver) GetPositionToken() *Token { - return v.location_token +func (b *boolResolver) GetPositionToken() *Token { + return b.locationToken } func (s *stringResolver) Evaluate(ctx *ExecutionContext) (*Value, *Error) { @@ -179,7 +187,7 @@ func (nv *nodeVariable) FilterApplied(name string) bool { return nv.expr.FilterApplied(name) } -func (nv *nodeVariable) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { +func (nv *nodeVariable) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { value, err := nv.expr.Evaluate(ctx) if err != nil { return err @@ -193,10 +201,14 @@ func (nv *nodeVariable) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Er } } - buffer.WriteString(value.String()) + writer.WriteString(value.String()) return nil } +func (executionCtxEval) Evaluate(ctx *ExecutionContext) (*Value, *Error) { + return AsValue(ctx), nil +} + func (vr *variableResolver) FilterApplied(name string) bool { return false } @@ -218,15 +230,15 @@ func (vr *variableResolver) String() string { func (vr *variableResolver) resolve(ctx *ExecutionContext) (*Value, error) { var current reflect.Value - var is_safe bool + var isSafe bool for idx, part := range vr.parts { if idx == 0 { // We're looking up the first part of the variable. // First we're having a look in our private // context (e. g. information provided by tags, like the forloop) - val, in_private := ctx.Private[vr.parts[0].s] - if !in_private { + val, inPrivate := ctx.Private[vr.parts[0].s] + if !inPrivate { // Nothing found? Then have a final lookup in the public context val = ctx.Public[vr.parts[0].s] } @@ -236,16 +248,16 @@ func (vr *variableResolver) resolve(ctx *ExecutionContext) (*Value, error) { // Before resolving the pointer, let's see if we have a method to call // Problem with resolving the pointer is we're changing the receiver - is_func := false + isFunc := false if part.typ == varTypeIdent { - func_value := current.MethodByName(part.s) - if func_value.IsValid() { - current = func_value - is_func = true + funcValue := current.MethodByName(part.s) + if funcValue.IsValid() { + current = funcValue + isFunc = true } } - if !is_func { + if !isFunc { // If current a pointer, resolve it if current.Kind() == reflect.Ptr { current = current.Elem() @@ -262,9 +274,14 @@ func (vr *variableResolver) resolve(ctx *ExecutionContext) (*Value, error) { // * slices/arrays/strings switch current.Kind() { case reflect.String, reflect.Array, reflect.Slice: - current = current.Index(part.i) + if part.i >= 0 && current.Len() > part.i { + current = current.Index(part.i) + } else { + // In Django, exceeding the length of a list is just empty. + return AsValue(nil), nil + } default: - return nil, fmt.Errorf("Can't access an index on type %s (variable %s)", + return nil, errors.Errorf("Can't access an index on type %s (variable %s)", current.Kind().String(), vr.String()) } case varTypeIdent: @@ -278,7 +295,7 @@ func (vr *variableResolver) resolve(ctx *ExecutionContext) (*Value, error) { case reflect.Map: current = current.MapIndex(reflect.ValueOf(part.s)) default: - return nil, fmt.Errorf("Can't access a field by name on type %s (variable %s)", + return nil, errors.Errorf("Can't access a field by name on type %s (variable %s)", current.Kind().String(), vr.String()) } default: @@ -295,10 +312,10 @@ func (vr *variableResolver) resolve(ctx *ExecutionContext) (*Value, error) { // If current is a reflect.ValueOf(pongo2.Value), then unpack it // Happens in function calls (as a return value) or by injecting // into the execution context (e.g. in a for-loop) - if current.Type() == reflect.TypeOf(&Value{}) { - tmp_value := current.Interface().(*Value) - current = tmp_value.val - is_safe = tmp_value.safe + if current.Type() == typeOfValuePtr { + tmpValue := current.Interface().(*Value) + current = tmpValue.val + isSafe = tmpValue.safe } // Check whether this is an interface and resolve it where required @@ -307,69 +324,73 @@ func (vr *variableResolver) resolve(ctx *ExecutionContext) (*Value, error) { } // Check if the part is a function call - if part.is_function_call || current.Kind() == reflect.Func { + if part.isFunctionCall || current.Kind() == reflect.Func { // Check for callable if current.Kind() != reflect.Func { - return nil, fmt.Errorf("'%s' is not a function (it is %s).", vr.String(), current.Kind().String()) + return nil, errors.Errorf("'%s' is not a function (it is %s)", vr.String(), current.Kind().String()) } // Check for correct function syntax and types // func(*Value, ...) *Value t := current.Type() + currArgs := part.callingArgs + + // If an implicit ExecCtx is needed + if t.NumIn() > 0 && t.In(0) == typeOfExecCtxPtr { + currArgs = append([]functionCallArgument{executionCtxEval{}}, currArgs...) + } // Input arguments - if len(part.calling_args) != t.NumIn() && !(len(part.calling_args) >= t.NumIn()-1 && t.IsVariadic()) { + if len(currArgs) != t.NumIn() && !(len(currArgs) >= t.NumIn()-1 && t.IsVariadic()) { return nil, - fmt.Errorf("Function input argument count (%d) of '%s' must be equal to the calling argument count (%d).", - t.NumIn(), vr.String(), len(part.calling_args)) + errors.Errorf("Function input argument count (%d) of '%s' must be equal to the calling argument count (%d).", + t.NumIn(), vr.String(), len(currArgs)) } // Output arguments if t.NumOut() != 1 { - return nil, fmt.Errorf("'%s' must have exactly 1 output argument.", vr.String()) + return nil, errors.Errorf("'%s' must have exactly 1 output argument", vr.String()) } // Evaluate all parameters - parameters := make([]reflect.Value, 0) + var parameters []reflect.Value - num_args := t.NumIn() - is_variadic := t.IsVariadic() - var fn_arg reflect.Type + numArgs := t.NumIn() + isVariadic := t.IsVariadic() + var fnArg reflect.Type - for idx, arg := range part.calling_args { + for idx, arg := range currArgs { pv, err := arg.Evaluate(ctx) if err != nil { return nil, err } - if is_variadic { + if isVariadic { if idx >= t.NumIn()-1 { - fn_arg = t.In(num_args - 1).Elem() + fnArg = t.In(numArgs - 1).Elem() } else { - fn_arg = t.In(idx) + fnArg = t.In(idx) } } else { - fn_arg = t.In(idx) + fnArg = t.In(idx) } - if fn_arg != reflect.TypeOf(new(Value)) { + if fnArg != typeOfValuePtr { // Function's argument is not a *pongo2.Value, then we have to check whether input argument is of the same type as the function's argument - if !is_variadic { - if fn_arg != reflect.TypeOf(pv.Interface()) && fn_arg.Kind() != reflect.Interface { - return nil, fmt.Errorf("Function input argument %d of '%s' must be of type %s or *pongo2.Value (not %T).", - idx, vr.String(), fn_arg.String(), pv.Interface()) - } else { - // Function's argument has another type, using the interface-value - parameters = append(parameters, reflect.ValueOf(pv.Interface())) + if !isVariadic { + if fnArg != reflect.TypeOf(pv.Interface()) && fnArg.Kind() != reflect.Interface { + return nil, errors.Errorf("Function input argument %d of '%s' must be of type %s or *pongo2.Value (not %T).", + idx, vr.String(), fnArg.String(), pv.Interface()) } + // Function's argument has another type, using the interface-value + parameters = append(parameters, reflect.ValueOf(pv.Interface())) } else { - if fn_arg != reflect.TypeOf(pv.Interface()) && fn_arg.Kind() != reflect.Interface { - return nil, fmt.Errorf("Function variadic input argument of '%s' must be of type %s or *pongo2.Value (not %T).", - vr.String(), fn_arg.String(), pv.Interface()) - } else { - // Function's argument has another type, using the interface-value - parameters = append(parameters, reflect.ValueOf(pv.Interface())) + if fnArg != reflect.TypeOf(pv.Interface()) && fnArg.Kind() != reflect.Interface { + return nil, errors.Errorf("Function variadic input argument of '%s' must be of type %s or *pongo2.Value (not %T).", + vr.String(), fnArg.String(), pv.Interface()) } + // Function's argument has another type, using the interface-value + parameters = append(parameters, reflect.ValueOf(pv.Interface())) } } else { // Function's argument is a *pongo2.Value @@ -377,31 +398,38 @@ func (vr *variableResolver) resolve(ctx *ExecutionContext) (*Value, error) { } } + // Check if any of the values are invalid + for _, p := range parameters { + if p.Kind() == reflect.Invalid { + return nil, errors.Errorf("Calling a function using an invalid parameter") + } + } + // Call it and get first return parameter back rv := current.Call(parameters)[0] - if rv.Type() != reflect.TypeOf(new(Value)) { + if rv.Type() != typeOfValuePtr { current = reflect.ValueOf(rv.Interface()) } else { // Return the function call value current = rv.Interface().(*Value).val - is_safe = rv.Interface().(*Value).safe + isSafe = rv.Interface().(*Value).safe } } + + if !current.IsValid() { + // Value is not valid (e. g. NIL value) + return AsValue(nil), nil + } } - if !current.IsValid() { - // Value is not valid (e. g. NIL value) - return AsValue(nil), nil - } - - return &Value{val: current, safe: is_safe}, nil + return &Value{val: current, safe: isSafe}, nil } func (vr *variableResolver) Evaluate(ctx *ExecutionContext) (*Value, *Error) { value, err := vr.resolve(ctx) if err != nil { - return AsValue(nil), ctx.Error(err.Error(), vr.location_token) + return AsValue(nil), ctx.Error(err.Error(), vr.locationToken) } return value, nil } @@ -436,7 +464,7 @@ func (p *Parser) parseVariableOrLiteral() (IEvaluator, *Error) { t := p.Current() if t == nil { - return nil, p.Error("Unexpected EOF, expected a number, string, keyword or identifier.", p.last_token) + return nil, p.Error("Unexpected EOF, expected a number, string, keyword or identifier.", p.lastToken) } // Is first part a number or a string, there's nothing to resolve (because there's only to return the value then) @@ -460,26 +488,26 @@ func (p *Parser) parseVariableOrLiteral() (IEvaluator, *Error) { return nil, p.Error(err.Error(), t) } fr := &floatResolver{ - location_token: t, - val: f, + locationToken: t, + val: f, } return fr, nil - } else { - i, err := strconv.Atoi(t.Val) - if err != nil { - return nil, p.Error(err.Error(), t) - } - nr := &intResolver{ - location_token: t, - val: i, - } - return nr, nil } + i, err := strconv.Atoi(t.Val) + if err != nil { + return nil, p.Error(err.Error(), t) + } + nr := &intResolver{ + locationToken: t, + val: i, + } + return nr, nil + case TokenString: p.Consume() sr := &stringResolver{ - location_token: t, - val: t.Val, + locationToken: t, + val: t.Val, } return sr, nil case TokenKeyword: @@ -487,14 +515,14 @@ func (p *Parser) parseVariableOrLiteral() (IEvaluator, *Error) { switch t.Val { case "true": br := &boolResolver{ - location_token: t, - val: true, + locationToken: t, + val: true, } return br, nil case "false": br := &boolResolver{ - location_token: t, - val: false, + locationToken: t, + val: false, } return br, nil default: @@ -503,7 +531,7 @@ func (p *Parser) parseVariableOrLiteral() (IEvaluator, *Error) { } resolver := &variableResolver{ - location_token: t, + locationToken: t, } // First part of a variable MUST be an identifier @@ -551,26 +579,26 @@ variableLoop: } else { // EOF return nil, p.Error("Unexpected EOF, expected either IDENTIFIER or NUMBER after DOT.", - p.last_token) + p.lastToken) } } else if p.Match(TokenSymbol, "(") != nil { // Function call // FunctionName '(' Comma-separated list of expressions ')' part := resolver.parts[len(resolver.parts)-1] - part.is_function_call = true + part.isFunctionCall = true argumentLoop: for { if p.Remaining() == 0 { - return nil, p.Error("Unexpected EOF, expected function call argument list.", p.last_token) + return nil, p.Error("Unexpected EOF, expected function call argument list.", p.lastToken) } if p.Peek(TokenSymbol, ")") == nil { // No closing bracket, so we're parsing an expression - expr_arg, err := p.ParseExpression() + exprArg, err := p.ParseExpression() if err != nil { return nil, err } - part.calling_args = append(part.calling_args, expr_arg) + part.callingArgs = append(part.callingArgs, exprArg) if p.Match(TokenSymbol, ")") != nil { // If there's a closing bracket after an expression, we will stop parsing the arguments @@ -601,7 +629,7 @@ variableLoop: func (p *Parser) parseVariableOrLiteralWithFilter() (*nodeFilteredVariable, *Error) { v := &nodeFilteredVariable{ - location_token: p.Current(), + locationToken: p.Current(), } // Parse the variable name @@ -621,15 +649,13 @@ filterLoop: } // Check sandbox filter restriction - if _, is_banned := p.template.set.bannedFilters[filter.name]; is_banned { + if _, isBanned := p.template.set.bannedFilters[filter.name]; isBanned { return nil, p.Error(fmt.Sprintf("Usage of filter '%s' is not allowed (sandbox restriction active).", filter.name), nil) } v.filterChain = append(v.filterChain, filter) continue filterLoop - - return nil, p.Error("This token is not allowed within a variable.", nil) } return v, nil @@ -637,7 +663,7 @@ filterLoop: func (p *Parser) parseVariableElement() (INode, *Error) { node := &nodeVariable{ - location_token: p.Current(), + locationToken: p.Current(), } p.Consume() // consume '{{' diff --git a/vendor/github.com/juju/errors/.gitignore b/vendor/github.com/juju/errors/.gitignore new file mode 100644 index 0000000..8365624 --- /dev/null +++ b/vendor/github.com/juju/errors/.gitignore @@ -0,0 +1,23 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test diff --git a/vendor/github.com/juju/errors/LICENSE b/vendor/github.com/juju/errors/LICENSE new file mode 100644 index 0000000..ade9307 --- /dev/null +++ b/vendor/github.com/juju/errors/LICENSE @@ -0,0 +1,191 @@ +All files in this repository are licensed as follows. If you contribute +to this repository, it is assumed that you license your contribution +under the same license unless you state otherwise. + +All files Copyright (C) 2015 Canonical Ltd. unless otherwise specified in the file. + +This software is licensed under the LGPLv3, included below. + +As a special exception to the GNU Lesser General Public License version 3 +("LGPL3"), the copyright holders of this Library give you permission to +convey to a third party a Combined Work that links statically or dynamically +to this Library without providing any Minimal Corresponding Source or +Minimal Application Code as set out in 4d or providing the installation +information set out in section 4e, provided that you comply with the other +provisions of LGPL3 and provided that you meet, for the Application the +terms and conditions of the license(s) which apply to the Application. + +Except as stated in this special exception, the provisions of LGPL3 will +continue to comply in full to this Library. If you modify this Library, you +may apply this exception to your version of this Library, but you are not +obliged to do so. If you do not wish to do so, delete this exception +statement from your version. This exception does not (and cannot) modify any +license terms which apply to the Application, with which you must still +comply. + + + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/vendor/github.com/juju/errors/Makefile b/vendor/github.com/juju/errors/Makefile new file mode 100644 index 0000000..ab7c2e6 --- /dev/null +++ b/vendor/github.com/juju/errors/Makefile @@ -0,0 +1,11 @@ +default: check + +check: + go test && go test -compiler gccgo + +docs: + godoc2md github.com/juju/errors > README.md + sed -i 's|\[godoc-link-here\]|[![GoDoc](https://godoc.org/github.com/juju/errors?status.svg)](https://godoc.org/github.com/juju/errors)|' README.md + + +.PHONY: default check docs diff --git a/vendor/github.com/juju/errors/README.md b/vendor/github.com/juju/errors/README.md new file mode 100644 index 0000000..782a6f4 --- /dev/null +++ b/vendor/github.com/juju/errors/README.md @@ -0,0 +1,543 @@ + +# errors + import "github.com/juju/errors" + +[![GoDoc](https://godoc.org/github.com/juju/errors?status.svg)](https://godoc.org/github.com/juju/errors) + +The juju/errors provides an easy way to annotate errors without losing the +orginal error context. + +The exported `New` and `Errorf` functions are designed to replace the +`errors.New` and `fmt.Errorf` functions respectively. The same underlying +error is there, but the package also records the location at which the error +was created. + +A primary use case for this library is to add extra context any time an +error is returned from a function. + + + if err := SomeFunc(); err != nil { + return err + } + +This instead becomes: + + + if err := SomeFunc(); err != nil { + return errors.Trace(err) + } + +which just records the file and line number of the Trace call, or + + + if err := SomeFunc(); err != nil { + return errors.Annotate(err, "more context") + } + +which also adds an annotation to the error. + +When you want to check to see if an error is of a particular type, a helper +function is normally exported by the package that returned the error, like the +`os` package does. The underlying cause of the error is available using the +`Cause` function. + + + os.IsNotExist(errors.Cause(err)) + +The result of the `Error()` call on an annotated error is the annotations joined +with colons, then the result of the `Error()` method for the underlying error +that was the cause. + + + err := errors.Errorf("original") + err = errors.Annotatef(err, "context") + err = errors.Annotatef(err, "more context") + err.Error() -> "more context: context: original" + +Obviously recording the file, line and functions is not very useful if you +cannot get them back out again. + + + errors.ErrorStack(err) + +will return something like: + + + first error + github.com/juju/errors/annotation_test.go:193: + github.com/juju/errors/annotation_test.go:194: annotation + github.com/juju/errors/annotation_test.go:195: + github.com/juju/errors/annotation_test.go:196: more context + github.com/juju/errors/annotation_test.go:197: + +The first error was generated by an external system, so there was no location +associated. The second, fourth, and last lines were generated with Trace calls, +and the other two through Annotate. + +Sometimes when responding to an error you want to return a more specific error +for the situation. + + + if err := FindField(field); err != nil { + return errors.Wrap(err, errors.NotFoundf(field)) + } + +This returns an error where the complete error stack is still available, and +`errors.Cause()` will return the `NotFound` error. + + + + + + +## func AlreadyExistsf +``` go +func AlreadyExistsf(format string, args ...interface{}) error +``` +AlreadyExistsf returns an error which satisfies IsAlreadyExists(). + + +## func Annotate +``` go +func Annotate(other error, message string) error +``` +Annotate is used to add extra context to an existing error. The location of +the Annotate call is recorded with the annotations. The file, line and +function are also recorded. + +For example: + + + if err := SomeFunc(); err != nil { + return errors.Annotate(err, "failed to frombulate") + } + + +## func Annotatef +``` go +func Annotatef(other error, format string, args ...interface{}) error +``` +Annotatef is used to add extra context to an existing error. The location of +the Annotate call is recorded with the annotations. The file, line and +function are also recorded. + +For example: + + + if err := SomeFunc(); err != nil { + return errors.Annotatef(err, "failed to frombulate the %s", arg) + } + + +## func Cause +``` go +func Cause(err error) error +``` +Cause returns the cause of the given error. This will be either the +original error, or the result of a Wrap or Mask call. + +Cause is the usual way to diagnose errors that may have been wrapped by +the other errors functions. + + +## func DeferredAnnotatef +``` go +func DeferredAnnotatef(err *error, format string, args ...interface{}) +``` +DeferredAnnotatef annotates the given error (when it is not nil) with the given +format string and arguments (like fmt.Sprintf). If *err is nil, DeferredAnnotatef +does nothing. This method is used in a defer statement in order to annotate any +resulting error with the same message. + +For example: + + + defer DeferredAnnotatef(&err, "failed to frombulate the %s", arg) + + +## func Details +``` go +func Details(err error) string +``` +Details returns information about the stack of errors wrapped by err, in +the format: + + + [{filename:99: error one} {otherfile:55: cause of error one}] + +This is a terse alternative to ErrorStack as it returns a single line. + + +## func ErrorStack +``` go +func ErrorStack(err error) string +``` +ErrorStack returns a string representation of the annotated error. If the +error passed as the parameter is not an annotated error, the result is +simply the result of the Error() method on that error. + +If the error is an annotated error, a multi-line string is returned where +each line represents one entry in the annotation stack. The full filename +from the call stack is used in the output. + + + first error + github.com/juju/errors/annotation_test.go:193: + github.com/juju/errors/annotation_test.go:194: annotation + github.com/juju/errors/annotation_test.go:195: + github.com/juju/errors/annotation_test.go:196: more context + github.com/juju/errors/annotation_test.go:197: + + +## func Errorf +``` go +func Errorf(format string, args ...interface{}) error +``` +Errorf creates a new annotated error and records the location that the +error is created. This should be a drop in replacement for fmt.Errorf. + +For example: + + + return errors.Errorf("validation failed: %s", message) + + +## func IsAlreadyExists +``` go +func IsAlreadyExists(err error) bool +``` +IsAlreadyExists reports whether the error was created with +AlreadyExistsf() or NewAlreadyExists(). + + +## func IsNotFound +``` go +func IsNotFound(err error) bool +``` +IsNotFound reports whether err was created with NotFoundf() or +NewNotFound(). + + +## func IsNotImplemented +``` go +func IsNotImplemented(err error) bool +``` +IsNotImplemented reports whether err was created with +NotImplementedf() or NewNotImplemented(). + + +## func IsNotSupported +``` go +func IsNotSupported(err error) bool +``` +IsNotSupported reports whether the error was created with +NotSupportedf() or NewNotSupported(). + + +## func IsNotValid +``` go +func IsNotValid(err error) bool +``` +IsNotValid reports whether the error was created with NotValidf() or +NewNotValid(). + + +## func IsUnauthorized +``` go +func IsUnauthorized(err error) bool +``` +IsUnauthorized reports whether err was created with Unauthorizedf() or +NewUnauthorized(). + + +## func Mask +``` go +func Mask(other error) error +``` +Mask hides the underlying error type, and records the location of the masking. + + +## func Maskf +``` go +func Maskf(other error, format string, args ...interface{}) error +``` +Mask masks the given error with the given format string and arguments (like +fmt.Sprintf), returning a new error that maintains the error stack, but +hides the underlying error type. The error string still contains the full +annotations. If you want to hide the annotations, call Wrap. + + +## func New +``` go +func New(message string) error +``` +New is a drop in replacement for the standard libary errors module that records +the location that the error is created. + +For example: + + + return errors.New("validation failed") + + +## func NewAlreadyExists +``` go +func NewAlreadyExists(err error, msg string) error +``` +NewAlreadyExists returns an error which wraps err and satisfies +IsAlreadyExists(). + + +## func NewNotFound +``` go +func NewNotFound(err error, msg string) error +``` +NewNotFound returns an error which wraps err that satisfies +IsNotFound(). + + +## func NewNotImplemented +``` go +func NewNotImplemented(err error, msg string) error +``` +NewNotImplemented returns an error which wraps err and satisfies +IsNotImplemented(). + + +## func NewNotSupported +``` go +func NewNotSupported(err error, msg string) error +``` +NewNotSupported returns an error which wraps err and satisfies +IsNotSupported(). + + +## func NewNotValid +``` go +func NewNotValid(err error, msg string) error +``` +NewNotValid returns an error which wraps err and satisfies IsNotValid(). + + +## func NewUnauthorized +``` go +func NewUnauthorized(err error, msg string) error +``` +NewUnauthorized returns an error which wraps err and satisfies +IsUnauthorized(). + + +## func NotFoundf +``` go +func NotFoundf(format string, args ...interface{}) error +``` +NotFoundf returns an error which satisfies IsNotFound(). + + +## func NotImplementedf +``` go +func NotImplementedf(format string, args ...interface{}) error +``` +NotImplementedf returns an error which satisfies IsNotImplemented(). + + +## func NotSupportedf +``` go +func NotSupportedf(format string, args ...interface{}) error +``` +NotSupportedf returns an error which satisfies IsNotSupported(). + + +## func NotValidf +``` go +func NotValidf(format string, args ...interface{}) error +``` +NotValidf returns an error which satisfies IsNotValid(). + + +## func Trace +``` go +func Trace(other error) error +``` +Trace adds the location of the Trace call to the stack. The Cause of the +resulting error is the same as the error parameter. If the other error is +nil, the result will be nil. + +For example: + + + if err := SomeFunc(); err != nil { + return errors.Trace(err) + } + + +## func Unauthorizedf +``` go +func Unauthorizedf(format string, args ...interface{}) error +``` +Unauthorizedf returns an error which satisfies IsUnauthorized(). + + +## func Forbiddenf +``` go +func Forbiddenf(format string, args ...interface{}) error +``` +Forbiddenf returns an error which satisfies IsForbidden(). + + +## func Wrap +``` go +func Wrap(other, newDescriptive error) error +``` +Wrap changes the Cause of the error. The location of the Wrap call is also +stored in the error stack. + +For example: + + + if err := SomeFunc(); err != nil { + newErr := &packageError{"more context", private_value} + return errors.Wrap(err, newErr) + } + + +## func Wrapf +``` go +func Wrapf(other, newDescriptive error, format string, args ...interface{}) error +``` +Wrapf changes the Cause of the error, and adds an annotation. The location +of the Wrap call is also stored in the error stack. + +For example: + + + if err := SomeFunc(); err != nil { + return errors.Wrapf(err, simpleErrorType, "invalid value %q", value) + } + + + +## type Err +``` go +type Err struct { + // contains filtered or unexported fields +} +``` +Err holds a description of an error along with information about +where the error was created. + +It may be embedded in custom error types to add extra information that +this errors package can understand. + + + + + + + + + +### func NewErr +``` go +func NewErr(format string, args ...interface{}) Err +``` +NewErr is used to return an Err for the purpose of embedding in other +structures. The location is not specified, and needs to be set with a call +to SetLocation. + +For example: + + + type FooError struct { + errors.Err + code int + } + + func NewFooError(code int) error { + err := &FooError{errors.NewErr("foo"), code} + err.SetLocation(1) + return err + } + + + + +### func (\*Err) Cause +``` go +func (e *Err) Cause() error +``` +The Cause of an error is the most recent error in the error stack that +meets one of these criteria: the original error that was raised; the new +error that was passed into the Wrap function; the most recently masked +error; or nil if the error itself is considered the Cause. Normally this +method is not invoked directly, but instead through the Cause stand alone +function. + + + +### func (\*Err) Error +``` go +func (e *Err) Error() string +``` +Error implements error.Error. + + + +### func (\*Err) Location +``` go +func (e *Err) Location() (filename string, line int) +``` +Location is the file and line of where the error was most recently +created or annotated. + + + +### func (\*Err) Message +``` go +func (e *Err) Message() string +``` +Message returns the message stored with the most recent location. This is +the empty string if the most recent call was Trace, or the message stored +with Annotate or Mask. + + + +### func (\*Err) SetLocation +``` go +func (e *Err) SetLocation(callDepth int) +``` +SetLocation records the source location of the error at callDepth stack +frames above the call. + + + +### func (\*Err) StackTrace +``` go +func (e *Err) StackTrace() []string +``` +StackTrace returns one string for each location recorded in the stack of +errors. The first value is the originating error, with a line for each +other annotation or tracing of the error. + + + +### func (\*Err) Underlying +``` go +func (e *Err) Underlying() error +``` +Underlying returns the previous error in the error stack, if any. A client +should not ever really call this method. It is used to build the error +stack and should not be introspected by client calls. Or more +specifically, clients should not depend on anything but the `Cause` of an +error. + + + + + + + + + +- - - +Generated by [godoc2md](http://godoc.org/github.com/davecheney/godoc2md) \ No newline at end of file diff --git a/vendor/github.com/juju/errors/doc.go b/vendor/github.com/juju/errors/doc.go new file mode 100644 index 0000000..35b119a --- /dev/null +++ b/vendor/github.com/juju/errors/doc.go @@ -0,0 +1,81 @@ +// Copyright 2013, 2014 Canonical Ltd. +// Licensed under the LGPLv3, see LICENCE file for details. + +/* +[godoc-link-here] + +The juju/errors provides an easy way to annotate errors without losing the +orginal error context. + +The exported `New` and `Errorf` functions are designed to replace the +`errors.New` and `fmt.Errorf` functions respectively. The same underlying +error is there, but the package also records the location at which the error +was created. + +A primary use case for this library is to add extra context any time an +error is returned from a function. + + if err := SomeFunc(); err != nil { + return err + } + +This instead becomes: + + if err := SomeFunc(); err != nil { + return errors.Trace(err) + } + +which just records the file and line number of the Trace call, or + + if err := SomeFunc(); err != nil { + return errors.Annotate(err, "more context") + } + +which also adds an annotation to the error. + +When you want to check to see if an error is of a particular type, a helper +function is normally exported by the package that returned the error, like the +`os` package does. The underlying cause of the error is available using the +`Cause` function. + + os.IsNotExist(errors.Cause(err)) + +The result of the `Error()` call on an annotated error is the annotations joined +with colons, then the result of the `Error()` method for the underlying error +that was the cause. + + err := errors.Errorf("original") + err = errors.Annotatef(err, "context") + err = errors.Annotatef(err, "more context") + err.Error() -> "more context: context: original" + +Obviously recording the file, line and functions is not very useful if you +cannot get them back out again. + + errors.ErrorStack(err) + +will return something like: + + first error + github.com/juju/errors/annotation_test.go:193: + github.com/juju/errors/annotation_test.go:194: annotation + github.com/juju/errors/annotation_test.go:195: + github.com/juju/errors/annotation_test.go:196: more context + github.com/juju/errors/annotation_test.go:197: + +The first error was generated by an external system, so there was no location +associated. The second, fourth, and last lines were generated with Trace calls, +and the other two through Annotate. + +Sometimes when responding to an error you want to return a more specific error +for the situation. + + if err := FindField(field); err != nil { + return errors.Wrap(err, errors.NotFoundf(field)) + } + +This returns an error where the complete error stack is still available, and +`errors.Cause()` will return the `NotFound` error. + +*/ +package errors diff --git a/vendor/github.com/juju/errors/error.go b/vendor/github.com/juju/errors/error.go new file mode 100644 index 0000000..b7df735 --- /dev/null +++ b/vendor/github.com/juju/errors/error.go @@ -0,0 +1,172 @@ +// Copyright 2014 Canonical Ltd. +// Licensed under the LGPLv3, see LICENCE file for details. + +package errors + +import ( + "fmt" + "reflect" + "runtime" +) + +// Err holds a description of an error along with information about +// where the error was created. +// +// It may be embedded in custom error types to add extra information that +// this errors package can understand. +type Err struct { + // message holds an annotation of the error. + message string + + // cause holds the cause of the error as returned + // by the Cause method. + cause error + + // previous holds the previous error in the error stack, if any. + previous error + + // file and line hold the source code location where the error was + // created. + file string + line int +} + +// NewErr is used to return an Err for the purpose of embedding in other +// structures. The location is not specified, and needs to be set with a call +// to SetLocation. +// +// For example: +// type FooError struct { +// errors.Err +// code int +// } +// +// func NewFooError(code int) error { +// err := &FooError{errors.NewErr("foo"), code} +// err.SetLocation(1) +// return err +// } +func NewErr(format string, args ...interface{}) Err { + return Err{ + message: fmt.Sprintf(format, args...), + } +} + +// NewErrWithCause is used to return an Err with case by other error for the purpose of embedding in other +// structures. The location is not specified, and needs to be set with a call +// to SetLocation. +// +// For example: +// type FooError struct { +// errors.Err +// code int +// } +// +// func (e *FooError) Annotate(format string, args ...interface{}) error { +// err := &FooError{errors.NewErrWithCause(e.Err, format, args...), e.code} +// err.SetLocation(1) +// return err +// }) +func NewErrWithCause(other error, format string, args ...interface{}) Err { + return Err{ + message: fmt.Sprintf(format, args...), + cause: Cause(other), + previous: other, + } +} + +// Location is the file and line of where the error was most recently +// created or annotated. +func (e *Err) Location() (filename string, line int) { + return e.file, e.line +} + +// Underlying returns the previous error in the error stack, if any. A client +// should not ever really call this method. It is used to build the error +// stack and should not be introspected by client calls. Or more +// specifically, clients should not depend on anything but the `Cause` of an +// error. +func (e *Err) Underlying() error { + return e.previous +} + +// The Cause of an error is the most recent error in the error stack that +// meets one of these criteria: the original error that was raised; the new +// error that was passed into the Wrap function; the most recently masked +// error; or nil if the error itself is considered the Cause. Normally this +// method is not invoked directly, but instead through the Cause stand alone +// function. +func (e *Err) Cause() error { + return e.cause +} + +// Message returns the message stored with the most recent location. This is +// the empty string if the most recent call was Trace, or the message stored +// with Annotate or Mask. +func (e *Err) Message() string { + return e.message +} + +// Error implements error.Error. +func (e *Err) Error() string { + // We want to walk up the stack of errors showing the annotations + // as long as the cause is the same. + err := e.previous + if !sameError(Cause(err), e.cause) && e.cause != nil { + err = e.cause + } + switch { + case err == nil: + return e.message + case e.message == "": + return err.Error() + } + return fmt.Sprintf("%s: %v", e.message, err) +} + +// Format implements fmt.Formatter +// When printing errors with %+v it also prints the stack trace. +// %#v unsurprisingly will print the real underlying type. +func (e *Err) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + switch { + case s.Flag('+'): + fmt.Fprintf(s, "%s", ErrorStack(e)) + return + case s.Flag('#'): + // avoid infinite recursion by wrapping e into a type + // that doesn't implement Formatter. + fmt.Fprintf(s, "%#v", (*unformatter)(e)) + return + } + fallthrough + case 's': + fmt.Fprintf(s, "%s", e.Error()) + } +} + +// helper for Format +type unformatter Err + +func (unformatter) Format() { /* break the fmt.Formatter interface */ } + +// SetLocation records the source location of the error at callDepth stack +// frames above the call. +func (e *Err) SetLocation(callDepth int) { + _, file, line, _ := runtime.Caller(callDepth + 1) + e.file = trimGoPath(file) + e.line = line +} + +// StackTrace returns one string for each location recorded in the stack of +// errors. The first value is the originating error, with a line for each +// other annotation or tracing of the error. +func (e *Err) StackTrace() []string { + return errorStack(e) +} + +// Ideally we'd have a way to check identity, but deep equals will do. +func sameError(e1, e2 error) bool { + return reflect.DeepEqual(e1, e2) +} diff --git a/vendor/github.com/juju/errors/error_test.go b/vendor/github.com/juju/errors/error_test.go new file mode 100644 index 0000000..ba9b718 --- /dev/null +++ b/vendor/github.com/juju/errors/error_test.go @@ -0,0 +1,178 @@ +// Copyright 2014 Canonical Ltd. +// Licensed under the LGPLv3, see LICENCE file for details. + +package errors_test + +import ( + "fmt" + "runtime" + + jc "github.com/juju/testing/checkers" + gc "gopkg.in/check.v1" + + "github.com/juju/errors" +) + +type errorsSuite struct{} + +var _ = gc.Suite(&errorsSuite{}) + +var someErr = errors.New("some error") //err varSomeErr + +func (*errorsSuite) TestErrorString(c *gc.C) { + for i, test := range []struct { + message string + generator func() error + expected string + }{ + { + message: "uncomparable errors", + generator: func() error { + err := errors.Annotatef(newNonComparableError("uncomparable"), "annotation") + return errors.Annotatef(err, "another") + }, + expected: "another: annotation: uncomparable", + }, { + message: "Errorf", + generator: func() error { + return errors.Errorf("first error") + }, + expected: "first error", + }, { + message: "annotated error", + generator: func() error { + err := errors.Errorf("first error") + return errors.Annotatef(err, "annotation") + }, + expected: "annotation: first error", + }, { + message: "test annotation format", + generator: func() error { + err := errors.Errorf("first %s", "error") + return errors.Annotatef(err, "%s", "annotation") + }, + expected: "annotation: first error", + }, { + message: "wrapped error", + generator: func() error { + err := newError("first error") + return errors.Wrap(err, newError("detailed error")) + }, + expected: "detailed error", + }, { + message: "wrapped annotated error", + generator: func() error { + err := errors.Errorf("first error") + err = errors.Annotatef(err, "annotated") + return errors.Wrap(err, fmt.Errorf("detailed error")) + }, + expected: "detailed error", + }, { + message: "annotated wrapped error", + generator: func() error { + err := errors.Errorf("first error") + err = errors.Wrap(err, fmt.Errorf("detailed error")) + return errors.Annotatef(err, "annotated") + }, + expected: "annotated: detailed error", + }, { + message: "traced, and annotated", + generator: func() error { + err := errors.New("first error") + err = errors.Trace(err) + err = errors.Annotate(err, "some context") + err = errors.Trace(err) + err = errors.Annotate(err, "more context") + return errors.Trace(err) + }, + expected: "more context: some context: first error", + }, { + message: "traced, and annotated, masked and annotated", + generator: func() error { + err := errors.New("first error") + err = errors.Trace(err) + err = errors.Annotate(err, "some context") + err = errors.Maskf(err, "masked") + err = errors.Annotate(err, "more context") + return errors.Trace(err) + }, + expected: "more context: masked: some context: first error", + }, + } { + c.Logf("%v: %s", i, test.message) + err := test.generator() + ok := c.Check(err.Error(), gc.Equals, test.expected) + if !ok { + c.Logf("%#v", test.generator()) + } + } +} + +type embed struct { + errors.Err +} + +func newEmbed(format string, args ...interface{}) *embed { + err := &embed{errors.NewErr(format, args...)} + err.SetLocation(1) + return err +} + +func (*errorsSuite) TestNewErr(c *gc.C) { + if runtime.Compiler == "gccgo" { + c.Skip("gccgo can't determine the location") + } + err := newEmbed("testing %d", 42) //err embedErr + c.Assert(err.Error(), gc.Equals, "testing 42") + c.Assert(errors.Cause(err), gc.Equals, err) + c.Assert(errors.Details(err), jc.Contains, tagToLocation["embedErr"].String()) +} + +func newEmbedWithCause(other error, format string, args ...interface{}) *embed { + err := &embed{errors.NewErrWithCause(other, format, args...)} + err.SetLocation(1) + return err +} + +func (*errorsSuite) TestNewErrWithCause(c *gc.C) { + if runtime.Compiler == "gccgo" { + c.Skip("gccgo can't determine the location") + } + causeErr := fmt.Errorf("external error") + err := newEmbedWithCause(causeErr, "testing %d", 43) //err embedCause + c.Assert(err.Error(), gc.Equals, "testing 43: external error") + c.Assert(errors.Cause(err), gc.Equals, causeErr) + c.Assert(errors.Details(err), jc.Contains, tagToLocation["embedCause"].String()) +} + +var _ error = (*embed)(nil) + +// This is an uncomparable error type, as it is a struct that supports the +// error interface (as opposed to a pointer type). +type error_ struct { + info string + slice []string +} + +// Create a non-comparable error +func newNonComparableError(message string) error { + return error_{info: message} +} + +func (e error_) Error() string { + return e.info +} + +func newError(message string) error { + return testError{message} +} + +// The testError is a value type error for ease of seeing results +// when the test fails. +type testError struct { + message string +} + +func (e testError) Error() string { + return e.message +} diff --git a/vendor/github.com/juju/errors/errortypes.go b/vendor/github.com/juju/errors/errortypes.go new file mode 100644 index 0000000..9b731c4 --- /dev/null +++ b/vendor/github.com/juju/errors/errortypes.go @@ -0,0 +1,309 @@ +// Copyright 2014 Canonical Ltd. +// Licensed under the LGPLv3, see LICENCE file for details. + +package errors + +import ( + "fmt" +) + +// wrap is a helper to construct an *wrapper. +func wrap(err error, format, suffix string, args ...interface{}) Err { + newErr := Err{ + message: fmt.Sprintf(format+suffix, args...), + previous: err, + } + newErr.SetLocation(2) + return newErr +} + +// notFound represents an error when something has not been found. +type notFound struct { + Err +} + +// NotFoundf returns an error which satisfies IsNotFound(). +func NotFoundf(format string, args ...interface{}) error { + return ¬Found{wrap(nil, format, " not found", args...)} +} + +// NewNotFound returns an error which wraps err that satisfies +// IsNotFound(). +func NewNotFound(err error, msg string) error { + return ¬Found{wrap(err, msg, "")} +} + +// IsNotFound reports whether err was created with NotFoundf() or +// NewNotFound(). +func IsNotFound(err error) bool { + err = Cause(err) + _, ok := err.(*notFound) + return ok +} + +// userNotFound represents an error when an inexistent user is looked up. +type userNotFound struct { + Err +} + +// UserNotFoundf returns an error which satisfies IsUserNotFound(). +func UserNotFoundf(format string, args ...interface{}) error { + return &userNotFound{wrap(nil, format, " user not found", args...)} +} + +// NewUserNotFound returns an error which wraps err and satisfies +// IsUserNotFound(). +func NewUserNotFound(err error, msg string) error { + return &userNotFound{wrap(err, msg, "")} +} + +// IsUserNotFound reports whether err was created with UserNotFoundf() or +// NewUserNotFound(). +func IsUserNotFound(err error) bool { + err = Cause(err) + _, ok := err.(*userNotFound) + return ok +} + +// unauthorized represents an error when an operation is unauthorized. +type unauthorized struct { + Err +} + +// Unauthorizedf returns an error which satisfies IsUnauthorized(). +func Unauthorizedf(format string, args ...interface{}) error { + return &unauthorized{wrap(nil, format, "", args...)} +} + +// NewUnauthorized returns an error which wraps err and satisfies +// IsUnauthorized(). +func NewUnauthorized(err error, msg string) error { + return &unauthorized{wrap(err, msg, "")} +} + +// IsUnauthorized reports whether err was created with Unauthorizedf() or +// NewUnauthorized(). +func IsUnauthorized(err error) bool { + err = Cause(err) + _, ok := err.(*unauthorized) + return ok +} + +// notImplemented represents an error when something is not +// implemented. +type notImplemented struct { + Err +} + +// NotImplementedf returns an error which satisfies IsNotImplemented(). +func NotImplementedf(format string, args ...interface{}) error { + return ¬Implemented{wrap(nil, format, " not implemented", args...)} +} + +// NewNotImplemented returns an error which wraps err and satisfies +// IsNotImplemented(). +func NewNotImplemented(err error, msg string) error { + return ¬Implemented{wrap(err, msg, "")} +} + +// IsNotImplemented reports whether err was created with +// NotImplementedf() or NewNotImplemented(). +func IsNotImplemented(err error) bool { + err = Cause(err) + _, ok := err.(*notImplemented) + return ok +} + +// alreadyExists represents and error when something already exists. +type alreadyExists struct { + Err +} + +// AlreadyExistsf returns an error which satisfies IsAlreadyExists(). +func AlreadyExistsf(format string, args ...interface{}) error { + return &alreadyExists{wrap(nil, format, " already exists", args...)} +} + +// NewAlreadyExists returns an error which wraps err and satisfies +// IsAlreadyExists(). +func NewAlreadyExists(err error, msg string) error { + return &alreadyExists{wrap(err, msg, "")} +} + +// IsAlreadyExists reports whether the error was created with +// AlreadyExistsf() or NewAlreadyExists(). +func IsAlreadyExists(err error) bool { + err = Cause(err) + _, ok := err.(*alreadyExists) + return ok +} + +// notSupported represents an error when something is not supported. +type notSupported struct { + Err +} + +// NotSupportedf returns an error which satisfies IsNotSupported(). +func NotSupportedf(format string, args ...interface{}) error { + return ¬Supported{wrap(nil, format, " not supported", args...)} +} + +// NewNotSupported returns an error which wraps err and satisfies +// IsNotSupported(). +func NewNotSupported(err error, msg string) error { + return ¬Supported{wrap(err, msg, "")} +} + +// IsNotSupported reports whether the error was created with +// NotSupportedf() or NewNotSupported(). +func IsNotSupported(err error) bool { + err = Cause(err) + _, ok := err.(*notSupported) + return ok +} + +// notValid represents an error when something is not valid. +type notValid struct { + Err +} + +// NotValidf returns an error which satisfies IsNotValid(). +func NotValidf(format string, args ...interface{}) error { + return ¬Valid{wrap(nil, format, " not valid", args...)} +} + +// NewNotValid returns an error which wraps err and satisfies IsNotValid(). +func NewNotValid(err error, msg string) error { + return ¬Valid{wrap(err, msg, "")} +} + +// IsNotValid reports whether the error was created with NotValidf() or +// NewNotValid(). +func IsNotValid(err error) bool { + err = Cause(err) + _, ok := err.(*notValid) + return ok +} + +// notProvisioned represents an error when something is not yet provisioned. +type notProvisioned struct { + Err +} + +// NotProvisionedf returns an error which satisfies IsNotProvisioned(). +func NotProvisionedf(format string, args ...interface{}) error { + return ¬Provisioned{wrap(nil, format, " not provisioned", args...)} +} + +// NewNotProvisioned returns an error which wraps err that satisfies +// IsNotProvisioned(). +func NewNotProvisioned(err error, msg string) error { + return ¬Provisioned{wrap(err, msg, "")} +} + +// IsNotProvisioned reports whether err was created with NotProvisionedf() or +// NewNotProvisioned(). +func IsNotProvisioned(err error) bool { + err = Cause(err) + _, ok := err.(*notProvisioned) + return ok +} + +// notAssigned represents an error when something is not yet assigned to +// something else. +type notAssigned struct { + Err +} + +// NotAssignedf returns an error which satisfies IsNotAssigned(). +func NotAssignedf(format string, args ...interface{}) error { + return ¬Assigned{wrap(nil, format, " not assigned", args...)} +} + +// NewNotAssigned returns an error which wraps err that satisfies +// IsNotAssigned(). +func NewNotAssigned(err error, msg string) error { + return ¬Assigned{wrap(err, msg, "")} +} + +// IsNotAssigned reports whether err was created with NotAssignedf() or +// NewNotAssigned(). +func IsNotAssigned(err error) bool { + err = Cause(err) + _, ok := err.(*notAssigned) + return ok +} + +// badRequest represents an error when a request has bad parameters. +type badRequest struct { + Err +} + +// BadRequestf returns an error which satisfies IsBadRequest(). +func BadRequestf(format string, args ...interface{}) error { + return &badRequest{wrap(nil, format, "", args...)} +} + +// NewBadRequest returns an error which wraps err that satisfies +// IsBadRequest(). +func NewBadRequest(err error, msg string) error { + return &badRequest{wrap(err, msg, "")} +} + +// IsBadRequest reports whether err was created with BadRequestf() or +// NewBadRequest(). +func IsBadRequest(err error) bool { + err = Cause(err) + _, ok := err.(*badRequest) + return ok +} + +// methodNotAllowed represents an error when an HTTP request +// is made with an inappropriate method. +type methodNotAllowed struct { + Err +} + +// MethodNotAllowedf returns an error which satisfies IsMethodNotAllowed(). +func MethodNotAllowedf(format string, args ...interface{}) error { + return &methodNotAllowed{wrap(nil, format, "", args...)} +} + +// NewMethodNotAllowed returns an error which wraps err that satisfies +// IsMethodNotAllowed(). +func NewMethodNotAllowed(err error, msg string) error { + return &methodNotAllowed{wrap(err, msg, "")} +} + +// IsMethodNotAllowed reports whether err was created with MethodNotAllowedf() or +// NewMethodNotAllowed(). +func IsMethodNotAllowed(err error) bool { + err = Cause(err) + _, ok := err.(*methodNotAllowed) + return ok +} + +// forbidden represents an error when a request cannot be completed because of +// missing privileges +type forbidden struct { + Err +} + +// Forbiddenf returns an error which satistifes IsForbidden() +func Forbiddenf(format string, args ...interface{}) error { + return &forbidden{wrap(nil, format, "", args...)} +} + +// NewForbidden returns an error which wraps err that satisfies +// IsForbidden(). +func NewForbidden(err error, msg string) error { + return &forbidden{wrap(err, msg, "")} +} + +// IsForbidden reports whether err was created with Forbiddenf() or +// NewForbidden(). +func IsForbidden(err error) bool { + err = Cause(err) + _, ok := err.(*forbidden) + return ok +} diff --git a/vendor/github.com/juju/errors/errortypes_test.go b/vendor/github.com/juju/errors/errortypes_test.go new file mode 100644 index 0000000..bb0ed36 --- /dev/null +++ b/vendor/github.com/juju/errors/errortypes_test.go @@ -0,0 +1,174 @@ +// Copyright 2013, 2014 Canonical Ltd. +// Licensed under the LGPLv3, see LICENCE file for details. + +package errors_test + +import ( + stderrors "errors" + "fmt" + "reflect" + "runtime" + + "github.com/juju/errors" + jc "github.com/juju/testing/checkers" + gc "gopkg.in/check.v1" +) + +// errorInfo holds information about a single error type: a satisfier +// function, wrapping and variable arguments constructors and message +// suffix. +type errorInfo struct { + satisfier func(error) bool + argsConstructor func(string, ...interface{}) error + wrapConstructor func(error, string) error + suffix string +} + +// allErrors holds information for all defined errors. When adding new +// errors, add them here as well to include them in tests. +var allErrors = []*errorInfo{ + {errors.IsNotFound, errors.NotFoundf, errors.NewNotFound, " not found"}, + {errors.IsUserNotFound, errors.UserNotFoundf, errors.NewUserNotFound, " user not found"}, + {errors.IsUnauthorized, errors.Unauthorizedf, errors.NewUnauthorized, ""}, + {errors.IsNotImplemented, errors.NotImplementedf, errors.NewNotImplemented, " not implemented"}, + {errors.IsAlreadyExists, errors.AlreadyExistsf, errors.NewAlreadyExists, " already exists"}, + {errors.IsNotSupported, errors.NotSupportedf, errors.NewNotSupported, " not supported"}, + {errors.IsNotValid, errors.NotValidf, errors.NewNotValid, " not valid"}, + {errors.IsNotProvisioned, errors.NotProvisionedf, errors.NewNotProvisioned, " not provisioned"}, + {errors.IsNotAssigned, errors.NotAssignedf, errors.NewNotAssigned, " not assigned"}, + {errors.IsMethodNotAllowed, errors.MethodNotAllowedf, errors.NewMethodNotAllowed, ""}, + {errors.IsBadRequest, errors.BadRequestf, errors.NewBadRequest, ""}, + {errors.IsForbidden, errors.Forbiddenf, errors.NewForbidden, ""}, +} + +type errorTypeSuite struct{} + +var _ = gc.Suite(&errorTypeSuite{}) + +func (t *errorInfo) satisfierName() string { + value := reflect.ValueOf(t.satisfier) + f := runtime.FuncForPC(value.Pointer()) + return f.Name() +} + +func (t *errorInfo) equal(t0 *errorInfo) bool { + if t0 == nil { + return false + } + return t.satisfierName() == t0.satisfierName() +} + +type errorTest struct { + err error + message string + errInfo *errorInfo +} + +func deferredAnnotatef(err error, format string, args ...interface{}) error { + errors.DeferredAnnotatef(&err, format, args...) + return err +} + +func mustSatisfy(c *gc.C, err error, errInfo *errorInfo) { + if errInfo != nil { + msg := fmt.Sprintf("%#v must satisfy %v", err, errInfo.satisfierName()) + c.Check(err, jc.Satisfies, errInfo.satisfier, gc.Commentf(msg)) + } +} + +func mustNotSatisfy(c *gc.C, err error, errInfo *errorInfo) { + if errInfo != nil { + msg := fmt.Sprintf("%#v must not satisfy %v", err, errInfo.satisfierName()) + c.Check(err, gc.Not(jc.Satisfies), errInfo.satisfier, gc.Commentf(msg)) + } +} + +func checkErrorMatches(c *gc.C, err error, message string, errInfo *errorInfo) { + if message == "" { + c.Check(err, gc.IsNil) + c.Check(errInfo, gc.IsNil) + } else { + c.Check(err, gc.ErrorMatches, message) + } +} + +func runErrorTests(c *gc.C, errorTests []errorTest, checkMustSatisfy bool) { + for i, t := range errorTests { + c.Logf("test %d: %T: %v", i, t.err, t.err) + checkErrorMatches(c, t.err, t.message, t.errInfo) + if checkMustSatisfy { + mustSatisfy(c, t.err, t.errInfo) + } + + // Check all other satisfiers to make sure none match. + for _, otherErrInfo := range allErrors { + if checkMustSatisfy && otherErrInfo.equal(t.errInfo) { + continue + } + mustNotSatisfy(c, t.err, otherErrInfo) + } + } +} + +func (*errorTypeSuite) TestDeferredAnnotatef(c *gc.C) { + // Ensure DeferredAnnotatef annotates the errors. + errorTests := []errorTest{} + for _, errInfo := range allErrors { + errorTests = append(errorTests, []errorTest{{ + deferredAnnotatef(nil, "comment"), + "", + nil, + }, { + deferredAnnotatef(stderrors.New("blast"), "comment"), + "comment: blast", + nil, + }, { + deferredAnnotatef(errInfo.argsConstructor("foo %d", 42), "comment %d", 69), + "comment 69: foo 42" + errInfo.suffix, + errInfo, + }, { + deferredAnnotatef(errInfo.argsConstructor(""), "comment"), + "comment: " + errInfo.suffix, + errInfo, + }, { + deferredAnnotatef(errInfo.wrapConstructor(stderrors.New("pow!"), "woo"), "comment"), + "comment: woo: pow!", + errInfo, + }}...) + } + + runErrorTests(c, errorTests, true) +} + +func (*errorTypeSuite) TestAllErrors(c *gc.C) { + errorTests := []errorTest{} + for _, errInfo := range allErrors { + errorTests = append(errorTests, []errorTest{{ + nil, + "", + nil, + }, { + errInfo.argsConstructor("foo %d", 42), + "foo 42" + errInfo.suffix, + errInfo, + }, { + errInfo.argsConstructor(""), + errInfo.suffix, + errInfo, + }, { + errInfo.wrapConstructor(stderrors.New("pow!"), "prefix"), + "prefix: pow!", + errInfo, + }, { + errInfo.wrapConstructor(stderrors.New("pow!"), ""), + "pow!", + errInfo, + }, { + errInfo.wrapConstructor(nil, "prefix"), + "prefix", + errInfo, + }}...) + } + + runErrorTests(c, errorTests, true) +} diff --git a/vendor/github.com/juju/errors/example_test.go b/vendor/github.com/juju/errors/example_test.go new file mode 100644 index 0000000..2a79cf4 --- /dev/null +++ b/vendor/github.com/juju/errors/example_test.go @@ -0,0 +1,23 @@ +// Copyright 2013, 2014 Canonical Ltd. +// Licensed under the LGPLv3, see LICENCE file for details. + +package errors_test + +import ( + "fmt" + + "github.com/juju/errors" +) + +func ExampleTrace() { + var err1 error = fmt.Errorf("something wicked this way comes") + var err2 error = nil + + // Tracing a non nil error will return an error + fmt.Println(errors.Trace(err1)) + // Tracing nil will return nil + fmt.Println(errors.Trace(err2)) + + // Output: something wicked this way comes + // +} diff --git a/vendor/github.com/juju/errors/export_test.go b/vendor/github.com/juju/errors/export_test.go new file mode 100644 index 0000000..db57ec8 --- /dev/null +++ b/vendor/github.com/juju/errors/export_test.go @@ -0,0 +1,12 @@ +// Copyright 2013, 2014 Canonical Ltd. +// Licensed under the LGPLv3, see LICENCE file for details. + +package errors + +// Since variables are declared before the init block, in order to get the goPath +// we need to return it rather than just reference it. +func GoPath() string { + return goPath +} + +var TrimGoPath = trimGoPath diff --git a/vendor/github.com/juju/errors/functions.go b/vendor/github.com/juju/errors/functions.go new file mode 100644 index 0000000..f86b09b --- /dev/null +++ b/vendor/github.com/juju/errors/functions.go @@ -0,0 +1,330 @@ +// Copyright 2014 Canonical Ltd. +// Licensed under the LGPLv3, see LICENCE file for details. + +package errors + +import ( + "fmt" + "strings" +) + +// New is a drop in replacement for the standard library errors module that records +// the location that the error is created. +// +// For example: +// return errors.New("validation failed") +// +func New(message string) error { + err := &Err{message: message} + err.SetLocation(1) + return err +} + +// Errorf creates a new annotated error and records the location that the +// error is created. This should be a drop in replacement for fmt.Errorf. +// +// For example: +// return errors.Errorf("validation failed: %s", message) +// +func Errorf(format string, args ...interface{}) error { + err := &Err{message: fmt.Sprintf(format, args...)} + err.SetLocation(1) + return err +} + +// Trace adds the location of the Trace call to the stack. The Cause of the +// resulting error is the same as the error parameter. If the other error is +// nil, the result will be nil. +// +// For example: +// if err := SomeFunc(); err != nil { +// return errors.Trace(err) +// } +// +func Trace(other error) error { + if other == nil { + return nil + } + err := &Err{previous: other, cause: Cause(other)} + err.SetLocation(1) + return err +} + +// Annotate is used to add extra context to an existing error. The location of +// the Annotate call is recorded with the annotations. The file, line and +// function are also recorded. +// +// For example: +// if err := SomeFunc(); err != nil { +// return errors.Annotate(err, "failed to frombulate") +// } +// +func Annotate(other error, message string) error { + if other == nil { + return nil + } + err := &Err{ + previous: other, + cause: Cause(other), + message: message, + } + err.SetLocation(1) + return err +} + +// Annotatef is used to add extra context to an existing error. The location of +// the Annotate call is recorded with the annotations. The file, line and +// function are also recorded. +// +// For example: +// if err := SomeFunc(); err != nil { +// return errors.Annotatef(err, "failed to frombulate the %s", arg) +// } +// +func Annotatef(other error, format string, args ...interface{}) error { + if other == nil { + return nil + } + err := &Err{ + previous: other, + cause: Cause(other), + message: fmt.Sprintf(format, args...), + } + err.SetLocation(1) + return err +} + +// DeferredAnnotatef annotates the given error (when it is not nil) with the given +// format string and arguments (like fmt.Sprintf). If *err is nil, DeferredAnnotatef +// does nothing. This method is used in a defer statement in order to annotate any +// resulting error with the same message. +// +// For example: +// +// defer DeferredAnnotatef(&err, "failed to frombulate the %s", arg) +// +func DeferredAnnotatef(err *error, format string, args ...interface{}) { + if *err == nil { + return + } + newErr := &Err{ + message: fmt.Sprintf(format, args...), + cause: Cause(*err), + previous: *err, + } + newErr.SetLocation(1) + *err = newErr +} + +// Wrap changes the Cause of the error. The location of the Wrap call is also +// stored in the error stack. +// +// For example: +// if err := SomeFunc(); err != nil { +// newErr := &packageError{"more context", private_value} +// return errors.Wrap(err, newErr) +// } +// +func Wrap(other, newDescriptive error) error { + err := &Err{ + previous: other, + cause: newDescriptive, + } + err.SetLocation(1) + return err +} + +// Wrapf changes the Cause of the error, and adds an annotation. The location +// of the Wrap call is also stored in the error stack. +// +// For example: +// if err := SomeFunc(); err != nil { +// return errors.Wrapf(err, simpleErrorType, "invalid value %q", value) +// } +// +func Wrapf(other, newDescriptive error, format string, args ...interface{}) error { + err := &Err{ + message: fmt.Sprintf(format, args...), + previous: other, + cause: newDescriptive, + } + err.SetLocation(1) + return err +} + +// Mask masks the given error with the given format string and arguments (like +// fmt.Sprintf), returning a new error that maintains the error stack, but +// hides the underlying error type. The error string still contains the full +// annotations. If you want to hide the annotations, call Wrap. +func Maskf(other error, format string, args ...interface{}) error { + if other == nil { + return nil + } + err := &Err{ + message: fmt.Sprintf(format, args...), + previous: other, + } + err.SetLocation(1) + return err +} + +// Mask hides the underlying error type, and records the location of the masking. +func Mask(other error) error { + if other == nil { + return nil + } + err := &Err{ + previous: other, + } + err.SetLocation(1) + return err +} + +// Cause returns the cause of the given error. This will be either the +// original error, or the result of a Wrap or Mask call. +// +// Cause is the usual way to diagnose errors that may have been wrapped by +// the other errors functions. +func Cause(err error) error { + var diag error + if err, ok := err.(causer); ok { + diag = err.Cause() + } + if diag != nil { + return diag + } + return err +} + +type causer interface { + Cause() error +} + +type wrapper interface { + // Message returns the top level error message, + // not including the message from the Previous + // error. + Message() string + + // Underlying returns the Previous error, or nil + // if there is none. + Underlying() error +} + +type locationer interface { + Location() (string, int) +} + +var ( + _ wrapper = (*Err)(nil) + _ locationer = (*Err)(nil) + _ causer = (*Err)(nil) +) + +// Details returns information about the stack of errors wrapped by err, in +// the format: +// +// [{filename:99: error one} {otherfile:55: cause of error one}] +// +// This is a terse alternative to ErrorStack as it returns a single line. +func Details(err error) string { + if err == nil { + return "[]" + } + var s []byte + s = append(s, '[') + for { + s = append(s, '{') + if err, ok := err.(locationer); ok { + file, line := err.Location() + if file != "" { + s = append(s, fmt.Sprintf("%s:%d", file, line)...) + s = append(s, ": "...) + } + } + if cerr, ok := err.(wrapper); ok { + s = append(s, cerr.Message()...) + err = cerr.Underlying() + } else { + s = append(s, err.Error()...) + err = nil + } + s = append(s, '}') + if err == nil { + break + } + s = append(s, ' ') + } + s = append(s, ']') + return string(s) +} + +// ErrorStack returns a string representation of the annotated error. If the +// error passed as the parameter is not an annotated error, the result is +// simply the result of the Error() method on that error. +// +// If the error is an annotated error, a multi-line string is returned where +// each line represents one entry in the annotation stack. The full filename +// from the call stack is used in the output. +// +// first error +// github.com/juju/errors/annotation_test.go:193: +// github.com/juju/errors/annotation_test.go:194: annotation +// github.com/juju/errors/annotation_test.go:195: +// github.com/juju/errors/annotation_test.go:196: more context +// github.com/juju/errors/annotation_test.go:197: +func ErrorStack(err error) string { + return strings.Join(errorStack(err), "\n") +} + +func errorStack(err error) []string { + if err == nil { + return nil + } + + // We want the first error first + var lines []string + for { + var buff []byte + if err, ok := err.(locationer); ok { + file, line := err.Location() + // Strip off the leading GOPATH/src path elements. + file = trimGoPath(file) + if file != "" { + buff = append(buff, fmt.Sprintf("%s:%d", file, line)...) + buff = append(buff, ": "...) + } + } + if cerr, ok := err.(wrapper); ok { + message := cerr.Message() + buff = append(buff, message...) + // If there is a cause for this error, and it is different to the cause + // of the underlying error, then output the error string in the stack trace. + var cause error + if err1, ok := err.(causer); ok { + cause = err1.Cause() + } + err = cerr.Underlying() + if cause != nil && !sameError(Cause(err), cause) { + if message != "" { + buff = append(buff, ": "...) + } + buff = append(buff, cause.Error()...) + } + } else { + buff = append(buff, err.Error()...) + err = nil + } + lines = append(lines, string(buff)) + if err == nil { + break + } + } + // reverse the lines to get the original error, which was at the end of + // the list, back to the start. + var result []string + for i := len(lines); i > 0; i-- { + result = append(result, lines[i-1]) + } + return result +} diff --git a/vendor/github.com/juju/errors/functions_test.go b/vendor/github.com/juju/errors/functions_test.go new file mode 100644 index 0000000..9f5a063 --- /dev/null +++ b/vendor/github.com/juju/errors/functions_test.go @@ -0,0 +1,305 @@ +// Copyright 2014 Canonical Ltd. +// Licensed under the LGPLv3, see LICENCE file for details. + +package errors_test + +import ( + "fmt" + "os" + "path/filepath" + "runtime" + "strings" + + jc "github.com/juju/testing/checkers" + gc "gopkg.in/check.v1" + + "github.com/juju/errors" +) + +type functionSuite struct { +} + +var _ = gc.Suite(&functionSuite{}) + +func (*functionSuite) TestNew(c *gc.C) { + err := errors.New("testing") //err newTest + c.Assert(err.Error(), gc.Equals, "testing") + c.Assert(errors.Cause(err), gc.Equals, err) + c.Assert(errors.Details(err), jc.Contains, tagToLocation["newTest"].String()) +} + +func (*functionSuite) TestErrorf(c *gc.C) { + err := errors.Errorf("testing %d", 42) //err errorfTest + c.Assert(err.Error(), gc.Equals, "testing 42") + c.Assert(errors.Cause(err), gc.Equals, err) + c.Assert(errors.Details(err), jc.Contains, tagToLocation["errorfTest"].String()) +} + +func (*functionSuite) TestTrace(c *gc.C) { + first := errors.New("first") + err := errors.Trace(first) //err traceTest + c.Assert(err.Error(), gc.Equals, "first") + c.Assert(errors.Cause(err), gc.Equals, first) + c.Assert(errors.Details(err), jc.Contains, tagToLocation["traceTest"].String()) + + c.Assert(errors.Trace(nil), gc.IsNil) +} + +func (*functionSuite) TestAnnotate(c *gc.C) { + first := errors.New("first") + err := errors.Annotate(first, "annotation") //err annotateTest + c.Assert(err.Error(), gc.Equals, "annotation: first") + c.Assert(errors.Cause(err), gc.Equals, first) + c.Assert(errors.Details(err), jc.Contains, tagToLocation["annotateTest"].String()) + + c.Assert(errors.Annotate(nil, "annotate"), gc.IsNil) +} + +func (*functionSuite) TestAnnotatef(c *gc.C) { + first := errors.New("first") + err := errors.Annotatef(first, "annotation %d", 2) //err annotatefTest + c.Assert(err.Error(), gc.Equals, "annotation 2: first") + c.Assert(errors.Cause(err), gc.Equals, first) + c.Assert(errors.Details(err), jc.Contains, tagToLocation["annotatefTest"].String()) + + c.Assert(errors.Annotatef(nil, "annotate"), gc.IsNil) +} + +func (*functionSuite) TestDeferredAnnotatef(c *gc.C) { + // NOTE: this test fails with gccgo + if runtime.Compiler == "gccgo" { + c.Skip("gccgo can't determine the location") + } + first := errors.New("first") + test := func() (err error) { + defer errors.DeferredAnnotatef(&err, "deferred %s", "annotate") + return first //err deferredAnnotate + } + err := test() + c.Assert(err.Error(), gc.Equals, "deferred annotate: first") + c.Assert(errors.Cause(err), gc.Equals, first) + c.Assert(errors.Details(err), jc.Contains, tagToLocation["deferredAnnotate"].String()) + + err = nil + errors.DeferredAnnotatef(&err, "deferred %s", "annotate") + c.Assert(err, gc.IsNil) +} + +func (*functionSuite) TestWrap(c *gc.C) { + first := errors.New("first") //err wrapFirst + detailed := errors.New("detailed") + err := errors.Wrap(first, detailed) //err wrapTest + c.Assert(err.Error(), gc.Equals, "detailed") + c.Assert(errors.Cause(err), gc.Equals, detailed) + c.Assert(errors.Details(err), jc.Contains, tagToLocation["wrapFirst"].String()) + c.Assert(errors.Details(err), jc.Contains, tagToLocation["wrapTest"].String()) +} + +func (*functionSuite) TestWrapOfNil(c *gc.C) { + detailed := errors.New("detailed") + err := errors.Wrap(nil, detailed) //err nilWrapTest + c.Assert(err.Error(), gc.Equals, "detailed") + c.Assert(errors.Cause(err), gc.Equals, detailed) + c.Assert(errors.Details(err), jc.Contains, tagToLocation["nilWrapTest"].String()) +} + +func (*functionSuite) TestWrapf(c *gc.C) { + first := errors.New("first") //err wrapfFirst + detailed := errors.New("detailed") + err := errors.Wrapf(first, detailed, "value %d", 42) //err wrapfTest + c.Assert(err.Error(), gc.Equals, "value 42: detailed") + c.Assert(errors.Cause(err), gc.Equals, detailed) + c.Assert(errors.Details(err), jc.Contains, tagToLocation["wrapfFirst"].String()) + c.Assert(errors.Details(err), jc.Contains, tagToLocation["wrapfTest"].String()) +} + +func (*functionSuite) TestWrapfOfNil(c *gc.C) { + detailed := errors.New("detailed") + err := errors.Wrapf(nil, detailed, "value %d", 42) //err nilWrapfTest + c.Assert(err.Error(), gc.Equals, "value 42: detailed") + c.Assert(errors.Cause(err), gc.Equals, detailed) + c.Assert(errors.Details(err), jc.Contains, tagToLocation["nilWrapfTest"].String()) +} + +func (*functionSuite) TestMask(c *gc.C) { + first := errors.New("first") + err := errors.Mask(first) //err maskTest + c.Assert(err.Error(), gc.Equals, "first") + c.Assert(errors.Cause(err), gc.Equals, err) + c.Assert(errors.Details(err), jc.Contains, tagToLocation["maskTest"].String()) + + c.Assert(errors.Mask(nil), gc.IsNil) +} + +func (*functionSuite) TestMaskf(c *gc.C) { + first := errors.New("first") + err := errors.Maskf(first, "masked %d", 42) //err maskfTest + c.Assert(err.Error(), gc.Equals, "masked 42: first") + c.Assert(errors.Cause(err), gc.Equals, err) + c.Assert(errors.Details(err), jc.Contains, tagToLocation["maskfTest"].String()) + + c.Assert(errors.Maskf(nil, "mask"), gc.IsNil) +} + +func (*functionSuite) TestCause(c *gc.C) { + c.Assert(errors.Cause(nil), gc.IsNil) + c.Assert(errors.Cause(someErr), gc.Equals, someErr) + + fmtErr := fmt.Errorf("simple") + c.Assert(errors.Cause(fmtErr), gc.Equals, fmtErr) + + err := errors.Wrap(someErr, fmtErr) + c.Assert(errors.Cause(err), gc.Equals, fmtErr) + + err = errors.Annotate(err, "annotated") + c.Assert(errors.Cause(err), gc.Equals, fmtErr) + + err = errors.Maskf(err, "maksed") + c.Assert(errors.Cause(err), gc.Equals, err) + + // Look for a file that we know isn't there. + dir := c.MkDir() + _, err = os.Stat(filepath.Join(dir, "not-there")) + c.Assert(os.IsNotExist(err), jc.IsTrue) + + err = errors.Annotatef(err, "wrap it") + // Now the error itself isn't a 'IsNotExist'. + c.Assert(os.IsNotExist(err), jc.IsFalse) + // However if we use the Check method, it is. + c.Assert(os.IsNotExist(errors.Cause(err)), jc.IsTrue) +} + +func (s *functionSuite) TestDetails(c *gc.C) { + if runtime.Compiler == "gccgo" { + c.Skip("gccgo can't determine the location") + } + c.Assert(errors.Details(nil), gc.Equals, "[]") + + otherErr := fmt.Errorf("other") + checkDetails(c, otherErr, "[{other}]") + + err0 := newEmbed("foo") //err TestStack#0 + checkDetails(c, err0, "[{$TestStack#0$: foo}]") + + err1 := errors.Annotate(err0, "bar") //err TestStack#1 + checkDetails(c, err1, "[{$TestStack#1$: bar} {$TestStack#0$: foo}]") + + err2 := errors.Trace(err1) //err TestStack#2 + checkDetails(c, err2, "[{$TestStack#2$: } {$TestStack#1$: bar} {$TestStack#0$: foo}]") +} + +type tracer interface { + StackTrace() []string +} + +func (*functionSuite) TestErrorStack(c *gc.C) { + for i, test := range []struct { + message string + generator func() error + expected string + tracer bool + }{ + { + message: "nil", + generator: func() error { + return nil + }, + }, { + message: "raw error", + generator: func() error { + return fmt.Errorf("raw") + }, + expected: "raw", + }, { + message: "single error stack", + generator: func() error { + return errors.New("first error") //err single + }, + expected: "$single$: first error", + tracer: true, + }, { + message: "annotated error", + generator: func() error { + err := errors.New("first error") //err annotated-0 + return errors.Annotate(err, "annotation") //err annotated-1 + }, + expected: "" + + "$annotated-0$: first error\n" + + "$annotated-1$: annotation", + tracer: true, + }, { + message: "wrapped error", + generator: func() error { + err := errors.New("first error") //err wrapped-0 + return errors.Wrap(err, newError("detailed error")) //err wrapped-1 + }, + expected: "" + + "$wrapped-0$: first error\n" + + "$wrapped-1$: detailed error", + tracer: true, + }, { + message: "annotated wrapped error", + generator: func() error { + err := errors.Errorf("first error") //err ann-wrap-0 + err = errors.Wrap(err, fmt.Errorf("detailed error")) //err ann-wrap-1 + return errors.Annotatef(err, "annotated") //err ann-wrap-2 + }, + expected: "" + + "$ann-wrap-0$: first error\n" + + "$ann-wrap-1$: detailed error\n" + + "$ann-wrap-2$: annotated", + tracer: true, + }, { + message: "traced, and annotated", + generator: func() error { + err := errors.New("first error") //err stack-0 + err = errors.Trace(err) //err stack-1 + err = errors.Annotate(err, "some context") //err stack-2 + err = errors.Trace(err) //err stack-3 + err = errors.Annotate(err, "more context") //err stack-4 + return errors.Trace(err) //err stack-5 + }, + expected: "" + + "$stack-0$: first error\n" + + "$stack-1$: \n" + + "$stack-2$: some context\n" + + "$stack-3$: \n" + + "$stack-4$: more context\n" + + "$stack-5$: ", + tracer: true, + }, { + message: "uncomparable, wrapped with a value error", + generator: func() error { + err := newNonComparableError("first error") //err mixed-0 + err = errors.Trace(err) //err mixed-1 + err = errors.Wrap(err, newError("value error")) //err mixed-2 + err = errors.Maskf(err, "masked") //err mixed-3 + err = errors.Annotate(err, "more context") //err mixed-4 + return errors.Trace(err) //err mixed-5 + }, + expected: "" + + "first error\n" + + "$mixed-1$: \n" + + "$mixed-2$: value error\n" + + "$mixed-3$: masked\n" + + "$mixed-4$: more context\n" + + "$mixed-5$: ", + tracer: true, + }, + } { + c.Logf("%v: %s", i, test.message) + err := test.generator() + expected := replaceLocations(test.expected) + stack := errors.ErrorStack(err) + ok := c.Check(stack, gc.Equals, expected) + if !ok { + c.Logf("%#v", err) + } + tracer, ok := err.(tracer) + c.Check(ok, gc.Equals, test.tracer) + if ok { + stackTrace := tracer.StackTrace() + c.Check(stackTrace, gc.DeepEquals, strings.Split(stack, "\n")) + } + } +} diff --git a/vendor/github.com/juju/errors/package_test.go b/vendor/github.com/juju/errors/package_test.go new file mode 100644 index 0000000..5bbb8f0 --- /dev/null +++ b/vendor/github.com/juju/errors/package_test.go @@ -0,0 +1,95 @@ +// Copyright 2013, 2014 Canonical Ltd. +// Licensed under the LGPLv3, see LICENCE file for details. + +package errors_test + +import ( + "fmt" + "io/ioutil" + "strings" + "testing" + + gc "gopkg.in/check.v1" + + "github.com/juju/errors" +) + +func Test(t *testing.T) { + gc.TestingT(t) +} + +func checkDetails(c *gc.C, err error, details string) { + c.Assert(err, gc.NotNil) + expectedDetails := replaceLocations(details) + c.Assert(errors.Details(err), gc.Equals, expectedDetails) +} + +func checkErr(c *gc.C, err, cause error, msg string, details string) { + c.Assert(err, gc.NotNil) + c.Assert(err.Error(), gc.Equals, msg) + c.Assert(errors.Cause(err), gc.Equals, cause) + expectedDetails := replaceLocations(details) + c.Assert(errors.Details(err), gc.Equals, expectedDetails) +} + +func replaceLocations(line string) string { + result := "" + for { + i := strings.Index(line, "$") + if i == -1 { + break + } + result += line[0:i] + line = line[i+1:] + i = strings.Index(line, "$") + if i == -1 { + panic("no second $") + } + result += location(line[0:i]).String() + line = line[i+1:] + } + result += line + return result +} + +func location(tag string) Location { + loc, ok := tagToLocation[tag] + if !ok { + panic(fmt.Sprintf("tag %q not found", tag)) + } + return loc +} + +type Location struct { + file string + line int +} + +func (loc Location) String() string { + return fmt.Sprintf("%s:%d", loc.file, loc.line) +} + +var tagToLocation = make(map[string]Location) + +func setLocationsForErrorTags(filename string) { + data, err := ioutil.ReadFile(filename) + if err != nil { + panic(err) + } + filename = "github.com/juju/errors/" + filename + lines := strings.Split(string(data), "\n") + for i, line := range lines { + if j := strings.Index(line, "//err "); j >= 0 { + tag := line[j+len("//err "):] + if _, found := tagToLocation[tag]; found { + panic(fmt.Sprintf("tag %q already processed previously", tag)) + } + tagToLocation[tag] = Location{file: filename, line: i + 1} + } + } +} + +func init() { + setLocationsForErrorTags("error_test.go") + setLocationsForErrorTags("functions_test.go") +} diff --git a/vendor/github.com/juju/errors/path.go b/vendor/github.com/juju/errors/path.go new file mode 100644 index 0000000..a7b726a --- /dev/null +++ b/vendor/github.com/juju/errors/path.go @@ -0,0 +1,38 @@ +// Copyright 2013, 2014 Canonical Ltd. +// Licensed under the LGPLv3, see LICENCE file for details. + +package errors + +import ( + "runtime" + "strings" +) + +// prefixSize is used internally to trim the user specific path from the +// front of the returned filenames from the runtime call stack. +var prefixSize int + +// goPath is the deduced path based on the location of this file as compiled. +var goPath string + +func init() { + _, file, _, ok := runtime.Caller(0) + if file == "?" { + return + } + if ok { + // We know that the end of the file should be: + // github.com/juju/errors/path.go + size := len(file) + suffix := len("github.com/juju/errors/path.go") + goPath = file[:size-suffix] + prefixSize = len(goPath) + } +} + +func trimGoPath(filename string) string { + if strings.HasPrefix(filename, goPath) { + return filename[prefixSize:] + } + return filename +} diff --git a/vendor/github.com/juju/errors/path_test.go b/vendor/github.com/juju/errors/path_test.go new file mode 100644 index 0000000..ef4f34f --- /dev/null +++ b/vendor/github.com/juju/errors/path_test.go @@ -0,0 +1,29 @@ +// Copyright 2013, 2014 Canonical Ltd. +// Licensed under the LGPLv3, see LICENCE file for details. + +package errors_test + +import ( + "path" + + gc "gopkg.in/check.v1" + + "github.com/juju/errors" +) + +type pathSuite struct{} + +var _ = gc.Suite(&pathSuite{}) + +func (*pathSuite) TestGoPathSet(c *gc.C) { + c.Assert(errors.GoPath(), gc.Not(gc.Equals), "") +} + +func (*pathSuite) TestTrimGoPath(c *gc.C) { + relativeImport := "github.com/foo/bar/baz.go" + filename := path.Join(errors.GoPath(), relativeImport) + c.Assert(errors.TrimGoPath(filename), gc.Equals, relativeImport) + + absoluteImport := "/usr/share/foo/bar/baz.go" + c.Assert(errors.TrimGoPath(absoluteImport), gc.Equals, absoluteImport) +}