protocolbuffers / protobuf

Protocol Buffers - Google's data interchange format
http://protobuf.dev
Other
65.62k stars 15.49k forks source link

[Python] Cannot use reflection to find a service by name if service does not have package #18943

Open inikolaev opened 1 week ago

inikolaev commented 1 week ago

What version of protobuf and what language are you using? Version: main Language: Python

What operating system (Linux, Windows, ...) and version? All

What runtime / compiler are you using (e.g., python version or gcc version) Not applicable

What did you do? Steps to reproduce the behavior:

  1. Deploy gRPC service that does not have a package
  2. Use reflection and DescriptorPool to find service by name
  3. KeyError is being raised

What did you expect to see It should be possible to find a service even though it does not have a pckage

I have debugged through the code and the culprit is this code that assumes that package name is not empty. I happened to have an example where this was not the case.

The fix I think is trivial - just check if package is empty or not before concatenating it with an object name.

anandolee commented 1 week ago

Can you help to provide a reproduce example code and .proto definition?

inikolaev commented 1 week ago

Sure!

Here's the minimal protobuf that I created to reproduce it:

syntax = "proto3";

option go_package = "proto/gen;servicepb";

service Service {
  rpc Method(Request) returns (Response);
}

message Request {
  string data = 1;
}

message Response {
  string data = 1;
}

and the following Python code:

import grpc
from google.protobuf.descriptor_pool import DescriptorPool
from grpc_reflection.v1alpha.proto_reflection_descriptor_database import ProtoReflectionDescriptorDatabase

with grpc.insecure_channel('localhost:50051', ) as channel:
    reflection_db = ProtoReflectionDescriptorDatabase(channel)
    desc_pool = DescriptorPool(reflection_db)
    service_desc = desc_pool.FindServiceByName("Service")
    method_desc = service_desc.FindMethodByName("Method")
    print(method_desc)

With current implementation it raises the following error:

Traceback (most recent call last):
  File "client.py", line 45, in <module>
    service_desc = desc_pool.FindServiceByName("Service")
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
KeyError: "Couldn't find service Service"

And if I debug an look at the DescriptorDatabase._file_desc_protos_by_symbol it looks like this:

{
  '.Service': <value>, 
  'Request': <value>,
  'Response': <value>
}

Please note the dot in the .Service which is caused by missing package, while other symbols from the same package don't have it - it's taken care of by _ExtractSymbols which handles empty package already.

I have submitted a PR if you want to take a look and help me get it through.