Backport Python features to 1.0.x

Backports per-object grpc_init/deinit and separated-file grpc protoc
codegen (#7538, #8246, #8920).
pull/8990/head
Masood Malekghassemi 8 years ago
parent 93904c0dc8
commit 53360f2d1c
  1. 580
      src/compiler/python_generator.cc
  2. 5
      src/compiler/python_generator.h
  3. 2
      src/python/grpcio/grpc/_cython/_cygrpc/call.pyx.pxi
  4. 2
      src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx.pxi
  5. 2
      src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx.pxi
  6. 14
      src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi
  7. 12
      src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi
  8. 2
      src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi
  9. 4
      src/python/grpcio/grpc/_cython/cygrpc.pyx
  10. 1
      src/python/grpcio_health_checking/.gitignore
  11. 1
      src/python/grpcio_tests/.gitignore
  12. 304
      src/python/grpcio_tests/tests/protoc_plugin/_split_definitions_test.py
  13. 30
      src/python/grpcio_tests/tests/protoc_plugin/protos/invocation_testing/__init__.py
  14. 39
      src/python/grpcio_tests/tests/protoc_plugin/protos/invocation_testing/same.proto
  15. 30
      src/python/grpcio_tests/tests/protoc_plugin/protos/invocation_testing/split_messages/__init__.py
  16. 35
      src/python/grpcio_tests/tests/protoc_plugin/protos/invocation_testing/split_messages/messages.proto
  17. 30
      src/python/grpcio_tests/tests/protoc_plugin/protos/invocation_testing/split_services/__init__.py
  18. 38
      src/python/grpcio_tests/tests/protoc_plugin/protos/invocation_testing/split_services/services.proto
  19. 4
      src/python/grpcio_tests/tests/tests.json

@ -35,6 +35,8 @@
#include <cassert>
#include <cctype>
#include <cstring>
#include <fstream>
#include <iostream>
#include <map>
#include <memory>
#include <ostream>
@ -66,64 +68,11 @@ using std::vector;
namespace grpc_python_generator {
GeneratorConfiguration::GeneratorConfiguration()
: grpc_package_root("grpc"), beta_package_root("grpc.beta") {}
PythonGrpcGenerator::PythonGrpcGenerator(const GeneratorConfiguration& config)
: config_(config) {}
PythonGrpcGenerator::~PythonGrpcGenerator() {}
bool PythonGrpcGenerator::Generate(
const FileDescriptor* file, const grpc::string& parameter,
GeneratorContext* context, grpc::string* error) const {
// Get output file name.
grpc::string file_name;
static const int proto_suffix_length = strlen(".proto");
if (file->name().size() > static_cast<size_t>(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());
bool success = false;
grpc::string code = "";
tie(success, code) = grpc_python_generator::GetServices(file, config_);
if (success) {
coded_out.WriteRaw(code.data(), code.size());
return true;
} else {
return false;
}
}
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<grpc::string, grpc::string> ListToDict(
const initializer_list<grpc::string>& values) {
assert(values.size() % 2 == 0);
map<grpc::string, grpc::string> value_map;
auto value_iter = values.begin();
for (unsigned i = 0; i < values.size()/2; ++i) {
grpc::string key = *value_iter;
++value_iter;
grpc::string value = *value_iter;
value_map[key] = value;
++value_iter;
}
return value_map;
}
typedef vector<const Descriptor*> DescriptorVector;
typedef map<grpc::string, grpc::string> StringMap;
typedef vector<grpc::string> StringVector;
// Provides RAII indentation handling. Use as:
// {
@ -138,18 +87,12 @@ class IndentScope {
printer_->Indent();
}
~IndentScope() {
printer_->Outdent();
}
~IndentScope() { printer_->Outdent(); }
private:
Printer* printer_;
};
////////////////////////////////
// END FORMATTING BOILERPLATE //
////////////////////////////////
// TODO(https://github.com/google/protobuf/issues/888):
// Export `ModuleName` from protobuf's
// `src/google/protobuf/compiler/python/python_generator.cc` file.
@ -173,12 +116,61 @@ grpc::string ModuleAlias(const grpc::string& filename) {
return module_name;
}
// Tucks all generator state in an anonymous namespace away from
// PythonGrpcGenerator and the header file, mostly to encourage future changes
// to not require updates to the grpcio-tools C++ code part. Assumes that it is
// only ever used from a single thread.
struct PrivateGenerator {
const GeneratorConfiguration& config;
const FileDescriptor* file;
bool generate_in_pb2_grpc;
Printer* out;
PrivateGenerator(const GeneratorConfiguration& config,
const FileDescriptor* file);
std::pair<bool, grpc::string> GetGrpcServices();
private:
bool PrintPreamble();
bool PrintBetaPreamble();
bool PrintGAServices();
bool PrintBetaServices();
bool PrintAddServicerToServer(
const grpc::string& package_qualified_service_name,
const ServiceDescriptor* service);
bool PrintServicer(const ServiceDescriptor* service);
bool PrintStub(const grpc::string& package_qualified_service_name,
const ServiceDescriptor* service);
bool PrintBetaServicer(const ServiceDescriptor* service);
bool PrintBetaServerFactory(
const grpc::string& package_qualified_service_name,
const ServiceDescriptor* service);
bool PrintBetaStub(const ServiceDescriptor* service);
bool PrintBetaStubFactory(const grpc::string& package_qualified_service_name,
const ServiceDescriptor* service);
// Get all comments (leading, leading_detached, trailing) and print them as a
// docstring. Any leading space of a line will be removed, but the line
// wrapping will not be changed.
template <typename DescriptorType>
void PrintAllComments(const DescriptorType* descriptor);
bool GetModuleAndMessagePath(const Descriptor* type, grpc::string* out);
};
bool GetModuleAndMessagePath(const Descriptor* type,
const ServiceDescriptor* service,
PrivateGenerator::PrivateGenerator(const GeneratorConfiguration& config,
const FileDescriptor* file)
: config(config), file(file) {}
bool PrivateGenerator::GetModuleAndMessagePath(const Descriptor* type,
grpc::string* out) {
const Descriptor* path_elem_type = type;
vector<const Descriptor*> message_path;
DescriptorVector message_path;
do {
message_path.push_back(path_elem_type);
path_elem_type = path_elem_type->containing_type();
@ -189,11 +181,15 @@ bool GetModuleAndMessagePath(const Descriptor* type,
file_name.find_last_of(".proto") == file_name.size() - 1)) {
return false;
}
grpc::string service_file_name = service->file()->name();
grpc::string module = service_file_name == file_name ?
"" : ModuleAlias(file_name) + ".";
grpc::string generator_file_name = file->name();
grpc::string module;
if (generator_file_name != file_name || generate_in_pb2_grpc) {
module = ModuleAlias(file_name) + ".";
} else {
module = "";
}
grpc::string message_type;
for (auto path_iter = message_path.rbegin();
for (DescriptorVector::reverse_iterator path_iter = message_path.rbegin();
path_iter != message_path.rend(); ++path_iter) {
message_type += (*path_iter)->name() + ".";
}
@ -203,53 +199,53 @@ bool GetModuleAndMessagePath(const Descriptor* type,
return true;
}
// Get all comments (leading, leading_detached, trailing) and print them as a
// docstring. Any leading space of a line will be removed, but the line wrapping
// will not be changed.
template <typename DescriptorType>
static void PrintAllComments(const DescriptorType* desc, Printer* printer) {
std::vector<grpc::string> comments;
grpc_generator::GetComment(desc, grpc_generator::COMMENTTYPE_LEADING_DETACHED,
void PrivateGenerator::PrintAllComments(const DescriptorType* descriptor) {
StringVector comments;
grpc_generator::GetComment(
descriptor, grpc_generator::COMMENTTYPE_LEADING_DETACHED, &comments);
grpc_generator::GetComment(descriptor, grpc_generator::COMMENTTYPE_LEADING,
&comments);
grpc_generator::GetComment(desc, grpc_generator::COMMENTTYPE_LEADING,
&comments);
grpc_generator::GetComment(desc, grpc_generator::COMMENTTYPE_TRAILING,
grpc_generator::GetComment(descriptor, grpc_generator::COMMENTTYPE_TRAILING,
&comments);
if (comments.empty()) {
return;
}
printer->Print("\"\"\"");
for (auto it = comments.begin(); it != comments.end(); ++it) {
out->Print("\"\"\"");
for (StringVector::iterator it = comments.begin(); it != comments.end();
++it) {
size_t start_pos = it->find_first_not_of(' ');
if (start_pos != grpc::string::npos) {
printer->Print(it->c_str() + start_pos);
out->Print(it->c_str() + start_pos);
}
printer->Print("\n");
out->Print("\n");
}
printer->Print("\"\"\"\n");
out->Print("\"\"\"\n");
}
bool PrintBetaServicer(const ServiceDescriptor* service,
Printer* out) {
bool PrivateGenerator::PrintBetaServicer(const ServiceDescriptor* service) {
out->Print("\n\n");
out->Print("class Beta$Service$Servicer(object):\n", "Service",
service->name());
{
IndentScope raii_class_indent(out);
out->Print("\"\"\"The Beta API is deprecated for 0.15.0 and later.\n"
out->Print(
"\"\"\"The Beta API is deprecated for 0.15.0 and later.\n"
"\nIt is recommended to use the GA API (classes and functions in this\n"
"file not marked beta) for all further purposes. This class was generated\n"
"only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0.\"\"\"\n");
PrintAllComments(service, out);
"file not marked beta) for all further purposes. This class was "
"generated\n"
"only to ease transition from grpcio<0.15.0 to "
"grpcio>=0.15.0.\"\"\"\n");
PrintAllComments(service);
for (int i = 0; i < service->method_count(); ++i) {
auto meth = service->method(i);
grpc::string arg_name = meth->client_streaming() ?
"request_iterator" : "request";
out->Print("def $Method$(self, $ArgName$, context):\n",
"Method", meth->name(), "ArgName", arg_name);
const MethodDescriptor* method = service->method(i);
grpc::string arg_name =
method->client_streaming() ? "request_iterator" : "request";
out->Print("def $Method$(self, $ArgName$, context):\n", "Method",
method->name(), "ArgName", arg_name);
{
IndentScope raii_method_indent(out);
PrintAllComments(meth, out);
PrintAllComments(method);
out->Print("context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)\n");
}
}
@ -257,52 +253,61 @@ bool PrintBetaServicer(const ServiceDescriptor* service,
return true;
}
bool PrintBetaStub(const ServiceDescriptor* service,
Printer* out) {
bool PrivateGenerator::PrintBetaStub(const ServiceDescriptor* service) {
out->Print("\n\n");
out->Print("class Beta$Service$Stub(object):\n", "Service", service->name());
{
IndentScope raii_class_indent(out);
out->Print("\"\"\"The Beta API is deprecated for 0.15.0 and later.\n"
out->Print(
"\"\"\"The Beta API is deprecated for 0.15.0 and later.\n"
"\nIt is recommended to use the GA API (classes and functions in this\n"
"file not marked beta) for all further purposes. This class was generated\n"
"only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0.\"\"\"\n");
PrintAllComments(service, out);
"file not marked beta) for all further purposes. This class was "
"generated\n"
"only to ease transition from grpcio<0.15.0 to "
"grpcio>=0.15.0.\"\"\"\n");
PrintAllComments(service);
for (int i = 0; i < service->method_count(); ++i) {
const MethodDescriptor* meth = service->method(i);
grpc::string arg_name = meth->client_streaming() ?
"request_iterator" : "request";
auto methdict = ListToDict({"Method", meth->name(), "ArgName", arg_name});
out->Print(methdict, "def $Method$(self, $ArgName$, timeout, metadata=None, with_call=False, protocol_options=None):\n");
const MethodDescriptor* method = service->method(i);
grpc::string arg_name =
method->client_streaming() ? "request_iterator" : "request";
StringMap method_dict;
method_dict["Method"] = method->name();
method_dict["ArgName"] = arg_name;
out->Print(method_dict,
"def $Method$(self, $ArgName$, timeout, metadata=None, "
"with_call=False, protocol_options=None):\n");
{
IndentScope raii_method_indent(out);
PrintAllComments(meth, out);
PrintAllComments(method);
out->Print("raise NotImplementedError()\n");
}
if (!meth->server_streaming()) {
out->Print(methdict, "$Method$.future = None\n");
if (!method->server_streaming()) {
out->Print(method_dict, "$Method$.future = None\n");
}
}
}
return true;
}
bool PrintBetaServerFactory(const grpc::string& package_qualified_service_name,
const ServiceDescriptor* service, Printer* out) {
bool PrivateGenerator::PrintBetaServerFactory(
const grpc::string& package_qualified_service_name,
const ServiceDescriptor* service) {
out->Print("\n\n");
out->Print("def beta_create_$Service$_server(servicer, pool=None, "
out->Print(
"def beta_create_$Service$_server(servicer, pool=None, "
"pool_size=None, default_timeout=None, maximum_timeout=None):\n",
"Service", service->name());
{
IndentScope raii_create_server_indent(out);
out->Print("\"\"\"The Beta API is deprecated for 0.15.0 and later.\n"
out->Print(
"\"\"\"The Beta API is deprecated for 0.15.0 and later.\n"
"\nIt is recommended to use the GA API (classes and functions in this\n"
"file not marked beta) for all further purposes. This function was\n"
"generated only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0"
"\"\"\"\n");
map<grpc::string, grpc::string> method_implementation_constructors;
map<grpc::string, grpc::string> input_message_modules_and_classes;
map<grpc::string, grpc::string> output_message_modules_and_classes;
StringMap method_implementation_constructors;
StringMap input_message_modules_and_classes;
StringMap output_message_modules_and_classes;
for (int i = 0; i < service->method_count(); ++i) {
const MethodDescriptor* method = service->method(i);
const grpc::string method_implementation_constructor =
@ -310,12 +315,12 @@ bool PrintBetaServerFactory(const grpc::string& package_qualified_service_name,
grpc::string(method->server_streaming() ? "stream_" : "unary_") +
"inline";
grpc::string input_message_module_and_class;
if (!GetModuleAndMessagePath(method->input_type(), service,
if (!GetModuleAndMessagePath(method->input_type(),
&input_message_module_and_class)) {
return false;
}
grpc::string output_message_module_and_class;
if (!GetModuleAndMessagePath(method->output_type(), service,
if (!GetModuleAndMessagePath(method->output_type(),
&output_message_module_and_class)) {
return false;
}
@ -327,28 +332,29 @@ bool PrintBetaServerFactory(const grpc::string& package_qualified_service_name,
make_pair(method->name(), output_message_module_and_class));
}
out->Print("request_deserializers = {\n");
for (auto name_and_input_module_class_pair =
for (StringMap::iterator name_and_input_module_class_pair =
input_message_modules_and_classes.begin();
name_and_input_module_class_pair !=
input_message_modules_and_classes.end();
name_and_input_module_class_pair++) {
IndentScope raii_indent(out);
out->Print("(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): "
out->Print(
"(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): "
"$InputTypeModuleAndClass$.FromString,\n",
"PackageQualifiedServiceName", package_qualified_service_name,
"MethodName", name_and_input_module_class_pair->first,
"InputTypeModuleAndClass",
name_and_input_module_class_pair->second);
"InputTypeModuleAndClass", name_and_input_module_class_pair->second);
}
out->Print("}\n");
out->Print("response_serializers = {\n");
for (auto name_and_output_module_class_pair =
for (StringMap::iterator name_and_output_module_class_pair =
output_message_modules_and_classes.begin();
name_and_output_module_class_pair !=
output_message_modules_and_classes.end();
name_and_output_module_class_pair++) {
IndentScope raii_indent(out);
out->Print("(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): "
out->Print(
"(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): "
"$OutputTypeModuleAndClass$.SerializeToString,\n",
"PackageQualifiedServiceName", package_qualified_service_name,
"MethodName", name_and_output_module_class_pair->first,
@ -357,7 +363,7 @@ bool PrintBetaServerFactory(const grpc::string& package_qualified_service_name,
}
out->Print("}\n");
out->Print("method_implementations = {\n");
for (auto name_and_implementation_constructor =
for (StringMap::iterator name_and_implementation_constructor =
method_implementation_constructors.begin();
name_and_implementation_constructor !=
method_implementation_constructors.end();
@ -365,56 +371,60 @@ bool PrintBetaServerFactory(const grpc::string& package_qualified_service_name,
IndentScope raii_descriptions_indent(out);
const grpc::string method_name =
name_and_implementation_constructor->first;
out->Print("(\'$PackageQualifiedServiceName$\', \'$Method$\'): "
out->Print(
"(\'$PackageQualifiedServiceName$\', \'$Method$\'): "
"face_utilities.$Constructor$(servicer.$Method$),\n",
"PackageQualifiedServiceName", package_qualified_service_name,
"Method", name_and_implementation_constructor->first,
"Constructor", name_and_implementation_constructor->second);
"Method", name_and_implementation_constructor->first, "Constructor",
name_and_implementation_constructor->second);
}
out->Print("}\n");
out->Print("server_options = beta_implementations.server_options("
out->Print(
"server_options = beta_implementations.server_options("
"request_deserializers=request_deserializers, "
"response_serializers=response_serializers, "
"thread_pool=pool, thread_pool_size=pool_size, "
"default_timeout=default_timeout, "
"maximum_timeout=maximum_timeout)\n");
out->Print("return beta_implementations.server(method_implementations, "
out->Print(
"return beta_implementations.server(method_implementations, "
"options=server_options)\n");
}
return true;
}
bool PrintBetaStubFactory(const grpc::string& package_qualified_service_name,
const ServiceDescriptor* service, Printer* out) {
map<grpc::string, grpc::string> dict = ListToDict({
"Service", service->name(),
});
bool PrivateGenerator::PrintBetaStubFactory(
const grpc::string& package_qualified_service_name,
const ServiceDescriptor* service) {
StringMap dict;
dict["Service"] = service->name();
out->Print("\n\n");
out->Print(dict, "def beta_create_$Service$_stub(channel, host=None,"
out->Print(dict,
"def beta_create_$Service$_stub(channel, host=None,"
" metadata_transformer=None, pool=None, pool_size=None):\n");
{
IndentScope raii_create_server_indent(out);
out->Print("\"\"\"The Beta API is deprecated for 0.15.0 and later.\n"
out->Print(
"\"\"\"The Beta API is deprecated for 0.15.0 and later.\n"
"\nIt is recommended to use the GA API (classes and functions in this\n"
"file not marked beta) for all further purposes. This function was\n"
"generated only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0"
"\"\"\"\n");
map<grpc::string, grpc::string> method_cardinalities;
map<grpc::string, grpc::string> input_message_modules_and_classes;
map<grpc::string, grpc::string> output_message_modules_and_classes;
StringMap method_cardinalities;
StringMap input_message_modules_and_classes;
StringMap output_message_modules_and_classes;
for (int i = 0; i < service->method_count(); ++i) {
const MethodDescriptor* method = service->method(i);
const grpc::string method_cardinality =
grpc::string(method->client_streaming() ? "STREAM" : "UNARY") +
"_" +
grpc::string(method->client_streaming() ? "STREAM" : "UNARY") + "_" +
grpc::string(method->server_streaming() ? "STREAM" : "UNARY");
grpc::string input_message_module_and_class;
if (!GetModuleAndMessagePath(method->input_type(), service,
if (!GetModuleAndMessagePath(method->input_type(),
&input_message_module_and_class)) {
return false;
}
grpc::string output_message_module_and_class;
if (!GetModuleAndMessagePath(method->output_type(), service,
if (!GetModuleAndMessagePath(method->output_type(),
&output_message_module_and_class)) {
return false;
}
@ -426,28 +436,29 @@ bool PrintBetaStubFactory(const grpc::string& package_qualified_service_name,
make_pair(method->name(), output_message_module_and_class));
}
out->Print("request_serializers = {\n");
for (auto name_and_input_module_class_pair =
for (StringMap::iterator name_and_input_module_class_pair =
input_message_modules_and_classes.begin();
name_and_input_module_class_pair !=
input_message_modules_and_classes.end();
name_and_input_module_class_pair++) {
IndentScope raii_indent(out);
out->Print("(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): "
out->Print(
"(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): "
"$InputTypeModuleAndClass$.SerializeToString,\n",
"PackageQualifiedServiceName", package_qualified_service_name,
"MethodName", name_and_input_module_class_pair->first,
"InputTypeModuleAndClass",
name_and_input_module_class_pair->second);
"InputTypeModuleAndClass", name_and_input_module_class_pair->second);
}
out->Print("}\n");
out->Print("response_deserializers = {\n");
for (auto name_and_output_module_class_pair =
for (StringMap::iterator name_and_output_module_class_pair =
output_message_modules_and_classes.begin();
name_and_output_module_class_pair !=
output_message_modules_and_classes.end();
name_and_output_module_class_pair++) {
IndentScope raii_indent(out);
out->Print("(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): "
out->Print(
"(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): "
"$OutputTypeModuleAndClass$.FromString,\n",
"PackageQualifiedServiceName", package_qualified_service_name,
"MethodName", name_and_output_module_class_pair->first,
@ -456,35 +467,39 @@ bool PrintBetaStubFactory(const grpc::string& package_qualified_service_name,
}
out->Print("}\n");
out->Print("cardinalities = {\n");
for (auto name_and_cardinality = method_cardinalities.begin();
for (StringMap::iterator name_and_cardinality =
method_cardinalities.begin();
name_and_cardinality != method_cardinalities.end();
name_and_cardinality++) {
IndentScope raii_descriptions_indent(out);
out->Print("\'$Method$\': cardinality.Cardinality.$Cardinality$,\n",
"Method", name_and_cardinality->first,
"Cardinality", name_and_cardinality->second);
"Method", name_and_cardinality->first, "Cardinality",
name_and_cardinality->second);
}
out->Print("}\n");
out->Print("stub_options = beta_implementations.stub_options("
out->Print(
"stub_options = beta_implementations.stub_options("
"host=host, metadata_transformer=metadata_transformer, "
"request_serializers=request_serializers, "
"response_deserializers=response_deserializers, "
"thread_pool=pool, thread_pool_size=pool_size)\n");
out->Print(
"return beta_implementations.dynamic_stub(channel, \'$PackageQualifiedServiceName$\', "
"return beta_implementations.dynamic_stub(channel, "
"\'$PackageQualifiedServiceName$\', "
"cardinalities, options=stub_options)\n",
"PackageQualifiedServiceName", package_qualified_service_name);
}
return true;
}
bool PrintStub(const grpc::string& package_qualified_service_name,
const ServiceDescriptor* service, Printer* out) {
bool PrivateGenerator::PrintStub(
const grpc::string& package_qualified_service_name,
const ServiceDescriptor* service) {
out->Print("\n\n");
out->Print("class $Service$Stub(object):\n", "Service", service->name());
{
IndentScope raii_class_indent(out);
PrintAllComments(service, out);
PrintAllComments(service);
out->Print("\n");
out->Print("def __init__(self, channel):\n");
{
@ -498,29 +513,27 @@ bool PrintStub(const grpc::string& package_qualified_service_name,
}
out->Print("\"\"\"\n");
for (int i = 0; i < service->method_count(); ++i) {
auto method = service->method(i);
auto multi_callable_constructor =
const MethodDescriptor* method = service->method(i);
grpc::string multi_callable_constructor =
grpc::string(method->client_streaming() ? "stream" : "unary") +
"_" +
grpc::string(method->server_streaming() ? "stream" : "unary");
"_" + grpc::string(method->server_streaming() ? "stream" : "unary");
grpc::string request_module_and_class;
if (!GetModuleAndMessagePath(method->input_type(), service,
if (!GetModuleAndMessagePath(method->input_type(),
&request_module_and_class)) {
return false;
}
grpc::string response_module_and_class;
if (!GetModuleAndMessagePath(method->output_type(), service,
if (!GetModuleAndMessagePath(method->output_type(),
&response_module_and_class)) {
return false;
}
out->Print("self.$Method$ = channel.$MultiCallableConstructor$(\n",
"Method", method->name(),
"MultiCallableConstructor", multi_callable_constructor);
"Method", method->name(), "MultiCallableConstructor",
multi_callable_constructor);
{
IndentScope raii_first_attribute_indent(out);
IndentScope raii_second_attribute_indent(out);
out->Print(
"'/$PackageQualifiedService$/$Method$',\n",
out->Print("'/$PackageQualifiedService$/$Method$',\n",
"PackageQualifiedService", package_qualified_service_name,
"Method", method->name());
out->Print(
@ -537,22 +550,22 @@ bool PrintStub(const grpc::string& package_qualified_service_name,
return true;
}
bool PrintServicer(const ServiceDescriptor* service, Printer* out) {
bool PrivateGenerator::PrintServicer(const ServiceDescriptor* service) {
out->Print("\n\n");
out->Print("class $Service$Servicer(object):\n", "Service", service->name());
{
IndentScope raii_class_indent(out);
PrintAllComments(service, out);
PrintAllComments(service);
for (int i = 0; i < service->method_count(); ++i) {
auto method = service->method(i);
grpc::string arg_name = method->client_streaming() ?
"request_iterator" : "request";
const MethodDescriptor* method = service->method(i);
grpc::string arg_name =
method->client_streaming() ? "request_iterator" : "request";
out->Print("\n");
out->Print("def $Method$(self, $ArgName$, context):\n",
"Method", method->name(), "ArgName", arg_name);
out->Print("def $Method$(self, $ArgName$, context):\n", "Method",
method->name(), "ArgName", arg_name);
{
IndentScope raii_method_indent(out);
PrintAllComments(method, out);
PrintAllComments(method);
out->Print("context.set_code(grpc.StatusCode.UNIMPLEMENTED)\n");
out->Print("context.set_details('Method not implemented!')\n");
out->Print("raise NotImplementedError('Method not implemented!')\n");
@ -562,8 +575,9 @@ bool PrintServicer(const ServiceDescriptor* service, Printer* out) {
return true;
}
bool PrintAddServicerToServer(const grpc::string& package_qualified_service_name,
const ServiceDescriptor* service, Printer* out) {
bool PrivateGenerator::PrintAddServicerToServer(
const grpc::string& package_qualified_service_name,
const ServiceDescriptor* service) {
out->Print("\n\n");
out->Print("def add_$Service$Servicer_to_server(servicer, server):\n",
"Service", service->name());
@ -574,32 +588,35 @@ bool PrintAddServicerToServer(const grpc::string& package_qualified_service_name
IndentScope raii_dict_first_indent(out);
IndentScope raii_dict_second_indent(out);
for (int i = 0; i < service->method_count(); ++i) {
auto method = service->method(i);
auto method_handler_constructor =
const MethodDescriptor* method = service->method(i);
grpc::string method_handler_constructor =
grpc::string(method->client_streaming() ? "stream" : "unary") +
"_" +
grpc::string(method->server_streaming() ? "stream" : "unary") +
"_rpc_method_handler";
grpc::string request_module_and_class;
if (!GetModuleAndMessagePath(method->input_type(), service,
if (!GetModuleAndMessagePath(method->input_type(),
&request_module_and_class)) {
return false;
}
grpc::string response_module_and_class;
if (!GetModuleAndMessagePath(method->output_type(), service,
if (!GetModuleAndMessagePath(method->output_type(),
&response_module_and_class)) {
return false;
}
out->Print("'$Method$': grpc.$MethodHandlerConstructor$(\n",
"Method", method->name(),
"MethodHandlerConstructor", method_handler_constructor);
out->Print("'$Method$': grpc.$MethodHandlerConstructor$(\n", "Method",
method->name(), "MethodHandlerConstructor",
method_handler_constructor);
{
IndentScope raii_call_first_indent(out);
IndentScope raii_call_second_indent(out);
out->Print("servicer.$Method$,\n", "Method", method->name());
out->Print("request_deserializer=$RequestModuleAndClass$.FromString,\n",
out->Print(
"request_deserializer=$RequestModuleAndClass$.FromString,\n",
"RequestModuleAndClass", request_module_and_class);
out->Print("response_serializer=$ResponseModuleAndClass$.SerializeToString,\n",
out->Print(
"response_serializer=$ResponseModuleAndClass$.SerializeToString,"
"\n",
"ResponseModuleAndClass", response_module_and_class);
}
out->Print("),\n");
@ -618,49 +635,186 @@ bool PrintAddServicerToServer(const grpc::string& package_qualified_service_name
return true;
}
bool PrintPreamble(const FileDescriptor* file,
const GeneratorConfiguration& config, Printer* out) {
out->Print("import $Package$\n", "Package", config.grpc_package_root);
bool PrivateGenerator::PrintBetaPreamble() {
out->Print("from $Package$ import implementations as beta_implementations\n",
"Package", config.beta_package_root);
out->Print("from $Package$ import interfaces as beta_interfaces\n",
"Package", config.beta_package_root);
out->Print("from $Package$ import interfaces as beta_interfaces\n", "Package",
config.beta_package_root);
return true;
}
bool PrivateGenerator::PrintPreamble() {
out->Print("import $Package$\n", "Package", config.grpc_package_root);
out->Print("from grpc.framework.common import cardinality\n");
out->Print("from grpc.framework.interfaces.face import utilities as face_utilities\n");
out->Print(
"from grpc.framework.interfaces.face import utilities as "
"face_utilities\n");
if (generate_in_pb2_grpc) {
out->Print("\n");
for (int i = 0; i < file->service_count(); ++i) {
const ServiceDescriptor* service = file->service(i);
for (int j = 0; j < service->method_count(); ++j) {
const MethodDescriptor* method = service->method(j);
const Descriptor* types[2] = {method->input_type(),
method->output_type()};
for (int k = 0; k < 2; ++k) {
const Descriptor* type = types[k];
grpc::string type_file_name = type->file()->name();
grpc::string module_name = ModuleName(type_file_name);
grpc::string module_alias = ModuleAlias(type_file_name);
out->Print("import $ModuleName$ as $ModuleAlias$\n", "ModuleName",
module_name, "ModuleAlias", module_alias);
}
}
}
}
return true;
}
} // namespace
bool PrivateGenerator::PrintGAServices() {
grpc::string package = file->package();
if (!package.empty()) {
package = package.append(".");
}
for (int i = 0; i < file->service_count(); ++i) {
const ServiceDescriptor* service = file->service(i);
grpc::string package_qualified_service_name = package + service->name();
if (!(PrintStub(package_qualified_service_name, service) &&
PrintServicer(service) &&
PrintAddServicerToServer(package_qualified_service_name, service))) {
return false;
}
}
return true;
}
pair<bool, grpc::string> GetServices(const FileDescriptor* file,
const GeneratorConfiguration& config) {
bool PrivateGenerator::PrintBetaServices() {
grpc::string package = file->package();
if (!package.empty()) {
package = package.append(".");
}
for (int i = 0; i < file->service_count(); ++i) {
const ServiceDescriptor* service = file->service(i);
grpc::string package_qualified_service_name = package + service->name();
if (!(PrintBetaServicer(service) && PrintBetaStub(service) &&
PrintBetaServerFactory(package_qualified_service_name, service) &&
PrintBetaStubFactory(package_qualified_service_name, service))) {
return false;
}
}
return true;
}
pair<bool, grpc::string> PrivateGenerator::GetGrpcServices() {
grpc::string output;
{
// Scope the output stream so it closes and finalizes output to the string.
StringOutputStream output_stream(&output);
Printer out(&output_stream, '$');
if (!PrintPreamble(file, config, &out)) {
Printer out_printer(&output_stream, '$');
out = &out_printer;
if (generate_in_pb2_grpc) {
if (!PrintPreamble()) {
return make_pair(false, "");
}
auto package = file->package();
if (!package.empty()) {
package = package.append(".");
if (!PrintGAServices()) {
return make_pair(false, "");
}
for (int i = 0; i < file->service_count(); ++i) {
auto service = file->service(i);
auto package_qualified_service_name = package + service->name();
if (!(PrintStub(package_qualified_service_name, service, &out) &&
PrintServicer(service, &out) &&
PrintAddServicerToServer(package_qualified_service_name, service, &out) &&
PrintBetaServicer(service, &out) &&
PrintBetaStub(service, &out) &&
PrintBetaServerFactory(package_qualified_service_name, service, &out) &&
PrintBetaStubFactory(package_qualified_service_name, service, &out))) {
} else {
out->Print("try:\n");
{
IndentScope raii_dict_try_indent(out);
out->Print(
"# THESE ELEMENTS WILL BE DEPRECATED.\n"
"# Please use the generated *_pb2_grpc.py files instead.\n");
if (!PrintPreamble()) {
return make_pair(false, "");
}
if (!PrintBetaPreamble()) {
return make_pair(false, "");
}
if (!PrintGAServices()) {
return make_pair(false, "");
}
if (!PrintBetaServices()) {
return make_pair(false, "");
}
}
out->Print("except ImportError:\n");
{
IndentScope raii_dict_except_indent(out);
out->Print("pass");
}
}
}
return make_pair(true, std::move(output));
}
} // namespace
GeneratorConfiguration::GeneratorConfiguration()
: grpc_package_root("grpc"), beta_package_root("grpc.beta") {}
PythonGrpcGenerator::PythonGrpcGenerator(const GeneratorConfiguration& config)
: config_(config) {}
PythonGrpcGenerator::~PythonGrpcGenerator() {}
static bool GenerateGrpc(GeneratorContext* context, PrivateGenerator& generator,
grpc::string file_name, bool generate_in_pb2_grpc) {
bool success;
std::unique_ptr<ZeroCopyOutputStream> output;
std::unique_ptr<CodedOutputStream> coded_output;
grpc::string grpc_code;
if (generate_in_pb2_grpc) {
output.reset(context->Open(file_name));
generator.generate_in_pb2_grpc = true;
} else {
output.reset(context->OpenForInsert(file_name, "module_scope"));
generator.generate_in_pb2_grpc = false;
}
coded_output.reset(new CodedOutputStream(output.get()));
tie(success, grpc_code) = generator.GetGrpcServices();
if (success) {
coded_output->WriteRaw(grpc_code.data(), grpc_code.size());
return true;
} else {
return false;
}
}
bool PythonGrpcGenerator::Generate(const FileDescriptor* file,
const grpc::string& parameter,
GeneratorContext* context,
grpc::string* error) const {
// Get output file name.
grpc::string pb2_file_name;
grpc::string pb2_grpc_file_name;
static const int proto_suffix_length = strlen(".proto");
if (file->name().size() > static_cast<size_t>(proto_suffix_length) &&
file->name().find_last_of(".proto") == file->name().size() - 1) {
grpc::string base =
file->name().substr(0, file->name().size() - proto_suffix_length);
pb2_file_name = base + "_pb2.py";
pb2_grpc_file_name = base + "_pb2_grpc.py";
} else {
*error = "Invalid proto file name. Proto file must end with .proto";
return false;
}
PrivateGenerator generator(config_, file);
if (parameter == "grpc_2_0") {
return GenerateGrpc(context, generator, pb2_grpc_file_name, true);
} else if (parameter == "") {
return GenerateGrpc(context, generator, pb2_grpc_file_name, true) &&
GenerateGrpc(context, generator, pb2_file_name, false);
} else {
*error = "Invalid parameter '" + parameter + "'.";
return false;
}
}
} // namespace grpc_python_generator

@ -57,14 +57,11 @@ class PythonGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator {
const grpc::string& parameter,
grpc::protobuf::compiler::GeneratorContext* context,
grpc::string* error) const;
private:
GeneratorConfiguration config_;
};
std::pair<bool, grpc::string> GetServices(
const grpc::protobuf::FileDescriptor* file,
const GeneratorConfiguration& config);
} // namespace grpc_python_generator
#endif // GRPC_INTERNAL_COMPILER_PYTHON_GENERATOR_H

@ -34,6 +34,7 @@ cdef class Call:
def __cinit__(self):
# Create an *empty* call
grpc_init()
self.c_call = NULL
self.references = []
@ -106,6 +107,7 @@ cdef class Call:
def __dealloc__(self):
if self.c_call != NULL:
grpc_call_destroy(self.c_call)
grpc_shutdown()
# The object *should* always be valid from Python. Used for debugging.
@property

@ -34,6 +34,7 @@ cdef class Channel:
def __cinit__(self, bytes target, ChannelArgs arguments=None,
ChannelCredentials channel_credentials=None):
grpc_init()
cdef grpc_channel_args *c_arguments = NULL
cdef char *c_target = NULL
self.c_channel = NULL
@ -103,3 +104,4 @@ cdef class Channel:
def __dealloc__(self):
if self.c_channel != NULL:
grpc_channel_destroy(self.c_channel)
grpc_shutdown()

@ -38,6 +38,7 @@ cdef int _INTERRUPT_CHECK_PERIOD_MS = 200
cdef class CompletionQueue:
def __cinit__(self):
grpc_init()
with nogil:
self.c_completion_queue = grpc_completion_queue_create(NULL)
self.is_shutting_down = False
@ -129,3 +130,4 @@ cdef class CompletionQueue:
self.c_completion_queue, c_deadline, NULL)
self._interpret_event(event)
grpc_completion_queue_destroy(self.c_completion_queue)
grpc_shutdown()

@ -33,6 +33,7 @@ cimport cpython
cdef class ChannelCredentials:
def __cinit__(self):
grpc_init()
self.c_credentials = NULL
self.c_ssl_pem_key_cert_pair.private_key = NULL
self.c_ssl_pem_key_cert_pair.certificate_chain = NULL
@ -47,11 +48,13 @@ cdef class ChannelCredentials:
def __dealloc__(self):
if self.c_credentials != NULL:
grpc_channel_credentials_release(self.c_credentials)
grpc_shutdown()
cdef class CallCredentials:
def __cinit__(self):
grpc_init()
self.c_credentials = NULL
self.references = []
@ -64,17 +67,20 @@ cdef class CallCredentials:
def __dealloc__(self):
if self.c_credentials != NULL:
grpc_call_credentials_release(self.c_credentials)
grpc_shutdown()
cdef class ServerCredentials:
def __cinit__(self):
grpc_init()
self.c_credentials = NULL
self.references = []
def __dealloc__(self):
if self.c_credentials != NULL:
grpc_server_credentials_release(self.c_credentials)
grpc_shutdown()
cdef class CredentialsMetadataPlugin:
@ -90,6 +96,7 @@ cdef class CredentialsMetadataPlugin:
successful).
name (bytes): Plugin name.
"""
grpc_init()
if not callable(plugin_callback):
raise ValueError('expected callable plugin_callback')
self.plugin_callback = plugin_callback
@ -105,10 +112,14 @@ cdef class CredentialsMetadataPlugin:
cpython.Py_INCREF(self)
return result
def __dealloc__(self):
grpc_shutdown()
cdef class AuthMetadataContext:
def __cinit__(self):
grpc_init()
self.context.service_url = NULL
self.context.method_name = NULL
@ -120,6 +131,9 @@ cdef class AuthMetadataContext:
def method_name(self):
return self.context.method_name
def __dealloc__(self):
grpc_shutdown()
cdef void plugin_get_metadata(
void *state, grpc_auth_metadata_context context,

@ -176,12 +176,14 @@ cdef class Timespec:
cdef class CallDetails:
def __cinit__(self):
grpc_init()
with nogil:
grpc_call_details_init(&self.c_details)
def __dealloc__(self):
with nogil:
grpc_call_details_destroy(&self.c_details)
grpc_shutdown()
@property
def method(self):
@ -232,6 +234,7 @@ cdef class Event:
cdef class ByteBuffer:
def __cinit__(self, bytes data):
grpc_init()
if data is None:
self.c_byte_buffer = NULL
return
@ -288,6 +291,7 @@ cdef class ByteBuffer:
def __dealloc__(self):
if self.c_byte_buffer != NULL:
grpc_byte_buffer_destroy(self.c_byte_buffer)
grpc_shutdown()
cdef class SslPemKeyCertPair:
@ -319,6 +323,7 @@ cdef class ChannelArg:
cdef class ChannelArgs:
def __cinit__(self, args):
grpc_init()
self.args = list(args)
for arg in self.args:
if not isinstance(arg, ChannelArg):
@ -333,6 +338,7 @@ cdef class ChannelArgs:
def __dealloc__(self):
with nogil:
gpr_free(self.c_args.arguments)
grpc_shutdown()
def __len__(self):
# self.args is never stale; it's only updated from this file
@ -399,6 +405,7 @@ cdef class _MetadataIterator:
cdef class Metadata:
def __cinit__(self, metadata):
grpc_init()
self.metadata = list(metadata)
for metadatum in metadata:
if not isinstance(metadatum, Metadatum):
@ -420,6 +427,7 @@ cdef class Metadata:
# it'd be nice if that were documented somewhere...)
# TODO(atash): document this in the C core
grpc_metadata_array_destroy(&self.c_metadata_array)
grpc_shutdown()
def __len__(self):
return self.c_metadata_array.count
@ -437,6 +445,7 @@ cdef class Metadata:
cdef class Operation:
def __cinit__(self):
grpc_init()
self.references = []
self._received_status_details = NULL
self._received_status_details_capacity = 0
@ -529,6 +538,7 @@ cdef class Operation:
# This means that we need to clean up after receive_status_on_client.
if self.c_op.type == GRPC_OP_RECV_STATUS_ON_CLIENT:
gpr_free(self._received_status_details)
grpc_shutdown()
def operation_send_initial_metadata(Metadata metadata, int flags):
cdef Operation op = Operation()
@ -645,6 +655,7 @@ cdef class _OperationsIterator:
cdef class Operations:
def __cinit__(self, operations):
grpc_init()
self.operations = list(operations) # normalize iterable
self.c_ops = NULL
self.c_nops = 0
@ -667,6 +678,7 @@ cdef class Operations:
def __dealloc__(self):
with nogil:
gpr_free(self.c_ops)
grpc_shutdown()
def __iter__(self):
return _OperationsIterator(self)

@ -35,6 +35,7 @@ import time
cdef class Server:
def __cinit__(self, ChannelArgs arguments=None):
grpc_init()
cdef grpc_channel_args *c_arguments = NULL
self.references = []
self.registered_completion_queues = []
@ -172,3 +173,4 @@ cdef class Server:
while not self.is_shutdown:
time.sleep(0)
grpc_server_destroy(self.c_server)
grpc_shutdown()

@ -55,12 +55,8 @@ cdef extern from "Python.h":
def _initialize():
grpc_init()
grpc_set_ssl_roots_override_callback(
<grpc_ssl_roots_override_callback>ssl_roots_override_callback)
if Py_AtExit(grpc_shutdown) != 0:
raise ImportError('failed to register gRPC library shutdown callbacks')
_initialize()

@ -1,5 +1,6 @@
*.proto
*_pb2.py
*_pb2_grpc.py
build/
grpcio_health_checking.egg-info/
dist/

@ -1,4 +1,5 @@
proto/
src/
*_pb2.py
*_pb2_grpc.py
*.egg-info/

@ -0,0 +1,304 @@
# Copyright 2016, 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.
import collections
from concurrent import futures
import contextlib
import distutils.spawn
import errno
import importlib
import os
import os.path
import pkgutil
import shutil
import subprocess
import sys
import tempfile
import threading
import unittest
import grpc
from grpc.tools import protoc
from tests.unit.framework.common import test_constants
_MESSAGES_IMPORT = b'import "messages.proto";'
@contextlib.contextmanager
def _system_path(path):
old_system_path = sys.path[:]
sys.path = sys.path[0:1] + path + sys.path[1:]
yield
sys.path = old_system_path
class DummySplitServicer(object):
def __init__(self, request_class, response_class):
self.request_class = request_class
self.response_class = response_class
def Call(self, request, context):
return self.response_class()
class SeparateTestMixin(object):
def testImportAttributes(self):
with _system_path([self.python_out_directory]):
pb2 = importlib.import_module(self.pb2_import)
pb2.Request
pb2.Response
if self.should_find_services_in_pb2:
pb2.TestServiceServicer
else:
with self.assertRaises(AttributeError):
pb2.TestServiceServicer
with _system_path([self.grpc_python_out_directory]):
pb2_grpc = importlib.import_module(self.pb2_grpc_import)
pb2_grpc.TestServiceServicer
with self.assertRaises(AttributeError):
pb2_grpc.Request
with self.assertRaises(AttributeError):
pb2_grpc.Response
def testCall(self):
with _system_path([self.python_out_directory]):
pb2 = importlib.import_module(self.pb2_import)
with _system_path([self.grpc_python_out_directory]):
pb2_grpc = importlib.import_module(self.pb2_grpc_import)
server = grpc.server(
futures.ThreadPoolExecutor(max_workers=test_constants.POOL_SIZE))
pb2_grpc.add_TestServiceServicer_to_server(
DummySplitServicer(
pb2.Request, pb2.Response), server)
port = server.add_insecure_port('[::]:0')
server.start()
channel = grpc.insecure_channel('localhost:{}'.format(port))
stub = pb2_grpc.TestServiceStub(channel)
request = pb2.Request()
expected_response = pb2.Response()
response = stub.Call(request)
self.assertEqual(expected_response, response)
class CommonTestMixin(object):
def testImportAttributes(self):
with _system_path([self.python_out_directory]):
pb2 = importlib.import_module(self.pb2_import)
pb2.Request
pb2.Response
if self.should_find_services_in_pb2:
pb2.TestServiceServicer
else:
with self.assertRaises(AttributeError):
pb2.TestServiceServicer
with _system_path([self.grpc_python_out_directory]):
pb2_grpc = importlib.import_module(self.pb2_grpc_import)
pb2_grpc.TestServiceServicer
with self.assertRaises(AttributeError):
pb2_grpc.Request
with self.assertRaises(AttributeError):
pb2_grpc.Response
def testCall(self):
with _system_path([self.python_out_directory]):
pb2 = importlib.import_module(self.pb2_import)
with _system_path([self.grpc_python_out_directory]):
pb2_grpc = importlib.import_module(self.pb2_grpc_import)
server = grpc.server(
futures.ThreadPoolExecutor(max_workers=test_constants.POOL_SIZE))
pb2_grpc.add_TestServiceServicer_to_server(
DummySplitServicer(
pb2.Request, pb2.Response), server)
port = server.add_insecure_port('[::]:0')
server.start()
channel = grpc.insecure_channel('localhost:{}'.format(port))
stub = pb2_grpc.TestServiceStub(channel)
request = pb2.Request()
expected_response = pb2.Response()
response = stub.Call(request)
self.assertEqual(expected_response, response)
class SameSeparateTest(unittest.TestCase, SeparateTestMixin):
def setUp(self):
same_proto_contents = pkgutil.get_data(
'tests.protoc_plugin.protos.invocation_testing', 'same.proto')
self.directory = tempfile.mkdtemp(suffix='same_separate', dir='.')
self.proto_directory = os.path.join(self.directory, 'proto_path')
self.python_out_directory = os.path.join(self.directory, 'python_out')
self.grpc_python_out_directory = os.path.join(self.directory, 'grpc_python_out')
os.makedirs(self.proto_directory)
os.makedirs(self.python_out_directory)
os.makedirs(self.grpc_python_out_directory)
same_proto_file = os.path.join(self.proto_directory, 'same_separate.proto')
open(same_proto_file, 'wb').write(same_proto_contents)
protoc_result = protoc.main([
'',
'--proto_path={}'.format(self.proto_directory),
'--python_out={}'.format(self.python_out_directory),
'--grpc_python_out=grpc_2_0:{}'.format(self.grpc_python_out_directory),
same_proto_file,
])
if protoc_result != 0:
raise Exception("unexpected protoc error")
open(os.path.join(self.grpc_python_out_directory, '__init__.py'), 'w').write('')
open(os.path.join(self.python_out_directory, '__init__.py'), 'w').write('')
self.pb2_import = 'same_separate_pb2'
self.pb2_grpc_import = 'same_separate_pb2_grpc'
self.should_find_services_in_pb2 = False
def tearDown(self):
shutil.rmtree(self.directory)
class SameCommonTest(unittest.TestCase, CommonTestMixin):
def setUp(self):
same_proto_contents = pkgutil.get_data(
'tests.protoc_plugin.protos.invocation_testing', 'same.proto')
self.directory = tempfile.mkdtemp(suffix='same_common', dir='.')
self.proto_directory = os.path.join(self.directory, 'proto_path')
self.python_out_directory = os.path.join(self.directory, 'python_out')
self.grpc_python_out_directory = self.python_out_directory
os.makedirs(self.proto_directory)
os.makedirs(self.python_out_directory)
same_proto_file = os.path.join(self.proto_directory, 'same_common.proto')
open(same_proto_file, 'wb').write(same_proto_contents)
protoc_result = protoc.main([
'',
'--proto_path={}'.format(self.proto_directory),
'--python_out={}'.format(self.python_out_directory),
'--grpc_python_out={}'.format(self.grpc_python_out_directory),
same_proto_file,
])
if protoc_result != 0:
raise Exception("unexpected protoc error")
open(os.path.join(self.python_out_directory, '__init__.py'), 'w').write('')
self.pb2_import = 'same_common_pb2'
self.pb2_grpc_import = 'same_common_pb2_grpc'
self.should_find_services_in_pb2 = True
def tearDown(self):
shutil.rmtree(self.directory)
class SplitCommonTest(unittest.TestCase, CommonTestMixin):
def setUp(self):
services_proto_contents = pkgutil.get_data(
'tests.protoc_plugin.protos.invocation_testing.split_services',
'services.proto')
messages_proto_contents = pkgutil.get_data(
'tests.protoc_plugin.protos.invocation_testing.split_messages',
'messages.proto')
self.directory = tempfile.mkdtemp(suffix='split_common', dir='.')
self.proto_directory = os.path.join(self.directory, 'proto_path')
self.python_out_directory = os.path.join(self.directory, 'python_out')
self.grpc_python_out_directory = self.python_out_directory
os.makedirs(self.proto_directory)
os.makedirs(self.python_out_directory)
services_proto_file = os.path.join(self.proto_directory,
'split_common_services.proto')
messages_proto_file = os.path.join(self.proto_directory,
'split_common_messages.proto')
open(services_proto_file, 'wb').write(services_proto_contents.replace(
_MESSAGES_IMPORT,
b'import "split_common_messages.proto";'
))
open(messages_proto_file, 'wb').write(messages_proto_contents)
protoc_result = protoc.main([
'',
'--proto_path={}'.format(self.proto_directory),
'--python_out={}'.format(self.python_out_directory),
'--grpc_python_out={}'.format(self.grpc_python_out_directory),
services_proto_file,
messages_proto_file,
])
if protoc_result != 0:
raise Exception("unexpected protoc error")
open(os.path.join(self.python_out_directory, '__init__.py'), 'w').write('')
self.pb2_import = 'split_common_messages_pb2'
self.pb2_grpc_import = 'split_common_services_pb2_grpc'
self.should_find_services_in_pb2 = False
def tearDown(self):
shutil.rmtree(self.directory)
class SplitSeparateTest(unittest.TestCase, SeparateTestMixin):
def setUp(self):
services_proto_contents = pkgutil.get_data(
'tests.protoc_plugin.protos.invocation_testing.split_services',
'services.proto')
messages_proto_contents = pkgutil.get_data(
'tests.protoc_plugin.protos.invocation_testing.split_messages',
'messages.proto')
self.directory = tempfile.mkdtemp(suffix='split_separate', dir='.')
self.proto_directory = os.path.join(self.directory, 'proto_path')
self.python_out_directory = os.path.join(self.directory, 'python_out')
self.grpc_python_out_directory = os.path.join(self.directory, 'grpc_python_out')
os.makedirs(self.proto_directory)
os.makedirs(self.python_out_directory)
os.makedirs(self.grpc_python_out_directory)
services_proto_file = os.path.join(self.proto_directory,
'split_separate_services.proto')
messages_proto_file = os.path.join(self.proto_directory,
'split_separate_messages.proto')
open(services_proto_file, 'wb').write(services_proto_contents.replace(
_MESSAGES_IMPORT,
b'import "split_separate_messages.proto";'
))
open(messages_proto_file, 'wb').write(messages_proto_contents)
protoc_result = protoc.main([
'',
'--proto_path={}'.format(self.proto_directory),
'--python_out={}'.format(self.python_out_directory),
'--grpc_python_out=grpc_2_0:{}'.format(self.grpc_python_out_directory),
services_proto_file,
messages_proto_file,
])
if protoc_result != 0:
raise Exception("unexpected protoc error")
open(os.path.join(self.python_out_directory, '__init__.py'), 'w').write('')
self.pb2_import = 'split_separate_messages_pb2'
self.pb2_grpc_import = 'split_separate_services_pb2_grpc'
self.should_find_services_in_pb2 = False
def tearDown(self):
shutil.rmtree(self.directory)
if __name__ == '__main__':
unittest.main(verbosity=2)

@ -0,0 +1,30 @@
# Copyright 2016, 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.

@ -0,0 +1,39 @@
// Copyright 2016, 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.
syntax = "proto3";
package grpc_protoc_plugin.invocation_testing;
message Request {}
message Response {}
service TestService {
rpc Call(Request) returns (Response);
}

@ -0,0 +1,30 @@
# Copyright 2016, 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.

@ -0,0 +1,35 @@
// Copyright 2016, 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.
syntax = "proto3";
package grpc_protoc_plugin.invocation_testing.split;
message Request {}
message Response {}

@ -0,0 +1,30 @@
# Copyright 2016, 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.

@ -0,0 +1,38 @@
// Copyright 2016, 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.
syntax = "proto3";
import "messages.proto";
package grpc_protoc_plugin.invocation_testing.split;
service TestService {
rpc Call(Request) returns (Response);
}

@ -34,6 +34,10 @@
"_rpc_test.RPCTest",
"_sanity_test.Sanity",
"_secure_interop_test.SecureInteropTest",
"_split_definitions_test.SameCommonTest",
"_split_definitions_test.SameSeparateTest",
"_split_definitions_test.SplitCommonTest",
"_split_definitions_test.SplitSeparateTest",
"_thread_cleanup_test.CleanupThreadTest",
"_utilities_test.ChannelConnectivityTest",
"beta_python_plugin_test.PythonPluginTest",

Loading…
Cancel
Save