aboutsummaryrefslogtreecommitdiffhomepage
path: root/grpc-common/python
diff options
context:
space:
mode:
authorGravatar Stanley Cheung <stanleycheung@google.com>2015-08-27 13:58:54 -0700
committerGravatar Stanley Cheung <stanleycheung@google.com>2015-08-27 13:58:54 -0700
commitb2bea23365acb0bcc1b5adba2d5b2fa5a22eb872 (patch)
treea676bf2467b26cdc733dec4fa1d6ce03861f6083 /grpc-common/python
parent60ff5c3c872b9630e256772c0440a169c7cfae10 (diff)
parent6905915db1fe4b66e40f03014f7f14c74f3fee16 (diff)
move grpc-common examples to here
Diffstat (limited to 'grpc-common/python')
-rw-r--r--grpc-common/python/helloworld/.gitignore1
-rw-r--r--grpc-common/python/helloworld/README.md116
-rwxr-xr-xgrpc-common/python/helloworld/greeter_client.py44
-rw-r--r--grpc-common/python/helloworld/greeter_server.py56
-rwxr-xr-xgrpc-common/python/helloworld/run_client.sh8
-rwxr-xr-xgrpc-common/python/helloworld/run_codegen.sh4
-rwxr-xr-xgrpc-common/python/helloworld/run_server.sh9
-rw-r--r--grpc-common/python/route_guide/.gitignore1
-rw-r--r--grpc-common/python/route_guide/README.md303
-rwxr-xr-xgrpc-common/python/route_guide/route_guide_client.py130
-rw-r--r--grpc-common/python/route_guide/route_guide_db.json601
-rw-r--r--grpc-common/python/route_guide/route_guide_pb2.py370
-rwxr-xr-xgrpc-common/python/route_guide/route_guide_resources.py53
-rw-r--r--grpc-common/python/route_guide/route_guide_server.py134
-rwxr-xr-xgrpc-common/python/route_guide/run_client.sh8
-rwxr-xr-xgrpc-common/python/route_guide/run_codegen.sh4
-rwxr-xr-xgrpc-common/python/route_guide/run_server.sh8
17 files changed, 1850 insertions, 0 deletions
diff --git a/grpc-common/python/helloworld/.gitignore b/grpc-common/python/helloworld/.gitignore
new file mode 100644
index 0000000000..0d20b6487c
--- /dev/null
+++ b/grpc-common/python/helloworld/.gitignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/grpc-common/python/helloworld/README.md b/grpc-common/python/helloworld/README.md
new file mode 100644
index 0000000000..0a6a718bae
--- /dev/null
+++ b/grpc-common/python/helloworld/README.md
@@ -0,0 +1,116 @@
+# gRPC Python Hello World
+
+This is a quick introduction with a simple example and installation instructions: for a more complete tutorial see [gRPC Basics: Python](https://github.com/grpc/grpc-common/tree/master/python/route_guide).
+
+### Install gRPC
+Make sure you have built gRPC Python from source on your system. Follow the instructions here:
+[https://github.com/grpc/grpc/blob/master/src/python/README.md](https://github.com/grpc/grpc/blob/master/src/python/README.md).
+
+This gives you a python virtual environment with installed gRPC Python
+in GRPC_ROOT/python2.7_virtual_environment. GRPC_ROOT is the path to which you
+have cloned the [gRPC git repo](https://github.com/grpc/grpc).
+
+### Get the source code
+
+The example code for our Hello World and our other examples live in the `grpc-common`
+GitHub repository. Clone this repository to your local machine by running the
+following command:
+
+
+```sh
+$ git clone https://github.com/grpc/grpc-common.git
+```
+
+Change your current directory to grpc-common/python/helloworld
+
+```sh
+$ cd grpc-common/python/helloworld/
+```
+
+### Defining a service
+
+The first step in creating our example is to define a *service*: an RPC
+service specifies the methods that can be called remotely with their parameters
+and return types. As you saw in the
+[overview](#protocolbuffers) above, gRPC does this using [protocol
+buffers](https://developers.google.com/protocol-buffers/docs/overview). We
+use the protocol buffers interface definition language (IDL) to define our
+service methods, and define the parameters and return
+types as protocol buffer message types. Both the client and the
+server use interface code generated from the service definition.
+
+Here's our example service definition. The `Greeting`
+service has one method, `hello`, that lets the server receive a single
+`HelloRequest`
+message from the remote client containing the user's name, then send back
+a greeting in a single `HelloReply`. This is the simplest type of RPC you
+can specify in gRPC.
+
+```
+syntax = "proto3";
+
+option java_package = "io.grpc.examples";
+
+package helloworld;
+
+// The greeting service definition.
+service Greeter {
+ // Sends a greeting
+ rpc SayHello (HelloRequest) returns (HelloReply) {}
+}
+
+// The request message containing the user's name.
+message HelloRequest {
+ string name = 1;
+}
+
+// The response message containing the greetings
+message HelloReply {
+ string message = 1;
+}
+
+```
+
+<a name="generating"></a>
+### Generating gRPC code
+
+Once we've defined our service, we use the protocol buffer compiler
+`protoc` to generate the special client and server code we need to create
+our application. The generated code contains both stub code for clients to
+use and an abstract interface for servers to implement, both with the method
+defined in our `Greeting` service.
+
+To generate the client and server side interfaces:
+
+```sh
+$ ./run_codegen.sh
+```
+Which internally invokes the proto-compiler as:
+
+```sh
+$ protoc -I ../../protos --python_out=. --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_python_plugin` ../../protos/helloworld.proto
+```
+
+Optionally, you can just skip the code generation step as the generated python module has already
+been generated for you (helloworld_pb2.py).
+
+### The client
+
+Client-side code can be found in [greeter_client.py](https://github.com/grpc/grpc-common/blob/master/python/helloworld/greeter_client.py).
+
+You can run the client using:
+
+```sh
+$ ./run_client.sh
+```
+
+
+### The server
+
+Server side code can be found in [greeter_server.py](https://github.com/grpc/grpc-common/blob/master/python/helloworld/greeter_server.py).
+
+You can run the server using:
+
+```sh
+$ ./run_server.sh
+```
diff --git a/grpc-common/python/helloworld/greeter_client.py b/grpc-common/python/helloworld/greeter_client.py
new file mode 100755
index 0000000000..370ce46770
--- /dev/null
+++ b/grpc-common/python/helloworld/greeter_client.py
@@ -0,0 +1,44 @@
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""The Python implementation of the GRPC helloworld.Greeter client."""
+
+import helloworld_pb2
+
+_TIMEOUT_SECONDS = 10
+
+
+def run():
+ with helloworld_pb2.early_adopter_create_Greeter_stub('localhost', 50051) as stub:
+ response = stub.SayHello(helloworld_pb2.HelloRequest(name='you'), _TIMEOUT_SECONDS)
+ print "Greeter client received: " + response.message
+
+
+if __name__ == '__main__':
+ run()
diff --git a/grpc-common/python/helloworld/greeter_server.py b/grpc-common/python/helloworld/greeter_server.py
new file mode 100644
index 0000000000..81353666b1
--- /dev/null
+++ b/grpc-common/python/helloworld/greeter_server.py
@@ -0,0 +1,56 @@
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""The Python implementation of the GRPC helloworld.Greeter server."""
+
+import time
+
+import helloworld_pb2
+
+_ONE_DAY_IN_SECONDS = 60 * 60 * 24
+
+
+class Greeter(helloworld_pb2.EarlyAdopterGreeterServicer):
+
+ def SayHello(self, request, context):
+ return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name)
+
+
+def serve():
+ server = helloworld_pb2.early_adopter_create_Greeter_server(
+ Greeter(), 50051, None, None)
+ server.start()
+ try:
+ while True:
+ time.sleep(_ONE_DAY_IN_SECONDS)
+ except KeyboardInterrupt:
+ server.stop()
+
+if __name__ == '__main__':
+ serve()
diff --git a/grpc-common/python/helloworld/run_client.sh b/grpc-common/python/helloworld/run_client.sh
new file mode 100755
index 0000000000..095e6bc2f0
--- /dev/null
+++ b/grpc-common/python/helloworld/run_client.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+# This is where you have cloned out the https://github.com/grpc/grpc repository
+# And built gRPC Python.
+# ADJUST THIS PATH TO WHERE YOUR ACTUAL LOCATION IS
+GRPC_ROOT=~/github/grpc
+
+$GRPC_ROOT/python2.7_virtual_environment/bin/python greeter_client.py
diff --git a/grpc-common/python/helloworld/run_codegen.sh b/grpc-common/python/helloworld/run_codegen.sh
new file mode 100755
index 0000000000..4d826c7946
--- /dev/null
+++ b/grpc-common/python/helloworld/run_codegen.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+# Runs the protoc with gRPC plugin to generate protocol messages and gRPC stubs.
+protoc -I ../../protos --python_out=. --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_python_plugin` ../../protos/helloworld.proto
diff --git a/grpc-common/python/helloworld/run_server.sh b/grpc-common/python/helloworld/run_server.sh
new file mode 100755
index 0000000000..13b009e6cc
--- /dev/null
+++ b/grpc-common/python/helloworld/run_server.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+# This is where you have cloned out the https://github.com/grpc/grpc repository
+# And built gRPC Python.
+# ADJUST THIS PATH TO WHERE YOUR ACTUAL LOCATION IS
+GRPC_ROOT=~/github/grpc
+
+$GRPC_ROOT/python2.7_virtual_environment/bin/python greeter_server.py
+
diff --git a/grpc-common/python/route_guide/.gitignore b/grpc-common/python/route_guide/.gitignore
new file mode 100644
index 0000000000..0d20b6487c
--- /dev/null
+++ b/grpc-common/python/route_guide/.gitignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/grpc-common/python/route_guide/README.md b/grpc-common/python/route_guide/README.md
new file mode 100644
index 0000000000..91bef0ac00
--- /dev/null
+++ b/grpc-common/python/route_guide/README.md
@@ -0,0 +1,303 @@
+#gRPC Basics: Python
+
+This tutorial provides a basic Python programmer's introduction to working with gRPC. By walking through this example you'll learn how to:
+
+- Define a service in a .proto file.
+- Generate server and client code using the protocol buffer compiler.
+- Use the Python gRPC API to write a simple client and server for your service.
+
+It assumes that you have read the [Getting started](https://github.com/grpc/grpc-common) guide and are familiar with [protocol buffers] (https://developers.google.com/protocol-buffers/docs/overview). Note that the example in this tutorial uses the proto3 version of the protocol buffers language, which is currently in alpha release:you can find out more in the [proto3 language guide](https://developers.google.com/protocol-buffers/docs/proto3) and see the [release notes](https://github.com/google/protobuf/releases) for the new version in the protocol buffers Github repository.
+
+This isn't a comprehensive guide to using gRPC in Python: more reference documentation is coming soon.
+
+
+## Why use gRPC?
+
+This example is a simple route mapping application that lets clients get information about features on their route, create a summary of their route, and exchange route information such as traffic updates with the server and other clients.
+
+With gRPC you can define your service once in a .proto file and implement clients and servers in any of gRPC's supported languages, which in turn can be run in environments ranging from servers inside Google to your own tablet, with all the complexity of communication between different languages and environments is handled for you by gRPC. You also get all the advantages of working with protocol buffers, including efficient serialization, a simple IDL, and easy interface updating.
+
+## Example code and setup
+
+The example code for this tutorial is in [grpc/grpc-common/python/route_guide](https://github.com/grpc/grpc-common/tree/master/python/route_guide). To download the example, clone the `grpc-common` repository by running the following command:
+```shell
+$ git clone https://github.com/grpc/grpc-common.git
+```
+
+Then change your current directory to `grpc-common/python/route_guide`:
+```shell
+$ cd grpc-common/python/route_guide
+```
+
+You also should have the relevant tools installed to generate the server and client interface code - if you don't already, follow the setup instructions in [the Python quick start guide](https://github.com/grpc/grpc-common/tree/master/python).
+
+## Defining the service
+
+Your first step (as you'll know from [Getting started](https://github.com/grpc/grpc-common)) is to define the gRPC *service* and the method *request* and *response* types using [protocol buffers](https://developers.google.com/protocol-buffers/docs/overview). You can see the complete .proto file in [`grpc-common/protos/route_guide.proto`](https://github.com/grpc/grpc-common/blob/master/protos/route_guide.proto).
+
+To define a service, you specify a named `service` in your .proto file:
+
+```protobuf
+service RouteGuide {
+ // (Method definitions not shown)
+}
+```
+
+Then you define `rpc` methods inside your service definition, specifying their request and response types. gRPC lets you define four kinds of service method, all of which are used in the `RouteGuide` service:
+
+- A *simple RPC* where the client sends a request to the server using the stub and waits for a response to come back, just like a normal function call.
+```protobuf
+ // Obtains the feature at a given position.
+ rpc GetFeature(Point) returns (Feature) {}
+```
+
+- A *response-streaming RPC* where the client sends a request to the server and gets a stream to read a sequence of messages back. The client reads from the returned stream until there are no more messages. As you can see in the example, you specify a response-streaming method by placing the `stream` keyword before the *response* type.
+```protobuf
+ // Obtains the Features available within the given Rectangle. Results are
+ // streamed rather than returned at once (e.g. in a response message with a
+ // repeated field), as the rectangle may cover a large area and contain a
+ // huge number of features.
+ rpc ListFeatures(Rectangle) returns (stream Feature) {}
+```
+
+- A *request-streaming RPC* where the client writes a sequence of messages and sends them to the server, again using a provided stream. Once the client has finished writing the messages, it waits for the server to read them all and return its response. You specify a request-streaming method by placing the `stream` keyword before the *request* type.
+```protobuf
+ // Accepts a stream of Points on a route being traversed, returning a
+ // RouteSummary when traversal is completed.
+ rpc RecordRoute(stream Point) returns (RouteSummary) {}
+```
+
+- A *bidirectionally-streaming RPC* where both sides send a sequence of messages using a read-write stream. The two streams operate independently, so clients and servers can read and write in whatever order they like: for example, the server could wait to receive all the client messages before writing its responses, or it could alternately read a message then write a message, or some other combination of reads and writes. The order of messages in each stream is preserved. You specify this type of method by placing the `stream` keyword before both the request and the response.
+```protobuf
+ // Accepts a stream of RouteNotes sent while a route is being traversed,
+ // while receiving other RouteNotes (e.g. from other users).
+ rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
+```
+
+Your .proto file also contains protocol buffer message type definitions for all the request and response types used in our service methods - for example, here's the `Point` message type:
+```protobuf
+// Points are represented as latitude-longitude pairs in the E7 representation
+// (degrees multiplied by 10**7 and rounded to the nearest integer).
+// Latitudes should be in the range +/- 90 degrees and longitude should be in
+// the range +/- 180 degrees (inclusive).
+message Point {
+ int32 latitude = 1;
+ int32 longitude = 2;
+}
+```
+
+## Generating client and server code
+
+Next you need to generate the gRPC client and server interfaces from your .proto service definition. You do this using the protocol buffer compiler `protoc` with a special gRPC Python plugin. Make sure you've installed protoc and followed the gRPC Python plugin [installation instructions](https://github.com/grpc/grpc/blob/master/INSTALL) first):
+
+With `protoc` and the gRPC Python plugin installed, use the following command to generate the Python code:
+
+```shell
+$ protoc -I ../../protos --python_out=. --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_python_plugin` ../../protos/route_guide.proto
+```
+
+Note that as we've already provided a version of the generated code in the example repository, running this command regenerates the appropriate file rather than creates a new one. The generated code file is called `route_guide_pb2.py` and contains:
+- classes for the messages defined in route_guide.proto
+- abstract classes for the service defined in route_guide.proto
+ - `EarlyAdopterRouteGuideServicer`, which defines the interface for implementations of the RouteGuide service
+ - `EarlyAdopterRouteGuideServer`, which may be started and stopped
+ - `EarlyAdopterRouteGuideStub`, which can be used by clients to invoke RouteGuide RPCs
+- functions for application use
+ - `early_adopter_create_RouteGuide_server`, which creates a gRPC server given an `EarlyAdopterRouteGuideServicer` object
+ - `early_adopter_create_RouteGuide_stub`, which can be used by clients to create a stub object
+
+<a name="server"></a>
+## Creating the server
+
+First let's look at how you create a `RouteGuide` server. If you're only interested in creating gRPC clients, you can skip this section and go straight to [Creating the client](#client) (though you might find it interesting anyway!).
+
+Creating and running a `RouteGuide` server breaks down into two work items:
+- Implementing the servicer interface generated from our service definition with functions that perform the actual "work" of the service.
+- Running a gRPC server to listen for requests from clients and transmit responses.
+
+You can find the example `RouteGuide` server in [grpc-common/python/route_guide/route_guide_server.py](https://github.com/grpc/grpc-common/blob/master/python/route_guide/route_guide_server.py).
+
+### Implementing RouteGuide
+
+`route_guide_server.py` has a `RouteGuideServicer` class that implements the generated interface `route_guide_pb2.EarlyAdopterRouteGuideServicer`:
+
+```python
+# RouteGuideServicer provides an implementation of the methods of the RouteGuide service.
+class RouteGuideServicer(route_guide_pb2.EarlyAdopterRouteGuideServicer):
+```
+
+`RouteGuideServicer` implements all the `RouteGuide` service methods.
+
+#### Simple RPC
+
+Let's look at the simplest type first, `GetFeature`, which just gets a `Point` from the client and returns the corresponding feature information from its database in a `Feature`.
+
+```python
+ def GetFeature(self, request, context):
+ feature = get_feature(self.db, request)
+ if feature is None:
+ return route_guide_pb2.Feature(name="", location=request)
+ else:
+ return feature
+```
+
+The method is passed a `route_guide_pb2.Point` request for the RPC, and an `RpcContext` object that provides RPC-specific information such as timeout limits. It returns a `route_guide_pb2.Feature` response.
+
+#### Response-streaming RPC
+
+Now let's look at the next method. `ListFeatures` is a response-streaming RPC that sends multiple `Feature`s to the client.
+
+```python
+ def ListFeatures(self, request, context):
+ left = min(request.lo.longitude, request.hi.longitude)
+ right = max(request.lo.longitude, request.hi.longitude)
+ top = max(request.lo.latitude, request.hi.latitude)
+ bottom = min(request.lo.latitude, request.hi.latitude)
+ for feature in self.db:
+ if (feature.location.longitude >= left and
+ feature.location.longitude <= right and
+ feature.location.latitude >= bottom and
+ feature.location.latitude <= top):
+ yield feature
+```
+
+Here the request message is a `route_guide_pb2.Rectangle` within which the client wants to find `Feature`s. Instead of returning a single response the method yields zero or more responses.
+
+#### Request-streaming RPC
+
+The request-streaming method `RecordRoute` uses an [iterator](https://docs.python.org/2/library/stdtypes.html#iterator-types) of request values and returns a single response value.
+
+```python
+ def RecordRoute(self, request_iterator, context):
+ point_count = 0
+ feature_count = 0
+ distance = 0.0
+ prev_point = None
+
+ start_time = time.time()
+ for point in request_iterator:
+ point_count += 1
+ if get_feature(self.db, point):
+ feature_count += 1
+ if prev_point:
+ distance += get_distance(prev_point, point)
+ prev_point = point
+
+ elapsed_time = time.time() - start_time
+ return route_guide_pb2.RouteSummary(point_count=point_count,
+ feature_count=feature_count,
+ distance=int(distance),
+ elapsed_time=int(elapsed_time))
+```
+
+#### Bidirectional streaming RPC
+
+Lastly let's look at the bidirectionally-streaming method `RouteChat`.
+
+```python
+ def RouteChat(self, request_iterator, context):
+ prev_notes = []
+ for new_note in request_iterator:
+ for prev_note in prev_notes:
+ if prev_note.location == new_note.location:
+ yield prev_note
+ prev_notes.append(new_note)
+```
+
+This method's semantics are a combination of those of the request-streaming method and the response-streaming method. It is passed an iterator of request values and is itself an iterator of response values.
+
+### Starting the server
+
+Once you have implemented all the `RouteGuide` methods, the next step is to start up a gRPC server so that clients can actually use your service:
+
+```python
+def serve():
+ server = route_guide_pb2.early_adopter_create_RouteGuide_server(
+ RouteGuideServicer(), 50051, None, None)
+ server.start()
+```
+
+Because `start()` does not block you may need to sleep-loop if there is nothing else for your code to do while serving.
+
+<a name="client"></a>
+## Creating the client
+
+You can see the complete example client code in [grpc-common/python/route_guide/route_guide_client.py](https://github.com/grpc/grpc-common/blob/master/python/route_guide/route_guide_client.py).
+
+### Creating a stub
+
+To call service methods, we first need to create a *stub*.
+
+We use the `early_adopter_create_RouteGuide_stub` function of the `route_guide_pb2` module, generated from our .proto.
+
+```python
+stub = RouteGuide::Stub.new('localhost', 50051)
+```
+
+The returned object implements all the methods defined by the `EarlyAdopterRouteGuideStub` interface, and is also a [context manager](https://docs.python.org/2/library/stdtypes.html#typecontextmanager). All RPCs invoked on the stub must be invoked within the stub's context, so it is common for stubs to be created and used with a [with statement](https://docs.python.org/2/reference/compound_stmts.html#the-with-statement):
+
+```python
+with route_guide_pb2.early_adopter_create_RouteGuide_stub('localhost', 50051) as stub:
+```
+
+### Calling service methods
+
+For RPC methods that return a single response ("response-unary" methods), gRPC Python supports both synchronous (blocking) and asynchronous (non-blocking) control flow semantics. For response-streaming RPC methods, calls immediately return an iterator of response values. Calls to that iterator's `next()` method block until the response to be yielded from the iterator becomes available.
+
+#### Simple RPC
+
+A synchronous call to the simple RPC `GetFeature` is nearly as straightforward as calling a local method. The RPC call waits for the server to respond, and will either return a response or raise an exception:
+
+```python
+feature = stub.GetFeature(point, timeout_in_seconds)
+```
+
+An asynchronous call to `GetFeature` is similar, but like calling a local method asynchronously in a thread pool:
+
+```python
+feature_future = stub.GetFeature.async(point, timeout_in_seconds)
+feature = feature_future.result()
+```
+
+#### Response-streaming RPC
+
+Calling the response-streaming `ListFeatures` is similar to working with sequence types:
+
+```python
+for feature in stub.ListFeatures(rectangle, timeout_in_seconds):
+```
+
+#### Request-streaming RPC
+
+Calling the request-streaming `RecordRoute` is similar to passing a sequence to a local method. Like the simple RPC above that also returns a single response, it can be called synchronously or asynchronously:
+
+```python
+route_summary = stub.RecordRoute(point_sequence, timeout_in_seconds)
+```
+
+```python
+route_summary_future = stub.RecordRoute.async(point_sequence, timeout_in_seconds)
+route_summary = route_summary_future.result()
+```
+
+#### Bidirectional streaming RPC
+
+Calling the bidirectionally-streaming `RouteChat` has (as is the case on the service-side) a combination of the request-streaming and response-streaming semantics:
+
+```python
+for received_route_note in stub.RouteChat(sent_routes, timeout_in_seconds):
+```
+
+## Try it out!
+
+Run the server, which will listen on port 50051:
+
+```shell
+$ python route_guide_server.py
+```
+
+Run the client (in a different terminal):
+
+```shell
+$ python route_guide_client.py
+```
diff --git a/grpc-common/python/route_guide/route_guide_client.py b/grpc-common/python/route_guide/route_guide_client.py
new file mode 100755
index 0000000000..078231543e
--- /dev/null
+++ b/grpc-common/python/route_guide/route_guide_client.py
@@ -0,0 +1,130 @@
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""The Python implementation of the gRPC route guide client."""
+
+import random
+import time
+
+import route_guide_pb2
+import route_guide_resources
+
+_TIMEOUT_SECONDS = 30
+
+
+def make_route_note(message, latitude, longitude):
+ return route_guide_pb2.RouteNote(
+ message=message,
+ location=route_guide_pb2.Point(latitude=latitude, longitude=longitude))
+
+
+def guide_get_one_feature(stub, point):
+ feature = stub.GetFeature(point, _TIMEOUT_SECONDS)
+ if not feature.location:
+ print "Server returned incomplete feature"
+ return
+
+ if feature.name:
+ print "Feature called %s at %s" % (feature.name, feature.location)
+ else:
+ print "Found no feature at %s" % feature.location
+
+
+def guide_get_feature(stub):
+ guide_get_one_feature(stub, route_guide_pb2.Point(latitude=409146138, longitude=-746188906))
+ guide_get_one_feature(stub, route_guide_pb2.Point(latitude=0, longitude=0))
+
+
+def guide_list_features(stub):
+ rect = route_guide_pb2.Rectangle(
+ lo=route_guide_pb2.Point(
+ latitude=400000000, longitude = -750000000),
+ hi=route_guide_pb2.Point(
+ latitude = 420000000, longitude = -730000000))
+ print "Looking for features between 40, -75 and 42, -73"
+
+ features = stub.ListFeatures(rect, _TIMEOUT_SECONDS)
+
+ for feature in features:
+ print "Feature called %s at %s" % (feature.name, feature.location)
+
+
+def generate_route(feature_list):
+ for _ in range(0, 10):
+ random_feature = feature_list[random.randint(0, len(feature_list) - 1)]
+ print "Visiting point %s" % random_feature.location
+ yield random_feature.location
+ time.sleep(random.uniform(0.5, 1.5))
+
+
+def guide_record_route(stub):
+ feature_list = route_guide_resources.read_route_guide_database()
+
+ route_iter = generate_route(feature_list)
+ route_summary = stub.RecordRoute(route_iter, _TIMEOUT_SECONDS)
+ print "Finished trip with %s points " % route_summary.point_count
+ print "Passed %s features " % route_summary.feature_count
+ print "Travelled %s meters " % route_summary.distance
+ print "It took %s seconds " % route_summary.elapsed_time
+
+
+def generate_messages():
+ messages = [
+ make_route_note("First message", 0, 0),
+ make_route_note("Second message", 0, 1),
+ make_route_note("Third message", 1, 0),
+ make_route_note("Fourth message", 0, 0),
+ make_route_note("Fifth message", 1, 0),
+ ]
+ for msg in messages:
+ print "Sending %s at %s" % (msg.message, msg.location)
+ yield msg
+ time.sleep(random.uniform(0.5, 1.0))
+
+
+def guide_route_chat(stub):
+ responses = stub.RouteChat(generate_messages(), _TIMEOUT_SECONDS)
+ for response in responses:
+ print "Received message %s at %s" % (response.message, response.location)
+
+
+def run():
+ with route_guide_pb2.early_adopter_create_RouteGuide_stub('localhost', 50051) as stub:
+ print "-------------- GetFeature --------------"
+ guide_get_feature(stub)
+ print "-------------- ListFeatures --------------"
+ guide_list_features(stub)
+ print "-------------- RecordRoute --------------"
+ guide_record_route(stub)
+ print "-------------- RouteChat --------------"
+ guide_route_chat(stub)
+
+
+if __name__ == '__main__':
+ run()
diff --git a/grpc-common/python/route_guide/route_guide_db.json b/grpc-common/python/route_guide/route_guide_db.json
new file mode 100644
index 0000000000..9d6a980ab7
--- /dev/null
+++ b/grpc-common/python/route_guide/route_guide_db.json
@@ -0,0 +1,601 @@
+[{
+ "location": {
+ "latitude": 407838351,
+ "longitude": -746143763
+ },
+ "name": "Patriots Path, Mendham, NJ 07945, USA"
+}, {
+ "location": {
+ "latitude": 408122808,
+ "longitude": -743999179
+ },
+ "name": "101 New Jersey 10, Whippany, NJ 07981, USA"
+}, {
+ "location": {
+ "latitude": 413628156,
+ "longitude": -749015468
+ },
+ "name": "U.S. 6, Shohola, PA 18458, USA"
+}, {
+ "location": {
+ "latitude": 419999544,
+ "longitude": -740371136
+ },
+ "name": "5 Conners Road, Kingston, NY 12401, USA"
+}, {
+ "location": {
+ "latitude": 414008389,
+ "longitude": -743951297
+ },
+ "name": "Mid Hudson Psychiatric Center, New Hampton, NY 10958, USA"
+}, {
+ "location": {
+ "latitude": 419611318,
+ "longitude": -746524769
+ },
+ "name": "287 Flugertown Road, Livingston Manor, NY 12758, USA"
+}, {
+ "location": {
+ "latitude": 406109563,
+ "longitude": -742186778
+ },
+ "name": "4001 Tremley Point Road, Linden, NJ 07036, USA"
+}, {
+ "location": {
+ "latitude": 416802456,
+ "longitude": -742370183
+ },
+ "name": "352 South Mountain Road, Wallkill, NY 12589, USA"
+}, {
+ "location": {
+ "latitude": 412950425,
+ "longitude": -741077389
+ },
+ "name": "Bailey Turn Road, Harriman, NY 10926, USA"
+}, {
+ "location": {
+ "latitude": 412144655,
+ "longitude": -743949739
+ },
+ "name": "193-199 Wawayanda Road, Hewitt, NJ 07421, USA"
+}, {
+ "location": {
+ "latitude": 415736605,
+ "longitude": -742847522
+ },
+ "name": "406-496 Ward Avenue, Pine Bush, NY 12566, USA"
+}, {
+ "location": {
+ "latitude": 413843930,
+ "longitude": -740501726
+ },
+ "name": "162 Merrill Road, Highland Mills, NY 10930, USA"
+}, {
+ "location": {
+ "latitude": 410873075,
+ "longitude": -744459023
+ },
+ "name": "Clinton Road, West Milford, NJ 07480, USA"
+}, {
+ "location": {
+ "latitude": 412346009,
+ "longitude": -744026814
+ },
+ "name": "16 Old Brook Lane, Warwick, NY 10990, USA"
+}, {
+ "location": {
+ "latitude": 402948455,
+ "longitude": -747903913
+ },
+ "name": "3 Drake Lane, Pennington, NJ 08534, USA"
+}, {
+ "location": {
+ "latitude": 406337092,
+ "longitude": -740122226
+ },
+ "name": "6324 8th Avenue, Brooklyn, NY 11220, USA"
+}, {
+ "location": {
+ "latitude": 406421967,
+ "longitude": -747727624
+ },
+ "name": "1 Merck Access Road, Whitehouse Station, NJ 08889, USA"
+}, {
+ "location": {
+ "latitude": 416318082,
+ "longitude": -749677716
+ },
+ "name": "78-98 Schalck Road, Narrowsburg, NY 12764, USA"
+}, {
+ "location": {
+ "latitude": 415301720,
+ "longitude": -748416257
+ },
+ "name": "282 Lakeview Drive Road, Highland Lake, NY 12743, USA"
+}, {
+ "location": {
+ "latitude": 402647019,
+ "longitude": -747071791
+ },
+ "name": "330 Evelyn Avenue, Hamilton Township, NJ 08619, USA"
+}, {
+ "location": {
+ "latitude": 412567807,
+ "longitude": -741058078
+ },
+ "name": "New York State Reference Route 987E, Southfields, NY 10975, USA"
+}, {
+ "location": {
+ "latitude": 416855156,
+ "longitude": -744420597
+ },
+ "name": "103-271 Tempaloni Road, Ellenville, NY 12428, USA"
+}, {
+ "location": {
+ "latitude": 404663628,
+ "longitude": -744820157
+ },
+ "name": "1300 Airport Road, North Brunswick Township, NJ 08902, USA"
+}, {
+ "location": {
+ "latitude": 407113723,
+ "longitude": -749746483
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 402133926,
+ "longitude": -743613249
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 400273442,
+ "longitude": -741220915
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 411236786,
+ "longitude": -744070769
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 411633782,
+ "longitude": -746784970
+ },
+ "name": "211-225 Plains Road, Augusta, NJ 07822, USA"
+}, {
+ "location": {
+ "latitude": 415830701,
+ "longitude": -742952812
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 413447164,
+ "longitude": -748712898
+ },
+ "name": "165 Pedersen Ridge Road, Milford, PA 18337, USA"
+}, {
+ "location": {
+ "latitude": 405047245,
+ "longitude": -749800722
+ },
+ "name": "100-122 Locktown Road, Frenchtown, NJ 08825, USA"
+}, {
+ "location": {
+ "latitude": 418858923,
+ "longitude": -746156790
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 417951888,
+ "longitude": -748484944
+ },
+ "name": "650-652 Willi Hill Road, Swan Lake, NY 12783, USA"
+}, {
+ "location": {
+ "latitude": 407033786,
+ "longitude": -743977337
+ },
+ "name": "26 East 3rd Street, New Providence, NJ 07974, USA"
+}, {
+ "location": {
+ "latitude": 417548014,
+ "longitude": -740075041
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 410395868,
+ "longitude": -744972325
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 404615353,
+ "longitude": -745129803
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 406589790,
+ "longitude": -743560121
+ },
+ "name": "611 Lawrence Avenue, Westfield, NJ 07090, USA"
+}, {
+ "location": {
+ "latitude": 414653148,
+ "longitude": -740477477
+ },
+ "name": "18 Lannis Avenue, New Windsor, NY 12553, USA"
+}, {
+ "location": {
+ "latitude": 405957808,
+ "longitude": -743255336
+ },
+ "name": "82-104 Amherst Avenue, Colonia, NJ 07067, USA"
+}, {
+ "location": {
+ "latitude": 411733589,
+ "longitude": -741648093
+ },
+ "name": "170 Seven Lakes Drive, Sloatsburg, NY 10974, USA"
+}, {
+ "location": {
+ "latitude": 412676291,
+ "longitude": -742606606
+ },
+ "name": "1270 Lakes Road, Monroe, NY 10950, USA"
+}, {
+ "location": {
+ "latitude": 409224445,
+ "longitude": -748286738
+ },
+ "name": "509-535 Alphano Road, Great Meadows, NJ 07838, USA"
+}, {
+ "location": {
+ "latitude": 406523420,
+ "longitude": -742135517
+ },
+ "name": "652 Garden Street, Elizabeth, NJ 07202, USA"
+}, {
+ "location": {
+ "latitude": 401827388,
+ "longitude": -740294537
+ },
+ "name": "349 Sea Spray Court, Neptune City, NJ 07753, USA"
+}, {
+ "location": {
+ "latitude": 410564152,
+ "longitude": -743685054
+ },
+ "name": "13-17 Stanley Street, West Milford, NJ 07480, USA"
+}, {
+ "location": {
+ "latitude": 408472324,
+ "longitude": -740726046
+ },
+ "name": "47 Industrial Avenue, Teterboro, NJ 07608, USA"
+}, {
+ "location": {
+ "latitude": 412452168,
+ "longitude": -740214052
+ },
+ "name": "5 White Oak Lane, Stony Point, NY 10980, USA"
+}, {
+ "location": {
+ "latitude": 409146138,
+ "longitude": -746188906
+ },
+ "name": "Berkshire Valley Management Area Trail, Jefferson, NJ, USA"
+}, {
+ "location": {
+ "latitude": 404701380,
+ "longitude": -744781745
+ },
+ "name": "1007 Jersey Avenue, New Brunswick, NJ 08901, USA"
+}, {
+ "location": {
+ "latitude": 409642566,
+ "longitude": -746017679
+ },
+ "name": "6 East Emerald Isle Drive, Lake Hopatcong, NJ 07849, USA"
+}, {
+ "location": {
+ "latitude": 408031728,
+ "longitude": -748645385
+ },
+ "name": "1358-1474 New Jersey 57, Port Murray, NJ 07865, USA"
+}, {
+ "location": {
+ "latitude": 413700272,
+ "longitude": -742135189
+ },
+ "name": "367 Prospect Road, Chester, NY 10918, USA"
+}, {
+ "location": {
+ "latitude": 404310607,
+ "longitude": -740282632
+ },
+ "name": "10 Simon Lake Drive, Atlantic Highlands, NJ 07716, USA"
+}, {
+ "location": {
+ "latitude": 409319800,
+ "longitude": -746201391
+ },
+ "name": "11 Ward Street, Mount Arlington, NJ 07856, USA"
+}, {
+ "location": {
+ "latitude": 406685311,
+ "longitude": -742108603
+ },
+ "name": "300-398 Jefferson Avenue, Elizabeth, NJ 07201, USA"
+}, {
+ "location": {
+ "latitude": 419018117,
+ "longitude": -749142781
+ },
+ "name": "43 Dreher Road, Roscoe, NY 12776, USA"
+}, {
+ "location": {
+ "latitude": 412856162,
+ "longitude": -745148837
+ },
+ "name": "Swan Street, Pine Island, NY 10969, USA"
+}, {
+ "location": {
+ "latitude": 416560744,
+ "longitude": -746721964
+ },
+ "name": "66 Pleasantview Avenue, Monticello, NY 12701, USA"
+}, {
+ "location": {
+ "latitude": 405314270,
+ "longitude": -749836354
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 414219548,
+ "longitude": -743327440
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 415534177,
+ "longitude": -742900616
+ },
+ "name": "565 Winding Hills Road, Montgomery, NY 12549, USA"
+}, {
+ "location": {
+ "latitude": 406898530,
+ "longitude": -749127080
+ },
+ "name": "231 Rocky Run Road, Glen Gardner, NJ 08826, USA"
+}, {
+ "location": {
+ "latitude": 407586880,
+ "longitude": -741670168
+ },
+ "name": "100 Mount Pleasant Avenue, Newark, NJ 07104, USA"
+}, {
+ "location": {
+ "latitude": 400106455,
+ "longitude": -742870190
+ },
+ "name": "517-521 Huntington Drive, Manchester Township, NJ 08759, USA"
+}, {
+ "location": {
+ "latitude": 400066188,
+ "longitude": -746793294
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 418803880,
+ "longitude": -744102673
+ },
+ "name": "40 Mountain Road, Napanoch, NY 12458, USA"
+}, {
+ "location": {
+ "latitude": 414204288,
+ "longitude": -747895140
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 414777405,
+ "longitude": -740615601
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 415464475,
+ "longitude": -747175374
+ },
+ "name": "48 North Road, Forestburgh, NY 12777, USA"
+}, {
+ "location": {
+ "latitude": 404062378,
+ "longitude": -746376177
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 405688272,
+ "longitude": -749285130
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 400342070,
+ "longitude": -748788996
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 401809022,
+ "longitude": -744157964
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 404226644,
+ "longitude": -740517141
+ },
+ "name": "9 Thompson Avenue, Leonardo, NJ 07737, USA"
+}, {
+ "location": {
+ "latitude": 410322033,
+ "longitude": -747871659
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 407100674,
+ "longitude": -747742727
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 418811433,
+ "longitude": -741718005
+ },
+ "name": "213 Bush Road, Stone Ridge, NY 12484, USA"
+}, {
+ "location": {
+ "latitude": 415034302,
+ "longitude": -743850945
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 411349992,
+ "longitude": -743694161
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 404839914,
+ "longitude": -744759616
+ },
+ "name": "1-17 Bergen Court, New Brunswick, NJ 08901, USA"
+}, {
+ "location": {
+ "latitude": 414638017,
+ "longitude": -745957854
+ },
+ "name": "35 Oakland Valley Road, Cuddebackville, NY 12729, USA"
+}, {
+ "location": {
+ "latitude": 412127800,
+ "longitude": -740173578
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 401263460,
+ "longitude": -747964303
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 412843391,
+ "longitude": -749086026
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 418512773,
+ "longitude": -743067823
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 404318328,
+ "longitude": -740835638
+ },
+ "name": "42-102 Main Street, Belford, NJ 07718, USA"
+}, {
+ "location": {
+ "latitude": 419020746,
+ "longitude": -741172328
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 404080723,
+ "longitude": -746119569
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 401012643,
+ "longitude": -744035134
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 404306372,
+ "longitude": -741079661
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 403966326,
+ "longitude": -748519297
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 405002031,
+ "longitude": -748407866
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 409532885,
+ "longitude": -742200683
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 416851321,
+ "longitude": -742674555
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 406411633,
+ "longitude": -741722051
+ },
+ "name": "3387 Richmond Terrace, Staten Island, NY 10303, USA"
+}, {
+ "location": {
+ "latitude": 413069058,
+ "longitude": -744597778
+ },
+ "name": "261 Van Sickle Road, Goshen, NY 10924, USA"
+}, {
+ "location": {
+ "latitude": 418465462,
+ "longitude": -746859398
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 411733222,
+ "longitude": -744228360
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 410248224,
+ "longitude": -747127767
+ },
+ "name": "3 Hasta Way, Newton, NJ 07860, USA"
+}]
diff --git a/grpc-common/python/route_guide/route_guide_pb2.py b/grpc-common/python/route_guide/route_guide_pb2.py
new file mode 100644
index 0000000000..2a4532bb75
--- /dev/null
+++ b/grpc-common/python/route_guide/route_guide_pb2.py
@@ -0,0 +1,370 @@
+# Generated by the protocol buffer compiler. DO NOT EDIT!
+# source: route_guide.proto
+
+import sys
+_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import message as _message
+from google.protobuf import reflection as _reflection
+from google.protobuf import symbol_database as _symbol_database
+from google.protobuf import descriptor_pb2
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+
+
+DESCRIPTOR = _descriptor.FileDescriptor(
+ name='route_guide.proto',
+ package='',
+ serialized_pb=_b('\n\x11route_guide.proto\",\n\x05Point\x12\x10\n\x08latitude\x18\x01 \x01(\x05\x12\x11\n\tlongitude\x18\x02 \x01(\x05\"3\n\tRectangle\x12\x12\n\x02lo\x18\x01 \x01(\x0b\x32\x06.Point\x12\x12\n\x02hi\x18\x02 \x01(\x0b\x32\x06.Point\"1\n\x07\x46\x65\x61ture\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x18\n\x08location\x18\x02 \x01(\x0b\x32\x06.Point\"6\n\tRouteNote\x12\x18\n\x08location\x18\x01 \x01(\x0b\x32\x06.Point\x12\x0f\n\x07message\x18\x02 \x01(\t\"b\n\x0cRouteSummary\x12\x13\n\x0bpoint_count\x18\x01 \x01(\x05\x12\x15\n\rfeature_count\x18\x02 \x01(\x05\x12\x10\n\x08\x64istance\x18\x03 \x01(\x05\x12\x14\n\x0c\x65lapsed_time\x18\x04 \x01(\x05\x32\xad\x01\n\nRouteGuide\x12 \n\nGetFeature\x12\x06.Point\x1a\x08.Feature\"\x00\x12(\n\x0cListFeatures\x12\n.Rectangle\x1a\x08.Feature\"\x00\x30\x01\x12(\n\x0bRecordRoute\x12\x06.Point\x1a\r.RouteSummary\"\x00(\x01\x12)\n\tRouteChat\x12\n.RouteNote\x1a\n.RouteNote\"\x00(\x01\x30\x01')
+)
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
+
+
+
+
+_POINT = _descriptor.Descriptor(
+ name='Point',
+ full_name='Point',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='latitude', full_name='Point.latitude', index=0,
+ number=1, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='longitude', full_name='Point.longitude', index=1,
+ number=2, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=21,
+ serialized_end=65,
+)
+
+
+_RECTANGLE = _descriptor.Descriptor(
+ name='Rectangle',
+ full_name='Rectangle',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='lo', full_name='Rectangle.lo', index=0,
+ number=1, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='hi', full_name='Rectangle.hi', index=1,
+ number=2, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=67,
+ serialized_end=118,
+)
+
+
+_FEATURE = _descriptor.Descriptor(
+ name='Feature',
+ full_name='Feature',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='name', full_name='Feature.name', index=0,
+ number=1, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='location', full_name='Feature.location', index=1,
+ number=2, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=120,
+ serialized_end=169,
+)
+
+
+_ROUTENOTE = _descriptor.Descriptor(
+ name='RouteNote',
+ full_name='RouteNote',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='location', full_name='RouteNote.location', index=0,
+ number=1, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='message', full_name='RouteNote.message', index=1,
+ number=2, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=171,
+ serialized_end=225,
+)
+
+
+_ROUTESUMMARY = _descriptor.Descriptor(
+ name='RouteSummary',
+ full_name='RouteSummary',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='point_count', full_name='RouteSummary.point_count', index=0,
+ number=1, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='feature_count', full_name='RouteSummary.feature_count', index=1,
+ number=2, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='distance', full_name='RouteSummary.distance', index=2,
+ number=3, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='elapsed_time', full_name='RouteSummary.elapsed_time', index=3,
+ number=4, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=227,
+ serialized_end=325,
+)
+
+_RECTANGLE.fields_by_name['lo'].message_type = _POINT
+_RECTANGLE.fields_by_name['hi'].message_type = _POINT
+_FEATURE.fields_by_name['location'].message_type = _POINT
+_ROUTENOTE.fields_by_name['location'].message_type = _POINT
+DESCRIPTOR.message_types_by_name['Point'] = _POINT
+DESCRIPTOR.message_types_by_name['Rectangle'] = _RECTANGLE
+DESCRIPTOR.message_types_by_name['Feature'] = _FEATURE
+DESCRIPTOR.message_types_by_name['RouteNote'] = _ROUTENOTE
+DESCRIPTOR.message_types_by_name['RouteSummary'] = _ROUTESUMMARY
+
+Point = _reflection.GeneratedProtocolMessageType('Point', (_message.Message,), dict(
+ DESCRIPTOR = _POINT,
+ __module__ = 'route_guide_pb2'
+ # @@protoc_insertion_point(class_scope:Point)
+ ))
+_sym_db.RegisterMessage(Point)
+
+Rectangle = _reflection.GeneratedProtocolMessageType('Rectangle', (_message.Message,), dict(
+ DESCRIPTOR = _RECTANGLE,
+ __module__ = 'route_guide_pb2'
+ # @@protoc_insertion_point(class_scope:Rectangle)
+ ))
+_sym_db.RegisterMessage(Rectangle)
+
+Feature = _reflection.GeneratedProtocolMessageType('Feature', (_message.Message,), dict(
+ DESCRIPTOR = _FEATURE,
+ __module__ = 'route_guide_pb2'
+ # @@protoc_insertion_point(class_scope:Feature)
+ ))
+_sym_db.RegisterMessage(Feature)
+
+RouteNote = _reflection.GeneratedProtocolMessageType('RouteNote', (_message.Message,), dict(
+ DESCRIPTOR = _ROUTENOTE,
+ __module__ = 'route_guide_pb2'
+ # @@protoc_insertion_point(class_scope:RouteNote)
+ ))
+_sym_db.RegisterMessage(RouteNote)
+
+RouteSummary = _reflection.GeneratedProtocolMessageType('RouteSummary', (_message.Message,), dict(
+ DESCRIPTOR = _ROUTESUMMARY,
+ __module__ = 'route_guide_pb2'
+ # @@protoc_insertion_point(class_scope:RouteSummary)
+ ))
+_sym_db.RegisterMessage(RouteSummary)
+
+
+import abc
+from grpc._adapter import fore
+from grpc._adapter import rear
+from grpc.framework.assembly import implementations
+from grpc.framework.assembly import utilities
+class EarlyAdopterRouteGuideServicer(object):
+ """<fill me in later!>"""
+ __metaclass__ = abc.ABCMeta
+ @abc.abstractmethod
+ def GetFeature(self, request):
+ raise NotImplementedError()
+ @abc.abstractmethod
+ def ListFeatures(self, request):
+ raise NotImplementedError()
+ @abc.abstractmethod
+ def RecordRoute(self, request_iterator):
+ raise NotImplementedError()
+ @abc.abstractmethod
+ def RouteChat(self, request_iterator):
+ raise NotImplementedError()
+class EarlyAdopterRouteGuideServer(object):
+ """<fill me in later!>"""
+ __metaclass__ = abc.ABCMeta
+ @abc.abstractmethod
+ def start(self):
+ raise NotImplementedError()
+ @abc.abstractmethod
+ def stop(self):
+ raise NotImplementedError()
+class EarlyAdopterRouteGuideStub(object):
+ """<fill me in later!>"""
+ __metaclass__ = abc.ABCMeta
+ @abc.abstractmethod
+ def GetFeature(self, request):
+ raise NotImplementedError()
+ GetFeature.async = None
+ @abc.abstractmethod
+ def ListFeatures(self, request):
+ raise NotImplementedError()
+ ListFeatures.async = None
+ @abc.abstractmethod
+ def RecordRoute(self, request_iterator):
+ raise NotImplementedError()
+ RecordRoute.async = None
+ @abc.abstractmethod
+ def RouteChat(self, request_iterator):
+ raise NotImplementedError()
+ RouteChat.async = None
+def early_adopter_create_RouteGuide_server(servicer, port, root_certificates, key_chain_pairs):
+ method_implementations = {
+ "GetFeature": utilities.unary_unary_inline(servicer.GetFeature),
+ "ListFeatures": utilities.unary_stream_inline(servicer.ListFeatures),
+ "RecordRoute": utilities.stream_unary_inline(servicer.RecordRoute),
+ "RouteChat": utilities.stream_stream_inline(servicer.RouteChat),
+ }
+ import route_guide_pb2
+ import route_guide_pb2
+ import route_guide_pb2
+ import route_guide_pb2
+ request_deserializers = {
+ "GetFeature": route_guide_pb2.Point.FromString,
+ "ListFeatures": route_guide_pb2.Rectangle.FromString,
+ "RecordRoute": route_guide_pb2.Point.FromString,
+ "RouteChat": route_guide_pb2.RouteNote.FromString,
+ }
+ response_serializers = {
+ "GetFeature": lambda x: x.SerializeToString(),
+ "ListFeatures": lambda x: x.SerializeToString(),
+ "RecordRoute": lambda x: x.SerializeToString(),
+ "RouteChat": lambda x: x.SerializeToString(),
+ }
+ link = fore.activated_fore_link(port, request_deserializers, response_serializers, root_certificates, key_chain_pairs)
+ return implementations.assemble_service(method_implementations, link)
+def early_adopter_create_RouteGuide_stub(host, port):
+ method_implementations = {
+ "GetFeature": utilities.unary_unary_inline(None),
+ "ListFeatures": utilities.unary_stream_inline(None),
+ "RecordRoute": utilities.stream_unary_inline(None),
+ "RouteChat": utilities.stream_stream_inline(None),
+ }
+ import route_guide_pb2
+ import route_guide_pb2
+ import route_guide_pb2
+ import route_guide_pb2
+ response_deserializers = {
+ "GetFeature": route_guide_pb2.Feature.FromString,
+ "ListFeatures": route_guide_pb2.Feature.FromString,
+ "RecordRoute": route_guide_pb2.RouteSummary.FromString,
+ "RouteChat": route_guide_pb2.RouteNote.FromString,
+ }
+ request_serializers = {
+ "GetFeature": lambda x: x.SerializeToString(),
+ "ListFeatures": lambda x: x.SerializeToString(),
+ "RecordRoute": lambda x: x.SerializeToString(),
+ "RouteChat": lambda x: x.SerializeToString(),
+ }
+ link = rear.activated_rear_link(host, port, request_serializers, response_deserializers)
+ return implementations.assemble_dynamic_inline_stub(method_implementations, link)
+# @@protoc_insertion_point(module_scope)
diff --git a/grpc-common/python/route_guide/route_guide_resources.py b/grpc-common/python/route_guide/route_guide_resources.py
new file mode 100755
index 0000000000..30c7711019
--- /dev/null
+++ b/grpc-common/python/route_guide/route_guide_resources.py
@@ -0,0 +1,53 @@
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Common resources used in the gRPC route guide example."""
+
+import json
+
+import route_guide_pb2
+
+
+def read_route_guide_database():
+ """Reads the route guide database.
+
+ Returns:
+ The full contents of the route guide database as a sequence of
+ route_guide_pb2.Features.
+ """
+ feature_list = []
+ with open("route_guide_db.json") as route_guide_db_file:
+ for item in json.load(route_guide_db_file):
+ feature = route_guide_pb2.Feature(
+ name=item["name"],
+ location=route_guide_pb2.Point(
+ latitude=item["location"]["latitude"],
+ longitude=item["location"]["longitude"]))
+ feature_list.append(feature)
+ return feature_list
diff --git a/grpc-common/python/route_guide/route_guide_server.py b/grpc-common/python/route_guide/route_guide_server.py
new file mode 100644
index 0000000000..44bbacf5f3
--- /dev/null
+++ b/grpc-common/python/route_guide/route_guide_server.py
@@ -0,0 +1,134 @@
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""The Python implementation of the gRPC route guide server."""
+
+import time
+import math
+
+import route_guide_pb2
+import route_guide_resources
+
+_ONE_DAY_IN_SECONDS = 60 * 60 * 24
+
+
+def get_feature(feature_db, point):
+ """Returns Feature at given location or None."""
+ for feature in feature_db:
+ if feature.location == point:
+ return feature
+ return None
+
+
+def get_distance(start, end):
+ """Distance between two points."""
+ coord_factor = 10000000.0
+ lat_1 = start.latitude / coord_factor
+ lat_2 = end.latitude / coord_factor
+ lon_1 = start.latitude / coord_factor
+ lon_2 = end.longitude / coord_factor
+ lat_rad_1 = math.radians(lat_1)
+ lat_rad_2 = math.radians(lat_2)
+ delta_lat_rad = math.radians(lat_2 - lat_1)
+ delta_lon_rad = math.radians(lon_2 - lon_1)
+
+ a = (pow(math.sin(delta_lat_rad / 2), 2) +
+ (math.cos(lat_rad_1) * math.cos(lat_rad_2) *
+ pow(math.sin(delta_lon_rad / 2), 2)))
+ c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
+ R = 6371000; # metres
+ return R * c;
+
+class RouteGuideServicer(route_guide_pb2.EarlyAdopterRouteGuideServicer):
+ """Provides methods that implement functionality of route guide server."""
+
+ def __init__(self):
+ self.db = route_guide_resources.read_route_guide_database()
+
+ def GetFeature(self, request, context):
+ feature = get_feature(self.db, request)
+ if feature is None:
+ return route_guide_pb2.Feature(name="", location=request)
+ else:
+ return feature
+
+ def ListFeatures(self, request, context):
+ left = min(request.lo.longitude, request.hi.longitude)
+ right = max(request.lo.longitude, request.hi.longitude)
+ top = max(request.lo.latitude, request.hi.latitude)
+ bottom = min(request.lo.latitude, request.hi.latitude)
+ for feature in self.db:
+ if (feature.location.longitude >= left and
+ feature.location.longitude <= right and
+ feature.location.latitude >= bottom and
+ feature.location.latitude <= top):
+ yield feature
+
+ def RecordRoute(self, request_iterator, context):
+ point_count = 0
+ feature_count = 0
+ distance = 0.0
+ prev_point = None
+
+ start_time = time.time()
+ for point in request_iterator:
+ point_count += 1
+ if get_feature(self.db, point):
+ feature_count += 1
+ if prev_point:
+ distance += get_distance(prev_point, point)
+ prev_point = point
+
+ elapsed_time = time.time() - start_time
+ return route_guide_pb2.RouteSummary(point_count=point_count,
+ feature_count=feature_count,
+ distance=int(distance),
+ elapsed_time=int(elapsed_time))
+
+ def RouteChat(self, request_iterator, context):
+ prev_notes = []
+ for new_note in request_iterator:
+ for prev_note in prev_notes:
+ if prev_note.location == new_note.location:
+ yield prev_note
+ prev_notes.append(new_note)
+
+
+def serve():
+ server = route_guide_pb2.early_adopter_create_RouteGuide_server(
+ RouteGuideServicer(), 50051, None, None)
+ server.start()
+ try:
+ while True:
+ time.sleep(_ONE_DAY_IN_SECONDS)
+ except KeyboardInterrupt:
+ server.stop()
+
+if __name__ == '__main__':
+ serve()
diff --git a/grpc-common/python/route_guide/run_client.sh b/grpc-common/python/route_guide/run_client.sh
new file mode 100755
index 0000000000..d2552c2858
--- /dev/null
+++ b/grpc-common/python/route_guide/run_client.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+# This is where you have cloned out the https://github.com/grpc/grpc repository
+# And built gRPC Python.
+# ADJUST THIS PATH TO WHERE YOUR ACTUAL LOCATION IS
+GRPC_ROOT=~/github/grpc
+
+$GRPC_ROOT/python2.7_virtual_environment/bin/python -B route_guide_client.py
diff --git a/grpc-common/python/route_guide/run_codegen.sh b/grpc-common/python/route_guide/run_codegen.sh
new file mode 100755
index 0000000000..689e0978de
--- /dev/null
+++ b/grpc-common/python/route_guide/run_codegen.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+# Runs the protoc with gRPC plugin to generate protocol messages and gRPC stubs.
+protoc -I . --python_out=. --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_python_plugin` route_guide.proto
diff --git a/grpc-common/python/route_guide/run_server.sh b/grpc-common/python/route_guide/run_server.sh
new file mode 100755
index 0000000000..8f759250c8
--- /dev/null
+++ b/grpc-common/python/route_guide/run_server.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+# This is where you have cloned out the https://github.com/grpc/grpc repository
+# And built gRPC Python.
+# ADJUST THIS PATH TO WHERE YOUR ACTUAL LOCATION IS
+GRPC_ROOT=~/github/grpc
+
+$GRPC_ROOT/python2.7_virtual_environment/bin/python -B route_guide_server.py