package python import ( "github.com/hashicorp/hcl/v2" "github.com/pulumi/pulumi/pkg/v3/codegen" "github.com/pulumi/pulumi/pkg/v3/codegen/hcl2" "github.com/pulumi/pulumi/pkg/v3/codegen/hcl2/model" "github.com/pulumi/pulumi/sdk/v3/go/common/util/contract" ) func isParameterReference(parameters codegen.Set, x model.Expression) bool { scopeTraversal, ok := x.(*model.ScopeTraversalExpression) if !ok { return false } return parameters.Has(scopeTraversal.Parts[0]) } // parseProxyApply attempts to match and rewrite the given parsed apply using the following patterns: // // - __apply(, eval(x, x[index])) -> [index] // - __apply(, eval(x, x.attr))) -> .attr // - __apply(traversal, eval(x, x.attr)) -> traversal.attr // // Each of these patterns matches an apply that can be handled by `pulumi.Output`'s `__getitem__` or `__getattr__` // method. The rewritten expressions will use those methods rather than calling `apply`. func (g *generator) parseProxyApply(parameters codegen.Set, args []model.Expression, then model.Expression) (model.Expression, bool) { if len(args) != 1 { return nil, false } arg := args[0] switch then := then.(type) { case *model.IndexExpression: // Rewrite `__apply(, eval(x, x[index]))` to `[index]`. if !isParameterReference(parameters, then.Collection) { return nil, false } then.Collection = arg case *model.ScopeTraversalExpression: if !isParameterReference(parameters, then) { return nil, false } switch arg := arg.(type) { case *model.RelativeTraversalExpression: arg.Traversal = append(arg.Traversal, then.Traversal[1:]...) arg.Parts = append(arg.Parts, then.Parts...) case *model.ScopeTraversalExpression: arg.Traversal = append(arg.Traversal, then.Traversal[1:]...) arg.Parts = append(arg.Parts, then.Parts...) } default: return nil, false } diags := arg.Typecheck(false) contract.Assert(len(diags) == 0) return arg, true } // lowerProxyApplies lowers certain calls to the apply intrinsic into proxied property accesses. Concretely, this // boils down to rewriting the following shapes // // - __apply(, eval(x, x[index])) // - __apply(, eval(x, x.attr))) // - __apply(scope.traversal, eval(x, x.attr)) // // into (respectively) // // - [index] // - .attr // - scope.traversal.attr // // These forms will use `pulumi.Output`'s `__getitem__` and `__getattr__` instead of calling `apply`. func (g *generator) lowerProxyApplies(expr model.Expression) (model.Expression, hcl.Diagnostics) { rewriter := func(expr model.Expression) (model.Expression, hcl.Diagnostics) { // Ignore the node if it is not a call to the apply intrinsic. apply, ok := expr.(*model.FunctionCallExpression) if !ok || apply.Name != hcl2.IntrinsicApply { return expr, nil } // Parse the apply call. args, then := hcl2.ParseApplyCall(apply) parameters := codegen.Set{} for _, p := range then.Parameters { parameters.Add(p) } // Attempt to match (call __apply (rvar) (call __applyArg 0)) if v, ok := g.parseProxyApply(parameters, args, then.Body); ok { return v, nil } return expr, nil } return model.VisitExpression(expr, model.IdentityVisitor, rewriter) }