Python server and client - not found response?

741 views
Skip to first unread message

Jesús García Crespo

unread,
Nov 21, 2016, 5:49:18 PM11/21/16
to grp...@googlegroups.com
Hi everyone,

First, I would like to say thanks to all of you for the great job!
I'm trying to wire two Python apps using grpcio 1.0.1. In the scenario that I'm going to describe below, the client asks for a determined book and the server wants to communicate that the book has not ben found. What would be the current way to do that with the following proto?

service Library {
  rpc BookGet(BookGetRequest) returns (BookGetResponse)
}

message Book {
  string id = 1;
}

message BookGetRequest {
  string id = 1;
}

message BookGetResponse {
  Book book = 1;
}

I've tried the following snippet but it won't work because I can't return `None` or the channel will fail to serialize the response:

class LibraryServer(object):
    def BookGet(self, request, context):
        """ Book not found. """
        context.set_code(grpc.StatusCode.NOT_FOUND)

If I return an empty message *and* set the status code to NOT_FOUND then I see an internal exception (message = "_Rendezvous of RPC that terminated with (StatusCode.NOT_FOUND, )") is raised on the client side:

class LibraryServer(object):
    def BookGet(self, request, context):
        """ Book not found. """
        context.set_code(grpc.StatusCode.NOT_FOUND)

I've also attempted these two approaches:

class LibraryServer(object):
    def BookGet(self, request, context):
        """ Book not found. """
        return library_mcp2.BookGetResponse()

class LibraryServer(object):
    def BookGet(self, request, context):
        """ Book not found. """
        return library_mcp2.BookGetResponse(book=None)

However, this is what I am seeing on the client:

resp = self.stub.BookGet(library_pb2.BookGetRequest(id='12345'))
resp is None          # False
resp.book is None     # False
resp.book.id is None  # False
resp.book.id == ''    # True

So I could only make use of the last assertion but that seems far from ideal, right? What am I doing wrong?

Thank you for your help!

--
Jesús García Crespo

Nathaniel Manista

unread,
Nov 23, 2016, 9:25:09 PM11/23/16
to Jesús García Crespo, grp...@googlegroups.com, Protocol Buffers
+prot...@googlegroups.com who will be able to contribute more on the protocol buffers side of the discussion

On Mon, Nov 21, 2016 at 2:48 PM, Jesús García Crespo <je...@sevein.com> wrote:
First, I would like to say thanks to all of you for the great job!

Aww, you're very welcome!
 
I'm trying to wire two Python apps using grpcio 1.0.1. In the scenario that I'm going to describe below, the client asks for a determined book and the server wants to communicate that the book has not ben found. What would be the current way to do that with the following proto?

service Library {
  rpc BookGet(BookGetRequest) returns (BookGetResponse)
}

message Book {
  string id = 1;
}

message BookGetRequest {
  string id = 1;
}

message BookGetResponse {
  Book book = 1;
}

I think the problem here is that the way your messages model your data doesn't allow for you to represent "no book was found" as a first-class concept. You could fake it with a sentinel value for the id of the not-really-found found Book (the empty string, or the string "this id represents a book not having been found", or something else), but I suspect that the first-class thing to do would be for BookGetResponse to have a more complicated type for its internal field - possibly a oneof that can communicate exactly one of the id of a found book or the fact that the book was not found.

I've tried the following snippet but it won't work because I can't return `None` or the channel will fail to serialize the response:

class LibraryServer(object):
    def BookGet(self, request, context):
        """ Book not found. """
        context.set_code(grpc.StatusCode.NOT_FOUND)

Yep. I go back and forth on how and whether that should be made to work.

If I return an empty message *and* set the status code to NOT_FOUND then I see an internal exception (message = "_Rendezvous of RPC that terminated with (StatusCode.NOT_FOUND, )") is raised on the client side:

class LibraryServer(object):
    def BookGet(self, request, context):
        """ Book not found. """
        context.set_code(grpc.StatusCode.NOT_FOUND)

Non-OK status codes always cause exceptions to be raised on the invocation side of an RPC. If you like you can incorporate that into the semantics of your application-level protocol but I advise against it because it strikes me as likely to make actual gRPC-level errors more difficult to handle.

I've also attempted these two approaches:

class LibraryServer(object):
    def BookGet(self, request, context):
        """ Book not found. """
        return library_mcp2.BookGetResponse()

class LibraryServer(object):
    def BookGet(self, request, context):
        """ Book not found. """
        return library_mcp2.BookGetResponse(book=None)

However, this is what I am seeing on the client:

resp = self.stub.BookGet(library_pb2.BookGetRequest(id='12345'))
resp is None          # False
resp.book is None     # False
resp.book.id is None  # False
resp.book.id == ''    # True

So I could only make use of the last assertion but that seems far from ideal, right?

I think this has to do with the changes in Protocol Buffers 3 and how fields and messages are never "not present" any more.

What am I doing wrong?

Probably nothing is wrong in the strictest sense; I think it's more a matter of shaping your messages so that they more clearly and more readily reflect the semantics of your application-level protocol.

Thank you for your help!

Thank you for writing!
-N

Jesús García Crespo

unread,
Nov 24, 2016, 7:29:13 PM11/24/16
to Nathaniel Manista, grp...@googlegroups.com, Protocol Buffers
Hi again,

On Wed, Nov 23, 2016 at 6:24 PM, Nathaniel Manista <nath...@google.com> wrote:
possibly a oneof that can communicate exactly one of the id of a found book or the fact that the book was not found.

This makes sense to me, I'll give it a shot!
 
resp = self.stub.BookGet(library_pb2.BookGetRequest(id='12345'))  
resp is None          # False
resp.book is None     # False
resp.book.id is None  # False
resp.book.id == ''    # True

So I could only make use of the last assertion but that seems far from ideal, right?

I think this has to do with the changes in Protocol Buffers 3 and how fields and messages are never "not present" any more.

Ok, I'm glad to know that was conscious choice and not a bug in my code :)
 
Thank you so much,

--
Jesús García Crespo

Feng Xiao

unread,
Nov 28, 2016, 5:19:47 PM11/28/16
to Jesús García Crespo, Nathaniel Manista, grp...@googlegroups.com, Protocol Buffers
In proto3 message fields (such as the "book" field in this case) do have field presence. You are not supposed to check it with None though, because if the field is not present, getters are supports to return a default instance. There is another HasField() method to be used to check whether the field is present or not. Try:

resp.HasField("book")   # should be false if the server returns an empty response.


--
You received this message because you are subscribed to the Google Groups "Protocol Buffers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to protobuf+unsubscribe@googlegroups.com.
To post to this group, send email to prot...@googlegroups.com.
Visit this group at https://groups.google.com/group/protobuf.
For more options, visit https://groups.google.com/d/optout.

Reply all
Reply to author
Forward
0 new messages