aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Tim Emiola <tbetbetbe@users.noreply.github.com>2015-02-19 16:14:16 -0800
committerGravatar Tim Emiola <tbetbetbe@users.noreply.github.com>2015-02-19 16:14:16 -0800
commitf4ce945da523f66330fb5df817bcee73013d9589 (patch)
tree481cc6b4ea64eee83f442a9cde83dcd92ec5c4e6
parent71a3a415a296369ace6107e54e3b5312d0bff35c (diff)
parentf8e297a3c0e2a8ad9babf02fad06f293b92fc0d0 (diff)
Merge pull request #631 from soltanmm/master
Added protoc plugin for Python GRPC.
-rw-r--r--Makefile102
-rw-r--r--build.json36
-rw-r--r--src/compiler/python_generator.cc332
-rw-r--r--src/compiler/python_generator.h51
-rw-r--r--src/compiler/python_plugin.cc87
-rw-r--r--vsprojects/vs2013/gpr.vcxproj1
-rw-r--r--vsprojects/vs2013/gpr.vcxproj.filters3
-rw-r--r--vsprojects/vs2013/gpr_shared.vcxproj1
-rw-r--r--vsprojects/vs2013/gpr_shared.vcxproj.filters3
9 files changed, 563 insertions, 53 deletions
diff --git a/Makefile b/Makefile
index 91c94c8071..e1dedb5912 100644
--- a/Makefile
+++ b/Makefile
@@ -332,7 +332,7 @@ endif
.SECONDARY = %.pb.h %.pb.cc
-PROTOC_PLUGINS = $(BINDIR)/$(CONFIG)/grpc_cpp_plugin $(BINDIR)/$(CONFIG)/grpc_ruby_plugin
+PROTOC_PLUGINS = $(BINDIR)/$(CONFIG)/grpc_cpp_plugin $(BINDIR)/$(CONFIG)/grpc_ruby_plugin $(BINDIR)/$(CONFIG)/grpc_python_plugin
ifeq ($(DEP_MISSING),)
all: static shared plugins
dep_error:
@@ -499,6 +499,8 @@ transport_metadata_test: $(BINDIR)/$(CONFIG)/transport_metadata_test
async_end2end_test: $(BINDIR)/$(CONFIG)/async_end2end_test
channel_arguments_test: $(BINDIR)/$(CONFIG)/channel_arguments_test
grpc_cpp_plugin: $(BINDIR)/$(CONFIG)/grpc_cpp_plugin
+grpc_ruby_plugin: $(BINDIR)/$(CONFIG)/grpc_ruby_plugin
+grpc_python_plugin: $(BINDIR)/$(CONFIG)/grpc_python_plugin
credentials_test: $(BINDIR)/$(CONFIG)/credentials_test
end2end_test: $(BINDIR)/$(CONFIG)/end2end_test
interop_client: $(BINDIR)/$(CONFIG)/interop_client
@@ -508,7 +510,6 @@ pubsub_publisher_test: $(BINDIR)/$(CONFIG)/pubsub_publisher_test
pubsub_subscriber_test: $(BINDIR)/$(CONFIG)/pubsub_subscriber_test
qps_client: $(BINDIR)/$(CONFIG)/qps_client
qps_server: $(BINDIR)/$(CONFIG)/qps_server
-grpc_ruby_plugin: $(BINDIR)/$(CONFIG)/grpc_ruby_plugin
status_test: $(BINDIR)/$(CONFIG)/status_test
thread_pool_test: $(BINDIR)/$(CONFIG)/thread_pool_test
chttp2_fake_security_cancel_after_accept_test: $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_after_accept_test
@@ -2047,6 +2048,7 @@ else
$(E) "[INSTALL] Installing grpc protoc plugins"
$(Q) $(INSTALL) $(BINDIR)/$(CONFIG)/grpc_cpp_plugin $(prefix)/bin/grpc_cpp_plugin
$(Q) $(INSTALL) $(BINDIR)/$(CONFIG)/grpc_ruby_plugin $(prefix)/bin/grpc_ruby_plugin
+ $(Q) $(INSTALL) $(BINDIR)/$(CONFIG)/grpc_python_plugin $(prefix)/bin/grpc_python_plugin
endif
clean:
@@ -7413,6 +7415,70 @@ ifneq ($(NO_DEPS),true)
endif
+GRPC_RUBY_PLUGIN_SRC = \
+ src/compiler/ruby_generator.cc \
+ src/compiler/ruby_plugin.cc \
+
+GRPC_RUBY_PLUGIN_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GRPC_RUBY_PLUGIN_SRC))))
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins if you don't have protobuf 3.0.0+.
+
+$(BINDIR)/$(CONFIG)/grpc_ruby_plugin: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/grpc_ruby_plugin: $(PROTOBUF_DEP) $(GRPC_RUBY_PLUGIN_OBJS)
+ $(E) "[HOSTLD] Linking $@"
+ $(Q) mkdir -p `dirname $@`
+ $(Q) $(HOST_LDXX) $(HOST_LDFLAGS) $(GRPC_RUBY_PLUGIN_OBJS) $(HOST_LDLIBSXX) $(HOST_LDLIBS_PROTOC) $(HOST_LDLIBS) $(HOST_LDLIBS_PROTOC) -o $(BINDIR)/$(CONFIG)/grpc_ruby_plugin
+
+endif
+
+$(OBJDIR)/$(CONFIG)/src/compiler/ruby_generator.o:
+$(OBJDIR)/$(CONFIG)/src/compiler/ruby_plugin.o:
+
+deps_grpc_ruby_plugin: $(GRPC_RUBY_PLUGIN_OBJS:.o=.dep)
+
+ifneq ($(NO_DEPS),true)
+-include $(GRPC_RUBY_PLUGIN_OBJS:.o=.dep)
+endif
+
+
+GRPC_PYTHON_PLUGIN_SRC = \
+ src/compiler/python_generator.cc \
+ src/compiler/python_plugin.cc \
+
+GRPC_PYTHON_PLUGIN_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GRPC_PYTHON_PLUGIN_SRC))))
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins if you don't have protobuf 3.0.0+.
+
+$(BINDIR)/$(CONFIG)/grpc_python_plugin: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/grpc_python_plugin: $(PROTOBUF_DEP) $(GRPC_PYTHON_PLUGIN_OBJS)
+ $(E) "[HOSTLD] Linking $@"
+ $(Q) mkdir -p `dirname $@`
+ $(Q) $(HOST_LDXX) $(HOST_LDFLAGS) $(GRPC_PYTHON_PLUGIN_OBJS) $(HOST_LDLIBSXX) $(HOST_LDLIBS_PROTOC) $(HOST_LDLIBS) $(HOST_LDLIBS_PROTOC) -o $(BINDIR)/$(CONFIG)/grpc_python_plugin
+
+endif
+
+$(OBJDIR)/$(CONFIG)/src/compiler/python_generator.o:
+$(OBJDIR)/$(CONFIG)/src/compiler/python_plugin.o:
+
+deps_grpc_python_plugin: $(GRPC_PYTHON_PLUGIN_OBJS:.o=.dep)
+
+ifneq ($(NO_DEPS),true)
+-include $(GRPC_PYTHON_PLUGIN_OBJS:.o=.dep)
+endif
+
+
CREDENTIALS_TEST_SRC = \
test/cpp/client/credentials_test.cc \
@@ -7708,38 +7774,6 @@ endif
endif
-GRPC_RUBY_PLUGIN_SRC = \
- src/compiler/ruby_generator.cc \
- src/compiler/ruby_plugin.cc \
-
-GRPC_RUBY_PLUGIN_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GRPC_RUBY_PLUGIN_SRC))))
-
-
-ifeq ($(NO_PROTOBUF),true)
-
-# You can't build the protoc plugins if you don't have protobuf 3.0.0+.
-
-$(BINDIR)/$(CONFIG)/grpc_ruby_plugin: protobuf_dep_error
-
-else
-
-$(BINDIR)/$(CONFIG)/grpc_ruby_plugin: $(PROTOBUF_DEP) $(GRPC_RUBY_PLUGIN_OBJS)
- $(E) "[HOSTLD] Linking $@"
- $(Q) mkdir -p `dirname $@`
- $(Q) $(HOST_LDXX) $(HOST_LDFLAGS) $(GRPC_RUBY_PLUGIN_OBJS) $(HOST_LDLIBSXX) $(HOST_LDLIBS_PROTOC) $(HOST_LDLIBS) $(HOST_LDLIBS_PROTOC) -o $(BINDIR)/$(CONFIG)/grpc_ruby_plugin
-
-endif
-
-$(OBJDIR)/$(CONFIG)/src/compiler/ruby_generator.o:
-$(OBJDIR)/$(CONFIG)/src/compiler/ruby_plugin.o:
-
-deps_grpc_ruby_plugin: $(GRPC_RUBY_PLUGIN_OBJS:.o=.dep)
-
-ifneq ($(NO_DEPS),true)
--include $(GRPC_RUBY_PLUGIN_OBJS:.o=.dep)
-endif
-
-
STATUS_TEST_SRC = \
test/cpp/util/status_test.cc \
diff --git a/build.json b/build.json
index 0f9e827422..8fdca5a255 100644
--- a/build.json
+++ b/build.json
@@ -1592,6 +1592,31 @@
"secure": false
},
{
+ "name": "grpc_ruby_plugin",
+ "build": "protoc",
+ "language": "c++",
+ "src": [
+ "src/compiler/ruby_generator.cc",
+ "src/compiler/ruby_plugin.cc"
+ ],
+ "deps": [],
+ "secure": false
+ },
+ {
+ "name": "grpc_python_plugin",
+ "build": "protoc",
+ "language": "c++",
+ "headers": [
+ "src/compiler/python_generator.h"
+ ],
+ "src": [
+ "src/compiler/python_generator.cc",
+ "src/compiler/python_plugin.cc"
+ ],
+ "deps": [],
+ "secure": false
+ },
+ {
"name": "credentials_test",
"build": "test",
"language": "c++",
@@ -1749,17 +1774,6 @@
]
},
{
- "name": "grpc_ruby_plugin",
- "build": "protoc",
- "language": "c++",
- "src": [
- "src/compiler/ruby_generator.cc",
- "src/compiler/ruby_plugin.cc"
- ],
- "deps": [],
- "secure": false
- },
- {
"name": "status_test",
"build": "test",
"language": "c++",
diff --git a/src/compiler/python_generator.cc b/src/compiler/python_generator.cc
new file mode 100644
index 0000000000..48d90624d6
--- /dev/null
+++ b/src/compiler/python_generator.cc
@@ -0,0 +1,332 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include <cassert>
+#include <cctype>
+#include <map>
+#include <ostream>
+#include <sstream>
+
+#include "src/compiler/python_generator.h"
+#include <google/protobuf/io/printer.h>
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+#include <google/protobuf/descriptor.pb.h>
+#include <google/protobuf/descriptor.h>
+
+using google::protobuf::FileDescriptor;
+using google::protobuf::ServiceDescriptor;
+using google::protobuf::MethodDescriptor;
+using google::protobuf::io::Printer;
+using google::protobuf::io::StringOutputStream;
+using std::initializer_list;
+using std::map;
+using std::string;
+
+namespace grpc_python_generator {
+namespace {
+//////////////////////////////////
+// BEGIN FORMATTING BOILERPLATE //
+//////////////////////////////////
+
+// Converts an initializer list of the form { key0, value0, key1, value1, ... }
+// into a map of key* to value*. Is merely a readability helper for later code.
+map<string, string> ListToDict(const initializer_list<string>& values) {
+ assert(values.size() % 2 == 0);
+ map<string, string> value_map;
+ auto value_iter = values.begin();
+ for (unsigned i = 0; i < values.size()/2; ++i) {
+ string key = *value_iter;
+ ++value_iter;
+ string value = *value_iter;
+ value_map[key] = value;
+ ++value_iter;
+ }
+ return value_map;
+}
+
+// Provides RAII indentation handling. Use as:
+// {
+// IndentScope raii_my_indent_var_name_here(my_py_printer);
+// // constructor indented my_py_printer
+// ...
+// // destructor called at end of scope, un-indenting my_py_printer
+// }
+class IndentScope {
+ public:
+ explicit IndentScope(Printer* printer) : printer_(printer) {
+ printer_->Indent();
+ }
+
+ ~IndentScope() {
+ printer_->Outdent();
+ }
+
+ private:
+ Printer* printer_;
+};
+
+////////////////////////////////
+// END FORMATTING BOILERPLATE //
+////////////////////////////////
+
+void PrintService(const ServiceDescriptor* service,
+ Printer* out) {
+ string doc = "<fill me in later!>";
+ map<string, string> dict = ListToDict({
+ "Service", service->name(),
+ "Documentation", doc,
+ });
+ out->Print(dict, "class $Service$Service(object):\n");
+ {
+ IndentScope raii_class_indent(out);
+ out->Print(dict, "\"\"\"$Documentation$\"\"\"\n");
+ out->Print("def __init__(self):\n");
+ {
+ IndentScope raii_method_indent(out);
+ out->Print("pass\n");
+ }
+ }
+}
+
+void PrintServicer(const ServiceDescriptor* service,
+ Printer* out) {
+ string doc = "<fill me in later!>";
+ map<string, string> dict = ListToDict({
+ "Service", service->name(),
+ "Documentation", doc,
+ });
+ out->Print(dict, "class $Service$Servicer(object):\n");
+ {
+ IndentScope raii_class_indent(out);
+ out->Print(dict, "\"\"\"$Documentation$\"\"\"\n");
+ for (int i = 0; i < service->method_count(); ++i) {
+ auto meth = service->method(i);
+ out->Print("def $Method$(self, arg):\n", "Method", meth->name());
+ {
+ IndentScope raii_method_indent(out);
+ out->Print("raise NotImplementedError()\n");
+ }
+ }
+ }
+}
+
+void PrintStub(const ServiceDescriptor* service,
+ Printer* out) {
+ string doc = "<fill me in later!>";
+ map<string, string> dict = ListToDict({
+ "Service", service->name(),
+ "Documentation", doc,
+ });
+ out->Print(dict, "class $Service$Stub(object):\n");
+ {
+ IndentScope raii_class_indent(out);
+ out->Print(dict, "\"\"\"$Documentation$\"\"\"\n");
+ for (int i = 0; i < service->method_count(); ++i) {
+ const MethodDescriptor* meth = service->method(i);
+ auto methdict = ListToDict({"Method", meth->name()});
+ out->Print(methdict, "def $Method$(self, arg):\n");
+ {
+ IndentScope raii_method_indent(out);
+ out->Print("raise NotImplementedError()\n");
+ }
+ out->Print(methdict, "$Method$.async = None\n");
+ }
+ }
+}
+
+void PrintStubImpl(const ServiceDescriptor* service,
+ Printer* out) {
+ map<string, string> dict = ListToDict({
+ "Service", service->name(),
+ });
+ out->Print(dict, "class _$Service$Stub($Service$Stub):\n");
+ {
+ IndentScope raii_class_indent(out);
+ out->Print("def __init__(self, face_stub, default_timeout):\n");
+ {
+ IndentScope raii_method_indent(out);
+ out->Print("self._face_stub = face_stub\n"
+ "self._default_timeout = default_timeout\n"
+ "stub_self = self\n");
+
+ for (int i = 0; i < service->method_count(); ++i) {
+ const MethodDescriptor* meth = service->method(i);
+ bool server_streaming = meth->server_streaming();
+ bool client_streaming = meth->client_streaming();
+ std::string blocking_call, future_call;
+ if (server_streaming) {
+ if (client_streaming) {
+ blocking_call = "stub_self._face_stub.inline_stream_in_stream_out";
+ future_call = blocking_call;
+ } else {
+ blocking_call = "stub_self._face_stub.inline_value_in_stream_out";
+ future_call = blocking_call;
+ }
+ } else {
+ if (client_streaming) {
+ blocking_call = "stub_self._face_stub.blocking_stream_in_value_out";
+ future_call = "stub_self._face_stub.future_stream_in_value_out";
+ } else {
+ blocking_call = "stub_self._face_stub.blocking_value_in_value_out";
+ future_call = "stub_self._face_stub.future_value_in_value_out";
+ }
+ }
+ // TODO(atash): use the solution described at
+ // http://stackoverflow.com/a/2982 to bind 'async' attribute
+ // functions to def'd functions instead of using callable attributes.
+ auto methdict = ListToDict({
+ "Method", meth->name(),
+ "BlockingCall", blocking_call,
+ "FutureCall", future_call
+ });
+ out->Print(methdict, "class $Method$(object):\n");
+ {
+ IndentScope raii_callable_indent(out);
+ out->Print("def __call__(self, arg):\n");
+ {
+ IndentScope raii_callable_call_indent(out);
+ out->Print(methdict,
+ "return $BlockingCall$(\"$Method$\", arg, "
+ "stub_self._default_timeout)\n");
+ }
+ out->Print("def async(self, arg):\n");
+ {
+ IndentScope raii_callable_async_indent(out);
+ out->Print(methdict,
+ "return $FutureCall$(\"$Method$\", arg, "
+ "stub_self._default_timeout)\n");
+ }
+ }
+ out->Print(methdict, "self.$Method$ = $Method$()\n");
+ }
+ }
+ }
+}
+
+void PrintStubGenerators(const ServiceDescriptor* service, Printer* out) {
+ map<string, string> dict = ListToDict({
+ "Service", service->name(),
+ });
+ // Write out a generator of linked pairs of Server/Stub
+ out->Print(dict, "def mock_$Service$(servicer, default_timeout):\n");
+ {
+ IndentScope raii_mock_indent(out);
+ out->Print("value_in_value_out = {}\n"
+ "value_in_stream_out = {}\n"
+ "stream_in_value_out = {}\n"
+ "stream_in_stream_out = {}\n");
+ for (int i = 0; i < service->method_count(); ++i) {
+ const MethodDescriptor* meth = service->method(i);
+ std::string super_interface, meth_dict;
+ bool server_streaming = meth->server_streaming();
+ bool client_streaming = meth->client_streaming();
+ if (server_streaming) {
+ if (client_streaming) {
+ super_interface = "InlineStreamInStreamOutMethod";
+ meth_dict = "stream_in_stream_out";
+ } else {
+ super_interface = "InlineValueInStreamOutMethod";
+ meth_dict = "value_in_stream_out";
+ }
+ } else {
+ if (client_streaming) {
+ super_interface = "InlineStreamInValueOutMethod";
+ meth_dict = "stream_in_value_out";
+ } else {
+ super_interface = "InlineValueInValueOutMethod";
+ meth_dict = "value_in_value_out";
+ }
+ }
+ map<string, string> methdict = ListToDict({
+ "Method", meth->name(),
+ "SuperInterface", super_interface,
+ "MethodDict", meth_dict
+ });
+ out->Print(
+ methdict, "class $Method$(_face_interfaces.$SuperInterface$):\n");
+ {
+ IndentScope raii_inline_class_indent(out);
+ out->Print("def service(self, request, context):\n");
+ {
+ IndentScope raii_inline_class_fn_indent(out);
+ out->Print(methdict, "return servicer.$Method$(request)\n");
+ }
+ }
+ out->Print(methdict, "$MethodDict$['$Method$'] = $Method$()\n");
+ }
+ out->Print(
+ "face_linked_pair = _face_testing.server_and_stub(default_timeout,"
+ "inline_value_in_value_out_methods=value_in_value_out,"
+ "inline_value_in_stream_out_methods=value_in_stream_out,"
+ "inline_stream_in_value_out_methods=stream_in_value_out,"
+ "inline_stream_in_stream_out_methods=stream_in_stream_out)\n");
+ out->Print("class LinkedPair(object):\n");
+ {
+ IndentScope raii_linked_pair(out);
+ out->Print("def __init__(self, server, stub):\n");
+ {
+ IndentScope raii_linked_pair_init(out);
+ out->Print("self.server = server\n"
+ "self.stub = stub\n");
+ }
+ }
+ out->Print(
+ dict,
+ "stub = _$Service$Stub(face_linked_pair.stub, default_timeout)\n");
+ out->Print("return LinkedPair(None, stub)\n");
+ }
+}
+
+} // namespace
+
+string GetServices(const FileDescriptor* file) {
+ string output;
+ StringOutputStream output_stream(&output);
+ Printer out(&output_stream, '$');
+ out.Print("import abc\n");
+ out.Print("import google3\n");
+ out.Print("from grpc.framework.face import demonstration as _face_testing\n");
+ out.Print("from grpc.framework.face import interfaces as _face_interfaces\n");
+
+ for (int i = 0; i < file->service_count(); ++i) {
+ auto service = file->service(i);
+ PrintService(service, &out);
+ PrintServicer(service, &out);
+ PrintStub(service, &out);
+ PrintStubImpl(service, &out);
+ PrintStubGenerators(service, &out);
+ }
+ return output;
+}
+
+} // namespace grpc_python_generator
diff --git a/src/compiler/python_generator.h b/src/compiler/python_generator.h
new file mode 100644
index 0000000000..673ef7b23b
--- /dev/null
+++ b/src/compiler/python_generator.h
@@ -0,0 +1,51 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#ifndef __GRPC_COMPILER_PYTHON_GENERATOR_H__
+#define __GRPC_COMPILER_PYTHON_GENERATOR_H__
+
+#include <string>
+
+namespace google {
+namespace protobuf {
+class FileDescriptor;
+} // namespace protobuf
+} // namespace google
+
+namespace grpc_python_generator {
+
+std::string GetServices(const google::protobuf::FileDescriptor* file);
+
+} // namespace grpc_python_generator
+
+#endif // __GRPC_COMPILER_PYTHON_GENERATOR_H__
diff --git a/src/compiler/python_plugin.cc b/src/compiler/python_plugin.cc
new file mode 100644
index 0000000000..ebe3660619
--- /dev/null
+++ b/src/compiler/python_plugin.cc
@@ -0,0 +1,87 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+// Generates a Python gRPC service interface out of Protobuf IDL.
+
+#include <memory>
+#include <string>
+
+#include "src/compiler/python_generator.h"
+#include <google/protobuf/compiler/code_generator.h>
+#include <google/protobuf/compiler/plugin.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream.h>
+#include <google/protobuf/descriptor.h>
+
+using google::protobuf::FileDescriptor;
+using google::protobuf::compiler::CodeGenerator;
+using google::protobuf::compiler::GeneratorContext;
+using google::protobuf::compiler::PluginMain;
+using google::protobuf::io::CodedOutputStream;
+using google::protobuf::io::ZeroCopyOutputStream;
+using std::string;
+
+class PythonGrpcGenerator : public CodeGenerator {
+ public:
+ PythonGrpcGenerator() {}
+ ~PythonGrpcGenerator() override {}
+
+ bool Generate(const FileDescriptor* file,
+ const string& parameter,
+ GeneratorContext* context,
+ string* error) const override {
+ // Get output file name.
+ string file_name;
+ static const int proto_suffix_length = 6; // length of ".proto"
+ if (file->name().size() > proto_suffix_length &&
+ file->name().find_last_of(".proto") == file->name().size() - 1) {
+ file_name = file->name().substr(
+ 0, file->name().size() - proto_suffix_length) + "_pb2.py";
+ } else {
+ *error = "Invalid proto file name. Proto file must end with .proto";
+ return false;
+ }
+
+ std::unique_ptr<ZeroCopyOutputStream> output(
+ context->OpenForInsert(file_name, "module_scope"));
+ CodedOutputStream coded_out(output.get());
+ string code = grpc_python_generator::GetServices(file);
+ coded_out.WriteRaw(code.data(), code.size());
+ return true;
+ }
+};
+
+int main(int argc, char* argv[]) {
+ PythonGrpcGenerator generator;
+ return PluginMain(argc, argv, &generator);
+}
diff --git a/vsprojects/vs2013/gpr.vcxproj b/vsprojects/vs2013/gpr.vcxproj
index f6516b8953..3d970079a4 100644
--- a/vsprojects/vs2013/gpr.vcxproj
+++ b/vsprojects/vs2013/gpr.vcxproj
@@ -101,7 +101,6 @@
<ClInclude Include="..\..\include\grpc\support\useful.h" />
</ItemGroup>
<ItemGroup>
- <ClInclude Include="..\..\src\core\support\cpu.h" />
<ClInclude Include="..\..\src\core\support\env.h" />
<ClInclude Include="..\..\src\core\support\file.h" />
<ClInclude Include="..\..\src\core\support\murmur_hash.h" />
diff --git a/vsprojects/vs2013/gpr.vcxproj.filters b/vsprojects/vs2013/gpr.vcxproj.filters
index 1e908a5ba5..9b78c3a390 100644
--- a/vsprojects/vs2013/gpr.vcxproj.filters
+++ b/vsprojects/vs2013/gpr.vcxproj.filters
@@ -167,9 +167,6 @@
</ClInclude>
</ItemGroup>
<ItemGroup>
- <ClInclude Include="..\..\src\core\support\cpu.h">
- <Filter>src\core\support</Filter>
- </ClInclude>
<ClInclude Include="..\..\src\core\support\env.h">
<Filter>src\core\support</Filter>
</ClInclude>
diff --git a/vsprojects/vs2013/gpr_shared.vcxproj b/vsprojects/vs2013/gpr_shared.vcxproj
index b9131e381c..892490e324 100644
--- a/vsprojects/vs2013/gpr_shared.vcxproj
+++ b/vsprojects/vs2013/gpr_shared.vcxproj
@@ -101,7 +101,6 @@
<ClInclude Include="..\..\include\grpc\support\useful.h" />
</ItemGroup>
<ItemGroup>
- <ClInclude Include="..\..\src\core\support\cpu.h" />
<ClInclude Include="..\..\src\core\support\env.h" />
<ClInclude Include="..\..\src\core\support\file.h" />
<ClInclude Include="..\..\src\core\support\murmur_hash.h" />
diff --git a/vsprojects/vs2013/gpr_shared.vcxproj.filters b/vsprojects/vs2013/gpr_shared.vcxproj.filters
index 1e908a5ba5..9b78c3a390 100644
--- a/vsprojects/vs2013/gpr_shared.vcxproj.filters
+++ b/vsprojects/vs2013/gpr_shared.vcxproj.filters
@@ -167,9 +167,6 @@
</ClInclude>
</ItemGroup>
<ItemGroup>
- <ClInclude Include="..\..\src\core\support\cpu.h">
- <Filter>src\core\support</Filter>
- </ClInclude>
<ClInclude Include="..\..\src\core\support\env.h">
<Filter>src\core\support</Filter>
</ClInclude>