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

proposal: text/template: make "template" action accept dynamic name as argument #33449

Closed
a8m opened this issue Aug 3, 2019 · 4 comments
Closed

Comments

@a8m
Copy link
Contributor

a8m commented Aug 3, 2019

Proposal

I'm suggesting to change the template action in text/template to accept "dynamic parameter" for its name; behave like a function that accepts 2 arguments. For example:

{{range $_, $p := .Templates}}
  {{$tmpl := printf "path/to/%s" $p.Name}}
  {{template $tmpl $}}
{{end}} 

The current solution for doing this, is to do something as described in StackOverflow:

tmpl := template.New("main")
tmpl.Funcs(template.FuncMap{
    "xtemplate": func(name string, v interface{}) (string, error) {
        var buf bytes.Buffer
        if err := tmpl.ExecuteTemplate(&buf, name, v); err != nil {
            return "", err
        }
        return buf.String(), nil
    },
})

I already hacked yesterday and added it to both the parser and the evaluator (exec.go), but I need to prettify it a bit, add proper tests and docs, and then I can make a CL (if it won't be rejected too quickly). It will be easier to consider this proposal when you see the additional complexity that comes with it.

Backward compatibility

This change doesn't break the compatibility promise of Go, but it requires to either add additional field to parse.TemplateNode or use parse.TemplateNode.Pipe for holding the template-name argument.
We can't change the type of parse.TemplateNode.Name, which is string.

@odeke-em odeke-em changed the title text/template: accept argument as name for template action proposal: text/template: make action accept dynamic name as argument Aug 3, 2019
@gopherbot gopherbot added this to the Proposal milestone Aug 3, 2019
@a8m a8m changed the title proposal: text/template: make action accept dynamic name as argument proposal: text/template: make "template" action accept dynamic name as argument Aug 3, 2019
@robpike
Copy link
Contributor

robpike commented Aug 5, 2019

This was deliberately left out of the design as it is infeasible to support in html/template. The template cannot be made secure when which template it is is unknown at compile time. In fact, it was implemented early on in text/template but was taken out for just this reason.

If it is reimplemented in text/template, the people will use text/template to get the feature and lose the safety of html/template, which is also unacceptable.

I strongly discourage doing this.

@rsc
Copy link
Contributor

rsc commented Aug 6, 2019

As Rob said, this proposal breaks a key design goal for text/template, namely the ability to implement the safety analysis in html/template. As such, this proposal can't be accepted as is.

If you can say more about the context in which this behavior arises, maybe we can suggest other approaches.

/cc @bep

@a8m
Copy link
Contributor Author

a8m commented Aug 6, 2019

Thanks for your feedback @robpike and @rsc. I just went over the html/template and was not aware about its escaping logic.

@rsc - I'm working on a tool that uses text/template and generates Go code as a result.
The tool loads (and parses) tens of templates, but (sometimes) uses only some of them based on a given configuration at runtime.
I took this approach, because I preferred to keep the templates simple, and hold most of the logic in Go program that does its checks and decides which template name to use.

Closing this issue.

@gwd
Copy link

gwd commented Sep 11, 2019

This was deliberately left out of the design as it is infeasible to support in html/template. The template cannot be made secure when which template it is is unknown at compile time.

Can you expand on this? Correct me if I'm wrong, but isn't it entirely possible to "call" a template before you define it? I.e.:

tmpl := template.New("x")
	tmpl, err := tmpl.Parse(`
	{{define "main"}}
	{{template "foo"}}
	{{end}}`)
	if err != nil {
	    fmt.Print("Parsing main failed: ", err)
	    return
	}
	
	tmpl, err = tmpl.Parse(`
	{{define "foo"}}
	Bar
	{{end}}`)
	
	if err != nil {
	    fmt.Print("Parsing foo failed: ", err)
	    return
	}

	buf := bytes.NewBuffer(nil)

	tmpl.ExecuteTemplate(buf, "main", nil)
	
	fmt.Printf(buf.String())

...seems to print out 'Bar' as expected. Are you not compiling "main" and "foo" separately, when Parse() is called? And would this not work in the opposite order? How do you know the contents of "foo" when you compile "main", and if you reversed the order, how would you know the context in which "foo" was being called?

@golang golang locked and limited conversation to collaborators Sep 10, 2020
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

5 participants