Skip to content

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

Merged
merged 18 commits into from
Nov 18, 2016
Merged

Conversation

wenchenglu
Copy link

No description provided.

@googlebot googlebot added the cla: yes This human has signed the Contributor License Agreement. label Nov 16, 2016
@wenchenglu
Copy link
Author

@jonparrott
PTAL

@wenchenglu
Copy link
Author

just noticed the travis errors, will fix those and ping for review soon.

Copy link
Contributor

@theacodes theacodes left a 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 \
Copy link
Contributor

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
Copy link
Contributor

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
Copy link
Contributor

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
Copy link
Contributor

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):
Copy link
Contributor

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():
Copy link
Contributor

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():
Copy link
Contributor

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(
Copy link
Contributor

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)
Copy link
Contributor

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use the latest version.

@wenchenglu
Copy link
Author

@jonparrott

Jon, thanks for you review, really nice comments. Ready for the second round of review now :)

@@ -0,0 +1,20 @@
FROM debian:jessie
Copy link
Contributor

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
Copy link
Contributor

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.

Copy link
Author

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):
Copy link
Contributor

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.

Copy link
Author

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?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's three options:

  1. Catch everywhere you access the dictionary.
  2. Have a top-level catch of all exceptions and map them to gRPC errors (you're already kind of doing this)
  3. Ignore exceptions and rely on gRPC's default behavior.

Copy link
Author

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.

Copy link
Author

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):
Copy link
Contributor

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.

Copy link
Author

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?

Copy link
Contributor

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."""
Copy link
Contributor

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"
Copy link
Contributor

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"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo. :)

Copy link
Author

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__':
Copy link
Contributor

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:
Copy link
Contributor

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):
Copy link
Contributor

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?

@wenchenglu
Copy link
Author

ptal


pip install -r requirements.txt

Install the [GRPC libraries and tools](https://github.com/grpc/grpc/blob/v1.0.x/INSTALL.md)
Copy link

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

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Contributor

@theacodes theacodes left a 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
Copy link
Contributor

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

Copy link
Author

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?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dockerfile

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@theacodes
Copy link
Contributor

I still want to drop the usage of the beta interface.

@wenchenglu
Copy link
Author

removed grpc beta interface, PTAL

class Bookstore(object):
"""An in-memory backend for storing Bookstore data."""

class ShelfDict(dict):
Copy link
Contributor

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?

Copy link
Author

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]
Copy link
Contributor

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.

Copy link
Author

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."""
Copy link
Contributor

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):
Copy link
Contributor

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.

Copy link
Author

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
Copy link
Contributor

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)

Copy link
Author

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,
Copy link
Contributor

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).

Copy link
Author

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)
Copy link
Contributor

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)

Copy link
Author

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

Copy link
Contributor

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
Copy link
Contributor

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?

Copy link
Author

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still using beta here?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

@wenchenglu
Copy link
Author

PTAL

import grpc


Code = grpc.StatusCode
Copy link
Contributor

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
Copy link
Contributor

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

@theacodes
Copy link
Contributor

A few final tiny things and this is good to go.

@wenchenglu
Copy link
Author

done, ptal

@theacodes
Copy link
Contributor

@dpebot can you merge when travis passes?

@dpebot
Copy link
Collaborator

dpebot commented Nov 17, 2016

Okay! I'll merge when all statuses are green.

@dpebot dpebot added the automerge Merge the pull request once unit tests and other checks pass. label Nov 17, 2016
@wenchenglu
Copy link
Author

@jonparrott

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

@jeffmendoza
Copy link
Contributor

@ryanmats Jon is sick today, do you know about any flake8 tools to fix errors?

@ryanmats
Copy link
Contributor

@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.

@wenchenglu
Copy link
Author

@waprin

fixed lint errors as you suggested, PTAL

@dpebot dpebot merged commit 9036dcb into GoogleCloudPlatform:master Nov 18, 2016
@theacodes
Copy link
Contributor

@waprin's suggestion was great. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
automerge Merge the pull request once unit tests and other checks pass. cla: yes This human has signed the Contributor License Agreement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants