Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

text/template: builtins FuncMap is not used unless Parse is called #56571

Closed
tylermmorton opened this issue Nov 4, 2022 · 5 comments
Closed

Comments

@tylermmorton
Copy link

tylermmorton commented Nov 4, 2022

What version of Go are you using (go version)?

1.18.5

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

GOARCH="arm64"
GOOS="darwin"

What did you do?

https://go.dev/play/p/nsZQiC5SVB6

What did you expect to see?

The builtins FuncMap should be applied when calling template.New()

What did you see instead?

panic: template: base:3: function "eq" not defined

In my program I would like to memoize parsed templates as parse.Tree objects so that they can be applied to templates later. An alternative approach would be to keep the instance of template.Template around but doing so maintains references to the functions that are passed to it, which is undesirable.

I propose that the builtins function map be applied to the template as soon as New is called and the internal init method executes.

I'm happy to contribute the change if this behavior is deemed unintended 😁

@ianlancetaylor
Copy link
Contributor

Based on your example, you want to call the parser directly. You can tell the parser to not complain about functions by, after the call to parse.New adding a line parser.Mode = parse.SkipFuncCheck.

Closing because I don't think there is anything to change in the text/template package. Please comment if you disagree.

@ianlancetaylor ianlancetaylor closed this as not planned Won't fix, can't repro, duplicate, stale Nov 4, 2022
@tylermmorton
Copy link
Author

tylermmorton commented Nov 4, 2022

Hi @ianlancetaylor, the issue doesn't lie in the parser. The issue is declaring a new template via template.New() does not initialize the internal func map with the built-in functions. The map of built-in functions is also not exported from the text/template package, so its not accessible for use in a call to Funcs().

In scenarios where you're using text/template/parse.Parse over text/template.Template.Parse (like memoize the parse.Tree objects, for example) the built-in functions will never get set, so calling template.Execute will panic with the error mentioned above.

I hope that makes more sense. I don't believe this issue should be closed

@ianlancetaylor
Copy link
Contributor

In that case, could you show a test case that demonstrates the problem? The test case you showed earlier doesn't use template.New. It uses parse.New. Thanks.

@tylermmorton
Copy link
Author

tylermmorton commented Nov 5, 2022

Sure, here's the code from the playground link in the OP:

package main

import (
	"bytes"
	"text/template"
	"text/template/parse"
)

func main() {
	tmpl := `
	  {{ define "base" }}
	    {{ if eq "true" "true" }}
	    {{ end }}
	  {{ end }} 
	`

	parser := parse.New("base")
	treeSet := make(map[string]*parse.Tree)
	_, err := parser.Parse(tmpl, "{{", "}}", treeSet)
	if err != nil {
		panic(err)
	}

	t := template.New("base") 
	for k, v := range treeSet {
		_, err := t.AddParseTree(k, v)
		if err != nil {
			panic(err)
		}
	}

	buf := bytes.Buffer{}
	err = t.ExecuteTemplate(&buf, "base", nil)
	if err != nil {
		panic(err) // panic here: function "eq" not defined
	}
}

See the Parse function implementation of text/template:

trees, err := parse.Parse(t.name, text, t.leftDelim, t.rightDelim, t.parseFuncs, builtins())

builtins are added to the template's FuncMap when Parse is called, since the expectation is that those functions will always be available.

But what if you use text/template/parse.Parse directly? You can't access builtins because its an unexported function, as with all of the functions it references.

func builtins() FuncMap {

Perhaps we could move the addition of builtins to the template's internal init function so that these functions are always available, whether the parsing was handled internally by the template or not.

func (t *Template) init() {

On line 77:
t.Funcs(builtins())

Without this, theres not much utility to text/template/parse except for internal to the text/template package.

@ianlancetaylor
Copy link
Contributor

I saw the code in the playground link. When I run it I see a panic after a call to Parse.New and parser.Parse. It panics before the call to template.New. You are saying there is a problem with template.New, and I'm asking to see code that demonstrates that problem. Thanks.

@golang golang locked and limited conversation to collaborators Nov 5, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants