Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions protoc-gen-connect-python/generator/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ type Config struct {

// Imports is how to import dependencies in the generated code.
Imports Imports

// Async indicates whether to only generate asynchronous code. If false,
// only synchronous code will be generated. If nil, both synchronous and
// asynchronous code will be generated.
Async *bool
}

func parseConfig(p string) Config {
Expand Down Expand Up @@ -64,6 +69,15 @@ func parseConfig(p string) Config {
case "relative":
cfg.Imports = ImportsRelative
}
case "async":
switch value {
case "true":
trueVal := true
cfg.Async = &trueVal
case "false":
falseVal := false
cfg.Async = &falseVal
}
Comment on lines +73 to +80
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

first time looking closely at the option parsing, but: should we be failing here on unrecognized option keys/values? (see https://github.com/connectrpc/connect-go/blob/e299aa60ffcf91633e48fcb3dd594cbbb187ec7f/cmd/protoc-gen-connect-go/main.go#L779)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah probably should - I think we can leave this for now given the future work on codegen

}
}
return cfg
Expand Down
9 changes: 8 additions & 1 deletion protoc-gen-connect-python/generator/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ func generateConnectFile(fd protoreflect.FileDescriptor, conf Config) (string, s
ModuleName: moduleName,
Imports: importStatements(fd, conf),
}
if conf.Async != nil {
if *conf.Async {
vars.SkipSync = true
} else {
vars.SkipAsync = true
}
}

svcs := fd.Services()
packageName := string(fd.Package())
Expand Down Expand Up @@ -109,7 +116,7 @@ func generateConnectFile(fd protoreflect.FileDescriptor, conf Config) (string, s
vars.Services = append(vars.Services, connectSvc)
}

var buf = &bytes.Buffer{}
buf := &bytes.Buffer{}
err := ConnectTemplate.Execute(buf, vars)
if err != nil {
return "", "", fmt.Errorf("failed to execute template: %w", err)
Expand Down
136 changes: 131 additions & 5 deletions protoc-gen-connect-python/generator/generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,11 @@ func TestGenerate(t *testing.T) {
t.Parallel()

tests := []struct {
name string
req *pluginpb.CodeGeneratorRequest
wantStrings []string
wantErr bool
name string
req *pluginpb.CodeGeneratorRequest
wantStrings []string
dontWantStrings []string
wantErr bool
}{
{
name: "empty request",
Expand Down Expand Up @@ -195,7 +196,127 @@ func TestGenerate(t *testing.T) {
},
},
wantErr: false,
wantStrings: []string{"def try_(self"},
wantStrings: []string{"class TestServiceASGIApplication", "class TestServiceWSGIApplication"},
},
{
name: "async only",
req: &pluginpb.CodeGeneratorRequest{
FileToGenerate: []string{"test.proto"},
Parameter: proto.String("async=true"),
ProtoFile: []*descriptorpb.FileDescriptorProto{
{
Name: proto.String("test.proto"),
Package: proto.String("test"),
Dependency: []string{"other.proto"},
Service: []*descriptorpb.ServiceDescriptorProto{
{
Name: proto.String("TestService"),
Method: []*descriptorpb.MethodDescriptorProto{
{
Name: proto.String("TestMethod"),
InputType: proto.String(".test.TestRequest"),
OutputType: proto.String(".test.TestResponse"),
},
{
Name: proto.String("TestMethod2"),
InputType: proto.String(".otherpackage.OtherRequest"),
OutputType: proto.String(".otherpackage.OtherResponse"),
},
// Reserved keyword
{
Name: proto.String("Try"),
InputType: proto.String(".otherpackage.OtherRequest"),
OutputType: proto.String(".otherpackage.OtherResponse"),
},
},
},
},
MessageType: []*descriptorpb.DescriptorProto{
{
Name: proto.String("TestRequest"),
},
{
Name: proto.String("TestResponse"),
},
},
},
{
Name: proto.String("other.proto"),
Package: proto.String("otherpackage"),
MessageType: []*descriptorpb.DescriptorProto{
{
Name: proto.String("OtherRequest"),
},
{
Name: proto.String("OtherResponse"),
},
},
},
},
},
wantErr: false,
wantStrings: []string{"class TestServiceASGIApplication"},
dontWantStrings: []string{"class TestServiceWSGIApplication"},
},
{
name: "sync only",
req: &pluginpb.CodeGeneratorRequest{
FileToGenerate: []string{"test.proto"},
Parameter: proto.String("async=false"),
ProtoFile: []*descriptorpb.FileDescriptorProto{
{
Name: proto.String("test.proto"),
Package: proto.String("test"),
Dependency: []string{"other.proto"},
Service: []*descriptorpb.ServiceDescriptorProto{
{
Name: proto.String("TestService"),
Method: []*descriptorpb.MethodDescriptorProto{
{
Name: proto.String("TestMethod"),
InputType: proto.String(".test.TestRequest"),
OutputType: proto.String(".test.TestResponse"),
},
{
Name: proto.String("TestMethod2"),
InputType: proto.String(".otherpackage.OtherRequest"),
OutputType: proto.String(".otherpackage.OtherResponse"),
},
// Reserved keyword
{
Name: proto.String("Try"),
InputType: proto.String(".otherpackage.OtherRequest"),
OutputType: proto.String(".otherpackage.OtherResponse"),
},
},
},
},
MessageType: []*descriptorpb.DescriptorProto{
{
Name: proto.String("TestRequest"),
},
{
Name: proto.String("TestResponse"),
},
},
},
{
Name: proto.String("other.proto"),
Package: proto.String("otherpackage"),
MessageType: []*descriptorpb.DescriptorProto{
{
Name: proto.String("OtherRequest"),
},
{
Name: proto.String("OtherResponse"),
},
},
},
},
},
wantErr: false,
wantStrings: []string{"class TestServiceWSGIApplication"},
dontWantStrings: []string{"class TestServiceASGIApplication"},
},
}

Expand All @@ -219,6 +340,11 @@ func TestGenerate(t *testing.T) {
t.Errorf("generate() missing expected string: %v", s)
}
}
for _, s := range tt.dontWantStrings {
if strings.Contains(resp.GetFile()[0].GetContent(), s) {
t.Errorf("generate() contains unexpected string: %v", s)
}
}
}
})
}
Expand Down
13 changes: 10 additions & 3 deletions protoc-gen-connect-python/generator/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ type ConnectTemplateVariables struct {
ModuleName string
Imports []ImportStatement
Services []*ConnectService
SkipAsync bool
SkipSync bool
}

type ConnectService struct {
Expand Down Expand Up @@ -61,9 +63,9 @@ from connectrpc.server import ConnectASGIApplication, ConnectWSGIApplication, En
{{if .Relative}}from . import {{.Name}}{{else}}import {{.Name}}{{end}} as {{.Alias}}
{{- end}}
{{- end}}
{{- range .Services}}


{{if not .SkipAsync }}
{{- range .Services}}
class {{.Name}}(Protocol):{{- range .Methods }}
{{if not .ResponseStream }}async {{end}}def {{.PythonName}}(self, request: {{if .RequestStream}}AsyncIterator[{{end}}{{.InputType}}{{if .RequestStream}}]{{end}}, ctx: RequestContext) -> {{if .ResponseStream}}AsyncIterator[{{end}}{{.OutputType}}{{if .ResponseStream}}]{{end}}:
raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
Expand Down Expand Up @@ -124,6 +126,9 @@ class {{.Name}}Client(ConnectClient):{{range .Methods}}
{{- end}}
)
{{end}}{{- end }}
{{end}}

{{if not .SkipSync }}
{{range .Services}}
class {{.Name}}Sync(Protocol):{{- range .Methods }}
def {{.PythonName}}(self, request: {{if .RequestStream}}Iterator[{{end}}{{.InputType}}{{if .RequestStream}}]{{end}}, ctx: RequestContext) -> {{if .ResponseStream}}Iterator[{{end}}{{.OutputType}}{{if .ResponseStream}}]{{end}}:
Expand Down Expand Up @@ -184,4 +189,6 @@ class {{.Name}}ClientSync(ConnectClientSync):{{range .Methods}}
use_get=use_get,
{{- end}}
)
{{end}}{{end}}`))
{{end}}{{end}}
{{end}}
`))
Loading