danielgtaylor / python-betterproto

Clean, modern, Python 3.6+ code generator & library for Protobuf 3 and async gRPC
MIT License
1.56k stars 218 forks source link

Functions are missing parameters when message name has sequential capital casing #184

Open al8 opened 3 years ago

al8 commented 3 years ago

"sequential capital casing" is what I'm calling the capital letters UIR in name EchoUIRequest.

for protobuf example

message EchoUIRequest {
  string value = 1;
  // Number of extra times to echo
  uint32 extra_times = 2;
}

service Echo {
  rpc Echo(EchoUIRequest) returns (EchoResponse);
}

With betterproto-2.0.0b2, when bindings are generated when the message name has sequential capital letters, the function is missing its parameters.

e.g.

class EchoStub(betterproto.ServiceStub):
    async def echo(self) -> "EchoResponse":

instead of

class EchoStub(betterproto.ServiceStub):
    async def echo(
        self, *, value: str = "", extra_times: int = 0
    ) -> "EchoResponse":

Using release betterproto-1.2.5, it works correctly.

Repro:

Using betterproto-2.0.0b2

Given: test.proto

syntax = "proto3";

package echo;

message EchoRequest {
  string value = 1;
  // Number of extra times to echo
  uint32 extra_times = 2;
}

message EchoUIRequest {
  string value = 1;
  // Number of extra times to echo
  uint32 extra_times = 2;
}

message EchoResponse {
  repeated string values = 1;
}

service Echo {
  rpc EchoCorrect(EchoRequest) returns (EchoResponse);
  rpc EchoIncorrect(EchoUIRequest) returns (EchoResponse);
}

When generated via python3 -m grpc_tools.protoc -I . --python_betterproto_out=. test.proto, the generated function EchoStub.echo_incorrect() is missing all its parameters, where EchoStub.echo_correct() has them.

echo/__init__.py contains:

# Generated by the protocol buffer compiler.  DO NOT EDIT!
# sources: test.proto
# plugin: python-betterproto
from dataclasses import dataclass
from typing import List

import betterproto
import grpclib

@dataclass(eq=False, repr=False)
class EchoRequest(betterproto.Message):
    value: str = betterproto.string_field(1)
    # Number of extra times to echo
    extra_times: int = betterproto.uint32_field(2)

    def __post_init__(self) -> None:
        super().__post_init__()

@dataclass(eq=False, repr=False)
class EchoUiRequest(betterproto.Message):
    value: str = betterproto.string_field(1)
    # Number of extra times to echo
    extra_times: int = betterproto.uint32_field(2)

    def __post_init__(self) -> None:
        super().__post_init__()

@dataclass(eq=False, repr=False)
class EchoResponse(betterproto.Message):
    values: List[str] = betterproto.string_field(1)

    def __post_init__(self) -> None:
        super().__post_init__()

class EchoStub(betterproto.ServiceStub):
    async def echo_correct(
        self, *, value: str = "", extra_times: int = 0
    ) -> "EchoResponse":

        request = EchoRequest()
        request.value = value
        request.extra_times = extra_times

        return await self._unary_unary("/echo.Echo/EchoCorrect", request, EchoResponse)

    async def echo_incorrect(self) -> "EchoResponse":

        request = EchoUiRequest()

        return await self._unary_unary(
            "/echo.Echo/EchoIncorrect", request, EchoResponse
        )

Expected: echo_correct and echo_incorrect should be identical.

Note: v1 appears correct (betterproto-1.2.5), as echo_correct() and echo_incorrect() are identical.

# Generated by the protocol buffer compiler.  DO NOT EDIT!
# sources: test.proto
# plugin: python-betterproto
from dataclasses import dataclass
from typing import List

import betterproto
import grpclib

@dataclass
class EchoRequest(betterproto.Message):
    value: str = betterproto.string_field(1)
    # Number of extra times to echo
    extra_times: int = betterproto.uint32_field(2)

@dataclass
class EchoUIRequest(betterproto.Message):
    value: str = betterproto.string_field(1)
    # Number of extra times to echo
    extra_times: int = betterproto.uint32_field(2)

@dataclass
class EchoResponse(betterproto.Message):
    values: List[str] = betterproto.string_field(1)

class EchoStub(betterproto.ServiceStub):
    async def echo_correct(
        self, *, value: str = "", extra_times: int = 0
    ) -> EchoResponse:
        request = EchoRequest()
        request.value = value
        request.extra_times = extra_times

        return await self._unary_unary("/echo.Echo/EchoCorrect", request, EchoResponse,)

    async def echo_incorrect(
        self, *, value: str = "", extra_times: int = 0
    ) -> EchoResponse:
        request = EchoUIRequest()
        request.value = value
        request.extra_times = extra_times

        return await self._unary_unary(
            "/echo.Echo/EchoIncorrect", request, EchoResponse,
        )
mplucinski commented 3 years ago

The same seems to happen when there's an underscore character anywhere in the message name.