diff --git a/lazy/init.go b/lazy/init.go index 4de2a83f74f..bfb9c4e0791 100644 --- a/lazy/init.go +++ b/lazy/init.go @@ -180,14 +180,15 @@ func (ini *Init) checkDone() { } func (ini *Init) withTimeout(ctx context.Context, timeout time.Duration, f func(ctx context.Context) (any, error)) (any, error) { - ctx, cancel := context.WithTimeout(ctx, timeout) + // Create a new context with a timeout not connected to the incoming context. + waitCtx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() c := make(chan verr, 1) go func() { v, err := f(ctx) select { - case <-ctx.Done(): + case <-waitCtx.Done(): return default: c <- verr{v: v, err: err} @@ -195,7 +196,7 @@ func (ini *Init) withTimeout(ctx context.Context, timeout time.Duration, f func( }() select { - case <-ctx.Done(): + case <-waitCtx.Done(): return nil, errors.New("timed out initializing value. You may have a circular loop in a shortcode, or your site may have resources that take longer to build than the `timeout` limit in your Hugo config file.") case ve := <-c: return ve.v, ve.err diff --git a/lazy/init_test.go b/lazy/init_test.go index 499ea2cce7b..efc329d797e 100644 --- a/lazy/init_test.go +++ b/lazy/init_test.go @@ -126,12 +126,6 @@ func TestInitAddWithTimeoutTimeout(t *testing.T) { init := New().AddWithTimeout(100*time.Millisecond, func(ctx context.Context) (any, error) { time.Sleep(500 * time.Millisecond) - select { - case <-ctx.Done(): - return nil, nil - default: - } - t.Fatal("slept") return nil, nil }) diff --git a/resources/transform.go b/resources/transform.go index 4ab51485e1b..fe438e36614 100644 --- a/resources/transform.go +++ b/resources/transform.go @@ -164,12 +164,12 @@ type resourceAdapter struct { *resourceAdapterInner } -func (r *resourceAdapter) Content(context.Context) (any, error) { +func (r *resourceAdapter) Content(ctx context.Context) (any, error) { r.init(false, true) if r.transformationsErr != nil { return nil, r.transformationsErr } - return r.target.Content(context.Background()) + return r.target.Content(ctx) } func (r *resourceAdapter) Err() resource.ResourceError { diff --git a/tpl/partials/integration_test.go b/tpl/partials/integration_test.go index fcebe6c05d8..3dbaf2ce41e 100644 --- a/tpl/partials/integration_test.go +++ b/tpl/partials/integration_test.go @@ -324,3 +324,31 @@ timeout = '200ms' b.Assert(err.Error(), qt.Contains, "timed out") } + +// See Issue #10789 +func TestReturnExecuteFromTemplateInPartial(t *testing.T) { + t.Parallel() + + files := ` +-- config.toml -- +baseURL = 'http://example.com/' +-- layouts/index.html -- +{{ $r := partial "foo" }} +FOO:{{ $r.Content }} +-- layouts/partials/foo.html -- +{{ $r := §§{{ partial "bar" }}§§ | resources.FromString "bar.html" | resources.ExecuteAsTemplate "bar.html" . }} +{{ return $r }} +-- layouts/partials/bar.html -- +BAR + ` + + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + }, + ).Build() + + b.AssertFileContent("public/index.html", "OO:BAR") + +} diff --git a/tpl/partials/partials.go b/tpl/partials/partials.go index d9a826aa41b..26ce0f5c66d 100644 --- a/tpl/partials/partials.go +++ b/tpl/partials/partials.go @@ -129,7 +129,10 @@ func (ns *Namespace) Include(ctx context.Context, name string, contextList ...an } func (ns *Namespace) includWithTimeout(ctx context.Context, name string, dataList ...any) includeResult { - ctx, cancel := context.WithTimeout(ctx, ns.deps.Timeout) + // There are situation where the ctx we pass on to the partial lives longer than + // the partial itself. For example, when the partial returns the result from reosurces.ExecuteAsTemplate. + // Because of that, create a completely new context here. + timeoutCtx, cancel := context.WithTimeout(context.Background(), ns.deps.Timeout) defer cancel() res := make(chan includeResult, 1) @@ -141,8 +144,8 @@ func (ns *Namespace) includWithTimeout(ctx context.Context, name string, dataLis select { case r := <-res: return r - case <-ctx.Done(): - err := ctx.Err() + case <-timeoutCtx.Done(): + err := timeoutCtx.Err() if err == context.DeadlineExceeded { err = fmt.Errorf("partial %q timed out after %s. This is most likely due to infinite recursion. If this is just a slow template, you can try to increase the 'timeout' config setting.", name, ns.deps.Timeout) }