URI: 
       Fix "context canceled" with partial - hugo - [fork] hugo port for 9front
  HTML git clone git@git.drkhsh.at/hugo.git
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
   DIR README
   DIR LICENSE
       ---
   DIR commit 3bbeb5688c4452a336af07be1e8cec44dac7d6ce
   DIR parent 184a67ac4704b88083c43a68a5f85800192d7ff1
  HTML Author: Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
       Date:   Sat,  4 Mar 2023 18:08:29 +0100
       
       Fix "context canceled" with partial
       
       Make sure the context used for timeouts isn't created based on the incoming
       context, as we have cases where this can cancel the context prematurely.
       
       Fixes #10789
       
       Diffstat:
         M lazy/init.go                        |       7 ++++---
         M lazy/init_test.go                   |       6 ------
         M resources/transform.go              |       4 ++--
         M tpl/partials/integration_test.go    |      28 ++++++++++++++++++++++++++++
         M tpl/partials/partials.go            |       9 ++++++---
       
       5 files changed, 40 insertions(+), 14 deletions(-)
       ---
   DIR diff --git 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
   DIR diff --git 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
                })
        
   DIR diff --git 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 {
   DIR diff --git 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")
       +
       +}
   DIR diff --git 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)
                        }