From d785536c7e0161d0e1f8b51fbb9b725a23ea4c52 Mon Sep 17 00:00:00 2001 From: Kevin Valk Date: Wed, 23 Aug 2023 00:44:51 +0200 Subject: [PATCH 1/9] feat: using emit_module, emit_generators and emit_async flags you can now generate query code that suites your need --- .gitignore | 1 + internal/config.go | 7 +- internal/gen.go | 385 ++++++++++++++++-------------------- internal/imports.go | 15 +- internal/poet/poet.go | 7 + internal/printer/printer.go | 59 ++++-- 6 files changed, 242 insertions(+), 232 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..917660a --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.wasm \ No newline at end of file diff --git a/internal/config.go b/internal/config.go index 009cb04..4f44501 100644 --- a/internal/config.go +++ b/internal/config.go @@ -1,9 +1,12 @@ package python type Config struct { + EmitModule bool `json:"emit_module"` // If true emits functions in module, else wraps in a class. + EmitGenerators bool `json:"emit_generators"` // Will we use generators or lists, defaults to true + EmitAsync bool `json:"emit_async"` // Emits async code instead of sync EmitExactTableNames bool `json:"emit_exact_table_names"` - EmitSyncQuerier bool `json:"emit_sync_querier"` - EmitAsyncQuerier bool `json:"emit_async_querier"` + EmitSyncQuerier bool `json:"emit_sync_querier"` // DEPRECATED ALIAS FOR: emit_type = 'class', emit_generators = True + EmitAsyncQuerier bool `json:"emit_async_querier"` // DEPRECATED ALIAS FOR: emit_type = 'class', emit_generators = True Package string `json:"package"` Out string `json:"out"` EmitPydanticModels bool `json:"emit_pydantic_models"` diff --git a/internal/gen.go b/internal/gen.go index ebe34b0..74f23fe 100644 --- a/internal/gen.go +++ b/internal/gen.go @@ -638,7 +638,7 @@ func typeRefNode(base string, parts ...string) *pyast.Node { return n } -func connMethodNode(method, name string, arg *pyast.Node) *pyast.Node { +func connMethodNode(method *pyast.Node, name string, arg *pyast.Node) *pyast.Node { args := []*pyast.Node{ { Node: &pyast.Node_Call{ @@ -657,7 +657,7 @@ func connMethodNode(method, name string, arg *pyast.Node) *pyast.Node { return &pyast.Node{ Node: &pyast.Node_Call{ Call: &pyast.Call{ - Func: typeRefNode("self", "_conn", method), + Func: method, Args: args, }, }, @@ -753,47 +753,9 @@ func buildModelsTree(ctx *pyTmplCtx, i *importer) *pyast.Node { return &pyast.Node{Node: &pyast.Node_Module{Module: mod}} } -func querierClassDef() *pyast.ClassDef { +func querierClassDef(name string, connectionAnnotation *pyast.Node) *pyast.ClassDef { return &pyast.ClassDef{ - Name: "Querier", - Body: []*pyast.Node{ - { - Node: &pyast.Node_FunctionDef{ - FunctionDef: &pyast.FunctionDef{ - Name: "__init__", - Args: &pyast.Arguments{ - Args: []*pyast.Arg{ - { - Arg: "self", - }, - { - Arg: "conn", - Annotation: typeRefNode("sqlalchemy", "engine", "Connection"), - }, - }, - }, - Body: []*pyast.Node{ - { - Node: &pyast.Node_Assign{ - Assign: &pyast.Assign{ - Targets: []*pyast.Node{ - poet.Attribute(poet.Name("self"), "_conn"), - }, - Value: poet.Name("conn"), - }, - }, - }, - }, - }, - }, - }, - }, - } -} - -func asyncQuerierClassDef() *pyast.ClassDef { - return &pyast.ClassDef{ - Name: "AsyncQuerier", + Name: name, Body: []*pyast.Node{ { Node: &pyast.Node_FunctionDef{ @@ -805,8 +767,8 @@ func asyncQuerierClassDef() *pyast.ClassDef { Arg: "self", }, { - Arg: "conn", - Annotation: typeRefNode("sqlalchemy", "ext", "asyncio", "AsyncConnection"), + Arg: "connection", + Annotation: connectionAnnotation, }, }, }, @@ -815,9 +777,9 @@ func asyncQuerierClassDef() *pyast.ClassDef { Node: &pyast.Node_Assign{ Assign: &pyast.Assign{ Targets: []*pyast.Node{ - poet.Attribute(poet.Name("self"), "_conn"), + poet.Attribute(poet.Name("self"), "_connection"), }, - Value: poet.Name("conn"), + Value: poet.Name("connection"), }, }, }, @@ -886,188 +848,181 @@ func buildQueryTree(ctx *pyTmplCtx, i *importer, source string) *pyast.Node { } } - if ctx.C.EmitSyncQuerier { - cls := querierClassDef() - for _, q := range ctx.Queries { - if !ctx.OutputQuery(q.SourceName) { - continue - } - f := &pyast.FunctionDef{ - Name: q.MethodName, - Args: &pyast.Arguments{ - Args: []*pyast.Arg{ - { - Arg: "self", - }, - }, - }, - } + functions := make([]*pyast.Node, 0, 10) - q.AddArgs(f.Args) - exec := connMethodNode("execute", q.ConstantName, q.ArgDictNode()) + // Define some reused types based on async or sync code + var connectionAnnotation *pyast.Node + if ctx.C.EmitAsync { + connectionAnnotation = typeRefNode("sqlalchemy", "ext", "asyncio", "AsyncConnection") + } else { + connectionAnnotation = typeRefNode("sqlalchemy", "engine", "Connection") + } - switch q.Cmd { - case ":one": - f.Body = append(f.Body, - assignNode("row", poet.Node( - &pyast.Call{ - Func: poet.Attribute(exec, "first"), - }, - )), - poet.Node( - &pyast.If{ - Test: poet.Node( - &pyast.Compare{ - Left: poet.Name("row"), - Ops: []*pyast.Node{ - poet.Is(), - }, - Comparators: []*pyast.Node{ - poet.Constant(nil), - }, - }, - ), - Body: []*pyast.Node{ - poet.Return( - poet.Constant(nil), - ), - }, - }, - ), - poet.Return(q.Ret.RowNode("row")), - ) - f.Returns = subscriptNode("Optional", q.Ret.Annotation()) - case ":many": - f.Body = append(f.Body, - assignNode("result", exec), - poet.Node( - &pyast.For{ - Target: poet.Name("row"), - Iter: poet.Name("result"), - Body: []*pyast.Node{ - poet.Expr( - poet.Yield( - q.Ret.RowNode("row"), - ), - ), - }, - }, - ), - ) - f.Returns = subscriptNode("Iterator", q.Ret.Annotation()) - case ":exec": - f.Body = append(f.Body, exec) - f.Returns = poet.Constant(nil) - case ":execrows": - f.Body = append(f.Body, - assignNode("result", exec), - poet.Return(poet.Attribute(poet.Name("result"), "rowcount")), - ) - f.Returns = poet.Name("int") - case ":execresult": - f.Body = append(f.Body, - poet.Return(exec), - ) - f.Returns = typeRefNode("sqlalchemy", "engine", "Result") - default: - panic("unknown cmd " + q.Cmd) - } + // We need to figure out how to access the SQLAlchemy connectionVar object + var connectionVar *pyast.Node + if ctx.C.EmitModule { + connectionVar = poet.Name("connection") + } else { + connectionVar = poet.Attribute(poet.Name("self"), "_connection") + } - cls.Body = append(cls.Body, poet.Node(f)) + // We loop through all queries and build our query functions + for _, q := range ctx.Queries { + if !ctx.OutputQuery(q.SourceName) { + continue + } + f := &pyast.FunctionDef{ + Name: q.MethodName, + Args: &pyast.Arguments{}, } - mod.Body = append(mod.Body, poet.Node(cls)) - } - if ctx.C.EmitAsyncQuerier { - cls := asyncQuerierClassDef() - for _, q := range ctx.Queries { - if !ctx.OutputQuery(q.SourceName) { - continue - } - f := &pyast.AsyncFunctionDef{ - Name: q.MethodName, - Args: &pyast.Arguments{ - Args: []*pyast.Arg{ - { - Arg: "self", - }, - }, - }, - } + if ctx.C.EmitModule { + f.Args.Args = append(f.Args.Args, &pyast.Arg{ + Arg: "connection", + Annotation: connectionAnnotation, + }) + } else { + f.Args.Args = append(f.Args.Args, &pyast.Arg{ + Arg: "self", + }) + } - q.AddArgs(f.Args) - exec := connMethodNode("execute", q.ConstantName, q.ArgDictNode()) + q.AddArgs(f.Args) - switch q.Cmd { - case ":one": - f.Body = append(f.Body, - assignNode("row", poet.Node( - &pyast.Call{ - Func: poet.Attribute(poet.Await(exec), "first"), - }, - )), - poet.Node( - &pyast.If{ - Test: poet.Node( - &pyast.Compare{ - Left: poet.Name("row"), - Ops: []*pyast.Node{ - poet.Is(), - }, - Comparators: []*pyast.Node{ - poet.Constant(nil), - }, + exec := poet.Expr(connMethodNode(poet.Attribute(connectionVar, "execute"), q.ConstantName, q.ArgDictNode())) + if ctx.C.EmitAsync { + exec = poet.Await(exec) + } + + switch q.Cmd { + case ":one": + f.Body = append(f.Body, + assignNode("row", poet.Node( + &pyast.Call{ + Func: poet.Attribute(exec, "first"), + }, + )), + poet.Node( + &pyast.If{ + Test: poet.Node( + &pyast.Compare{ + Left: poet.Name("row"), + Ops: []*pyast.Node{ + poet.Is(), }, - ), - Body: []*pyast.Node{ - poet.Return( + Comparators: []*pyast.Node{ poet.Constant(nil), - ), + }, }, + ), + Body: []*pyast.Node{ + poet.Return( + poet.Constant(nil), + ), }, - ), - poet.Return(q.Ret.RowNode("row")), - ) - f.Returns = subscriptNode("Optional", q.Ret.Annotation()) - case ":many": - stream := connMethodNode("stream", q.ConstantName, q.ArgDictNode()) - f.Body = append(f.Body, - assignNode("result", poet.Await(stream)), - poet.Node( - &pyast.AsyncFor{ - Target: poet.Name("row"), - Iter: poet.Name("result"), - Body: []*pyast.Node{ - poet.Expr( - poet.Yield( - q.Ret.RowNode("row"), + }, + ), + poet.Return(q.Ret.RowNode("row")), + ) + f.Returns = subscriptNode("Optional", q.Ret.Annotation()) + case ":many": + if ctx.C.EmitGenerators { + if ctx.C.EmitAsync { + // If we are using generators and async, we are switching to stream implementation + exec = poet.Await(connMethodNode(poet.Attribute(connectionVar, "stream"), q.ConstantName, q.ArgDictNode())) + + f.Returns = subscriptNode("AsyncIterator", q.Ret.Annotation()) + f.Body = append(f.Body, + assignNode("result", exec), + poet.Node( + &pyast.AsyncFor{ + Target: poet.Name("row"), + Iter: poet.Name("result"), + Body: []*pyast.Node{ + poet.Expr( + poet.Yield( + q.Ret.RowNode("row"), + ), + ), + }, + }, + )) + } else { + f.Returns = subscriptNode("Iterator", q.Ret.Annotation()) + f.Body = append(f.Body, + assignNode("result", exec), + poet.Node( + &pyast.For{ + Target: poet.Name("row"), + Iter: poet.Name("result"), + Body: []*pyast.Node{ + poet.Expr( + poet.Yield( + q.Ret.RowNode("row"), + ), ), - ), + }, }, - }, - ), - ) - f.Returns = subscriptNode("AsyncIterator", q.Ret.Annotation()) - case ":exec": - f.Body = append(f.Body, poet.Await(exec)) - f.Returns = poet.Constant(nil) - case ":execrows": - f.Body = append(f.Body, - assignNode("result", poet.Await(exec)), - poet.Return(poet.Attribute(poet.Name("result"), "rowcount")), - ) - f.Returns = poet.Name("int") - case ":execresult": + )) + } + } else { f.Body = append(f.Body, - poet.Return(poet.Await(exec)), - ) - f.Returns = typeRefNode("sqlalchemy", "engine", "Result") - default: - panic("unknown cmd " + q.Cmd) + assignNode("result", poet.Node( + &pyast.Call{ + Func: poet.Attribute(exec, "all"), + }, + )), + poet.Node(&pyast.Return{ + Value: poet.Node( + &pyast.For{ + Target: poet.Name("row"), + Iter: poet.Name("result"), + Body: []*pyast.Node{ + q.Ret.RowNode("row"), + }, + }, + ), + }, + )) + f.Returns = subscriptNode("List", q.Ret.Annotation()) } + case ":exec": + f.Body = append(f.Body, exec) + f.Returns = poet.Constant(nil) + case ":execrows": + f.Body = append(f.Body, + assignNode("result", exec), + poet.Return(poet.Attribute(poet.Name("result"), "rowcount")), + ) + f.Returns = poet.Name("int") + case ":execresult": + f.Body = append(f.Body, + poet.Return(exec), + ) + f.Returns = typeRefNode("sqlalchemy", "engine", "Result") + default: + panic("unknown cmd " + q.Cmd) + } - cls.Body = append(cls.Body, poet.Node(f)) + // If we are emitting async code, we have to swap our sync func for an async one and fix the connection annotation. + if ctx.C.EmitAsync { + functions = append(functions, poet.Node(&pyast.AsyncFunctionDef{ + Name: f.Name, + Args: f.Args, + Body: f.Body, + Returns: f.Returns, + })) + } else { + functions = append(functions, poet.Node(f)) } + } + + // Lets see how to add all functions + if ctx.C.EmitModule { + mod.Body = append(mod.Body, functions...) + } else { + cls := querierClassDef("Querier", connectionAnnotation) + cls.Body = append(cls.Body, functions...) mod.Body = append(mod.Body, poet.Node(cls)) } @@ -1099,6 +1054,14 @@ func Generate(_ context.Context, req *plugin.CodeGenRequest) (*plugin.CodeGenRes } } + // TODO: Remove when when we drop support for deprecated EmitSyncQuerier and EmitAsyncQuerier options + if conf.EmitAsyncQuerier || conf.EmitSyncQuerier { + conf.EmitModule = false + conf.EmitGenerators = true + conf.EmitAsync = conf.EmitAsyncQuerier + // TODO/NOTE: We now have a breaking change because we emit only one flavor. What do we want to do? + } + enums := buildEnums(req) models := buildModels(conf, req) queries, err := buildQueries(conf, req, models) diff --git a/internal/imports.go b/internal/imports.go index b78f9bd..de9cc1b 100644 --- a/internal/imports.go +++ b/internal/imports.go @@ -151,7 +151,7 @@ func (i *importer) queryImportSpecs(fileName string) (map[string]importSpec, map pkg := make(map[string]importSpec) pkg["sqlalchemy"] = importSpec{Module: "sqlalchemy"} - if i.C.EmitAsyncQuerier { + if i.C.EmitAsync { pkg["sqlalchemy.ext.asyncio"] = importSpec{Module: "sqlalchemy.ext.asyncio"} } @@ -185,11 +185,14 @@ func (i *importer) queryImportSpecs(fileName string) (map[string]importSpec, map std["typing.Optional"] = importSpec{Module: "typing", Name: "Optional"} } if q.Cmd == ":many" { - if i.C.EmitSyncQuerier { - std["typing.Iterator"] = importSpec{Module: "typing", Name: "Iterator"} - } - if i.C.EmitAsyncQuerier { - std["typing.AsyncIterator"] = importSpec{Module: "typing", Name: "AsyncIterator"} + if i.C.EmitGenerators { + if i.C.EmitAsync { + std["typing.AsyncIterator"] = importSpec{Module: "typing", Name: "AsyncIterator"} + } else { + std["typing.Iterator"] = importSpec{Module: "typing", Name: "Iterator"} + } + } else { + std["typing.List"] = importSpec{Module: "typing", Name: "List"} } } queryValueModelImports(q.Ret) diff --git a/internal/poet/poet.go b/internal/poet/poet.go index 22b488c..fc22995 100644 --- a/internal/poet/poet.go +++ b/internal/poet/poet.go @@ -161,6 +161,13 @@ func Node(node proto) *ast.Node { // case *ast.Node_Subscript: // w.printSubscript(n.Subscript, indent) + case *ast.Return: + return &ast.Node{ + Node: &ast.Node_Return{ + Return: n, + }, + } + case *ast.Yield: return &ast.Node{ Node: &ast.Node_Yield{ diff --git a/internal/printer/printer.go b/internal/printer/printer.go index 0660c6a..e57ad63 100644 --- a/internal/printer/printer.go +++ b/internal/printer/printer.go @@ -83,7 +83,7 @@ func (w *writer) printNode(node *ast.Node, indent int32) { w.printNode(n.Expr.Value, indent) case *ast.Node_For: - w.printFor(n.For, indent) + w.printFor(n.For, false, indent) case *ast.Node_FunctionDef: w.printFunctionDef(n.FunctionDef, indent) @@ -162,12 +162,11 @@ func (w *writer) printAssign(a *ast.Assign, indent int32) { } func (w *writer) printAsyncFor(n *ast.AsyncFor, indent int32) { - w.print("async ") w.printFor(&ast.For{ Target: n.Target, Iter: n.Iter, Body: n.Body, - }, indent) + }, true, indent) } func (w *writer) printAsyncFunctionDef(afd *ast.AsyncFunctionDef, indent int32) { @@ -341,17 +340,45 @@ func (w *writer) printDict(d *ast.Dict, indent int32) { w.print("}") } -func (w *writer) printFor(n *ast.For, indent int32) { - w.print("for ") - w.printNode(n.Target, indent) - w.print(" in ") - w.printNode(n.Iter, indent) - w.print(":\n") - for i, node := range n.Body { +func (w *writer) printFor(n *ast.For, isAsync bool, indent int32) { + // We should always have a body + if len(n.Body) <= 0 { + panic(n) + } + + // TODO: How to better support list comprehension? Maybe add a flag to AST node ForNode and AsyncNode? + _, isCall := n.Body[0].Node.(*ast.Node_Call) + if len(n.Body) == 1 && isCall { + w.print("[\n") w.printIndent(indent + 1) - w.printNode(node, indent+1) - if i != len(n.Body)-1 { - w.print("\n") + w.printNode(n.Body[0], indent+1) + w.print("\n") + w.printIndent(indent + 1) + if isAsync { + w.print("async ") + } + w.print("for ") + w.printNode(n.Target, indent) + w.print(" in ") + w.printNode(n.Iter, indent) + w.print("\n") + w.printIndent(indent) + w.print("]\n") + } else { + if isAsync { + w.print("async ") + } + w.print("for ") + w.printNode(n.Target, indent) + w.print(" in ") + w.printNode(n.Iter, indent) + w.print(":\n") + for i, node := range n.Body { + w.printIndent(indent + 1) + w.printNode(node, indent+1) + if i != len(n.Body)-1 { + w.print("\n") + } } } } @@ -455,14 +482,20 @@ func (w *writer) printModule(mod *ast.Module, indent int32) { _, isImport := mod.Body[i-1].Node.(*ast.Node_ImportGroup) prevIsImport = isImport } + _, isClassDef := node.Node.(*ast.Node_ClassDef) + _, isFunctionDef := node.Node.(*ast.Node_FunctionDef) + _, isAsyncFunctionDef := node.Node.(*ast.Node_AsyncFunctionDef) _, isAssign := node.Node.(*ast.Node_Assign) + if isClassDef || isAssign { if prevIsImport { w.print("\n") } else { w.print("\n\n") } + } else if isAsyncFunctionDef || isFunctionDef { + w.print("\n\n\n") } w.printNode(node, indent) if isAssign { From c7137dfacd2381316c51128c40190c316bfb5ef5 Mon Sep 17 00:00:00 2001 From: Kevin Valk Date: Wed, 23 Aug 2023 00:48:51 +0200 Subject: [PATCH 2/9] docs: udated README --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7359dce..5c9a66e 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ sql: plugin: py options: package: authors - emit_sync_querier: true - emit_async_querier: true + emit_module: false + emit_generators: true + emit_async: false ``` From ba256bd0b0e9aec7b3ebc5221c28b110ff894899 Mon Sep 17 00:00:00 2001 From: Kevin Valk Date: Wed, 23 Aug 2023 00:58:53 +0200 Subject: [PATCH 3/9] chore: updated go version to 1.21.0 and uploading of plugin as artifact --- .github/workflows/ci.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0941f54..626d650 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,5 +12,9 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-go@v4 with: - go-version: '1.21.0-rc.2' + go-version: '1.21.0' - run: make + - uses: actions/upload-artifact@v3 + with: + name: sqlc-gen-python + path: plugin/sqlc-gen-python.wasm \ No newline at end of file From bfa71a905b9c2ef794867bcee4127fd6cfb96a72 Mon Sep 17 00:00:00 2001 From: Kevin Valk Date: Thu, 24 Aug 2023 11:27:32 +0200 Subject: [PATCH 4/9] feat: added output_models_file_name and output_querier_file options to configure output files --- README.md | 11 +++++++++++ internal/config.go | 15 +++++++++------ internal/gen.go | 44 +++++++++++++++++++++++++++++--------------- 3 files changed, 49 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 5c9a66e..aa5cfa7 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +# README ## Usage ```yaml @@ -20,3 +21,13 @@ sql: emit_generators: true emit_async: false ``` + +## Multiple packages +If you have have a mono-repository setup you may want to split the output of queries and models. You can achieve this by using the `output_models_file_name` +and `output_querier_file` fields. If `output_models_file_name` is set to `null` for it will not output the `models.py` file. Setting `output_querier_file` to false will prevent outputting any query files. Combining these you can set one codegen to only output models while the other codegen outputs only queries. Make sure the `package` configuration is set equally so the query files import correctly the models. + +SQLC needs at least one query, so you may need to add a unused query like the following in your schema and reuse the `schema` as `queries`. +```sql +-- name: Skip :one +SELECT 1; +``` \ No newline at end of file diff --git a/internal/config.go b/internal/config.go index 4f44501..e7e1d00 100644 --- a/internal/config.go +++ b/internal/config.go @@ -1,15 +1,18 @@ package python +// TODO: Where are these properly documented? type Config struct { - EmitModule bool `json:"emit_module"` // If true emits functions in module, else wraps in a class. - EmitGenerators bool `json:"emit_generators"` // Will we use generators or lists, defaults to true - EmitAsync bool `json:"emit_async"` // Emits async code instead of sync + EmitAsync bool `json:"emit_async"` // Emits async code instead of sync EmitExactTableNames bool `json:"emit_exact_table_names"` + EmitGenerators bool `json:"emit_generators"` // Will we use generators or lists, defaults to true + EmitModule bool `json:"emit_module"` // If true emits functions in module, else wraps in a class. + EmitPydanticModels bool `json:"emit_pydantic_models"` EmitSyncQuerier bool `json:"emit_sync_querier"` // DEPRECATED ALIAS FOR: emit_type = 'class', emit_generators = True EmitAsyncQuerier bool `json:"emit_async_querier"` // DEPRECATED ALIAS FOR: emit_type = 'class', emit_generators = True - Package string `json:"package"` + InflectionExcludeTableNames []string `json:"inflection_exclude_table_names"` Out string `json:"out"` - EmitPydanticModels bool `json:"emit_pydantic_models"` + OutputModelsFileName *string `json:"output_models_file_name,omitempty"` // Can be string or null to exclude generating models file + OutputQuerierFile bool `json:"output_querier_file,omitempty"` // Skips outputting queries + Package string `json:"package"` QueryParameterLimit *int32 `json:"query_parameter_limit"` - InflectionExcludeTableNames []string `json:"inflection_exclude_table_names"` } diff --git a/internal/gen.go b/internal/gen.go index 74f23fe..d588c8c 100644 --- a/internal/gen.go +++ b/internal/gen.go @@ -1047,7 +1047,13 @@ func HashComment(s string) string { } func Generate(_ context.Context, req *plugin.CodeGenRequest) (*plugin.CodeGenResponse, error) { - var conf Config + // Setup our defaults for our Config struct and parse our config file + defaultModelsFileName := "models.py" + conf := Config{ + OutputModelsFileName: &defaultModelsFileName, + OutputQuerierFile: true, + } + if len(req.PluginOptions) > 0 { if err := json.Unmarshal(req.PluginOptions, &conf); err != nil { return nil, err @@ -1086,26 +1092,34 @@ func Generate(_ context.Context, req *plugin.CodeGenRequest) (*plugin.CodeGenRes } output := map[string]string{} - result := pyprint.Print(buildModelsTree(&tctx, i), pyprint.Options{}) - tctx.SourceName = "models.py" - output["models.py"] = string(result.Python) - files := map[string]struct{}{} - for _, q := range queries { - files[q.SourceName] = struct{}{} + // Generate the model file. + if conf.OutputModelsFileName != nil { + result := pyprint.Print(buildModelsTree(&tctx, i), pyprint.Options{}) + tctx.SourceName = *conf.OutputModelsFileName + output[*conf.OutputModelsFileName] = string(result.Python) } - for source := range files { - tctx.SourceName = source - result := pyprint.Print(buildQueryTree(&tctx, i, source), pyprint.Options{}) - name := source - if !strings.HasSuffix(name, ".py") { - name = strings.TrimSuffix(name, ".sql") - name += ".py" + // Generate for each .sql file a corresponding Python query file. + if conf.OutputQuerierFile { + files := map[string]struct{}{} + for _, q := range queries { + files[q.SourceName] = struct{}{} + } + + for source := range files { + tctx.SourceName = source + result := pyprint.Print(buildQueryTree(&tctx, i, source), pyprint.Options{}) + name := source + if !strings.HasSuffix(name, ".py") { + name = strings.TrimSuffix(name, ".sql") + name += ".py" + } + output[name] = string(result.Python) } - output[name] = string(result.Python) } + // Finally we send our outputs back to SQLC resp := plugin.CodeGenResponse{} for filename, code := range output { From f2390827d3f80b30dd4b227374eebe25eef06849 Mon Sep 17 00:00:00 2001 From: Kevin Valk Date: Thu, 24 Aug 2023 20:05:01 +0200 Subject: [PATCH 5/9] feat: support sqlc.embed --- internal/gen.go | 102 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 93 insertions(+), 9 deletions(-) diff --git a/internal/gen.go b/internal/gen.go index d588c8c..3431c26 100644 --- a/internal/gen.go +++ b/internal/gen.go @@ -53,6 +53,8 @@ type Field struct { Name string Type pyType Comment string + // EmbedFields contains the embedded fields that require scanning. + EmbedFields []Field } type Struct struct { @@ -105,14 +107,42 @@ func (v QueryValue) RowNode(rowVar string) *pyast.Node { call := &pyast.Call{ Func: v.Annotation(), } - for i, f := range v.Struct.Fields { - call.Keywords = append(call.Keywords, &pyast.Keyword{ - Arg: f.Name, - Value: subscriptNode( + rowIndex := 0 // We need to keep track of the index in the row variable. + for _, f := range v.Struct.Fields { + + var valueNode *pyast.Node + // Check if we are using sqlc.embed, if so we need to create a new object. + if len(f.EmbedFields) > 0 { + // We keep this separate so we can easily add all arguments. + embed_call := &pyast.Call{Func: f.Type.Annotation()} + + // Now add all field Initializers for the embedded model that index into the original row. + for i, embedField := range f.EmbedFields { + embed_call.Keywords = append(embed_call.Keywords, &pyast.Keyword{ + Arg: embedField.Name, + Value: subscriptNode( + rowVar, + constantInt(rowIndex+i), + ), + }) + } + + valueNode = &pyast.Node{ + Node: &pyast.Node_Call{ + Call: embed_call, + }, + } + + rowIndex += len(f.EmbedFields) + } else { + valueNode = subscriptNode( rowVar, - constantInt(i), - ), - }) + constantInt(rowIndex), + ) + rowIndex++ + } + + call.Keywords = append(call.Keywords, &pyast.Keyword{Arg: f.Name, Value: valueNode}) } return &pyast.Node{ Node: &pyast.Node_Call{ @@ -336,6 +366,47 @@ func paramName(p *plugin.Parameter) string { type pyColumn struct { id int32 *plugin.Column + embed *pyEmbed +} + +type pyEmbed struct { + modelType string + modelName string + fields []Field +} + +// Taken from https://github.com/sqlc-dev/sqlc/blob/8c59fbb9938a0bad3d9971fc2c10ea1f83cc1d0b/internal/codegen/golang/result.go#L123-L126 +// look through all the structs and attempt to find a matching one to embed +// We need the name of the struct and its field names. +func newGoEmbed(embed *plugin.Identifier, structs []Struct, defaultSchema string) *pyEmbed { + if embed == nil { + return nil + } + + for _, s := range structs { + embedSchema := defaultSchema + if embed.Schema != "" { + embedSchema = embed.Schema + } + + // compare the other attributes + if embed.Catalog != s.Table.Catalog || embed.Name != s.Table.Name || embedSchema != s.Table.Schema { + continue + } + + fields := make([]Field, len(s.Fields)) + for i, f := range s.Fields { + fields[i] = f + } + + return &pyEmbed{ + modelType: s.Name, + modelName: s.Name, + fields: fields, + } + } + + return nil } func columnsToStruct(req *plugin.CodeGenRequest, name string, columns []pyColumn) *Struct { @@ -359,10 +430,22 @@ func columnsToStruct(req *plugin.CodeGenRequest, name string, columns []pyColumn if suffix > 0 { fieldName = fmt.Sprintf("%s_%d", fieldName, suffix) } - gs.Fields = append(gs.Fields, Field{ + + f := Field{ Name: fieldName, Type: makePyType(req, c.Column), - }) + } + + if c.embed != nil { + f.Type = pyType{ + InnerType: "models." + modelName(c.embed.modelType, req.Settings), + IsArray: false, + IsNull: false, + } + f.EmbedFields = c.embed.fields + } + + gs.Fields = append(gs.Fields, f) seen[colName]++ } return &gs @@ -476,6 +559,7 @@ func buildQueries(conf Config, req *plugin.CodeGenRequest, structs []Struct) ([] columns = append(columns, pyColumn{ id: int32(i), Column: c, + embed: newGoEmbed(c.EmbedTable, structs, req.Catalog.DefaultSchema), }) } gs = columnsToStruct(req, query.Name+"Row", columns) From 449d140f5941bf13be39eabd79a86fec4fe3eaec Mon Sep 17 00:00:00 2001 From: Kevin Valk Date: Wed, 21 Feb 2024 10:26:34 +0100 Subject: [PATCH 6/9] feat: added type annotations --- internal/ast/ast.pb.go | 12 ++++++++++-- internal/gen.go | 33 ++++++++++++++++++++++++++++----- internal/postgresql_type.go | 2 +- internal/printer/printer.go | 4 ++++ 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/internal/ast/ast.pb.go b/internal/ast/ast.pb.go index bdb4c69..80329d1 100644 --- a/internal/ast/ast.pb.go +++ b/internal/ast/ast.pb.go @@ -647,8 +647,9 @@ type AnnAssign struct { Target *Name `protobuf:"bytes,1,opt,name=target,proto3" json:"target,omitempty"` Annotation *Node `protobuf:"bytes,2,opt,name=annotation,proto3" json:"annotation,omitempty"` - Simple int32 `protobuf:"varint,3,opt,name=simple,proto3" json:"simple,omitempty"` - Comment string `protobuf:"bytes,4,opt,name=Comment,json=comment,proto3" json:"Comment,omitempty"` + Value *Node `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` + Simple int32 `protobuf:"varint,4,opt,name=simple,proto3" json:"simple,omitempty"` + Comment string `protobuf:"bytes,5,opt,name=Comment,json=comment,proto3" json:"Comment,omitempty"` } func (x *AnnAssign) Reset() { @@ -697,6 +698,13 @@ func (x *AnnAssign) GetAnnotation() *Node { return nil } +func (x *AnnAssign) GetValue() *Node { + if x != nil { + return x.Value + } + return nil +} + func (x *AnnAssign) GetSimple() int32 { if x != nil { return x.Simple diff --git a/internal/gen.go b/internal/gen.go index 3431c26..1a50aa6 100644 --- a/internal/gen.go +++ b/internal/gen.go @@ -702,12 +702,23 @@ func pydanticNode(name string) *pyast.ClassDef { } } -func fieldNode(f Field) *pyast.Node { +func fieldNode(f Field, defaultNone bool) *pyast.Node { + // TODO: Current AST is showing limitation as annotated assign does not support a value :'(, manually edited :'( + var value *pyast.Node = nil + if defaultNone && f.Type.IsNull { + value = &pyast.Node{ + Node: &pyast.Node_Name{ + Name: &pyast.Name{Id: "None"}, + }, + } + } + return &pyast.Node{ Node: &pyast.Node_AnnAssign{ AnnAssign: &pyast.AnnAssign{ Target: &pyast.Name{Id: f.Name}, Annotation: f.Type.Annotation(), + Value: value, Comment: f.Comment, }, }, @@ -825,7 +836,7 @@ func buildModelsTree(ctx *pyTmplCtx, i *importer) *pyast.Node { }) } for _, f := range m.Fields { - def.Body = append(def.Body, fieldNode(f)) + def.Body = append(def.Body, fieldNode(f, false)) } mod.Body = append(mod.Body, &pyast.Node{ Node: &pyast.Node_ClassDef{ @@ -904,6 +915,8 @@ func buildQueryTree(ctx *pyTmplCtx, i *importer, source string) *pyast.Node { } queryText := fmt.Sprintf("-- name: %s \\\\%s\n%s\n", q.MethodName, q.Cmd, q.SQL) mod.Body = append(mod.Body, assignNode(q.ConstantName, poet.Constant(queryText))) + + // Generate params structures for _, arg := range q.Args { if arg.EmitStruct() { var def *pyast.ClassDef @@ -912,8 +925,18 @@ func buildQueryTree(ctx *pyTmplCtx, i *importer, source string) *pyast.Node { } else { def = dataclassNode(arg.Struct.Name) } - for _, f := range arg.Struct.Fields { - def.Body = append(def.Body, fieldNode(f)) + + // We need a copy as we want to make sure that nullable params are at the end of the dataclass + fields := make([]Field, len(arg.Struct.Fields)) + copy(fields, arg.Struct.Fields) + + // Place all nullable fields at the end and try to keep the original order as much as possible + sort.SliceStable(fields, func(i int, j int) bool { + return (fields[j].Type.IsNull && fields[i].Type.IsNull != fields[j].Type.IsNull) || i < j + }) + + for _, f := range fields { + def.Body = append(def.Body, fieldNode(f, true)) } mod.Body = append(mod.Body, poet.Node(def)) } @@ -926,7 +949,7 @@ func buildQueryTree(ctx *pyTmplCtx, i *importer, source string) *pyast.Node { def = dataclassNode(q.Ret.Struct.Name) } for _, f := range q.Ret.Struct.Fields { - def.Body = append(def.Body, fieldNode(f)) + def.Body = append(def.Body, fieldNode(f, false)) } mod.Body = append(mod.Body, poet.Node(def)) } diff --git a/internal/postgresql_type.go b/internal/postgresql_type.go index 0053154..ba72841 100644 --- a/internal/postgresql_type.go +++ b/internal/postgresql_type.go @@ -22,7 +22,7 @@ func postgresType(req *plugin.CodeGenRequest, col *plugin.Column) string { case "json", "jsonb": return "Any" case "bytea", "blob", "pg_catalog.bytea": - return "memoryview" + return "bytes" case "date": return "datetime.date" case "pg_catalog.time", "pg_catalog.timetz": diff --git a/internal/printer/printer.go b/internal/printer/printer.go index e57ad63..b761b54 100644 --- a/internal/printer/printer.go +++ b/internal/printer/printer.go @@ -140,6 +140,10 @@ func (w *writer) printAnnAssign(aa *ast.AnnAssign, indent int32) { w.printName(aa.Target, indent) w.print(": ") w.printNode(aa.Annotation, indent) + if aa.Value != nil { + w.print(" = ") + w.printNode(aa.Value, indent) + } } func (w *writer) printArg(a *ast.Arg, indent int32) { From 3e513130d69348b81c25f47bbb6f72317b9df0ca Mon Sep 17 00:00:00 2001 From: Kevin Valk Date: Wed, 21 Feb 2024 11:30:30 +0100 Subject: [PATCH 7/9] fix: ensure we are fully backwards compatible --- .gitignore | 1 - internal/config.go | 2 +- internal/endtoend/endtoend_test.go | 3 +- .../testdata/emit_pydantic_models/sqlc.yaml | 2 +- .../endtoend/testdata/exec_result/sqlc.yaml | 2 +- .../endtoend/testdata/exec_rows/sqlc.yaml | 2 +- .../inflection_exclude_table_names/sqlc.yaml | 2 +- .../query_parameter_limit_two/sqlc.yaml | 2 +- .../query_parameter_limit_undefined/sqlc.yaml | 2 +- .../query_parameter_limit_zero/sqlc.yaml | 2 +- .../query_parameter_no_limit/sqlc.yaml | 2 +- internal/gen.go | 212 ++++++++++-------- internal/imports.go | 10 +- 13 files changed, 136 insertions(+), 108 deletions(-) diff --git a/.gitignore b/.gitignore index 0c43e36..70bf0a1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ bin -*.wasm # Devenv .envrc diff --git a/internal/config.go b/internal/config.go index e7e1d00..24009a2 100644 --- a/internal/config.go +++ b/internal/config.go @@ -4,7 +4,7 @@ package python type Config struct { EmitAsync bool `json:"emit_async"` // Emits async code instead of sync EmitExactTableNames bool `json:"emit_exact_table_names"` - EmitGenerators bool `json:"emit_generators"` // Will we use generators or lists, defaults to true + EmitGenerators bool `json:"emit_generators"` // Will we use generators or lists, defaults to false EmitModule bool `json:"emit_module"` // If true emits functions in module, else wraps in a class. EmitPydanticModels bool `json:"emit_pydantic_models"` EmitSyncQuerier bool `json:"emit_sync_querier"` // DEPRECATED ALIAS FOR: emit_type = 'class', emit_generators = True diff --git a/internal/endtoend/endtoend_test.go b/internal/endtoend/endtoend_test.go index bd66c27..62c9a8e 100644 --- a/internal/endtoend/endtoend_test.go +++ b/internal/endtoend/endtoend_test.go @@ -100,8 +100,9 @@ func TestGenerate(t *testing.T) { cmd := exec.Command(sqlc, "diff") cmd.Dir = dir got, err := cmd.CombinedOutput() + // TODO: We are diffing patches! Does this make sense and what should we provide to the end user? if diff := cmp.Diff(string(want), string(got)); diff != "" { - t.Errorf("sqlc diff mismatch (-want +got):\n%s", diff) + t.Errorf("sqlc diff mismatch (-want +got):\n%s", string(got)) } if len(want) == 0 && err != nil { t.Error(err) diff --git a/internal/endtoend/testdata/emit_pydantic_models/sqlc.yaml b/internal/endtoend/testdata/emit_pydantic_models/sqlc.yaml index 180ce29..d879adf 100644 --- a/internal/endtoend/testdata/emit_pydantic_models/sqlc.yaml +++ b/internal/endtoend/testdata/emit_pydantic_models/sqlc.yaml @@ -3,7 +3,7 @@ plugins: - name: py wasm: url: file://../../../../bin/sqlc-gen-python.wasm - sha256: "a6c5d174c407007c3717eea36ff0882744346e6ba991f92f71d6ab2895204c0e" + sha256: "c97fad53818679a948c68f3ffe84530d7ca4999f636d3f3d89202c6c08ee224d" sql: - schema: schema.sql queries: query.sql diff --git a/internal/endtoend/testdata/exec_result/sqlc.yaml b/internal/endtoend/testdata/exec_result/sqlc.yaml index 2adbd31..b097b32 100644 --- a/internal/endtoend/testdata/exec_result/sqlc.yaml +++ b/internal/endtoend/testdata/exec_result/sqlc.yaml @@ -3,7 +3,7 @@ plugins: - name: py wasm: url: file://../../../../bin/sqlc-gen-python.wasm - sha256: "a6c5d174c407007c3717eea36ff0882744346e6ba991f92f71d6ab2895204c0e" + sha256: "c97fad53818679a948c68f3ffe84530d7ca4999f636d3f3d89202c6c08ee224d" sql: - schema: schema.sql queries: query.sql diff --git a/internal/endtoend/testdata/exec_rows/sqlc.yaml b/internal/endtoend/testdata/exec_rows/sqlc.yaml index 2adbd31..b097b32 100644 --- a/internal/endtoend/testdata/exec_rows/sqlc.yaml +++ b/internal/endtoend/testdata/exec_rows/sqlc.yaml @@ -3,7 +3,7 @@ plugins: - name: py wasm: url: file://../../../../bin/sqlc-gen-python.wasm - sha256: "a6c5d174c407007c3717eea36ff0882744346e6ba991f92f71d6ab2895204c0e" + sha256: "c97fad53818679a948c68f3ffe84530d7ca4999f636d3f3d89202c6c08ee224d" sql: - schema: schema.sql queries: query.sql diff --git a/internal/endtoend/testdata/inflection_exclude_table_names/sqlc.yaml b/internal/endtoend/testdata/inflection_exclude_table_names/sqlc.yaml index aba5400..7659ffe 100644 --- a/internal/endtoend/testdata/inflection_exclude_table_names/sqlc.yaml +++ b/internal/endtoend/testdata/inflection_exclude_table_names/sqlc.yaml @@ -3,7 +3,7 @@ plugins: - name: py wasm: url: file://../../../../bin/sqlc-gen-python.wasm - sha256: "a6c5d174c407007c3717eea36ff0882744346e6ba991f92f71d6ab2895204c0e" + sha256: "c97fad53818679a948c68f3ffe84530d7ca4999f636d3f3d89202c6c08ee224d" sql: - schema: schema.sql queries: query.sql diff --git a/internal/endtoend/testdata/query_parameter_limit_two/sqlc.yaml b/internal/endtoend/testdata/query_parameter_limit_two/sqlc.yaml index e389988..961d105 100644 --- a/internal/endtoend/testdata/query_parameter_limit_two/sqlc.yaml +++ b/internal/endtoend/testdata/query_parameter_limit_two/sqlc.yaml @@ -3,7 +3,7 @@ plugins: - name: py wasm: url: file://../../../../bin/sqlc-gen-python.wasm - sha256: "a6c5d174c407007c3717eea36ff0882744346e6ba991f92f71d6ab2895204c0e" + sha256: "c97fad53818679a948c68f3ffe84530d7ca4999f636d3f3d89202c6c08ee224d" sql: - schema: schema.sql queries: query.sql diff --git a/internal/endtoend/testdata/query_parameter_limit_undefined/sqlc.yaml b/internal/endtoend/testdata/query_parameter_limit_undefined/sqlc.yaml index 66d7a14..9bea901 100644 --- a/internal/endtoend/testdata/query_parameter_limit_undefined/sqlc.yaml +++ b/internal/endtoend/testdata/query_parameter_limit_undefined/sqlc.yaml @@ -3,7 +3,7 @@ plugins: - name: py wasm: url: file://../../../../bin/sqlc-gen-python.wasm - sha256: "a6c5d174c407007c3717eea36ff0882744346e6ba991f92f71d6ab2895204c0e" + sha256: "c97fad53818679a948c68f3ffe84530d7ca4999f636d3f3d89202c6c08ee224d" sql: - schema: schema.sql queries: query.sql diff --git a/internal/endtoend/testdata/query_parameter_limit_zero/sqlc.yaml b/internal/endtoend/testdata/query_parameter_limit_zero/sqlc.yaml index 274f730..6f3bb75 100644 --- a/internal/endtoend/testdata/query_parameter_limit_zero/sqlc.yaml +++ b/internal/endtoend/testdata/query_parameter_limit_zero/sqlc.yaml @@ -3,7 +3,7 @@ plugins: - name: py wasm: url: file://../../../../bin/sqlc-gen-python.wasm - sha256: "a6c5d174c407007c3717eea36ff0882744346e6ba991f92f71d6ab2895204c0e" + sha256: "c97fad53818679a948c68f3ffe84530d7ca4999f636d3f3d89202c6c08ee224d" sql: - schema: schema.sql queries: query.sql diff --git a/internal/endtoend/testdata/query_parameter_no_limit/sqlc.yaml b/internal/endtoend/testdata/query_parameter_no_limit/sqlc.yaml index b563730..d5c4da5 100644 --- a/internal/endtoend/testdata/query_parameter_no_limit/sqlc.yaml +++ b/internal/endtoend/testdata/query_parameter_no_limit/sqlc.yaml @@ -3,7 +3,7 @@ plugins: - name: py wasm: url: file://../../../../bin/sqlc-gen-python.wasm - sha256: "a6c5d174c407007c3717eea36ff0882744346e6ba991f92f71d6ab2895204c0e" + sha256: "c97fad53818679a948c68f3ffe84530d7ca4999f636d3f3d89202c6c08ee224d" sql: - schema: schema.sql queries: query.sql diff --git a/internal/gen.go b/internal/gen.go index 22e40bf..66863ce 100644 --- a/internal/gen.go +++ b/internal/gen.go @@ -845,7 +845,7 @@ func querierClassDef(name string, connectionAnnotation *pyast.Node) *pyast.Class Arg: "self", }, { - Arg: "connection", + Arg: "conn", Annotation: connectionAnnotation, }, }, @@ -855,9 +855,9 @@ func querierClassDef(name string, connectionAnnotation *pyast.Node) *pyast.Class Node: &pyast.Node_Assign{ Assign: &pyast.Assign{ Targets: []*pyast.Node{ - poet.Attribute(poet.Name("self"), "_connection"), + poet.Attribute(poet.Name("self"), "_conn"), }, - Value: poet.Name("connection"), + Value: poet.Name("conn"), }, }, }, @@ -869,80 +869,12 @@ func querierClassDef(name string, connectionAnnotation *pyast.Node) *pyast.Class } } -func buildQueryTree(ctx *pyTmplCtx, i *importer, source string) *pyast.Node { - mod := moduleNode(ctx.SqlcVersion, source) - std, pkg := i.queryImportSpecs(source) - mod.Body = append(mod.Body, buildImportGroup(std), buildImportGroup(pkg)) - mod.Body = append(mod.Body, &pyast.Node{ - Node: &pyast.Node_ImportGroup{ - ImportGroup: &pyast.ImportGroup{ - Imports: []*pyast.Node{ - { - Node: &pyast.Node_ImportFrom{ - ImportFrom: &pyast.ImportFrom{ - Module: ctx.C.Package, - Names: []*pyast.Node{ - poet.Alias("models"), - }, - }, - }, - }, - }, - }, - }, - }) - - for _, q := range ctx.Queries { - if !ctx.OutputQuery(q.SourceName) { - continue - } - queryText := fmt.Sprintf("-- name: %s \\\\%s\n%s\n", q.MethodName, q.Cmd, q.SQL) - mod.Body = append(mod.Body, assignNode(q.ConstantName, poet.Constant(queryText))) - - // Generate params structures - for _, arg := range q.Args { - if arg.EmitStruct() { - var def *pyast.ClassDef - if ctx.C.EmitPydanticModels { - def = pydanticNode(arg.Struct.Name) - } else { - def = dataclassNode(arg.Struct.Name) - } - - // We need a copy as we want to make sure that nullable params are at the end of the dataclass - fields := make([]Field, len(arg.Struct.Fields)) - copy(fields, arg.Struct.Fields) - - // Place all nullable fields at the end and try to keep the original order as much as possible - sort.SliceStable(fields, func(i int, j int) bool { - return (fields[j].Type.IsNull && fields[i].Type.IsNull != fields[j].Type.IsNull) || i < j - }) - - for _, f := range fields { - def.Body = append(def.Body, fieldNode(f, true)) - } - mod.Body = append(mod.Body, poet.Node(def)) - } - } - if q.Ret.EmitStruct() { - var def *pyast.ClassDef - if ctx.C.EmitPydanticModels { - def = pydanticNode(q.Ret.Struct.Name) - } else { - def = dataclassNode(q.Ret.Struct.Name) - } - for _, f := range q.Ret.Struct.Fields { - def.Body = append(def.Body, fieldNode(f, false)) - } - mod.Body = append(mod.Body, poet.Node(def)) - } - } - +func buildQuerierClass(ctx *pyTmplCtx, isAsync bool) []*pyast.Node { functions := make([]*pyast.Node, 0, 10) // Define some reused types based on async or sync code var connectionAnnotation *pyast.Node - if ctx.C.EmitAsync { + if isAsync { connectionAnnotation = typeRefNode("sqlalchemy", "ext", "asyncio", "AsyncConnection") } else { connectionAnnotation = typeRefNode("sqlalchemy", "engine", "Connection") @@ -951,9 +883,9 @@ func buildQueryTree(ctx *pyTmplCtx, i *importer, source string) *pyast.Node { // We need to figure out how to access the SQLAlchemy connectionVar object var connectionVar *pyast.Node if ctx.C.EmitModule { - connectionVar = poet.Name("connection") + connectionVar = poet.Name("conn") } else { - connectionVar = poet.Attribute(poet.Name("self"), "_connection") + connectionVar = poet.Attribute(poet.Name("self"), "_conn") } // We loop through all queries and build our query functions @@ -968,7 +900,7 @@ func buildQueryTree(ctx *pyTmplCtx, i *importer, source string) *pyast.Node { if ctx.C.EmitModule { f.Args.Args = append(f.Args.Args, &pyast.Arg{ - Arg: "connection", + Arg: "conn", Annotation: connectionAnnotation, }) } else { @@ -980,7 +912,7 @@ func buildQueryTree(ctx *pyTmplCtx, i *importer, source string) *pyast.Node { q.AddArgs(f.Args) exec := poet.Expr(connMethodNode(poet.Attribute(connectionVar, "execute"), q.ConstantName, q.ArgDictNode())) - if ctx.C.EmitAsync { + if isAsync { exec = poet.Await(exec) } @@ -1017,7 +949,7 @@ func buildQueryTree(ctx *pyTmplCtx, i *importer, source string) *pyast.Node { f.Returns = subscriptNode("Optional", q.Ret.Annotation()) case ":many": if ctx.C.EmitGenerators { - if ctx.C.EmitAsync { + if isAsync { // If we are using generators and async, we are switching to stream implementation exec = poet.Await(connMethodNode(poet.Attribute(connectionVar, "stream"), q.ConstantName, q.ArgDictNode())) @@ -1094,8 +1026,8 @@ func buildQueryTree(ctx *pyTmplCtx, i *importer, source string) *pyast.Node { panic("unknown cmd " + q.Cmd) } - // If we are emitting async code, we have to swap our sync func for an async one and fix the connection annotation. - if ctx.C.EmitAsync { + // If we are emitting async code, we have to swap our sync func for an async one and fix the conn annotation. + if isAsync { functions = append(functions, poet.Node(&pyast.AsyncFunctionDef{ Name: f.Name, Args: f.Args, @@ -1107,13 +1039,115 @@ func buildQueryTree(ctx *pyTmplCtx, i *importer, source string) *pyast.Node { } } - // Lets see how to add all functions + return functions +} + +func buildQueryTree(ctx *pyTmplCtx, i *importer, source string) *pyast.Node { + mod := moduleNode(ctx.SqlcVersion, source) + std, pkg := i.queryImportSpecs(source) + mod.Body = append(mod.Body, buildImportGroup(std), buildImportGroup(pkg)) + mod.Body = append(mod.Body, &pyast.Node{ + Node: &pyast.Node_ImportGroup{ + ImportGroup: &pyast.ImportGroup{ + Imports: []*pyast.Node{ + { + Node: &pyast.Node_ImportFrom{ + ImportFrom: &pyast.ImportFrom{ + Module: ctx.C.Package, + Names: []*pyast.Node{ + poet.Alias("models"), + }, + }, + }, + }, + }, + }, + }, + }) + + for _, q := range ctx.Queries { + if !ctx.OutputQuery(q.SourceName) { + continue + } + queryText := fmt.Sprintf("-- name: %s \\\\%s\n%s\n", q.MethodName, q.Cmd, q.SQL) + mod.Body = append(mod.Body, assignNode(q.ConstantName, poet.Constant(queryText))) + + // Generate params structures + for _, arg := range q.Args { + if arg.EmitStruct() { + var def *pyast.ClassDef + if ctx.C.EmitPydanticModels { + def = pydanticNode(arg.Struct.Name) + } else { + def = dataclassNode(arg.Struct.Name) + } + + // We need a copy as we want to make sure that nullable params are at the end of the dataclass + fields := make([]Field, len(arg.Struct.Fields)) + copy(fields, arg.Struct.Fields) + + // Place all nullable fields at the end and try to keep the original order as much as possible + sort.SliceStable(fields, func(i int, j int) bool { + return (fields[j].Type.IsNull && fields[i].Type.IsNull != fields[j].Type.IsNull) || i < j + }) + + for _, f := range fields { + def.Body = append(def.Body, fieldNode(f, true)) + } + mod.Body = append(mod.Body, poet.Node(def)) + } + } + if q.Ret.EmitStruct() { + var def *pyast.ClassDef + if ctx.C.EmitPydanticModels { + def = pydanticNode(q.Ret.Struct.Name) + } else { + def = dataclassNode(q.Ret.Struct.Name) + } + for _, f := range q.Ret.Struct.Fields { + def.Body = append(def.Body, fieldNode(f, false)) + } + mod.Body = append(mod.Body, poet.Node(def)) + } + } + + // Lets see how to add all functions, we can either add them to the module directly or from within a class. if ctx.C.EmitModule { - mod.Body = append(mod.Body, functions...) + mod.Body = append(mod.Body, buildQuerierClass(ctx, ctx.C.EmitAsync)...) } else { - cls := querierClassDef("Querier", connectionAnnotation) - cls.Body = append(cls.Body, functions...) - mod.Body = append(mod.Body, poet.Node(cls)) + asyncConnectionAnnotation := typeRefNode("sqlalchemy", "ext", "asyncio", "AsyncConnection") + syncConnectionAnnotation := typeRefNode("sqlalchemy", "engine", "Connection") + + // NOTE: For backwards compatibility we support generating multiple classes, but this is definitely suboptimal. + // It is much better to use the `emit_async: bool` config to select what type to emit + if ctx.C.EmitAsyncQuerier || ctx.C.EmitSyncQuerier { + + // When using these backwards compatible settings we force behavior! + ctx.C.EmitModule = false + ctx.C.EmitGenerators = true + + if ctx.C.EmitSyncQuerier { + cls := querierClassDef("Querier", syncConnectionAnnotation) + cls.Body = append(cls.Body, buildQuerierClass(ctx, false)...) + mod.Body = append(mod.Body, poet.Node(cls)) + } + if ctx.C.EmitAsyncQuerier { + cls := querierClassDef("AsyncQuerier", asyncConnectionAnnotation) + cls.Body = append(cls.Body, buildQuerierClass(ctx, true)...) + mod.Body = append(mod.Body, poet.Node(cls)) + } + } else { + var connectionAnnotation *pyast.Node + if ctx.C.EmitAsync { + connectionAnnotation = asyncConnectionAnnotation + } else { + connectionAnnotation = syncConnectionAnnotation + } + + cls := querierClassDef("Querier", connectionAnnotation) + cls.Body = append(cls.Body, buildQuerierClass(ctx, ctx.C.EmitAsync)...) + mod.Body = append(mod.Body, poet.Node(cls)) + } } return poet.Node(mod) @@ -1150,14 +1184,6 @@ func Generate(_ context.Context, req *plugin.GenerateRequest) (*plugin.GenerateR } } - // TODO: Remove when when we drop support for deprecated EmitSyncQuerier and EmitAsyncQuerier options - if conf.EmitAsyncQuerier || conf.EmitSyncQuerier { - conf.EmitModule = false - conf.EmitGenerators = true - conf.EmitAsync = conf.EmitAsyncQuerier - // TODO/NOTE: We now have a breaking change because we emit only one flavor. What do we want to do? - } - enums := buildEnums(req) models := buildModels(conf, req) queries, err := buildQueries(conf, req, models) diff --git a/internal/imports.go b/internal/imports.go index c8b2548..bf4da20 100644 --- a/internal/imports.go +++ b/internal/imports.go @@ -132,7 +132,7 @@ func (i *importer) queryImportSpecs(fileName string) (map[string]importSpec, map pkg := make(map[string]importSpec) pkg["sqlalchemy"] = importSpec{Module: "sqlalchemy"} - if i.C.EmitAsync { + if i.C.EmitAsync || i.C.EmitAsyncQuerier { pkg["sqlalchemy.ext.asyncio"] = importSpec{Module: "sqlalchemy.ext.asyncio"} } @@ -154,10 +154,12 @@ func (i *importer) queryImportSpecs(fileName string) (map[string]importSpec, map std["typing.Optional"] = importSpec{Module: "typing", Name: "Optional"} } if q.Cmd == ":many" { - if i.C.EmitGenerators { - if i.C.EmitAsync { + // NOTE: We are adding backwards compatible behavior + if i.C.EmitGenerators || i.C.EmitSyncQuerier || i.C.EmitAsyncQuerier { + if i.C.EmitAsync || i.C.EmitAsyncQuerier { std["typing.AsyncIterator"] = importSpec{Module: "typing", Name: "AsyncIterator"} - } else { + } + if !i.C.EmitAsync || i.C.EmitSyncQuerier { std["typing.Iterator"] = importSpec{Module: "typing", Name: "Iterator"} } } else { From 6efc6246c74181bd7e4415dd59fbe14b48087751 Mon Sep 17 00:00:00 2001 From: Kevin Valk Date: Wed, 21 Feb 2024 11:44:23 +0100 Subject: [PATCH 8/9] fix: updated ast.proto with manual patch for the AnnAssign node --- .gitignore | 2 +- internal/ast/ast.pb.go | 365 +++++++++++++++++++++-------------------- internal/gen.go | 1 - protos/ast/ast.proto | 5 +- 4 files changed, 188 insertions(+), 185 deletions(-) diff --git a/.gitignore b/.gitignore index 70bf0a1..60b4f3d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,4 @@ bin .envrc .direnv .devenv* -devenv.local.nix \ No newline at end of file +devenv.local.nix diff --git a/internal/ast/ast.pb.go b/internal/ast/ast.pb.go index df9c6d4..5eaa4fa 100644 --- a/internal/ast/ast.pb.go +++ b/internal/ast/ast.pb.go @@ -649,7 +649,7 @@ type AnnAssign struct { Target *Name `protobuf:"bytes,1,opt,name=target,proto3" json:"target,omitempty"` Annotation *Node `protobuf:"bytes,2,opt,name=annotation,proto3" json:"annotation,omitempty"` - Value *Node `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` + Value *Node `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` Simple int32 `protobuf:"varint,4,opt,name=simple,proto3" json:"simple,omitempty"` Comment string `protobuf:"bytes,5,opt,name=Comment,json=comment,proto3" json:"Comment,omitempty"` } @@ -2309,149 +2309,151 @@ var file_ast_ast_proto_rawDesc = []byte{ 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x1f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x74, - 0x74, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x74, 0x74, 0x72, 0x22, 0x8b, + 0x74, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x74, 0x74, 0x72, 0x22, 0xac, 0x01, 0x0a, 0x09, 0x41, 0x6e, 0x6e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x12, 0x21, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x29, 0x0a, 0x0a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x0a, - 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x69, - 0x6d, 0x70, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x73, 0x69, 0x6d, 0x70, - 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x42, 0x0a, 0x03, - 0x41, 0x72, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x72, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x61, 0x72, 0x67, 0x12, 0x29, 0x0a, 0x0a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, - 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x0a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x22, 0x55, 0x0a, 0x09, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1c, 0x0a, - 0x04, 0x61, 0x72, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x61, 0x73, - 0x74, 0x2e, 0x41, 0x72, 0x67, 0x52, 0x04, 0x61, 0x72, 0x67, 0x73, 0x12, 0x2a, 0x0a, 0x0c, 0x6b, - 0x77, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x08, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x41, 0x72, 0x67, 0x52, 0x0a, 0x6b, 0x77, 0x6f, - 0x6e, 0x6c, 0x79, 0x61, 0x72, 0x67, 0x73, 0x22, 0x6b, 0x0a, 0x08, 0x41, 0x73, 0x79, 0x6e, 0x63, - 0x46, 0x6f, 0x72, 0x12, 0x21, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x06, - 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x1d, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x72, 0x18, 0x02, + 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, + 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, + 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x73, 0x69, 0x6d, + 0x70, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x42, 0x0a, + 0x03, 0x41, 0x72, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x72, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x61, 0x72, 0x67, 0x12, 0x29, 0x0a, 0x0a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, + 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x0a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x22, 0x55, 0x0a, 0x09, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1c, + 0x0a, 0x04, 0x61, 0x72, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x61, + 0x73, 0x74, 0x2e, 0x41, 0x72, 0x67, 0x52, 0x04, 0x61, 0x72, 0x67, 0x73, 0x12, 0x2a, 0x0a, 0x0c, + 0x6b, 0x77, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x41, 0x72, 0x67, 0x52, 0x0a, 0x6b, 0x77, + 0x6f, 0x6e, 0x6c, 0x79, 0x61, 0x72, 0x67, 0x73, 0x22, 0x6b, 0x0a, 0x08, 0x41, 0x73, 0x79, 0x6e, + 0x63, 0x46, 0x6f, 0x72, 0x12, 0x21, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, - 0x04, 0x69, 0x74, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x03, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, - 0x62, 0x6f, 0x64, 0x79, 0x22, 0x8e, 0x01, 0x0a, 0x10, 0x41, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x75, - 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x66, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, - 0x04, 0x41, 0x72, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x61, 0x73, - 0x74, 0x2e, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x04, 0x61, 0x72, 0x67, - 0x73, 0x12, 0x1d, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, - 0x12, 0x23, 0x0a, 0x07, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x07, 0x72, 0x65, - 0x74, 0x75, 0x72, 0x6e, 0x73, 0x22, 0x68, 0x0a, 0x06, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x12, - 0x23, 0x0a, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x07, 0x74, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x73, 0x12, 0x1f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x22, - 0x6e, 0x0a, 0x04, 0x43, 0x61, 0x6c, 0x6c, 0x12, 0x1d, 0x0a, 0x04, 0x66, 0x75, 0x6e, 0x63, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, - 0x52, 0x04, 0x66, 0x75, 0x6e, 0x63, 0x12, 0x1d, 0x0a, 0x04, 0x61, 0x72, 0x67, 0x73, 0x18, 0x02, + 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x1d, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x72, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, + 0x52, 0x04, 0x69, 0x74, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, - 0x04, 0x61, 0x72, 0x67, 0x73, 0x12, 0x28, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, - 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4b, 0x65, - 0x79, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x22, - 0xb8, 0x01, 0x0a, 0x08, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x44, 0x65, 0x66, 0x12, 0x12, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x1f, 0x0a, 0x05, 0x62, 0x61, 0x73, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x62, 0x61, 0x73, 0x65, - 0x73, 0x12, 0x25, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x03, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x08, - 0x6b, 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x12, 0x1d, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, - 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, - 0x65, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x31, 0x0a, 0x0e, 0x64, 0x65, 0x63, 0x6f, 0x72, - 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x0e, 0x64, 0x65, 0x63, 0x6f, - 0x72, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x22, 0x1d, 0x0a, 0x07, 0x43, 0x6f, - 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x22, 0x72, 0x0a, 0x07, 0x43, 0x6f, 0x6d, - 0x70, 0x61, 0x72, 0x65, 0x12, 0x1d, 0x0a, 0x04, 0x6c, 0x65, 0x66, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6c, - 0x65, 0x66, 0x74, 0x12, 0x1b, 0x0a, 0x03, 0x6f, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x03, 0x6f, 0x70, 0x73, - 0x12, 0x2b, 0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, - 0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x22, 0x54, 0x0a, - 0x08, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x12, 0x15, 0x0a, 0x03, 0x73, 0x74, 0x72, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, - 0x12, 0x12, 0x0a, 0x03, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x48, 0x00, 0x52, - 0x03, 0x69, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x04, 0x6e, 0x6f, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x08, 0x48, 0x00, 0x52, 0x04, 0x6e, 0x6f, 0x6e, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x22, 0x48, 0x0a, 0x04, 0x44, 0x69, 0x63, 0x74, 0x12, 0x1d, 0x0a, 0x04, 0x6b, - 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, - 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x12, 0x21, 0x0a, 0x06, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, - 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0x27, 0x0a, - 0x04, 0x45, 0x78, 0x70, 0x72, 0x12, 0x1f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, + 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x8e, 0x01, 0x0a, 0x10, 0x41, 0x73, 0x79, 0x6e, 0x63, 0x46, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x66, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x22, + 0x0a, 0x04, 0x41, 0x72, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x61, + 0x73, 0x74, 0x2e, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x04, 0x61, 0x72, + 0x67, 0x73, 0x12, 0x1d, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62, 0x6f, 0x64, + 0x79, 0x12, 0x23, 0x0a, 0x07, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x07, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x22, 0x68, 0x0a, 0x06, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, + 0x12, 0x23, 0x0a, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x07, 0x74, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x73, 0x12, 0x1f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x66, 0x0a, 0x03, 0x46, 0x6f, 0x72, 0x12, 0x21, 0x0a, - 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, - 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x12, 0x1d, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, - 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x69, 0x74, 0x65, 0x72, 0x12, - 0x1d, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, - 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x89, - 0x01, 0x0a, 0x0b, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x66, 0x12, 0x12, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x04, 0x41, 0x72, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x0e, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, - 0x52, 0x04, 0x61, 0x72, 0x67, 0x73, 0x12, 0x1d, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x03, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, + 0x22, 0x6e, 0x0a, 0x04, 0x43, 0x61, 0x6c, 0x6c, 0x12, 0x1d, 0x0a, 0x04, 0x66, 0x75, 0x6e, 0x63, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, + 0x65, 0x52, 0x04, 0x66, 0x75, 0x6e, 0x63, 0x12, 0x1d, 0x0a, 0x04, 0x61, 0x72, 0x67, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, + 0x52, 0x04, 0x61, 0x72, 0x67, 0x73, 0x12, 0x28, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x77, 0x6f, 0x72, + 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4b, + 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, 0x73, + 0x22, 0xb8, 0x01, 0x0a, 0x08, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x44, 0x65, 0x66, 0x12, 0x12, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x12, 0x1f, 0x0a, 0x05, 0x62, 0x61, 0x73, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x62, 0x61, 0x73, + 0x65, 0x73, 0x12, 0x25, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, - 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x23, 0x0a, 0x07, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, - 0x65, 0x52, 0x07, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x22, 0x66, 0x0a, 0x02, 0x49, 0x66, - 0x12, 0x1d, 0x0a, 0x04, 0x74, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, - 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x74, 0x65, 0x73, 0x74, 0x12, - 0x1d, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, - 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x22, - 0x0a, 0x07, 0x6f, 0x72, 0x5f, 0x65, 0x6c, 0x73, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x06, 0x6f, 0x72, 0x65, 0x6c, - 0x73, 0x65, 0x22, 0x29, 0x0a, 0x06, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x1f, 0x0a, 0x05, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, - 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x5b, 0x0a, - 0x0a, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x12, 0x16, 0x0a, 0x06, 0x6d, - 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x6f, 0x64, - 0x75, 0x6c, 0x65, 0x12, 0x1f, 0x0a, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x32, 0x0a, 0x0b, 0x49, 0x6d, - 0x70, 0x6f, 0x72, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x23, 0x0a, 0x07, 0x69, 0x6d, 0x70, - 0x6f, 0x72, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, - 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x07, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x22, 0x04, - 0x0a, 0x02, 0x49, 0x73, 0x22, 0x3c, 0x0a, 0x07, 0x4b, 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, 0x12, - 0x10, 0x0a, 0x03, 0x61, 0x72, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x72, - 0x67, 0x12, 0x1f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x22, 0x27, 0x0a, 0x06, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x1d, 0x0a, 0x04, - 0x62, 0x6f, 0x64, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, - 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x16, 0x0a, 0x04, 0x4e, - 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x02, 0x69, 0x64, 0x22, 0x06, 0x0a, 0x04, 0x50, 0x61, 0x73, 0x73, 0x22, 0x29, 0x0a, 0x06, 0x52, - 0x65, 0x74, 0x75, 0x72, 0x6e, 0x12, 0x1f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, + 0x08, 0x6b, 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x12, 0x1d, 0x0a, 0x04, 0x62, 0x6f, 0x64, + 0x79, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, + 0x64, 0x65, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x31, 0x0a, 0x0e, 0x64, 0x65, 0x63, 0x6f, + 0x72, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x0e, 0x64, 0x65, 0x63, + 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x22, 0x1d, 0x0a, 0x07, 0x43, + 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x22, 0x72, 0x0a, 0x07, 0x43, 0x6f, + 0x6d, 0x70, 0x61, 0x72, 0x65, 0x12, 0x1d, 0x0a, 0x04, 0x6c, 0x65, 0x66, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, + 0x6c, 0x65, 0x66, 0x74, 0x12, 0x1b, 0x0a, 0x03, 0x6f, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x03, 0x6f, 0x70, + 0x73, 0x12, 0x2b, 0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, + 0x65, 0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x22, 0x54, + 0x0a, 0x08, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x12, 0x15, 0x0a, 0x03, 0x73, 0x74, + 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, 0x73, 0x74, 0x72, 0x69, 0x6e, + 0x67, 0x12, 0x12, 0x0a, 0x03, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x48, 0x00, + 0x52, 0x03, 0x69, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x04, 0x6e, 0x6f, 0x6e, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x04, 0x6e, 0x6f, 0x6e, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x22, 0x48, 0x0a, 0x04, 0x44, 0x69, 0x63, 0x74, 0x12, 0x1d, 0x0a, 0x04, + 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, + 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x12, 0x21, 0x0a, 0x06, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, + 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0x27, + 0x0a, 0x04, 0x45, 0x78, 0x70, 0x72, 0x12, 0x1f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x66, 0x0a, 0x03, 0x46, 0x6f, 0x72, 0x12, 0x21, + 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, + 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x12, 0x1d, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x69, 0x74, 0x65, 0x72, + 0x12, 0x1d, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, + 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, + 0x89, 0x01, 0x0a, 0x0b, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x66, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x04, 0x41, 0x72, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0e, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, + 0x73, 0x52, 0x04, 0x61, 0x72, 0x67, 0x73, 0x12, 0x1d, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, + 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x23, 0x0a, 0x07, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, + 0x64, 0x65, 0x52, 0x07, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x22, 0x66, 0x0a, 0x02, 0x49, + 0x66, 0x12, 0x1d, 0x0a, 0x04, 0x74, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x74, 0x65, 0x73, 0x74, + 0x12, 0x1d, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, + 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, + 0x22, 0x0a, 0x07, 0x6f, 0x72, 0x5f, 0x65, 0x6c, 0x73, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x06, 0x6f, 0x72, 0x65, + 0x6c, 0x73, 0x65, 0x22, 0x29, 0x0a, 0x06, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x1f, 0x0a, + 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, + 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x5b, + 0x0a, 0x0a, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x12, 0x16, 0x0a, 0x06, + 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x6f, + 0x64, 0x75, 0x6c, 0x65, 0x12, 0x1f, 0x0a, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, + 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x32, 0x0a, 0x0b, 0x49, + 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x23, 0x0a, 0x07, 0x69, 0x6d, + 0x70, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, + 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x07, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x22, + 0x04, 0x0a, 0x02, 0x49, 0x73, 0x22, 0x3c, 0x0a, 0x07, 0x4b, 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, + 0x12, 0x10, 0x0a, 0x03, 0x61, 0x72, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, + 0x72, 0x67, 0x12, 0x1f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x22, 0x27, 0x0a, 0x06, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x1d, 0x0a, + 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, + 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x16, 0x0a, 0x04, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x64, 0x22, 0x06, 0x0a, 0x04, 0x50, 0x61, 0x73, 0x73, 0x22, 0x29, 0x0a, 0x06, + 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x12, 0x1f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x4d, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x12, 0x1f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1f, 0x0a, 0x05, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x4d, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x12, 0x1f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1f, 0x0a, 0x05, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, - 0x73, 0x6c, 0x69, 0x63, 0x65, 0x22, 0x28, 0x0a, 0x05, 0x59, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x1f, - 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, - 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x42, - 0x71, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x73, 0x74, 0x42, 0x08, 0x41, 0x73, 0x74, 0x50, - 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x73, 0x71, 0x6c, 0x63, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x73, 0x71, 0x6c, 0x63, - 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x70, 0x79, 0x74, 0x68, 0x6f, 0x6e, 0x2f, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x61, 0x73, 0x74, 0xa2, 0x02, 0x03, 0x41, 0x58, 0x58, 0xaa, 0x02, - 0x03, 0x41, 0x73, 0x74, 0xca, 0x02, 0x03, 0x41, 0x73, 0x74, 0xe2, 0x02, 0x0f, 0x41, 0x73, 0x74, - 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x03, 0x41, - 0x73, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x05, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x22, 0x28, 0x0a, 0x05, 0x59, 0x69, 0x65, 0x6c, 0x64, 0x12, + 0x1f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, + 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x42, 0x71, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x73, 0x74, 0x42, 0x08, 0x41, 0x73, 0x74, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x71, 0x6c, 0x63, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x73, 0x71, 0x6c, + 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x70, 0x79, 0x74, 0x68, 0x6f, 0x6e, 0x2f, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x61, 0x73, 0x74, 0xa2, 0x02, 0x03, 0x41, 0x58, 0x58, 0xaa, + 0x02, 0x03, 0x41, 0x73, 0x74, 0xca, 0x02, 0x03, 0x41, 0x73, 0x74, 0xe2, 0x02, 0x0f, 0x41, 0x73, + 0x74, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x03, + 0x41, 0x73, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2535,53 +2537,54 @@ var file_ast_ast_proto_depIdxs = []int32{ 0, // 31: ast.Attribute.value:type_name -> ast.Node 26, // 32: ast.AnnAssign.target:type_name -> ast.Name 0, // 33: ast.AnnAssign.annotation:type_name -> ast.Node - 0, // 34: ast.Arg.annotation:type_name -> ast.Node - 5, // 35: ast.Arguments.args:type_name -> ast.Arg - 5, // 36: ast.Arguments.kw_only_args:type_name -> ast.Arg - 0, // 37: ast.AsyncFor.target:type_name -> ast.Node - 0, // 38: ast.AsyncFor.iter:type_name -> ast.Node - 0, // 39: ast.AsyncFor.body:type_name -> ast.Node - 6, // 40: ast.AsyncFunctionDef.Args:type_name -> ast.Arguments - 0, // 41: ast.AsyncFunctionDef.body:type_name -> ast.Node - 0, // 42: ast.AsyncFunctionDef.returns:type_name -> ast.Node - 0, // 43: ast.Assign.targets:type_name -> ast.Node - 0, // 44: ast.Assign.value:type_name -> ast.Node - 0, // 45: ast.Call.func:type_name -> ast.Node - 0, // 46: ast.Call.args:type_name -> ast.Node - 24, // 47: ast.Call.keywords:type_name -> ast.Keyword - 0, // 48: ast.ClassDef.bases:type_name -> ast.Node - 0, // 49: ast.ClassDef.keywords:type_name -> ast.Node - 0, // 50: ast.ClassDef.body:type_name -> ast.Node - 0, // 51: ast.ClassDef.decorator_list:type_name -> ast.Node - 0, // 52: ast.Compare.left:type_name -> ast.Node - 0, // 53: ast.Compare.ops:type_name -> ast.Node - 0, // 54: ast.Compare.comparators:type_name -> ast.Node - 0, // 55: ast.Dict.keys:type_name -> ast.Node - 0, // 56: ast.Dict.values:type_name -> ast.Node - 0, // 57: ast.Expr.value:type_name -> ast.Node - 0, // 58: ast.For.target:type_name -> ast.Node - 0, // 59: ast.For.iter:type_name -> ast.Node - 0, // 60: ast.For.body:type_name -> ast.Node - 6, // 61: ast.FunctionDef.Args:type_name -> ast.Arguments - 0, // 62: ast.FunctionDef.body:type_name -> ast.Node - 0, // 63: ast.FunctionDef.returns:type_name -> ast.Node - 0, // 64: ast.If.test:type_name -> ast.Node - 0, // 65: ast.If.body:type_name -> ast.Node - 0, // 66: ast.If.or_else:type_name -> ast.Node - 0, // 67: ast.Import.names:type_name -> ast.Node - 0, // 68: ast.ImportFrom.names:type_name -> ast.Node - 0, // 69: ast.ImportGroup.imports:type_name -> ast.Node - 0, // 70: ast.Keyword.value:type_name -> ast.Node - 0, // 71: ast.Module.body:type_name -> ast.Node - 0, // 72: ast.Return.value:type_name -> ast.Node - 26, // 73: ast.Subscript.value:type_name -> ast.Name - 0, // 74: ast.Subscript.slice:type_name -> ast.Node - 0, // 75: ast.Yield.value:type_name -> ast.Node - 76, // [76:76] is the sub-list for method output_type - 76, // [76:76] is the sub-list for method input_type - 76, // [76:76] is the sub-list for extension type_name - 76, // [76:76] is the sub-list for extension extendee - 0, // [0:76] is the sub-list for field type_name + 0, // 34: ast.AnnAssign.value:type_name -> ast.Node + 0, // 35: ast.Arg.annotation:type_name -> ast.Node + 5, // 36: ast.Arguments.args:type_name -> ast.Arg + 5, // 37: ast.Arguments.kw_only_args:type_name -> ast.Arg + 0, // 38: ast.AsyncFor.target:type_name -> ast.Node + 0, // 39: ast.AsyncFor.iter:type_name -> ast.Node + 0, // 40: ast.AsyncFor.body:type_name -> ast.Node + 6, // 41: ast.AsyncFunctionDef.Args:type_name -> ast.Arguments + 0, // 42: ast.AsyncFunctionDef.body:type_name -> ast.Node + 0, // 43: ast.AsyncFunctionDef.returns:type_name -> ast.Node + 0, // 44: ast.Assign.targets:type_name -> ast.Node + 0, // 45: ast.Assign.value:type_name -> ast.Node + 0, // 46: ast.Call.func:type_name -> ast.Node + 0, // 47: ast.Call.args:type_name -> ast.Node + 24, // 48: ast.Call.keywords:type_name -> ast.Keyword + 0, // 49: ast.ClassDef.bases:type_name -> ast.Node + 0, // 50: ast.ClassDef.keywords:type_name -> ast.Node + 0, // 51: ast.ClassDef.body:type_name -> ast.Node + 0, // 52: ast.ClassDef.decorator_list:type_name -> ast.Node + 0, // 53: ast.Compare.left:type_name -> ast.Node + 0, // 54: ast.Compare.ops:type_name -> ast.Node + 0, // 55: ast.Compare.comparators:type_name -> ast.Node + 0, // 56: ast.Dict.keys:type_name -> ast.Node + 0, // 57: ast.Dict.values:type_name -> ast.Node + 0, // 58: ast.Expr.value:type_name -> ast.Node + 0, // 59: ast.For.target:type_name -> ast.Node + 0, // 60: ast.For.iter:type_name -> ast.Node + 0, // 61: ast.For.body:type_name -> ast.Node + 6, // 62: ast.FunctionDef.Args:type_name -> ast.Arguments + 0, // 63: ast.FunctionDef.body:type_name -> ast.Node + 0, // 64: ast.FunctionDef.returns:type_name -> ast.Node + 0, // 65: ast.If.test:type_name -> ast.Node + 0, // 66: ast.If.body:type_name -> ast.Node + 0, // 67: ast.If.or_else:type_name -> ast.Node + 0, // 68: ast.Import.names:type_name -> ast.Node + 0, // 69: ast.ImportFrom.names:type_name -> ast.Node + 0, // 70: ast.ImportGroup.imports:type_name -> ast.Node + 0, // 71: ast.Keyword.value:type_name -> ast.Node + 0, // 72: ast.Module.body:type_name -> ast.Node + 0, // 73: ast.Return.value:type_name -> ast.Node + 26, // 74: ast.Subscript.value:type_name -> ast.Name + 0, // 75: ast.Subscript.slice:type_name -> ast.Node + 0, // 76: ast.Yield.value:type_name -> ast.Node + 77, // [77:77] is the sub-list for method output_type + 77, // [77:77] is the sub-list for method input_type + 77, // [77:77] is the sub-list for extension type_name + 77, // [77:77] is the sub-list for extension extendee + 0, // [0:77] is the sub-list for field type_name } func init() { file_ast_ast_proto_init() } diff --git a/internal/gen.go b/internal/gen.go index 66863ce..d9ae54d 100644 --- a/internal/gen.go +++ b/internal/gen.go @@ -686,7 +686,6 @@ func pydanticNode(name string) *pyast.ClassDef { } func fieldNode(f Field, defaultNone bool) *pyast.Node { - // TODO: Current AST is showing limitation as annotated assign does not support a value :'(, manually edited :'( var value *pyast.Node = nil if defaultNone && f.Type.IsNull { value = &pyast.Node{ diff --git a/protos/ast/ast.proto b/protos/ast/ast.proto index a8daa62..4188f36 100644 --- a/protos/ast/ast.proto +++ b/protos/ast/ast.proto @@ -59,8 +59,9 @@ message AnnAssign { Name target = 1 [json_name="target"]; Node annotation = 2 [json_name="annotation"]; - int32 simple = 3 [json_name="simple"]; - string Comment = 4 [json_name="comment"]; + Node value = 3 [json_name="value"]; + int32 simple = 4 [json_name="simple"]; + string Comment = 5 [json_name="comment"]; } message Arg From 3d5cd342d23ae143ed892170ffecda20ff9a1958 Mon Sep 17 00:00:00 2001 From: Kevin Valk Date: Wed, 21 Feb 2024 12:05:34 +0100 Subject: [PATCH 9/9] feat: automatic release of the plugin on tagging --- .github/workflows/ci.yml | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6542dc3..f6f780e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,8 @@ name: go on: push: + tags: + - v* branches: - main pull_request: @@ -22,7 +24,30 @@ jobs: - run: sqlc diff working-directory: examples - - uses: actions/upload-artifact@v3 + - name: Generate checksum + id: checksum + run: |- + echo "sha256=$(sha256sum bin/sqlc-gen-python.wasm | awk '{ print $1 }')" >> $GITHUB_OUTPUT + + - uses: actions/upload-artifact@v4 with: name: sqlc-gen-python - path: plugin/sqlc-gen-python.wasm \ No newline at end of file + path: bin/sqlc-gen-python.wasm + + - name: Release the build on tag + uses: softprops/action-gh-release@v1 + if: startsWith(github.ref, 'refs/tags/') + with: + body: | + # Configuration + ```yaml + version: '2' + plugins: + - name: py + wasm: + url: ${{ github.server_url }}/${{ github.repository}}/releases/download/${{ github.ref_name }}/sqlc-gen-python.wasm + sha256: ${{ steps.checksum.outputs.sha256 }} + ``` + generate_release_notes: true + files: | + bin/sqlc-gen-python.wasm \ No newline at end of file