if err != nil {
return includeResult{err: err}
}
- return ns.doInclude(ctx, v, dataList...)
+ return ns.doInclude(ctx, "", v, dataList...)
}
func (ns *Namespace) lookup(name string) (*tplimpl.TemplInfo, error) {
// include is a helper function that lookups and executes the named partial.
// Returns the final template name and the rendered output.
-func (ns *Namespace) doInclude(ctx context.Context, templ *tplimpl.TemplInfo, dataList ...any) includeResult {
+func (ns *Namespace) doInclude(ctx context.Context, key string, templ *tplimpl.TemplInfo, dataList ...any) includeResult {
var data any
if len(dataList) > 0 {
data = dataList[0]
w = b
}
- if err := ns.deps.GetTemplateStore().ExecuteWithContext(ctx, templ, w, data); err != nil {
+ if err := ns.deps.GetTemplateStore().ExecuteWithContextAndKey(ctx, key, templ, w, data); err != nil {
return includeResult{err: err}
}
Name: name,
Variants: variants,
}
+ keyString := key.Key()
+
depsManagerIn := tpl.Context.GetDependencyManagerInCurrentScope(ctx)
ti, err := ns.lookup(name)
if err != nil {
if parent := tpl.Context.CurrentTemplate.Get(ctx); parent != nil {
for parent != nil {
- if parent.CurrentTemplateInfoOps == ti {
+ if parent.CurrentTemplateInfoOps == ti && parent.Key == keyString {
// This will deadlock if we continue.
return nil, fmt.Errorf("circular call stack detected in partial %q", ti.Filename())
}
}
}
- r, found, err := ns.cachedPartials.cache.GetOrCreate(key.Key(), func(string) (includeResult, error) {
+ r, found, err := ns.cachedPartials.cache.GetOrCreate(keyString, func(string) (includeResult, error) {
var depsManagerShared identity.Manager
if ns.deps.Conf.Watching() {
// We need to create a shared dependency manager to pass downwards
depsManagerShared = identity.NewManager("partials")
ctx = tpl.Context.DependencyManagerScopedProvider.Set(ctx, depsManagerShared.(identity.DependencyManagerScopedProvider))
}
- r := ns.doInclude(ctx, ti, context)
+ r := ns.doInclude(ctx, keyString, ti, context)
if ns.deps.Conf.Watching() {
r.mangager = depsManagerShared
}
b.Assert(err.Error(), qt.Contains, `error calling partialCached: circular call stack detected in partial`)
}
+// See Issue #13889
+func TestIncludeCachedDifferentKey(t *testing.T) {
+ t.Parallel()
+
+ files := `
+-- config.toml --
+baseURL = 'http://example.com/'
+timeout = '200ms'
+-- layouts/index.html --
+{{ partialCached "foo.html" "a" "a" }}
+-- layouts/partials/foo.html --
+{{ if eq . "a" }}
+{{ partialCached "bar.html" . }}
+{{ else }}
+DONE
+{{ end }}
+-- layouts/partials/bar.html --
+{{ partialCached "foo.html" "b" "b" }}
+ `
+ b := hugolib.Test(t, files)
+
+ b.AssertFileContent("public/index.html", `
+DONE
+`)
+}
+
// See Issue #10789
func TestReturnExecuteFromTemplateInPartial(t *testing.T) {
t.Parallel()
}
func (t *TemplateStore) ExecuteWithContext(ctx context.Context, ti *TemplInfo, wr io.Writer, data any) error {
+ return t.ExecuteWithContextAndKey(ctx, "", ti, wr, data)
+}
+
+func (t *TemplateStore) ExecuteWithContextAndKey(ctx context.Context, key string, ti *TemplInfo, wr io.Writer, data any) error {
defer func() {
ti.executionCounter.Add(1)
if ti.base != nil {
currentTi := &tpl.CurrentTemplateInfo{
Parent: parent,
Level: level,
+ Key: key,
CurrentTemplateInfoOps: ti,
}