2020-04-03 06:29:05 +00:00
|
|
|
package python
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"bytes"
|
2020-04-07 02:43:16 +00:00
|
|
|
"fmt"
|
2020-04-03 06:29:05 +00:00
|
|
|
"io"
|
|
|
|
"math/big"
|
|
|
|
"strings"
|
2024-03-29 14:24:29 +00:00
|
|
|
"unicode"
|
2020-04-03 06:29:05 +00:00
|
|
|
|
|
|
|
"github.com/hashicorp/hcl/v2"
|
2020-04-07 02:43:16 +00:00
|
|
|
"github.com/hashicorp/hcl/v2/hclsyntax"
|
2022-04-18 09:03:42 +00:00
|
|
|
"github.com/zclconf/go-cty/cty"
|
|
|
|
|
2021-03-17 13:20:05 +00:00
|
|
|
"github.com/pulumi/pulumi/pkg/v3/codegen/hcl2/model"
|
2021-09-30 03:11:56 +00:00
|
|
|
"github.com/pulumi/pulumi/pkg/v3/codegen/pcl"
|
2022-04-18 09:03:42 +00:00
|
|
|
"github.com/pulumi/pulumi/pkg/v3/codegen/schema"
|
2021-03-17 13:20:05 +00:00
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
|
2020-04-03 06:29:05 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type nameInfo int
|
|
|
|
|
2020-04-25 05:04:24 +00:00
|
|
|
func (nameInfo) Format(name string) string {
|
|
|
|
return PyName(name)
|
2020-04-03 06:29:05 +00:00
|
|
|
}
|
|
|
|
|
2020-08-06 22:09:23 +00:00
|
|
|
func (g *generator) lowerExpression(expr model.Expression, typ model.Type) (model.Expression, []*quoteTemp) {
|
2020-04-03 06:29:05 +00:00
|
|
|
// TODO(pdg): diagnostics
|
|
|
|
|
2021-09-30 03:11:56 +00:00
|
|
|
expr = pcl.RewritePropertyReferences(expr)
|
[program-gen] Emit Output-returning JSON serialization methods without rewriting applies (#15371)
### Description
A while ago we started implementing [specialized JSON serialization
methods](https://github.com/pulumi/pulumi/issues/12519) for Pulumi
programs which can accept nested outputs without having to rewrite and
combine applies.
- `Output.SerializeJson` in .NET
- `pulumi.jsonStringify` in nodejs
- `pulumi.Output.json_dumps` in Python
This PR extends program-gen for TypeScript, C# and Python to start
emitting these JSON serialization functions (when necessary). The PR
special-cases the `toJSON` PCL function when rewriting applies so that
nested outputs aren't rewritted.
Example PCL program and generated results:
> Also check out the downstream codegen tests to see improved generated
examples
```
resource vpc "aws:ec2:Vpc" {
cidrBlock = "10.100.0.0/16"
instanceTenancy = "default"
}
resource policy "aws:iam/policy:Policy" {
description = "test"
policy = toJSON({
"Version" = "2012-10-17"
"Interpolated" = "arn:${vpc.arn}:value"
"Value" = vpc.id
})
}
```
### Generated TypeScript Before
```typescript
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const vpc = new aws.ec2.Vpc("vpc", {
cidrBlock: "10.100.0.0/16",
instanceTenancy: "default",
});
const policy = new aws.iam.Policy("policy", {
description: "test",
policy: pulumi.all([vpc.arn, vpc.id]).apply(([arn, id]) => JSON.stringify({
Version: "2012-10-17",
Interpolated: `arn:${arn}:value`,
Value: id,
})),
});
```
### Generated TypeScript After
```typescript
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const vpc = new aws.ec2.Vpc("vpc", {
cidrBlock: "10.100.0.0/16",
instanceTenancy: "default",
});
const policy = new aws.iam.Policy("policy", {
description: "test",
policy: pulumi.jsonStringify({
Version: "2012-10-17",
Interpolated: pulumi.interpolate`arn:${vpc.arn}:value`,
Value: vpc.id,
}),
});
```
### Generated Python Before
```python
import pulumi
import json
import pulumi_aws as aws
vpc = aws.ec2.Vpc("vpc",
cidr_block="10.100.0.0/16",
instance_tenancy="default")
policy = aws.iam.Policy("policy",
description="test",
policy=pulumi.Output.all(vpc.arn, vpc.id).apply(lambda arn, id: json.dumps({
"Version": "2012-10-17",
"Interpolated": f"arn:{arn}:value",
"Value": id,
})))
```
### Generated Python After
```python
import pulumi
import json
import pulumi_aws as aws
vpc = aws.ec2.Vpc("vpc",
cidr_block="10.100.0.0/16",
instance_tenancy="default")
policy = aws.iam.Policy("policy",
description="test",
policy=pulumi.Output.json_dumps({
"Version": "2012-10-17",
"Interpolated": vpc.arn.apply(lambda arn: f"arn:{arn}:value"),
"Value": vpc.id,
}))
```
### Generated C# Before
```csharp
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var vpc = new Aws.Ec2.Vpc("vpc", new()
{
CidrBlock = "10.100.0.0/16",
InstanceTenancy = "default",
});
var policy = new Aws.Iam.Policy("policy", new()
{
Description = "test",
PolicyDocument = Output.Tuple(vpc.Arn, vpc.Id).Apply(values =>
{
var arn = values.Item1;
var id = values.Item2;
return JsonSerializer.Serialize(new Dictionary<string, object?>
{
["Version"] = "2012-10-17",
["Interpolated"] = $"arn:{arn}:value",
["Value"] = id,
});
}),
});
});
```
### Generated C# After
```csharp
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var vpc = new Aws.Ec2.Vpc("vpc", new()
{
CidrBlock = "10.100.0.0/16",
InstanceTenancy = "default",
});
var policy = new Aws.Iam.Policy("policy", new()
{
Description = "test",
PolicyDocument = Output.JsonSerialize(Output.Create(new Dictionary<string, object?>
{
["Version"] = "2012-10-17",
["Interpolated"] = vpc.Arn.Apply(arn => $"arn:{arn}:value"),
["Value"] = vpc.Id,
})),
});
});
```
## Checklist
- [ ] I have run `make tidy` to update any new dependencies
- [x] I have run `make lint` to verify my code passes the lint check
- [x] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [x] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [x] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2024-02-20 15:48:46 +00:00
|
|
|
skipToJSONWhenRewritingApplies := true
|
|
|
|
expr, diags := pcl.RewriteAppliesWithSkipToJSON(expr, nameInfo(0), false, skipToJSONWhenRewritingApplies)
|
2022-07-07 20:15:47 +00:00
|
|
|
expr, lowerProxyDiags := g.lowerProxyApplies(expr)
|
|
|
|
expr, convertDiags := pcl.RewriteConversions(expr, typ)
|
|
|
|
expr, quotes, quoteDiags := g.rewriteQuotes(expr)
|
|
|
|
|
|
|
|
diags = diags.Extend(lowerProxyDiags)
|
|
|
|
diags = diags.Extend(convertDiags)
|
|
|
|
diags = diags.Extend(quoteDiags)
|
|
|
|
|
2022-07-13 21:29:34 +00:00
|
|
|
g.diagnostics = g.diagnostics.Extend(diags)
|
2020-08-06 22:09:23 +00:00
|
|
|
|
[codegen/python] Fix nested quotes. (#4539)
Unlike most languages with interpolated strings, Python's formatted
string literals do not allow the nesting of quotes. For example,
this expression is not legal Python:
f"Foo {"bar"} baz"
If an interpolation requires quotes, those quotes nust differ from the
quotes used by the enclosing literal. We can fix the previous example
by rewriting it with single quotes:
f"Foo {'bar'} baz"
However, this presents a problem if there are more than two levels of
nesting, as Python only has two kinds of quotes (four if the outermost
string uses """ or '''): in this case, the expression becomes
unspellable, and must be assigned to a local that is then used in place
of the original expression. So this:
f"Foo {bar[f'index {baz["qux"]}']} zed"
becomes this:
index = "qux"
f"Foo {bar[f'index {baz[index]}']}"
To put it bluntly, Python code generation reqiures register allocation,
but for quotes. These changes implement exactly that.
These changes also include a fix for traversals that access values that
are dictionaries rather than objects, and must use indexers rather than
attributes.
2020-04-30 23:34:25 +00:00
|
|
|
return expr, quotes
|
2020-04-16 23:44:34 +00:00
|
|
|
}
|
2020-04-03 06:29:05 +00:00
|
|
|
|
2020-04-16 23:44:34 +00:00
|
|
|
func (g *generator) GetPrecedence(expr model.Expression) int {
|
|
|
|
// Precedence is taken from https://docs.python.org/3/reference/expressions.html#operator-precedence.
|
|
|
|
switch expr := expr.(type) {
|
|
|
|
case *model.AnonymousFunctionExpression:
|
|
|
|
return 1
|
|
|
|
case *model.ConditionalExpression:
|
|
|
|
return 2
|
|
|
|
case *model.BinaryOpExpression:
|
|
|
|
switch expr.Operation {
|
|
|
|
case hclsyntax.OpLogicalOr:
|
|
|
|
return 3
|
|
|
|
case hclsyntax.OpLogicalAnd:
|
|
|
|
return 4
|
|
|
|
case hclsyntax.OpGreaterThan, hclsyntax.OpGreaterThanOrEqual, hclsyntax.OpLessThan, hclsyntax.OpLessThanOrEqual,
|
|
|
|
hclsyntax.OpEqual, hclsyntax.OpNotEqual:
|
|
|
|
return 6
|
|
|
|
case hclsyntax.OpAdd, hclsyntax.OpSubtract:
|
|
|
|
return 11
|
|
|
|
case hclsyntax.OpMultiply, hclsyntax.OpDivide, hclsyntax.OpModulo:
|
|
|
|
return 12
|
|
|
|
default:
|
|
|
|
contract.Failf("unexpected binary expression %v", expr)
|
|
|
|
}
|
|
|
|
case *model.UnaryOpExpression:
|
|
|
|
return 13
|
|
|
|
case *model.FunctionCallExpression, *model.IndexExpression, *model.RelativeTraversalExpression,
|
|
|
|
*model.TemplateJoinExpression:
|
|
|
|
return 16
|
|
|
|
case *model.ForExpression, *model.ObjectConsExpression, *model.SplatExpression, *model.TupleConsExpression:
|
|
|
|
return 17
|
|
|
|
case *model.LiteralValueExpression, *model.ScopeTraversalExpression, *model.TemplateExpression:
|
|
|
|
return 18
|
|
|
|
default:
|
|
|
|
contract.Failf("unexpected expression %v of type %T", expr, expr)
|
|
|
|
}
|
|
|
|
return 0
|
2020-04-03 06:29:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) GenAnonymousFunctionExpression(w io.Writer, expr *model.AnonymousFunctionExpression) {
|
|
|
|
g.Fgen(w, "lambda")
|
|
|
|
for i, p := range expr.Signature.Parameters {
|
|
|
|
if i > 0 {
|
|
|
|
g.Fgen(w, ",")
|
|
|
|
}
|
2024-08-12 13:07:33 +00:00
|
|
|
g.Fgenf(w, " %s", PyName(p.Name))
|
2020-04-03 06:29:05 +00:00
|
|
|
}
|
|
|
|
|
2020-04-16 23:44:34 +00:00
|
|
|
g.Fgenf(w, ": %.v", expr.Body)
|
2020-04-03 06:29:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) GenBinaryOpExpression(w io.Writer, expr *model.BinaryOpExpression) {
|
2020-04-16 23:44:34 +00:00
|
|
|
opstr, precedence := "", g.GetPrecedence(expr)
|
2020-04-07 02:43:16 +00:00
|
|
|
switch expr.Operation {
|
|
|
|
case hclsyntax.OpAdd:
|
|
|
|
opstr = "+"
|
|
|
|
case hclsyntax.OpDivide:
|
|
|
|
opstr = "/"
|
|
|
|
case hclsyntax.OpEqual:
|
|
|
|
opstr = "=="
|
|
|
|
case hclsyntax.OpGreaterThan:
|
|
|
|
opstr = ">"
|
|
|
|
case hclsyntax.OpGreaterThanOrEqual:
|
|
|
|
opstr = ">="
|
|
|
|
case hclsyntax.OpLessThan:
|
|
|
|
opstr = "<"
|
|
|
|
case hclsyntax.OpLessThanOrEqual:
|
|
|
|
opstr = "<="
|
|
|
|
case hclsyntax.OpLogicalAnd:
|
|
|
|
opstr = "and"
|
|
|
|
case hclsyntax.OpLogicalOr:
|
|
|
|
opstr = "or"
|
|
|
|
case hclsyntax.OpModulo:
|
|
|
|
opstr = "%"
|
|
|
|
case hclsyntax.OpMultiply:
|
|
|
|
opstr = "*"
|
|
|
|
case hclsyntax.OpNotEqual:
|
|
|
|
opstr = "!="
|
|
|
|
case hclsyntax.OpSubtract:
|
|
|
|
opstr = "-"
|
2020-04-16 23:44:34 +00:00
|
|
|
default:
|
|
|
|
opstr, precedence = ",", 0
|
2020-04-07 02:43:16 +00:00
|
|
|
}
|
|
|
|
|
2020-04-16 23:44:34 +00:00
|
|
|
g.Fgenf(w, "%.[1]*[2]v %[3]v %.[1]*[4]o", precedence, expr.LeftOperand, opstr, expr.RightOperand)
|
2020-04-03 06:29:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) GenConditionalExpression(w io.Writer, expr *model.ConditionalExpression) {
|
2020-04-16 23:44:34 +00:00
|
|
|
g.Fgenf(w, "%.2v if %.2v else %.2v", expr.TrueResult, expr.Condition, expr.FalseResult)
|
2020-04-03 06:29:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) GenForExpression(w io.Writer, expr *model.ForExpression) {
|
all: Fix revive issues
Fixes the following issues found by revive
included in the latest release of golangci-lint.
Full list of issues:
**pkg**
```
backend/display/object_diff.go:47:10: superfluous-else: if block ends with a break statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) (revive)
backend/display/object_diff.go:716:12: redefines-builtin-id: redefinition of the built-in function delete (revive)
backend/display/object_diff.go:742:14: redefines-builtin-id: redefinition of the built-in function delete (revive)
backend/display/object_diff.go:983:10: superfluous-else: if block ends with a continue statement, so drop this else and outdent its block (revive)
backend/httpstate/backend.go:1814:4: redefines-builtin-id: redefinition of the built-in function cap (revive)
backend/httpstate/backend.go:1824:5: redefines-builtin-id: redefinition of the built-in function cap (revive)
backend/httpstate/client/client.go:444:2: if-return: redundant if ...; err != nil check, just return error instead. (revive)
backend/httpstate/client/client.go:455:2: if-return: redundant if ...; err != nil check, just return error instead. (revive)
cmd/pulumi/org.go:113:4: if-return: redundant if ...; err != nil check, just return error instead. (revive)
cmd/pulumi/util.go:216:2: if-return: redundant if ...; err != nil check, just return error instead. (revive)
codegen/docs/gen.go:428:2: redefines-builtin-id: redefinition of the built-in function copy (revive)
codegen/hcl2/model/expression.go:2151:5: redefines-builtin-id: redefinition of the built-in function close (revive)
codegen/hcl2/syntax/comments.go:151:2: redefines-builtin-id: redefinition of the built-in function close (revive)
codegen/hcl2/syntax/comments.go:329:3: redefines-builtin-id: redefinition of the built-in function close (revive)
codegen/hcl2/syntax/comments.go:381:5: redefines-builtin-id: redefinition of the built-in function close (revive)
codegen/nodejs/gen.go:1367:5: redefines-builtin-id: redefinition of the built-in function copy (revive)
codegen/python/gen_program_expressions.go:136:2: redefines-builtin-id: redefinition of the built-in function close (revive)
codegen/python/gen_program_expressions.go:142:3: redefines-builtin-id: redefinition of the built-in function close (revive)
codegen/report/report.go:126:6: redefines-builtin-id: redefinition of the built-in function panic (revive)
codegen/schema/docs_test.go:210:10: superfluous-else: if block ends with a continue statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) (revive)
codegen/schema/schema.go:790:2: redefines-builtin-id: redefinition of the built-in type any (revive)
codegen/schema/schema.go:793:4: redefines-builtin-id: redefinition of the built-in type any (revive)
resource/deploy/plan.go:506:2: if-return: redundant if ...; err != nil check, just return error instead. (revive)
resource/deploy/snapshot_test.go:59:3: redefines-builtin-id: redefinition of the built-in function copy (revive)
resource/deploy/state_builder.go:108:2: redefines-builtin-id: redefinition of the built-in function copy (revive)
```
**sdk**
```
go/common/resource/plugin/context.go:142:2: redefines-builtin-id: redefinition of the built-in function copy (revive)
go/common/resource/plugin/plugin.go:142:12: superfluous-else: if block ends with a break statement, so drop this else and outdent its block (revive)
go/common/resource/properties_diff.go:114:2: redefines-builtin-id: redefinition of the built-in function len (revive)
go/common/resource/properties_diff.go:117:4: redefines-builtin-id: redefinition of the built-in function len (revive)
go/common/resource/properties_diff.go:122:4: redefines-builtin-id: redefinition of the built-in function len (revive)
go/common/resource/properties_diff.go:127:4: redefines-builtin-id: redefinition of the built-in function len (revive)
go/common/resource/properties_diff.go:132:4: redefines-builtin-id: redefinition of the built-in function len (revive)
go/common/util/deepcopy/copy.go:30:1: redefines-builtin-id: redefinition of the built-in function copy (revive)
go/common/workspace/creds.go:242:2: if-return: redundant if ...; err != nil check, just return error instead. (revive)
go/pulumi-language-go/main.go:569:2: if-return: redundant if ...; err != nil check, just return error instead. (revive)
go/pulumi-language-go/main.go:706:2: if-return: redundant if ...; err != nil check, just return error instead. (revive)
go/pulumi/run_test.go:925:2: redefines-builtin-id: redefinition of the built-in type any (revive)
go/pulumi/run_test.go:933:3: redefines-builtin-id: redefinition of the built-in type any (revive)
nodejs/cmd/pulumi-language-nodejs/main.go:778:2: if-return: redundant if ...; err != nil check, just return error instead. (revive)
python/cmd/pulumi-language-python/main.go:1011:2: if-return: redundant if ...; err != nil check, just return error instead. (revive)
python/cmd/pulumi-language-python/main.go:863:2: if-return: redundant if ...; err != nil check, just return error instead. (revive)
python/python.go:230:2: redefines-builtin-id: redefinition of the built-in function print (revive)
```
**tests**
```
integration/integration_util_test.go:282:11: superfluous-else: if block ends with a continue statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) (revive)
```
2023-03-20 23:48:02 +00:00
|
|
|
closedelim := "]"
|
2020-04-07 02:43:16 +00:00
|
|
|
if expr.Key != nil {
|
|
|
|
// Dictionary comprehension
|
|
|
|
//
|
|
|
|
// TODO(pdg): grouping
|
2020-04-16 23:44:34 +00:00
|
|
|
g.Fgenf(w, "{%.v: %.v", expr.Key, expr.Value)
|
all: Fix revive issues
Fixes the following issues found by revive
included in the latest release of golangci-lint.
Full list of issues:
**pkg**
```
backend/display/object_diff.go:47:10: superfluous-else: if block ends with a break statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) (revive)
backend/display/object_diff.go:716:12: redefines-builtin-id: redefinition of the built-in function delete (revive)
backend/display/object_diff.go:742:14: redefines-builtin-id: redefinition of the built-in function delete (revive)
backend/display/object_diff.go:983:10: superfluous-else: if block ends with a continue statement, so drop this else and outdent its block (revive)
backend/httpstate/backend.go:1814:4: redefines-builtin-id: redefinition of the built-in function cap (revive)
backend/httpstate/backend.go:1824:5: redefines-builtin-id: redefinition of the built-in function cap (revive)
backend/httpstate/client/client.go:444:2: if-return: redundant if ...; err != nil check, just return error instead. (revive)
backend/httpstate/client/client.go:455:2: if-return: redundant if ...; err != nil check, just return error instead. (revive)
cmd/pulumi/org.go:113:4: if-return: redundant if ...; err != nil check, just return error instead. (revive)
cmd/pulumi/util.go:216:2: if-return: redundant if ...; err != nil check, just return error instead. (revive)
codegen/docs/gen.go:428:2: redefines-builtin-id: redefinition of the built-in function copy (revive)
codegen/hcl2/model/expression.go:2151:5: redefines-builtin-id: redefinition of the built-in function close (revive)
codegen/hcl2/syntax/comments.go:151:2: redefines-builtin-id: redefinition of the built-in function close (revive)
codegen/hcl2/syntax/comments.go:329:3: redefines-builtin-id: redefinition of the built-in function close (revive)
codegen/hcl2/syntax/comments.go:381:5: redefines-builtin-id: redefinition of the built-in function close (revive)
codegen/nodejs/gen.go:1367:5: redefines-builtin-id: redefinition of the built-in function copy (revive)
codegen/python/gen_program_expressions.go:136:2: redefines-builtin-id: redefinition of the built-in function close (revive)
codegen/python/gen_program_expressions.go:142:3: redefines-builtin-id: redefinition of the built-in function close (revive)
codegen/report/report.go:126:6: redefines-builtin-id: redefinition of the built-in function panic (revive)
codegen/schema/docs_test.go:210:10: superfluous-else: if block ends with a continue statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) (revive)
codegen/schema/schema.go:790:2: redefines-builtin-id: redefinition of the built-in type any (revive)
codegen/schema/schema.go:793:4: redefines-builtin-id: redefinition of the built-in type any (revive)
resource/deploy/plan.go:506:2: if-return: redundant if ...; err != nil check, just return error instead. (revive)
resource/deploy/snapshot_test.go:59:3: redefines-builtin-id: redefinition of the built-in function copy (revive)
resource/deploy/state_builder.go:108:2: redefines-builtin-id: redefinition of the built-in function copy (revive)
```
**sdk**
```
go/common/resource/plugin/context.go:142:2: redefines-builtin-id: redefinition of the built-in function copy (revive)
go/common/resource/plugin/plugin.go:142:12: superfluous-else: if block ends with a break statement, so drop this else and outdent its block (revive)
go/common/resource/properties_diff.go:114:2: redefines-builtin-id: redefinition of the built-in function len (revive)
go/common/resource/properties_diff.go:117:4: redefines-builtin-id: redefinition of the built-in function len (revive)
go/common/resource/properties_diff.go:122:4: redefines-builtin-id: redefinition of the built-in function len (revive)
go/common/resource/properties_diff.go:127:4: redefines-builtin-id: redefinition of the built-in function len (revive)
go/common/resource/properties_diff.go:132:4: redefines-builtin-id: redefinition of the built-in function len (revive)
go/common/util/deepcopy/copy.go:30:1: redefines-builtin-id: redefinition of the built-in function copy (revive)
go/common/workspace/creds.go:242:2: if-return: redundant if ...; err != nil check, just return error instead. (revive)
go/pulumi-language-go/main.go:569:2: if-return: redundant if ...; err != nil check, just return error instead. (revive)
go/pulumi-language-go/main.go:706:2: if-return: redundant if ...; err != nil check, just return error instead. (revive)
go/pulumi/run_test.go:925:2: redefines-builtin-id: redefinition of the built-in type any (revive)
go/pulumi/run_test.go:933:3: redefines-builtin-id: redefinition of the built-in type any (revive)
nodejs/cmd/pulumi-language-nodejs/main.go:778:2: if-return: redundant if ...; err != nil check, just return error instead. (revive)
python/cmd/pulumi-language-python/main.go:1011:2: if-return: redundant if ...; err != nil check, just return error instead. (revive)
python/cmd/pulumi-language-python/main.go:863:2: if-return: redundant if ...; err != nil check, just return error instead. (revive)
python/python.go:230:2: redefines-builtin-id: redefinition of the built-in function print (revive)
```
**tests**
```
integration/integration_util_test.go:282:11: superfluous-else: if block ends with a continue statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) (revive)
```
2023-03-20 23:48:02 +00:00
|
|
|
closedelim = "}"
|
2020-04-07 02:43:16 +00:00
|
|
|
} else {
|
|
|
|
// List comprehension
|
2020-04-16 23:44:34 +00:00
|
|
|
g.Fgenf(w, "[%.v", expr.Value)
|
2020-04-07 02:43:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if expr.KeyVariable == nil {
|
2020-04-16 23:44:34 +00:00
|
|
|
g.Fgenf(w, " for %v in %.v", expr.ValueVariable.Name, expr.Collection)
|
2020-04-07 02:43:16 +00:00
|
|
|
} else {
|
2020-04-16 23:44:34 +00:00
|
|
|
g.Fgenf(w, " for %v, %v in %.v", expr.KeyVariable.Name, expr.ValueVariable.Name, expr.Collection)
|
2020-04-07 02:43:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if expr.Condition != nil {
|
2020-04-16 23:44:34 +00:00
|
|
|
g.Fgenf(w, " if %.v", expr.Condition)
|
2020-04-07 02:43:16 +00:00
|
|
|
}
|
|
|
|
|
all: Fix revive issues
Fixes the following issues found by revive
included in the latest release of golangci-lint.
Full list of issues:
**pkg**
```
backend/display/object_diff.go:47:10: superfluous-else: if block ends with a break statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) (revive)
backend/display/object_diff.go:716:12: redefines-builtin-id: redefinition of the built-in function delete (revive)
backend/display/object_diff.go:742:14: redefines-builtin-id: redefinition of the built-in function delete (revive)
backend/display/object_diff.go:983:10: superfluous-else: if block ends with a continue statement, so drop this else and outdent its block (revive)
backend/httpstate/backend.go:1814:4: redefines-builtin-id: redefinition of the built-in function cap (revive)
backend/httpstate/backend.go:1824:5: redefines-builtin-id: redefinition of the built-in function cap (revive)
backend/httpstate/client/client.go:444:2: if-return: redundant if ...; err != nil check, just return error instead. (revive)
backend/httpstate/client/client.go:455:2: if-return: redundant if ...; err != nil check, just return error instead. (revive)
cmd/pulumi/org.go:113:4: if-return: redundant if ...; err != nil check, just return error instead. (revive)
cmd/pulumi/util.go:216:2: if-return: redundant if ...; err != nil check, just return error instead. (revive)
codegen/docs/gen.go:428:2: redefines-builtin-id: redefinition of the built-in function copy (revive)
codegen/hcl2/model/expression.go:2151:5: redefines-builtin-id: redefinition of the built-in function close (revive)
codegen/hcl2/syntax/comments.go:151:2: redefines-builtin-id: redefinition of the built-in function close (revive)
codegen/hcl2/syntax/comments.go:329:3: redefines-builtin-id: redefinition of the built-in function close (revive)
codegen/hcl2/syntax/comments.go:381:5: redefines-builtin-id: redefinition of the built-in function close (revive)
codegen/nodejs/gen.go:1367:5: redefines-builtin-id: redefinition of the built-in function copy (revive)
codegen/python/gen_program_expressions.go:136:2: redefines-builtin-id: redefinition of the built-in function close (revive)
codegen/python/gen_program_expressions.go:142:3: redefines-builtin-id: redefinition of the built-in function close (revive)
codegen/report/report.go:126:6: redefines-builtin-id: redefinition of the built-in function panic (revive)
codegen/schema/docs_test.go:210:10: superfluous-else: if block ends with a continue statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) (revive)
codegen/schema/schema.go:790:2: redefines-builtin-id: redefinition of the built-in type any (revive)
codegen/schema/schema.go:793:4: redefines-builtin-id: redefinition of the built-in type any (revive)
resource/deploy/plan.go:506:2: if-return: redundant if ...; err != nil check, just return error instead. (revive)
resource/deploy/snapshot_test.go:59:3: redefines-builtin-id: redefinition of the built-in function copy (revive)
resource/deploy/state_builder.go:108:2: redefines-builtin-id: redefinition of the built-in function copy (revive)
```
**sdk**
```
go/common/resource/plugin/context.go:142:2: redefines-builtin-id: redefinition of the built-in function copy (revive)
go/common/resource/plugin/plugin.go:142:12: superfluous-else: if block ends with a break statement, so drop this else and outdent its block (revive)
go/common/resource/properties_diff.go:114:2: redefines-builtin-id: redefinition of the built-in function len (revive)
go/common/resource/properties_diff.go:117:4: redefines-builtin-id: redefinition of the built-in function len (revive)
go/common/resource/properties_diff.go:122:4: redefines-builtin-id: redefinition of the built-in function len (revive)
go/common/resource/properties_diff.go:127:4: redefines-builtin-id: redefinition of the built-in function len (revive)
go/common/resource/properties_diff.go:132:4: redefines-builtin-id: redefinition of the built-in function len (revive)
go/common/util/deepcopy/copy.go:30:1: redefines-builtin-id: redefinition of the built-in function copy (revive)
go/common/workspace/creds.go:242:2: if-return: redundant if ...; err != nil check, just return error instead. (revive)
go/pulumi-language-go/main.go:569:2: if-return: redundant if ...; err != nil check, just return error instead. (revive)
go/pulumi-language-go/main.go:706:2: if-return: redundant if ...; err != nil check, just return error instead. (revive)
go/pulumi/run_test.go:925:2: redefines-builtin-id: redefinition of the built-in type any (revive)
go/pulumi/run_test.go:933:3: redefines-builtin-id: redefinition of the built-in type any (revive)
nodejs/cmd/pulumi-language-nodejs/main.go:778:2: if-return: redundant if ...; err != nil check, just return error instead. (revive)
python/cmd/pulumi-language-python/main.go:1011:2: if-return: redundant if ...; err != nil check, just return error instead. (revive)
python/cmd/pulumi-language-python/main.go:863:2: if-return: redundant if ...; err != nil check, just return error instead. (revive)
python/python.go:230:2: redefines-builtin-id: redefinition of the built-in function print (revive)
```
**tests**
```
integration/integration_util_test.go:282:11: superfluous-else: if block ends with a continue statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) (revive)
```
2023-03-20 23:48:02 +00:00
|
|
|
g.Fprint(w, closedelim)
|
2020-04-03 06:29:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) genApply(w io.Writer, expr *model.FunctionCallExpression) {
|
|
|
|
// Extract the list of outputs and the continuation expression from the `__apply` arguments.
|
2021-09-30 03:11:56 +00:00
|
|
|
applyArgs, then := pcl.ParseApplyCall(expr)
|
2020-04-03 06:29:05 +00:00
|
|
|
|
|
|
|
if len(applyArgs) == 1 {
|
|
|
|
// If we only have a single output, just generate a normal `.apply`.
|
2020-04-16 23:44:34 +00:00
|
|
|
g.Fgenf(w, "%.16v.apply(%.v)", applyArgs[0], then)
|
2020-04-03 06:29:05 +00:00
|
|
|
} else {
|
|
|
|
// Otherwise, generate a call to `pulumi.all([]).apply()`.
|
2024-08-12 13:07:33 +00:00
|
|
|
g.Fgen(w, "pulumi.Output.all(\n")
|
|
|
|
g.Indented(func() {
|
|
|
|
for i, arg := range applyArgs {
|
|
|
|
argName := then.Signature.Parameters[i].Name
|
|
|
|
g.Fgenf(w, "%s%s=%v", g.Indent, argName, arg)
|
|
|
|
if i < len(applyArgs)-1 {
|
|
|
|
g.Fgen(w, ",")
|
|
|
|
}
|
|
|
|
g.Fgen(w, "\n")
|
2020-04-03 06:29:05 +00:00
|
|
|
}
|
2024-08-12 13:07:33 +00:00
|
|
|
})
|
|
|
|
g.Fgen(w, ").apply(lambda resolved_outputs: ")
|
|
|
|
rewrittenLambdaBody := rewriteApplyLambdaBody(then, "resolved_outputs")
|
|
|
|
g.Fgenf(w, "%.v)\n", rewrittenLambdaBody)
|
2020-04-03 06:29:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-06 22:09:23 +00:00
|
|
|
// functionName computes the Python package, module, and name for the given function token.
|
2020-04-07 02:43:16 +00:00
|
|
|
func functionName(tokenArg model.Expression) (string, string, string, hcl.Diagnostics) {
|
|
|
|
token := tokenArg.(*model.TemplateExpression).Parts[0].(*model.LiteralValueExpression).Value.AsString()
|
|
|
|
tokenRange := tokenArg.SyntaxNode().Range()
|
|
|
|
|
[codegen/python] Fix nested quotes. (#4539)
Unlike most languages with interpolated strings, Python's formatted
string literals do not allow the nesting of quotes. For example,
this expression is not legal Python:
f"Foo {"bar"} baz"
If an interpolation requires quotes, those quotes nust differ from the
quotes used by the enclosing literal. We can fix the previous example
by rewriting it with single quotes:
f"Foo {'bar'} baz"
However, this presents a problem if there are more than two levels of
nesting, as Python only has two kinds of quotes (four if the outermost
string uses """ or '''): in this case, the expression becomes
unspellable, and must be assigned to a local that is then used in place
of the original expression. So this:
f"Foo {bar[f'index {baz["qux"]}']} zed"
becomes this:
index = "qux"
f"Foo {bar[f'index {baz[index]}']}"
To put it bluntly, Python code generation reqiures register allocation,
but for quotes. These changes implement exactly that.
These changes also include a fix for traversals that access values that
are dictionaries rather than objects, and must use indexers rather than
attributes.
2020-04-30 23:34:25 +00:00
|
|
|
// Compute the resource type from the Pulumi type token.
|
2021-09-30 03:11:56 +00:00
|
|
|
pkg, module, member, diagnostics := pcl.DecomposeToken(token, tokenRange)
|
2023-02-23 20:51:11 +00:00
|
|
|
return makeValidIdentifier(pkg), strings.ReplaceAll(module, "/", "."), title(member), diagnostics
|
2020-04-07 02:43:16 +00:00
|
|
|
}
|
|
|
|
|
2022-01-21 14:03:25 +00:00
|
|
|
var functionImports = map[string][]string{
|
|
|
|
"fileArchive": {"pulumi"},
|
2022-04-25 19:59:30 +00:00
|
|
|
"remoteArchive": {"pulumi"},
|
|
|
|
"assetArchive": {"pulumi"},
|
2022-01-21 14:03:25 +00:00
|
|
|
"fileAsset": {"pulumi"},
|
2022-04-25 19:59:30 +00:00
|
|
|
"stringAsset": {"pulumi"},
|
|
|
|
"remoteAsset": {"pulumi"},
|
2022-01-21 14:03:25 +00:00
|
|
|
"filebase64": {"base64"},
|
|
|
|
"filebase64sha256": {"base64", "hashlib"},
|
|
|
|
"readDir": {"os"},
|
|
|
|
"toBase64": {"base64"},
|
2022-09-16 23:12:29 +00:00
|
|
|
"fromBase64": {"base64"},
|
2022-01-21 14:03:25 +00:00
|
|
|
"toJSON": {"json"},
|
|
|
|
"sha1": {"hashlib"},
|
2022-03-16 00:05:36 +00:00
|
|
|
"stack": {"pulumi"},
|
|
|
|
"project": {"pulumi"},
|
2024-08-19 03:58:19 +00:00
|
|
|
"organization": {"pulumi"},
|
2022-03-16 00:05:36 +00:00
|
|
|
"cwd": {"os"},
|
[program-gen] Fix generated utility functions for filebase64, filebase64sha256, sha1 and mimeType (#14857)
# Description
While writing program tests for generated helper utility functions
`filebase64`, `filebase64sha256`, `sha1` and `mimeType` with the idea to
increase code coverage, it turned out that those are completely broken
in all of the languages containing syntax errors, missing imports and
wrong indentation. This PR fixes them and extends the `functions`
program to show how they now look like and to show that they compile.
Also adding example usage of `stack()`, `project()` and `cwd()` in the
test program.
## Checklist
- [ ] I have run `make tidy` to update any new dependencies
- [ ] I have run `make lint` to verify my code passes the lint check
- [ ] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [x] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [x] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2023-12-15 11:26:00 +00:00
|
|
|
"mimeType": {"mimetypes"},
|
2020-04-21 17:24:42 +00:00
|
|
|
}
|
|
|
|
|
2022-01-21 14:03:25 +00:00
|
|
|
func (g *generator) getFunctionImports(x *model.FunctionCallExpression) []string {
|
2021-09-30 03:11:56 +00:00
|
|
|
if x.Name != pcl.Invoke {
|
2020-04-21 17:24:42 +00:00
|
|
|
return functionImports[x.Name]
|
|
|
|
}
|
|
|
|
|
|
|
|
pkg, _, _, diags := functionName(x.Args[0])
|
2023-02-21 23:27:34 +00:00
|
|
|
contract.Assertf(len(diags) == 0, "unexpected diagnostics: %v", diags)
|
2022-01-21 14:03:25 +00:00
|
|
|
return []string{"pulumi_" + pkg}
|
2020-04-21 17:24:42 +00:00
|
|
|
}
|
|
|
|
|
2020-04-03 06:29:05 +00:00
|
|
|
func (g *generator) GenFunctionCallExpression(w io.Writer, expr *model.FunctionCallExpression) {
|
|
|
|
switch expr.Name {
|
2021-09-30 03:11:56 +00:00
|
|
|
case pcl.IntrinsicConvert:
|
2022-04-18 09:03:42 +00:00
|
|
|
from := expr.Args[0]
|
|
|
|
to := pcl.LowerConversion(from, expr.Signature.ReturnType)
|
|
|
|
output, isOutput := to.(*model.OutputType)
|
|
|
|
if isOutput {
|
|
|
|
to = output.ElementType
|
|
|
|
}
|
|
|
|
switch to := to.(type) {
|
|
|
|
case *model.EnumType:
|
|
|
|
components := strings.Split(to.Token, ":")
|
|
|
|
contract.Assertf(len(components) == 3, "malformed token %v", to.Token)
|
|
|
|
enum, ok := pcl.GetSchemaForType(to)
|
|
|
|
if !ok {
|
|
|
|
// No schema was provided
|
|
|
|
g.Fgenf(w, "%.v", expr.Args[0])
|
|
|
|
return
|
|
|
|
}
|
2022-12-07 12:03:41 +00:00
|
|
|
var moduleNameOverrides map[string]string
|
|
|
|
if pkg, err := enum.(*schema.EnumType).PackageReference.Definition(); err == nil {
|
2024-05-30 22:43:12 +00:00
|
|
|
if pkgInfo, ok := pkg.Language["python"].(PackageInfo); ok {
|
|
|
|
moduleNameOverrides = pkgInfo.ModuleNameOverrides
|
|
|
|
}
|
2022-12-07 12:03:41 +00:00
|
|
|
}
|
2022-04-18 09:03:42 +00:00
|
|
|
pkg := strings.ReplaceAll(components[0], "-", "_")
|
[program-gen] Fix enum resolution from types of the form Union[string, Enum] and emit fully qualified enum cases (#15696)
# Description
This PR improves enum type resolution from strings. When we try to
resolve `Union[string, Enum]` for a string expression, we choose
`string` because it is the more general type since not every string is
assignable to `Enum`. However, here we spacial case strings that are
actually part of that `Enum`.
The result is that `pcl.LowerConversion` will choose `Enum` from
`Union[string, Enum]` when the value of the input string is compatible
with the enum. This greatly improves program-gen for all of typescript,
python, csharp and go which now will emit the fully qualified enum cases
instead of emitting strings.
Closes https://github.com/pulumi/pulumi-dotnet/issues/41 which is
supposed to be a duplicate of
https://github.com/pulumi/pulumi-azure-native/issues/2616 but that is
not the case (the former is about unions of objects, the latter is
unions of enums and strings)
## Checklist
- [ ] I have run `make tidy` to update any new dependencies
- [x] I have run `make lint` to verify my code passes the lint check
- [x] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [x] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [x] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2024-03-15 17:49:12 +00:00
|
|
|
enumName := tokenToName(to.Token)
|
2022-12-07 12:03:41 +00:00
|
|
|
if m := tokenToModule(to.Token, nil, moduleNameOverrides); m != "" {
|
[program-gen] Fix enum resolution from types of the form Union[string, Enum] and emit fully qualified enum cases (#15696)
# Description
This PR improves enum type resolution from strings. When we try to
resolve `Union[string, Enum]` for a string expression, we choose
`string` because it is the more general type since not every string is
assignable to `Enum`. However, here we spacial case strings that are
actually part of that `Enum`.
The result is that `pcl.LowerConversion` will choose `Enum` from
`Union[string, Enum]` when the value of the input string is compatible
with the enum. This greatly improves program-gen for all of typescript,
python, csharp and go which now will emit the fully qualified enum cases
instead of emitting strings.
Closes https://github.com/pulumi/pulumi-dotnet/issues/41 which is
supposed to be a duplicate of
https://github.com/pulumi/pulumi-azure-native/issues/2616 but that is
not the case (the former is about unions of objects, the latter is
unions of enums and strings)
## Checklist
- [ ] I have run `make tidy` to update any new dependencies
- [x] I have run `make lint` to verify my code passes the lint check
- [x] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [x] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [x] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2024-03-15 17:49:12 +00:00
|
|
|
modParts := strings.Split(m, "/")
|
|
|
|
if len(modParts) == 2 && strings.EqualFold(modParts[1], enumName) {
|
|
|
|
m = modParts[0]
|
|
|
|
}
|
|
|
|
pkg += "." + strings.ReplaceAll(m, "/", ".")
|
2022-04-18 09:03:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if isOutput {
|
|
|
|
g.Fgenf(w, "%.v.apply(lambda x: %s.%s(x))", from, pkg, enumName)
|
|
|
|
} else {
|
2022-12-01 22:41:24 +00:00
|
|
|
diag := pcl.GenEnum(to, from, func(member *schema.Enum) {
|
2022-04-18 09:03:42 +00:00
|
|
|
tag := member.Name
|
|
|
|
if tag == "" {
|
|
|
|
tag = fmt.Sprintf("%v", member.Value)
|
|
|
|
}
|
|
|
|
tag, err := makeSafeEnumName(tag, enumName)
|
2023-02-21 23:27:34 +00:00
|
|
|
contract.AssertNoErrorf(err, "error sanitizing enum name")
|
2022-04-18 09:03:42 +00:00
|
|
|
g.Fgenf(w, "%s.%s.%s", pkg, enumName, tag)
|
|
|
|
}, func(from model.Expression) {
|
|
|
|
g.Fgenf(w, "%s.%s(%.v)", pkg, enumName, from)
|
|
|
|
})
|
2022-12-01 22:41:24 +00:00
|
|
|
if diag != nil {
|
|
|
|
g.diagnostics = append(g.diagnostics, diag)
|
|
|
|
}
|
2022-04-18 09:03:42 +00:00
|
|
|
}
|
2020-08-06 22:09:23 +00:00
|
|
|
default:
|
2022-04-18 09:03:42 +00:00
|
|
|
switch arg := from.(type) {
|
|
|
|
case *model.ObjectConsExpression:
|
|
|
|
g.genObjectConsExpression(w, arg, expr.Type())
|
|
|
|
default:
|
|
|
|
g.Fgenf(w, "%.v", expr.Args[0])
|
|
|
|
}
|
|
|
|
|
2020-08-06 22:09:23 +00:00
|
|
|
}
|
2021-09-30 03:11:56 +00:00
|
|
|
case pcl.IntrinsicApply:
|
2020-04-03 06:29:05 +00:00
|
|
|
g.genApply(w, expr)
|
2020-04-16 23:44:34 +00:00
|
|
|
case "element":
|
|
|
|
g.Fgenf(w, "%.16v[%.v]", expr.Args[0], expr.Args[1])
|
2020-04-07 02:43:16 +00:00
|
|
|
case "entries":
|
2020-04-16 23:44:34 +00:00
|
|
|
g.Fgenf(w, `[{"key": k, "value": v} for k, v in %.v]`, expr.Args[0])
|
2020-04-03 06:29:05 +00:00
|
|
|
case "fileArchive":
|
2020-04-16 23:44:34 +00:00
|
|
|
g.Fgenf(w, "pulumi.FileArchive(%.v)", expr.Args[0])
|
2022-04-25 19:59:30 +00:00
|
|
|
case "remoteArchive":
|
|
|
|
g.Fgenf(w, "pulumi.RemoteArchive(%.v)", expr.Args[0])
|
|
|
|
case "assetArchive":
|
|
|
|
g.Fgenf(w, "pulumi.AssetArchive(%.v)", expr.Args[0])
|
2020-04-03 06:29:05 +00:00
|
|
|
case "fileAsset":
|
2020-04-16 23:44:34 +00:00
|
|
|
g.Fgenf(w, "pulumi.FileAsset(%.v)", expr.Args[0])
|
2022-04-25 19:59:30 +00:00
|
|
|
case "stringAsset":
|
|
|
|
g.Fgenf(w, "pulumi.StringAsset(%.v)", expr.Args[0])
|
|
|
|
case "remoteAsset":
|
2024-07-02 13:34:10 +00:00
|
|
|
g.Fgenf(w, "pulumi.RemoteAsset(%.v)", expr.Args[0])
|
2021-09-10 22:09:28 +00:00
|
|
|
case "filebase64":
|
|
|
|
g.Fgenf(w, "(lambda path: base64.b64encode(open(path).read().encode()).decode())(%.v)", expr.Args[0])
|
2022-01-21 14:03:25 +00:00
|
|
|
case "filebase64sha256":
|
|
|
|
// Assuming the existence of the following helper method
|
|
|
|
g.Fgenf(w, "computeFilebase64sha256(%v)", expr.Args[0])
|
2023-03-10 11:14:28 +00:00
|
|
|
case "notImplemented":
|
|
|
|
g.Fgenf(w, "not_implemented(%v)", expr.Args[0])
|
2023-05-25 20:12:13 +00:00
|
|
|
case "singleOrNone":
|
|
|
|
g.Fgenf(w, "single_or_none(%v)", expr.Args[0])
|
[program-gen] Fix generated utility functions for filebase64, filebase64sha256, sha1 and mimeType (#14857)
# Description
While writing program tests for generated helper utility functions
`filebase64`, `filebase64sha256`, `sha1` and `mimeType` with the idea to
increase code coverage, it turned out that those are completely broken
in all of the languages containing syntax errors, missing imports and
wrong indentation. This PR fixes them and extends the `functions`
program to show how they now look like and to show that they compile.
Also adding example usage of `stack()`, `project()` and `cwd()` in the
test program.
## Checklist
- [ ] I have run `make tidy` to update any new dependencies
- [ ] I have run `make lint` to verify my code passes the lint check
- [ ] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [x] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [x] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2023-12-15 11:26:00 +00:00
|
|
|
case "mimeType":
|
|
|
|
g.Fgenf(w, "mimetypes.guess_type(%v)[0]", expr.Args[0])
|
2021-09-30 03:11:56 +00:00
|
|
|
case pcl.Invoke:
|
2023-01-11 22:17:14 +00:00
|
|
|
if expr.Signature.MultiArgumentInputs {
|
|
|
|
err := fmt.Errorf("python program-gen does not implement MultiArgumentInputs for function '%v'",
|
|
|
|
expr.Args[0])
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
2020-04-07 02:43:16 +00:00
|
|
|
pkg, module, fn, diags := functionName(expr.Args[0])
|
2023-02-21 23:27:34 +00:00
|
|
|
contract.Assertf(len(diags) == 0, "unexpected diagnostics: %v", diags)
|
2020-04-07 02:43:16 +00:00
|
|
|
if module != "" {
|
|
|
|
module = "." + module
|
|
|
|
}
|
|
|
|
name := fmt.Sprintf("%s%s.%s", pkg, module, PyName(fn))
|
|
|
|
|
2021-11-18 22:43:13 +00:00
|
|
|
isOut := pcl.IsOutputVersionInvokeCall(expr)
|
|
|
|
if isOut {
|
2023-12-12 12:19:42 +00:00
|
|
|
name = name + "_output"
|
2021-11-18 22:43:13 +00:00
|
|
|
}
|
|
|
|
|
2021-07-29 03:41:23 +00:00
|
|
|
if len(expr.Args) == 1 {
|
|
|
|
g.Fprintf(w, "%s()", name)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-04-07 02:43:16 +00:00
|
|
|
optionsBag := ""
|
|
|
|
if len(expr.Args) == 3 {
|
|
|
|
var buf bytes.Buffer
|
2020-04-16 23:44:34 +00:00
|
|
|
g.Fgenf(&buf, ", %.v", expr.Args[2])
|
2020-04-07 02:43:16 +00:00
|
|
|
optionsBag = buf.String()
|
|
|
|
}
|
|
|
|
|
2020-04-21 20:52:24 +00:00
|
|
|
g.Fgenf(w, "%s(", name)
|
|
|
|
|
2022-10-07 12:02:12 +00:00
|
|
|
genFuncArgs := func(objectExpr *model.ObjectConsExpression) {
|
|
|
|
indenter := func(f func()) { f() }
|
|
|
|
if len(objectExpr.Items) > 1 {
|
|
|
|
indenter = g.Indented
|
|
|
|
}
|
|
|
|
indenter(func() {
|
|
|
|
for i, item := range objectExpr.Items {
|
|
|
|
// Ignore non-literal keys
|
|
|
|
key, ok := item.Key.(*model.LiteralValueExpression)
|
|
|
|
if !ok || !key.Value.Type().Equals(cty.String) {
|
|
|
|
continue
|
2020-08-06 22:09:23 +00:00
|
|
|
}
|
2022-10-07 12:02:12 +00:00
|
|
|
keyVal := PyName(key.Value.AsString())
|
|
|
|
if i == 0 {
|
|
|
|
g.Fgenf(w, "%s=%.v", keyVal, item.Value)
|
|
|
|
} else {
|
|
|
|
g.Fgenf(w, ",\n%s%s=%.v", g.Indent, keyVal, item.Value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-10-08 12:46:25 +00:00
|
|
|
switch arg := expr.Args[1].(type) {
|
|
|
|
case *model.FunctionCallExpression:
|
|
|
|
if argsObject, ok := arg.Args[0].(*model.ObjectConsExpression); ok {
|
2022-10-07 12:02:12 +00:00
|
|
|
genFuncArgs(argsObject)
|
2020-08-06 22:09:23 +00:00
|
|
|
}
|
2020-04-21 20:52:24 +00:00
|
|
|
|
2022-10-08 12:46:25 +00:00
|
|
|
case *model.ObjectConsExpression:
|
|
|
|
genFuncArgs(arg)
|
2022-10-07 12:02:12 +00:00
|
|
|
}
|
|
|
|
|
2020-04-21 20:52:24 +00:00
|
|
|
g.Fgenf(w, "%v)", optionsBag)
|
2021-07-29 03:41:23 +00:00
|
|
|
case "join":
|
|
|
|
g.Fgenf(w, "%.16v.join(%v)", expr.Args[0], expr.Args[1])
|
2020-04-16 23:44:34 +00:00
|
|
|
case "length":
|
|
|
|
g.Fgenf(w, "len(%.v)", expr.Args[0])
|
|
|
|
case "lookup":
|
|
|
|
if len(expr.Args) == 3 {
|
|
|
|
g.Fgenf(w, "(lambda v, def: v if v is not None else def)(%.16v[%.v], %.v)",
|
|
|
|
expr.Args[0], expr.Args[1], expr.Args[2])
|
|
|
|
} else {
|
|
|
|
g.Fgenf(w, "%.16v[%.v]", expr.Args[0], expr.Args[1])
|
|
|
|
}
|
2020-04-07 02:43:16 +00:00
|
|
|
case "range":
|
|
|
|
g.Fprint(w, "range(")
|
|
|
|
for i, arg := range expr.Args {
|
|
|
|
if i > 0 {
|
|
|
|
g.Fprint(w, ", ")
|
|
|
|
}
|
2020-04-16 23:44:34 +00:00
|
|
|
g.Fgenf(w, "%.v", arg)
|
2020-04-07 02:43:16 +00:00
|
|
|
}
|
|
|
|
g.Fprint(w, ")")
|
2020-04-16 23:44:34 +00:00
|
|
|
case "readFile":
|
|
|
|
g.Fgenf(w, "(lambda path: open(path).read())(%.v)", expr.Args[0])
|
|
|
|
case "readDir":
|
|
|
|
g.Fgenf(w, "os.listdir(%.v)", expr.Args[0])
|
2020-06-30 18:35:24 +00:00
|
|
|
case "secret":
|
2022-12-11 13:07:18 +00:00
|
|
|
g.Fgenf(w, "pulumi.Output.secret(%v)", expr.Args[0])
|
2023-01-31 12:31:00 +00:00
|
|
|
case "unsecret":
|
|
|
|
g.Fgenf(w, "pulumi.Output.unsecret(%v)", expr.Args[0])
|
2020-04-16 23:44:34 +00:00
|
|
|
case "split":
|
|
|
|
g.Fgenf(w, "%.16v.split(%.v)", expr.Args[1], expr.Args[0])
|
2021-07-29 03:41:23 +00:00
|
|
|
case "toBase64":
|
|
|
|
g.Fgenf(w, "base64.b64encode(%.16v.encode()).decode()", expr.Args[0])
|
2022-09-16 23:12:29 +00:00
|
|
|
case "fromBase64":
|
|
|
|
g.Fgenf(w, "base64.b64decode(%.16v.encode()).decode()", expr.Args[0])
|
2020-04-03 06:29:05 +00:00
|
|
|
case "toJSON":
|
[program-gen] Emit Output-returning JSON serialization methods without rewriting applies (#15371)
### Description
A while ago we started implementing [specialized JSON serialization
methods](https://github.com/pulumi/pulumi/issues/12519) for Pulumi
programs which can accept nested outputs without having to rewrite and
combine applies.
- `Output.SerializeJson` in .NET
- `pulumi.jsonStringify` in nodejs
- `pulumi.Output.json_dumps` in Python
This PR extends program-gen for TypeScript, C# and Python to start
emitting these JSON serialization functions (when necessary). The PR
special-cases the `toJSON` PCL function when rewriting applies so that
nested outputs aren't rewritted.
Example PCL program and generated results:
> Also check out the downstream codegen tests to see improved generated
examples
```
resource vpc "aws:ec2:Vpc" {
cidrBlock = "10.100.0.0/16"
instanceTenancy = "default"
}
resource policy "aws:iam/policy:Policy" {
description = "test"
policy = toJSON({
"Version" = "2012-10-17"
"Interpolated" = "arn:${vpc.arn}:value"
"Value" = vpc.id
})
}
```
### Generated TypeScript Before
```typescript
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const vpc = new aws.ec2.Vpc("vpc", {
cidrBlock: "10.100.0.0/16",
instanceTenancy: "default",
});
const policy = new aws.iam.Policy("policy", {
description: "test",
policy: pulumi.all([vpc.arn, vpc.id]).apply(([arn, id]) => JSON.stringify({
Version: "2012-10-17",
Interpolated: `arn:${arn}:value`,
Value: id,
})),
});
```
### Generated TypeScript After
```typescript
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const vpc = new aws.ec2.Vpc("vpc", {
cidrBlock: "10.100.0.0/16",
instanceTenancy: "default",
});
const policy = new aws.iam.Policy("policy", {
description: "test",
policy: pulumi.jsonStringify({
Version: "2012-10-17",
Interpolated: pulumi.interpolate`arn:${vpc.arn}:value`,
Value: vpc.id,
}),
});
```
### Generated Python Before
```python
import pulumi
import json
import pulumi_aws as aws
vpc = aws.ec2.Vpc("vpc",
cidr_block="10.100.0.0/16",
instance_tenancy="default")
policy = aws.iam.Policy("policy",
description="test",
policy=pulumi.Output.all(vpc.arn, vpc.id).apply(lambda arn, id: json.dumps({
"Version": "2012-10-17",
"Interpolated": f"arn:{arn}:value",
"Value": id,
})))
```
### Generated Python After
```python
import pulumi
import json
import pulumi_aws as aws
vpc = aws.ec2.Vpc("vpc",
cidr_block="10.100.0.0/16",
instance_tenancy="default")
policy = aws.iam.Policy("policy",
description="test",
policy=pulumi.Output.json_dumps({
"Version": "2012-10-17",
"Interpolated": vpc.arn.apply(lambda arn: f"arn:{arn}:value"),
"Value": vpc.id,
}))
```
### Generated C# Before
```csharp
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var vpc = new Aws.Ec2.Vpc("vpc", new()
{
CidrBlock = "10.100.0.0/16",
InstanceTenancy = "default",
});
var policy = new Aws.Iam.Policy("policy", new()
{
Description = "test",
PolicyDocument = Output.Tuple(vpc.Arn, vpc.Id).Apply(values =>
{
var arn = values.Item1;
var id = values.Item2;
return JsonSerializer.Serialize(new Dictionary<string, object?>
{
["Version"] = "2012-10-17",
["Interpolated"] = $"arn:{arn}:value",
["Value"] = id,
});
}),
});
});
```
### Generated C# After
```csharp
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var vpc = new Aws.Ec2.Vpc("vpc", new()
{
CidrBlock = "10.100.0.0/16",
InstanceTenancy = "default",
});
var policy = new Aws.Iam.Policy("policy", new()
{
Description = "test",
PolicyDocument = Output.JsonSerialize(Output.Create(new Dictionary<string, object?>
{
["Version"] = "2012-10-17",
["Interpolated"] = vpc.Arn.Apply(arn => $"arn:{arn}:value"),
["Value"] = vpc.Id,
})),
});
});
```
## Checklist
- [ ] I have run `make tidy` to update any new dependencies
- [x] I have run `make lint` to verify my code passes the lint check
- [x] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [x] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [x] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2024-02-20 15:48:46 +00:00
|
|
|
if model.ContainsOutputs(expr.Args[0].Type()) {
|
|
|
|
g.Fgenf(w, "pulumi.Output.json_dumps(%.v)", expr.Args[0])
|
|
|
|
} else {
|
|
|
|
g.Fgenf(w, "json.dumps(%.v)", expr.Args[0])
|
|
|
|
}
|
2021-09-10 18:40:38 +00:00
|
|
|
case "sha1":
|
|
|
|
g.Fgenf(w, "hashlib.sha1(%v.encode()).hexdigest()", expr.Args[0])
|
2022-03-16 00:05:36 +00:00
|
|
|
case "project":
|
|
|
|
g.Fgen(w, "pulumi.get_project()")
|
|
|
|
case "stack":
|
|
|
|
g.Fgen(w, "pulumi.get_stack()")
|
2024-08-19 03:58:19 +00:00
|
|
|
case "organization":
|
|
|
|
g.Fgen(w, "pulumi.get_organization()")
|
2022-03-16 00:05:36 +00:00
|
|
|
case "cwd":
|
|
|
|
g.Fgen(w, "os.getcwd()")
|
2024-04-16 11:13:25 +00:00
|
|
|
case "getOutput":
|
|
|
|
g.Fgenf(w, "%v.get_output(%v)", expr.Args[0], expr.Args[1])
|
2020-04-03 06:29:05 +00:00
|
|
|
default:
|
|
|
|
var rng hcl.Range
|
|
|
|
if expr.Syntax != nil {
|
|
|
|
rng = expr.Syntax.Range()
|
|
|
|
}
|
|
|
|
g.genNYI(w, "FunctionCallExpression: %v (%v)", expr.Name, rng)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) GenIndexExpression(w io.Writer, expr *model.IndexExpression) {
|
2020-04-16 23:44:34 +00:00
|
|
|
g.Fgenf(w, "%.16v[%.v]", expr.Collection, expr.Key)
|
2020-04-03 06:29:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type runeWriter interface {
|
|
|
|
WriteRune(c rune) (int, error)
|
|
|
|
}
|
|
|
|
|
2024-03-29 14:24:29 +00:00
|
|
|
func escapeRune(c rune) string {
|
|
|
|
if c < 0xFF {
|
|
|
|
return fmt.Sprintf("\\x%02x", c)
|
|
|
|
} else if c < 0xFFFF {
|
|
|
|
return fmt.Sprintf("\\u%04x", c)
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("\\U%08x", c)
|
|
|
|
}
|
|
|
|
|
2023-01-06 00:07:45 +00:00
|
|
|
//nolint:errcheck
|
2020-04-03 06:29:05 +00:00
|
|
|
func (g *generator) genEscapedString(w runeWriter, v string, escapeNewlines, escapeBraces bool) {
|
|
|
|
for _, c := range v {
|
|
|
|
switch c {
|
|
|
|
case '\n':
|
|
|
|
if escapeNewlines {
|
|
|
|
w.WriteRune('\\')
|
2024-03-29 14:24:29 +00:00
|
|
|
w.WriteRune('n')
|
|
|
|
} else {
|
|
|
|
w.WriteRune(c)
|
2020-04-03 06:29:05 +00:00
|
|
|
}
|
2024-03-29 14:24:29 +00:00
|
|
|
continue
|
2020-04-03 06:29:05 +00:00
|
|
|
case '"', '\\':
|
2020-04-21 16:18:09 +00:00
|
|
|
if escapeNewlines {
|
|
|
|
w.WriteRune('\\')
|
2024-03-29 14:24:29 +00:00
|
|
|
w.WriteRune(c)
|
|
|
|
continue
|
2020-04-21 16:18:09 +00:00
|
|
|
}
|
2020-04-03 06:29:05 +00:00
|
|
|
case '{', '}':
|
|
|
|
if escapeBraces {
|
|
|
|
w.WriteRune(c)
|
2024-03-29 14:24:29 +00:00
|
|
|
w.WriteRune(c)
|
|
|
|
continue
|
2020-04-03 06:29:05 +00:00
|
|
|
}
|
|
|
|
}
|
2024-03-29 14:24:29 +00:00
|
|
|
|
|
|
|
if unicode.IsPrint(c) {
|
|
|
|
w.WriteRune(c)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is a non-printable character. We'll emit a Python escape sequence for it.
|
|
|
|
codepoint := escapeRune(c)
|
|
|
|
for _, r := range codepoint {
|
|
|
|
w.WriteRune(r)
|
|
|
|
}
|
2020-04-03 06:29:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
[codegen/python] Fix nested quotes. (#4539)
Unlike most languages with interpolated strings, Python's formatted
string literals do not allow the nesting of quotes. For example,
this expression is not legal Python:
f"Foo {"bar"} baz"
If an interpolation requires quotes, those quotes nust differ from the
quotes used by the enclosing literal. We can fix the previous example
by rewriting it with single quotes:
f"Foo {'bar'} baz"
However, this presents a problem if there are more than two levels of
nesting, as Python only has two kinds of quotes (four if the outermost
string uses """ or '''): in this case, the expression becomes
unspellable, and must be assigned to a local that is then used in place
of the original expression. So this:
f"Foo {bar[f'index {baz["qux"]}']} zed"
becomes this:
index = "qux"
f"Foo {bar[f'index {baz[index]}']}"
To put it bluntly, Python code generation reqiures register allocation,
but for quotes. These changes implement exactly that.
These changes also include a fix for traversals that access values that
are dictionaries rather than objects, and must use indexers rather than
attributes.
2020-04-30 23:34:25 +00:00
|
|
|
func (g *generator) genStringLiteral(w io.Writer, quotes, v string) {
|
2020-04-03 06:29:05 +00:00
|
|
|
builder := &strings.Builder{}
|
[codegen/python] Fix nested quotes. (#4539)
Unlike most languages with interpolated strings, Python's formatted
string literals do not allow the nesting of quotes. For example,
this expression is not legal Python:
f"Foo {"bar"} baz"
If an interpolation requires quotes, those quotes nust differ from the
quotes used by the enclosing literal. We can fix the previous example
by rewriting it with single quotes:
f"Foo {'bar'} baz"
However, this presents a problem if there are more than two levels of
nesting, as Python only has two kinds of quotes (four if the outermost
string uses """ or '''): in this case, the expression becomes
unspellable, and must be assigned to a local that is then used in place
of the original expression. So this:
f"Foo {bar[f'index {baz["qux"]}']} zed"
becomes this:
index = "qux"
f"Foo {bar[f'index {baz[index]}']}"
To put it bluntly, Python code generation reqiures register allocation,
but for quotes. These changes implement exactly that.
These changes also include a fix for traversals that access values that
are dictionaries rather than objects, and must use indexers rather than
attributes.
2020-04-30 23:34:25 +00:00
|
|
|
|
|
|
|
builder.WriteString(quotes)
|
|
|
|
escapeNewlines := quotes == `"` || quotes == `'`
|
|
|
|
g.genEscapedString(builder, v, escapeNewlines, false)
|
|
|
|
builder.WriteString(quotes)
|
2020-04-03 06:29:05 +00:00
|
|
|
|
|
|
|
g.Fgenf(w, "%s", builder.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) GenLiteralValueExpression(w io.Writer, expr *model.LiteralValueExpression) {
|
2021-06-24 16:17:55 +00:00
|
|
|
typ := expr.Type()
|
|
|
|
if cns, ok := typ.(*model.ConstType); ok {
|
|
|
|
typ = cns.Type
|
|
|
|
}
|
|
|
|
|
|
|
|
switch typ {
|
2020-04-03 06:29:05 +00:00
|
|
|
case model.BoolType:
|
|
|
|
if expr.Value.True() {
|
|
|
|
g.Fgen(w, "True")
|
|
|
|
} else {
|
|
|
|
g.Fgen(w, "False")
|
|
|
|
}
|
2020-08-06 20:12:27 +00:00
|
|
|
case model.NoneType:
|
|
|
|
g.Fgen(w, "None")
|
2020-04-03 06:29:05 +00:00
|
|
|
case model.NumberType:
|
|
|
|
bf := expr.Value.AsBigFloat()
|
|
|
|
if i, acc := bf.Int64(); acc == big.Exact {
|
|
|
|
g.Fgenf(w, "%d", i)
|
|
|
|
} else {
|
|
|
|
f, _ := bf.Float64()
|
|
|
|
g.Fgenf(w, "%g", f)
|
|
|
|
}
|
|
|
|
case model.StringType:
|
[codegen/python] Fix nested quotes. (#4539)
Unlike most languages with interpolated strings, Python's formatted
string literals do not allow the nesting of quotes. For example,
this expression is not legal Python:
f"Foo {"bar"} baz"
If an interpolation requires quotes, those quotes nust differ from the
quotes used by the enclosing literal. We can fix the previous example
by rewriting it with single quotes:
f"Foo {'bar'} baz"
However, this presents a problem if there are more than two levels of
nesting, as Python only has two kinds of quotes (four if the outermost
string uses """ or '''): in this case, the expression becomes
unspellable, and must be assigned to a local that is then used in place
of the original expression. So this:
f"Foo {bar[f'index {baz["qux"]}']} zed"
becomes this:
index = "qux"
f"Foo {bar[f'index {baz[index]}']}"
To put it bluntly, Python code generation reqiures register allocation,
but for quotes. These changes implement exactly that.
These changes also include a fix for traversals that access values that
are dictionaries rather than objects, and must use indexers rather than
attributes.
2020-04-30 23:34:25 +00:00
|
|
|
quotes := g.quotes[expr]
|
|
|
|
g.genStringLiteral(w, quotes, expr.Value.AsString())
|
2020-04-03 06:29:05 +00:00
|
|
|
default:
|
|
|
|
contract.Failf("unexpected literal type in GenLiteralValueExpression: %v (%v)", expr.Type(),
|
|
|
|
expr.SyntaxNode().Range())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) GenObjectConsExpression(w io.Writer, expr *model.ObjectConsExpression) {
|
2020-08-06 22:09:23 +00:00
|
|
|
g.genObjectConsExpression(w, expr, expr.Type())
|
|
|
|
}
|
|
|
|
|
2023-12-23 11:26:01 +00:00
|
|
|
func objectKey(item model.ObjectConsItem) string {
|
|
|
|
switch key := item.Key.(type) {
|
|
|
|
case *model.LiteralValueExpression:
|
|
|
|
return key.Value.AsString()
|
|
|
|
case *model.TemplateExpression:
|
|
|
|
// assume a template expression has one constant part that is a LiteralValueExpression
|
|
|
|
if len(key.Parts) == 1 {
|
|
|
|
if literal, ok := key.Parts[0].(*model.LiteralValueExpression); ok {
|
|
|
|
return literal.Value.AsString()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2020-08-06 22:09:23 +00:00
|
|
|
func (g *generator) genObjectConsExpression(w io.Writer, expr *model.ObjectConsExpression, destType model.Type) {
|
|
|
|
typeName := g.argumentTypeName(expr, destType) // Example: aws.s3.BucketLoggingArgs
|
2024-07-17 08:11:49 +00:00
|
|
|
td := g.typedDictEnabled(expr, destType) || g.insideTypedDict
|
|
|
|
if typeName != "" && !td {
|
|
|
|
// If a typeName exists, and it's not for a typedDict, treat this as an Input Class
|
|
|
|
// e.g. aws.s3.BucketLoggingArgs(key="value", foo="bar", ...)
|
2020-08-06 22:09:23 +00:00
|
|
|
if len(expr.Items) == 0 {
|
|
|
|
g.Fgenf(w, "%s()", typeName)
|
|
|
|
} else {
|
|
|
|
g.Fgenf(w, "%s(\n", typeName)
|
|
|
|
g.Indented(func() {
|
|
|
|
for _, item := range expr.Items {
|
|
|
|
g.Fgenf(w, "%s", g.Indent)
|
2023-12-23 11:26:01 +00:00
|
|
|
propertyKey := objectKey(item)
|
|
|
|
g.Fprint(w, PyName(propertyKey))
|
2020-08-06 22:09:23 +00:00
|
|
|
g.Fgenf(w, "=%.v,\n", item.Value)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
g.Fgenf(w, "%s)", g.Indent)
|
|
|
|
}
|
2020-04-03 06:29:05 +00:00
|
|
|
} else {
|
2024-07-17 08:11:49 +00:00
|
|
|
// Otherwise treat this as a typed or untyped dictionary e.g. {"key": "value", "foo": "bar", ...}
|
2020-08-06 22:09:23 +00:00
|
|
|
if len(expr.Items) == 0 {
|
|
|
|
g.Fgen(w, "{}")
|
|
|
|
} else {
|
|
|
|
g.Fgen(w, "{")
|
|
|
|
g.Indented(func() {
|
|
|
|
for _, item := range expr.Items {
|
2024-07-17 08:11:49 +00:00
|
|
|
if td {
|
|
|
|
// If we're inside a typedDict, use the PyName of the key and keep track of
|
|
|
|
// the fact that we're inside a typedDict for the recursive calls when
|
|
|
|
// printing the value.
|
|
|
|
g.insideTypedDict = true
|
|
|
|
propertyKey := objectKey(item)
|
|
|
|
key := PyName(propertyKey)
|
|
|
|
g.Fgenf(w, "\n%s%q: %.v,", g.Indent, key, item.Value)
|
|
|
|
g.insideTypedDict = false
|
|
|
|
} else {
|
|
|
|
g.Fgenf(w, "\n%s%.v: %.v,", g.Indent, item.Key, item.Value)
|
|
|
|
}
|
2020-08-06 22:09:23 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
g.Fgenf(w, "\n%s}", g.Indent)
|
|
|
|
}
|
2020-04-03 06:29:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-21 16:18:09 +00:00
|
|
|
func (g *generator) genRelativeTraversal(w io.Writer, traversal hcl.Traversal, parts []model.Traversable) {
|
[codegen/python] Fix nested quotes. (#4539)
Unlike most languages with interpolated strings, Python's formatted
string literals do not allow the nesting of quotes. For example,
this expression is not legal Python:
f"Foo {"bar"} baz"
If an interpolation requires quotes, those quotes nust differ from the
quotes used by the enclosing literal. We can fix the previous example
by rewriting it with single quotes:
f"Foo {'bar'} baz"
However, this presents a problem if there are more than two levels of
nesting, as Python only has two kinds of quotes (four if the outermost
string uses """ or '''): in this case, the expression becomes
unspellable, and must be assigned to a local that is then used in place
of the original expression. So this:
f"Foo {bar[f'index {baz["qux"]}']} zed"
becomes this:
index = "qux"
f"Foo {bar[f'index {baz[index]}']}"
To put it bluntly, Python code generation reqiures register allocation,
but for quotes. These changes implement exactly that.
These changes also include a fix for traversals that access values that
are dictionaries rather than objects, and must use indexers rather than
attributes.
2020-04-30 23:34:25 +00:00
|
|
|
for _, traverser := range traversal {
|
2020-04-03 06:29:05 +00:00
|
|
|
var key cty.Value
|
2020-04-21 16:18:09 +00:00
|
|
|
switch traverser := traverser.(type) {
|
2020-04-03 06:29:05 +00:00
|
|
|
case hcl.TraverseAttr:
|
2023-06-08 23:44:09 +00:00
|
|
|
key = cty.StringVal(PyName(traverser.Name))
|
2020-04-03 06:29:05 +00:00
|
|
|
case hcl.TraverseIndex:
|
2020-04-21 16:18:09 +00:00
|
|
|
key = traverser.Key
|
2020-04-03 06:29:05 +00:00
|
|
|
default:
|
2020-04-21 16:18:09 +00:00
|
|
|
contract.Failf("unexpected traverser of type %T (%v)", traverser, traverser.SourceRange())
|
2020-04-03 06:29:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
switch key.Type() {
|
|
|
|
case cty.String:
|
2023-06-08 23:44:09 +00:00
|
|
|
keyVal := PyName(key.AsString())
|
2023-02-21 23:27:34 +00:00
|
|
|
contract.Assertf(isLegalIdentifier(keyVal), "illegal identifier: %q", keyVal)
|
[codegen/python] Fix nested quotes. (#4539)
Unlike most languages with interpolated strings, Python's formatted
string literals do not allow the nesting of quotes. For example,
this expression is not legal Python:
f"Foo {"bar"} baz"
If an interpolation requires quotes, those quotes nust differ from the
quotes used by the enclosing literal. We can fix the previous example
by rewriting it with single quotes:
f"Foo {'bar'} baz"
However, this presents a problem if there are more than two levels of
nesting, as Python only has two kinds of quotes (four if the outermost
string uses """ or '''): in this case, the expression becomes
unspellable, and must be assigned to a local that is then used in place
of the original expression. So this:
f"Foo {bar[f'index {baz["qux"]}']} zed"
becomes this:
index = "qux"
f"Foo {bar[f'index {baz[index]}']}"
To put it bluntly, Python code generation reqiures register allocation,
but for quotes. These changes implement exactly that.
These changes also include a fix for traversals that access values that
are dictionaries rather than objects, and must use indexers rather than
attributes.
2020-04-30 23:34:25 +00:00
|
|
|
g.Fgenf(w, ".%s", keyVal)
|
2020-04-03 06:29:05 +00:00
|
|
|
case cty.Number:
|
|
|
|
idx, _ := key.AsBigFloat().Int64()
|
|
|
|
g.Fgenf(w, "[%d]", idx)
|
|
|
|
default:
|
[codegen/python] Fix nested quotes. (#4539)
Unlike most languages with interpolated strings, Python's formatted
string literals do not allow the nesting of quotes. For example,
this expression is not legal Python:
f"Foo {"bar"} baz"
If an interpolation requires quotes, those quotes nust differ from the
quotes used by the enclosing literal. We can fix the previous example
by rewriting it with single quotes:
f"Foo {'bar'} baz"
However, this presents a problem if there are more than two levels of
nesting, as Python only has two kinds of quotes (four if the outermost
string uses """ or '''): in this case, the expression becomes
unspellable, and must be assigned to a local that is then used in place
of the original expression. So this:
f"Foo {bar[f'index {baz["qux"]}']} zed"
becomes this:
index = "qux"
f"Foo {bar[f'index {baz[index]}']}"
To put it bluntly, Python code generation reqiures register allocation,
but for quotes. These changes implement exactly that.
These changes also include a fix for traversals that access values that
are dictionaries rather than objects, and must use indexers rather than
attributes.
2020-04-30 23:34:25 +00:00
|
|
|
keyExpr := &model.LiteralValueExpression{Value: key}
|
|
|
|
diags := keyExpr.Typecheck(false)
|
|
|
|
contract.Ignore(diags)
|
|
|
|
|
|
|
|
g.Fgenf(w, "[%v]", keyExpr)
|
2020-04-03 06:29:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) GenRelativeTraversalExpression(w io.Writer, expr *model.RelativeTraversalExpression) {
|
2020-04-16 23:44:34 +00:00
|
|
|
g.Fgenf(w, "%.16v", expr.Source)
|
2020-04-07 02:43:16 +00:00
|
|
|
g.genRelativeTraversal(w, expr.Traversal, expr.Parts)
|
2020-04-03 06:29:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) GenScopeTraversalExpression(w io.Writer, expr *model.ScopeTraversalExpression) {
|
2020-04-07 02:43:16 +00:00
|
|
|
rootName := PyName(expr.RootName)
|
2023-04-05 11:19:52 +00:00
|
|
|
if g.isComponent {
|
|
|
|
configVars := map[string]*pcl.ConfigVariable{}
|
|
|
|
for _, configVar := range g.program.ConfigVariables() {
|
|
|
|
configVars[configVar.Name()] = configVar
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, isConfig := configVars[expr.RootName]; isConfig {
|
|
|
|
if _, configReference := expr.Parts[0].(*pcl.ConfigVariable); configReference {
|
|
|
|
rootName = fmt.Sprintf("args[\"%s\"]", expr.RootName)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-16 23:44:34 +00:00
|
|
|
if _, ok := expr.Parts[0].(*model.SplatVariable); ok {
|
2020-04-07 02:43:16 +00:00
|
|
|
rootName = "__item"
|
|
|
|
}
|
|
|
|
|
|
|
|
g.Fgen(w, rootName)
|
|
|
|
g.genRelativeTraversal(w, expr.Traversal.SimpleSplit().Rel, expr.Parts)
|
2020-04-03 06:29:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) GenSplatExpression(w io.Writer, expr *model.SplatExpression) {
|
2020-04-16 23:44:34 +00:00
|
|
|
g.Fgenf(w, "[%.v for __item in %.v]", expr.Each, expr.Source)
|
2020-04-03 06:29:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) GenTemplateExpression(w io.Writer, expr *model.TemplateExpression) {
|
[codegen/python] Fix nested quotes. (#4539)
Unlike most languages with interpolated strings, Python's formatted
string literals do not allow the nesting of quotes. For example,
this expression is not legal Python:
f"Foo {"bar"} baz"
If an interpolation requires quotes, those quotes nust differ from the
quotes used by the enclosing literal. We can fix the previous example
by rewriting it with single quotes:
f"Foo {'bar'} baz"
However, this presents a problem if there are more than two levels of
nesting, as Python only has two kinds of quotes (four if the outermost
string uses """ or '''): in this case, the expression becomes
unspellable, and must be assigned to a local that is then used in place
of the original expression. So this:
f"Foo {bar[f'index {baz["qux"]}']} zed"
becomes this:
index = "qux"
f"Foo {bar[f'index {baz[index]}']}"
To put it bluntly, Python code generation reqiures register allocation,
but for quotes. These changes implement exactly that.
These changes also include a fix for traversals that access values that
are dictionaries rather than objects, and must use indexers rather than
attributes.
2020-04-30 23:34:25 +00:00
|
|
|
quotes := g.quotes[expr]
|
|
|
|
escapeNewlines := quotes == `"` || quotes == `'`
|
2020-04-03 06:29:05 +00:00
|
|
|
|
2020-04-29 13:20:20 +00:00
|
|
|
prefix, escapeBraces := "", false
|
[codegen/python] Fix nested quotes. (#4539)
Unlike most languages with interpolated strings, Python's formatted
string literals do not allow the nesting of quotes. For example,
this expression is not legal Python:
f"Foo {"bar"} baz"
If an interpolation requires quotes, those quotes nust differ from the
quotes used by the enclosing literal. We can fix the previous example
by rewriting it with single quotes:
f"Foo {'bar'} baz"
However, this presents a problem if there are more than two levels of
nesting, as Python only has two kinds of quotes (four if the outermost
string uses """ or '''): in this case, the expression becomes
unspellable, and must be assigned to a local that is then used in place
of the original expression. So this:
f"Foo {bar[f'index {baz["qux"]}']} zed"
becomes this:
index = "qux"
f"Foo {bar[f'index {baz[index]}']}"
To put it bluntly, Python code generation reqiures register allocation,
but for quotes. These changes implement exactly that.
These changes also include a fix for traversals that access values that
are dictionaries rather than objects, and must use indexers rather than
attributes.
2020-04-30 23:34:25 +00:00
|
|
|
for _, part := range expr.Parts {
|
2021-06-24 16:17:55 +00:00
|
|
|
if lit, ok := part.(*model.LiteralValueExpression); !ok || !model.StringType.AssignableFrom(lit.Type()) {
|
2020-04-29 13:20:20 +00:00
|
|
|
prefix, escapeBraces = "f", true
|
[codegen/python] Fix nested quotes. (#4539)
Unlike most languages with interpolated strings, Python's formatted
string literals do not allow the nesting of quotes. For example,
this expression is not legal Python:
f"Foo {"bar"} baz"
If an interpolation requires quotes, those quotes nust differ from the
quotes used by the enclosing literal. We can fix the previous example
by rewriting it with single quotes:
f"Foo {'bar'} baz"
However, this presents a problem if there are more than two levels of
nesting, as Python only has two kinds of quotes (four if the outermost
string uses """ or '''): in this case, the expression becomes
unspellable, and must be assigned to a local that is then used in place
of the original expression. So this:
f"Foo {bar[f'index {baz["qux"]}']} zed"
becomes this:
index = "qux"
f"Foo {bar[f'index {baz[index]}']}"
To put it bluntly, Python code generation reqiures register allocation,
but for quotes. These changes implement exactly that.
These changes also include a fix for traversals that access values that
are dictionaries rather than objects, and must use indexers rather than
attributes.
2020-04-30 23:34:25 +00:00
|
|
|
break
|
2020-04-03 06:29:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
b := bufio.NewWriter(w)
|
|
|
|
defer b.Flush()
|
|
|
|
|
2020-04-21 16:18:09 +00:00
|
|
|
g.Fprintf(b, "%s%s", prefix, quotes)
|
2020-04-03 06:29:05 +00:00
|
|
|
for _, expr := range expr.Parts {
|
2021-06-24 16:17:55 +00:00
|
|
|
if lit, ok := expr.(*model.LiteralValueExpression); ok && model.StringType.AssignableFrom(lit.Type()) {
|
2020-04-29 13:20:20 +00:00
|
|
|
g.genEscapedString(b, lit.Value.AsString(), escapeNewlines, escapeBraces)
|
2020-04-03 06:29:05 +00:00
|
|
|
} else {
|
2020-04-16 23:44:34 +00:00
|
|
|
g.Fgenf(b, "{%.v}", expr)
|
2020-04-03 06:29:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
g.Fprint(b, quotes)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) GenTemplateJoinExpression(w io.Writer, expr *model.TemplateJoinExpression) {
|
|
|
|
g.genNYI(w, "TemplateJoinExpression")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) GenTupleConsExpression(w io.Writer, expr *model.TupleConsExpression) {
|
|
|
|
switch len(expr.Expressions) {
|
|
|
|
case 0:
|
|
|
|
g.Fgen(w, "[]")
|
|
|
|
case 1:
|
2020-04-16 23:44:34 +00:00
|
|
|
g.Fgenf(w, "[%.v]", expr.Expressions[0])
|
2020-04-03 06:29:05 +00:00
|
|
|
default:
|
|
|
|
g.Fgen(w, "[")
|
|
|
|
g.Indented(func() {
|
|
|
|
for _, v := range expr.Expressions {
|
2020-04-16 23:44:34 +00:00
|
|
|
g.Fgenf(w, "\n%s%.v,", g.Indent, v)
|
2020-04-03 06:29:05 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
g.Fgen(w, "\n", g.Indent, "]")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) GenUnaryOpExpression(w io.Writer, expr *model.UnaryOpExpression) {
|
2020-04-16 23:44:34 +00:00
|
|
|
opstr, precedence := "", g.GetPrecedence(expr)
|
2020-04-07 02:43:16 +00:00
|
|
|
switch expr.Operation {
|
|
|
|
case hclsyntax.OpLogicalNot:
|
|
|
|
opstr = "not "
|
|
|
|
case hclsyntax.OpNegate:
|
|
|
|
opstr = "-"
|
|
|
|
}
|
2020-04-16 23:44:34 +00:00
|
|
|
g.Fgenf(w, "%[2]v%.[1]*[3]v", precedence, opstr, expr.Operand)
|
2020-04-03 06:29:05 +00:00
|
|
|
}
|