Added protoc plugin for Python GRPC.
diff --git a/src/compiler/python_generator.cc b/src/compiler/python_generator.cc
new file mode 100644
index 0000000..48d9062
--- /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