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 pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy