-
Notifications
You must be signed in to change notification settings - Fork 6.6k
add grpc bookstore python samples #660
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@jonparrott |
just noticed the travis errors, will fix those and ping for review soon. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Finished first pass. Please let me know when you're ready for another pass.
ADD . /bookstore/ | ||
|
||
RUN cd /bookstore \ | ||
&& pip install --ignore-installed six \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't do this, use a virtualenv:
RUN virtualenv /env
ENV VIRTUAL_ENV /env
ENV PATH /env/bin:$PATH
ADD . /bookstore/
WORKDIR /bookstore
RUN pip install -r requirements.txt
You'll probably want to start by creating a virtual Python environment, so that | ||
you can install dependencies without installing them globally. To do this, run: | ||
|
||
mkdir bookstore-env |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you don't need to do mkdir
, virtualenv
will do it for you.
|
||
## Installing Dependencies | ||
|
||
You'll probably want to start by creating a virtual Python environment, so that |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Be definitive: Install the dependencies using virtualenv:
(Note: I will eventually replace this README with an autogenerated one, but I'll keep the protoc instructions and such)
import bookstore_pb2 | ||
|
||
from google.protobuf import empty_pb2 | ||
from grpc.beta import implementations |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just import grpc
, don't use the beta interface anymore.
from grpc.beta import implementations | ||
|
||
|
||
def _add_endpoints_metadata(api_key): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of doing nested functions and closures, prefer a callable class:
class ApiKeyMetadata(object):
def __init__(self, api_key):
self.api_key = api_key
def __call__(self, metadata):
...
return struct_pb2.Value() | ||
|
||
|
||
def _create_sample_bookstore(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for the _
.
return store | ||
|
||
|
||
def _serve(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for the _
.
|
||
def _serve(): | ||
"""Configures and runs the bookstore API server.""" | ||
parser = argparse.ArgumentParser( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apply all of my comments about the argparse section in the client to this argparse section as well.
|
||
store = _create_sample_bookstore() | ||
server = bookstore_pb2.beta_create_Bookstore_server(BookstoreServicer(store)) | ||
server.add_insecure_port('[::]:%d' % args.port) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
prefer format
over %
.
@@ -0,0 +1,3 @@ | |||
# The sample bookstore has been built and tested with GRPC version | |||
# 1.0.0 It may work with other versions. | |||
grpcio == 1.0.0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use the latest version.
Jon, thanks for you review, really nice comments. Ready for the second round of review now :) |
@@ -0,0 +1,20 @@ | |||
FROM debian:jessie |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Our other endpoints sample uses the python runtime, any strong reason for not using it here?
&& apt-get install -y python-dev python-pip virtualenv \ | ||
&& rm -rf /var/lib/apt/lists/* | ||
|
||
RUN virtualenv /env |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I should've mentioned this during the last review, but use python 3, please. virtualenv -p python3.4
. If you use the Python runtime, use -p python3.5
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done, is this what you meant?
class Bookstore(object): | ||
"""Stores and manipulates bookstore data.""" | ||
|
||
class ShelfDict(dict): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't do this, just catch the KeyErrors
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there are many places used that ShelfDict, catch all over the places?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's three options:
- Catch everywhere you access the dictionary.
- Have a top-level catch of all exceptions and map them to gRPC errors (you're already kind of doing this)
- Ignore exceptions and rely on gRPC's default behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I played with this a bit, and think option 2 is the best which gives better errors with having to catch everywhere.
Let me know how you think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I played with this a bit, and think option 2 is the best which gives better errors with having to catch everywhere.
Let me know how you think.
raise status.StatusException(status.Code.NOT_FOUND, | ||
'Unable to find book "%s"' % key) | ||
|
||
class ShelfInfo(object): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't do nested classes, there is rarely a need for it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do you mean move ShelfInfo to the top-level class?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes.
import status | ||
|
||
class Bookstore(object): | ||
"""Stores and manipulates bookstore data.""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe a better description here? "An in-memory backend for storing Bookstore data."
store = bookstore.Bookstore() | ||
|
||
shelf = bookstore_pb2.Shelf() | ||
shelf.theme = "Fiction" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use single quotes for string throughout (except docstrings)
(_, fiction) = store.create_shelf(shelf) | ||
|
||
book = bookstore_pb2.Book() | ||
book.title = "REAMDE" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wow, nice catch :)
server.stop(args.shutdown_grace_duration) | ||
|
||
|
||
if __name__ == '__main__': |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move argpase stuff down here?
"""A context manager that automatically handles StatusException.""" | ||
try: | ||
yield | ||
except StatusException as se: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how about just exc
instead of se
?
|
||
|
||
@contextmanager | ||
def context(ctx): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
grpc_context
instead of ctx
?
ptal |
|
||
pip install -r requirements.txt | ||
|
||
Install the [GRPC libraries and tools](https://github.com/grpc/grpc/blob/v1.0.x/INSTALL.md) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Follow prequisites in GRPC Quickstart is better than the INSTALL.md
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Getting close.
@@ -0,0 +1,19 @@ | |||
FROM gcr.io/google_appengine/python |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely link to the Python runtime page on github: https://github.com/GoogleCloudPlatform/python-runtime
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do you mean adding a comment in this Dockerfile? something like this:
https://github.com/GoogleCloudPlatform/python-runtime
Or adding this link in the README.md?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dockerfile
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
I still want to drop the usage of the beta interface. |
removed grpc beta interface, PTAL |
class Bookstore(object): | ||
"""An in-memory backend for storing Bookstore data.""" | ||
|
||
class ShelfDict(dict): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought we were going to remove ShelfDict
and BookDict
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, you mentioned that. then you provided 3 options, and I think the option 2 is the best to avoid duplicate catch statement and still returning the useful errors. Do you have a better suggestion to do that without using wrapper dict?
|
||
def delete_book(self, shelf_id, book_id): | ||
with self._lock: | ||
_ = self._shelves[shelf_id]._books[book_id] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can probably just remove this line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think so, done
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
"""The Python GRPC Bookstore Client Example.""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/GRPC/gRPC.
import bookstore_pb2 | ||
|
||
|
||
class ApiKeyMetadata(object): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can remove this class now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
"""The Python GRPC Bookstore Client Example.""" | ||
|
||
import argparse | ||
import grpc |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
grpc goes into the second section (third-party imports)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here? or next to "from google.protobuf import empty_pb2"?
channel = grpc.insecure_channel('{}:{}'.format(host,port)) | ||
|
||
stub = bookstore_pb2.BookstoreStub(channel) | ||
shelves = stub.ListShelves(empty_pb2.Empty(), timeout, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general, put a newline after the opening (
to avoid hanging indents. (please do this throughout).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
|
||
shelf = bookstore_pb2.Shelf() | ||
shelf.theme = 'Fiction' | ||
(_, fiction) = store.create_shelf(shelf) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for these parens (there's a few more instances of this, please do it throughout)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done, and another place
after grep, only find two for statement using that now:
for (_, s) in self.shelves.iteritems()]
for (, book) in self._shelves[shelf_id]._books.iteritems()]
not sure if it is recommended to remove parens there
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, go ahead.
@@ -0,0 +1,3 @@ | |||
# The sample bookstore has been built and tested with GRPC version |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment really doesn't add a lot of value, remove it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
|
||
from contextlib import contextmanager | ||
|
||
from grpc.beta import interfaces |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still using beta here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed
PTAL |
import grpc | ||
|
||
|
||
Code = grpc.StatusCode |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this used anywhere?
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
import status |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
imports should be:
from contextlib import contextmanager
import grpc
import status
A few final tiny things and this is good to go. |
done, ptal |
@dpebot can you merge when travis passes? |
Okay! I'll merge when all statuses are green. |
I saw there are many flake8 lint errors. Do I have to manually fix them one by one? or is there some tool I can use? thanks |
@ryanmats Jon is sick today, do you know about any flake8 tools to fix errors? |
@jeffmendoza @wlu2016 i'm not aware of any flake8 tools to automatically fix errors, but there might be. Jon said he would be on chat from time to time today so maybe he'd know. |
fixed lint errors as you suggested, PTAL |
@waprin's suggestion was great. Thanks! |
No description provided.