Q:

Compatibility between a gRPC server in Go and a client in Python

I want to know about the compatibility about a gRPC service in Go and a client in Python.
For instance if the service is implemented in Go, it will have signatures like this:
...

func (s *routeGuideServer) GetFeature(ctx context.Context, point *pb.Point) (*pb.Feature, error) {
        ...
}
...

func (s *routeGuideServer) ListFeatures(rect *pb.Rectangle, stream pb.RouteGuide_ListFeaturesServer) error {
        ...
}
...

func (s *routeGuideServer) RecordRoute(stream pb.RouteGuide_RecordRouteServer) error {
        ...
}
...

func (s *routeGuideServer) RouteChat(stream pb.RouteGuide_RouteChatServer) error {
        ...
}

Note how error objects are returned.
Now, if I have to implement the client in Python it will be something like this:
feature = stub.GetFeature(point)
for received_route_note in stub.RouteChat(sent_route_note_iterator):

What happens to the error field that is returned by the Go service implementation? If there is an error from the server side how can it be handled in the Python client?

A:

gRPC provides error handling that fits the language being used.
As you noted, in a Go server you return an error:
func (s *routeGuideServer) RouteChat(stream pb.RouteGuide_RouteChatServer) error {
for {
    in, err := stream.Recv()
    if err == io.EOF {
        return nil
    }
    if err != nil {
        return err
    }
    ...

In a Python client, you execute the API call in a try statement (contrived example, since the official documentation doesn't demonstrate this):
while True:
  try:
    received_route_note = stub.RouteChat(sent_route_note_iterator)
  except grpc.Error as e:
    print(e.details())
    break

Conversely, in a Python server, you set the context and, optionally, raise an exception:

example from the Python Generated Code Reference:
def TellFortune(self, request, context):
  """Returns the horoscope and zodiac sign for the given month and day.
  errors: invalid month or day, fortune unavailable
  """
  context.set_code(grpc.StatusCode.UNIMPLEMENTED)
  context.set_details('Method not implemented!')
  raise NotImplementedError('Method not implemented!')

example from the grpc repository
def UnUn(self, request, context):
  if _application_common.UNARY_UNARY_REQUEST == request:
      return _application_common.UNARY_UNARY_RESPONSE
  else:
      context.set_code(grpc.StatusCode.INVALID_ARGUMENT)
      context.set_details('Something is wrong with your request!')
      return services_pb2.Down()

And in a Go client an error object is (one of) the result parameters:
stream, err := client.RouteChat(ctx)
if err != nil {
    log.Fatalf("%v.RouteChat(_) = _, %v", client, err)
}

It's unfortunate that the official documentation doesn't seem to explicitly cover error handling practices.
A good 3rd party reference I found is at http://avi.im/grpc-errors/

