From 59af1bf4689d2992d23e0493465caec455bd05b2 Mon Sep 17 00:00:00 2001 From: Muxi Yan Date: Fri, 28 Oct 2016 10:29:43 -0700 Subject: [PATCH 01/45] Update messages.proto and add a new test --- src/objective-c/tests/InteropTests.m | 48 +++++++++++++ .../tests/RemoteTestClient/messages.proto | 71 ++++++++++++++++--- 2 files changed, 110 insertions(+), 9 deletions(-) diff --git a/src/objective-c/tests/InteropTests.m b/src/objective-c/tests/InteropTests.m index f04a7e6441f..f38e703005b 100644 --- a/src/objective-c/tests/InteropTests.m +++ b/src/objective-c/tests/InteropTests.m @@ -321,6 +321,54 @@ [self waitForExpectationsWithTimeout:4 handler:nil]; } +- (void)testErroneousPingPongRPC { + XCTAssertNotNil(self.class.host); + __weak XCTestExpectation *expectation = [self expectationWithDescription:@"PingPong"]; + + NSArray *requests = @[@27182, @8, @1828, @45904]; + NSArray *responses = @[@31415, @9, @2653, @58979]; + + GRXBufferedPipe *requestsBuffer = [[GRXBufferedPipe alloc] init]; + + __block int index = 0; + + RMTStreamingOutputCallRequest *request = + [RMTStreamingOutputCallRequest messageWithPayloadSize:requests[index] + requestedResponseSize:responses[index]]; + + [requestsBuffer writeValue:request]; + + [_service fullDuplexCallWithRequestsWriter:requestsBuffer + eventHandler:^(BOOL done, + RMTStreamingOutputCallResponse *response, + NSError *error) { + if (index == 0) { + XCTAssertNil(error, @"Finished with unexpected error: %@", error); + XCTAssertNotNil(response, @"Event handler called without an event."); + XCTAssertFalse(done); + + id expected = [RMTStreamingOutputCallResponse messageWithPayloadSize:responses[index]]; + XCTAssertEqualObjects(response, expected); + index += 1; + + RMTStreamingOutputCallRequest *request = + [RMTStreamingOutputCallRequest messageWithPayloadSize:requests[index] + requestedResponseSize:responses[index]]; + RMTEchoStatus *status = [RMTEchoStatus message]; + status.code = 7; + status.message = @"Error message!"; + request.responseStatus = status; + [requestsBuffer writeValue:request]; + } else { + XCTAssertNil(response); + XCTAssertNotNil(error); + + [expectation fulfill]; + } + }]; + [self waitForExpectationsWithTimeout:4 handler:nil]; +} + #ifndef GRPC_COMPILE_WITH_CRONET // TODO(makdharma@): Fix this test - (void)testEmptyStreamRPC { diff --git a/src/objective-c/tests/RemoteTestClient/messages.proto b/src/objective-c/tests/RemoteTestClient/messages.proto index 85d93c2ff96..b8b8b668d04 100644 --- a/src/objective-c/tests/RemoteTestClient/messages.proto +++ b/src/objective-c/tests/RemoteTestClient/messages.proto @@ -1,4 +1,5 @@ -// Copyright 2015, Google Inc. + +// Copyright 2015-2016, Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -35,34 +36,45 @@ package grpc.testing; option objc_class_prefix = "RMT"; +// TODO(dgq): Go back to using well-known types once +// https://github.com/grpc/grpc/issues/6980 has been fixed. +// import "google/protobuf/wrappers.proto"; +message BoolValue { + // The bool value. + bool value = 1; +} + +// DEPRECATED, don't use. To be removed shortly. // The type of payload that should be returned. enum PayloadType { // Compressable text format. COMPRESSABLE = 0; - - // Uncompressable binary format. - UNCOMPRESSABLE = 1; - - // Randomly chosen from all other formats defined in this enum. - RANDOM = 2; } // A block of data, to simply increase gRPC message size. message Payload { + // DEPRECATED, don't use. To be removed shortly. // The type of data in body. PayloadType type = 1; // Primary contents of payload. bytes body = 2; } +// A protobuf representation for grpc status. This is used by test +// clients to specify a status that the server should attempt to return. +message EchoStatus { + int32 code = 1; + string message = 2; +} + // Unary request. message SimpleRequest { + // DEPRECATED, don't use. To be removed shortly. // Desired payload type in the response from the server. // If response_type is RANDOM, server randomly chooses one from other formats. PayloadType response_type = 1; // Desired payload size in the response from the server. - // If response_type is COMPRESSABLE, this denotes the size before compression. int32 response_size = 2; // Optional input payload sent along with the request. @@ -73,6 +85,18 @@ message SimpleRequest { // Whether SimpleResponse should include OAuth scope. bool fill_oauth_scope = 5; + + // Whether to request the server to compress the response. This field is + // "nullable" in order to interoperate seamlessly with clients not able to + // implement the full compression tests by introspecting the call to verify + // the response's compression status. + BoolValue response_compressed = 6; + + // Whether server should return a given status + EchoStatus response_status = 7; + + // Whether the server should expect this request to be compressed. + BoolValue expect_compressed = 8; } // Unary response, as configured by the request. @@ -91,6 +115,12 @@ message StreamingInputCallRequest { // Optional input payload sent along with the request. Payload payload = 1; + // Whether the server should expect this request to be compressed. This field + // is "nullable" in order to interoperate seamlessly with servers not able to + // implement the full compression tests by introspecting the call to verify + // the request's compression status. + BoolValue expect_compressed = 2; + // Not expecting any payload from the response. } @@ -103,16 +133,22 @@ message StreamingInputCallResponse { // Configuration for a particular response. message ResponseParameters { // Desired payload sizes in responses from the server. - // If response_type is COMPRESSABLE, this denotes the size before compression. int32 size = 1; // Desired interval between consecutive responses in the response stream in // microseconds. int32 interval_us = 2; + + // Whether to request the server to compress the response. This field is + // "nullable" in order to interoperate seamlessly with clients not able to + // implement the full compression tests by introspecting the call to verify + // the response's compression status. + BoolValue compressed = 3; } // Server-streaming request. message StreamingOutputCallRequest { + // DEPRECATED, don't use. To be removed shortly. // Desired payload type in the response from the server. // If response_type is RANDOM, the payload from each response in the stream // might be of different types. This is to simulate a mixed type of payload @@ -124,6 +160,9 @@ message StreamingOutputCallRequest { // Optional input payload sent along with the request. Payload payload = 3; + + // Whether server should return a given status + EchoStatus response_status = 7; } // Server-streaming response, as configured by the request and parameters. @@ -131,3 +170,17 @@ message StreamingOutputCallResponse { // Payload to increase response size. Payload payload = 1; } + +// For reconnect interop test only. +// Client tells server what reconnection parameters it used. +message ReconnectParams { + int32 max_reconnect_backoff_ms = 1; +} + +// For reconnect interop test only. +// Server tells client whether its reconnects are following the spec and the +// reconnect backoffs it saw. +message ReconnectInfo { + bool passed = 1; + repeated int32 backoff_ms = 2; +} From 14fe5187babcea2117d680ed212e92e6c6107bbc Mon Sep 17 00:00:00 2001 From: Muxi Yan Date: Thu, 10 Nov 2016 17:20:29 -0800 Subject: [PATCH 02/45] tests hacks --- src/core/ext/transport/chttp2/transport/writing.c | 4 ++-- src/objective-c/tests/InteropTests.m | 13 +++++-------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/core/ext/transport/chttp2/transport/writing.c b/src/core/ext/transport/chttp2/transport/writing.c index e0d87725e9d..cef4fe3ee6a 100644 --- a/src/core/ext/transport/chttp2/transport/writing.c +++ b/src/core/ext/transport/chttp2/transport/writing.c @@ -301,10 +301,10 @@ static void finalize_outbuf(grpc_exec_ctx *exec_ctx, &transport_writing->outbuf); } if (!transport_writing->is_client && !stream_writing->read_closed) { - gpr_slice_buffer_add(&transport_writing->outbuf, +/* gpr_slice_buffer_add(&transport_writing->outbuf, grpc_chttp2_rst_stream_create( stream_writing->id, GRPC_CHTTP2_NO_ERROR, - &stream_writing->stats)); + &stream_writing->stats)); */ } stream_writing->send_trailing_metadata = NULL; stream_writing->sent_trailing_metadata = 1; diff --git a/src/objective-c/tests/InteropTests.m b/src/objective-c/tests/InteropTests.m index f38e703005b..add63614e79 100644 --- a/src/objective-c/tests/InteropTests.m +++ b/src/objective-c/tests/InteropTests.m @@ -325,16 +325,13 @@ XCTAssertNotNil(self.class.host); __weak XCTestExpectation *expectation = [self expectationWithDescription:@"PingPong"]; - NSArray *requests = @[@27182, @8, @1828, @45904]; - NSArray *responses = @[@31415, @9, @2653, @58979]; - GRXBufferedPipe *requestsBuffer = [[GRXBufferedPipe alloc] init]; __block int index = 0; RMTStreamingOutputCallRequest *request = - [RMTStreamingOutputCallRequest messageWithPayloadSize:requests[index] - requestedResponseSize:responses[index]]; + [RMTStreamingOutputCallRequest messageWithPayloadSize:@256 + requestedResponseSize:@256]; [requestsBuffer writeValue:request]; @@ -347,13 +344,13 @@ XCTAssertNotNil(response, @"Event handler called without an event."); XCTAssertFalse(done); - id expected = [RMTStreamingOutputCallResponse messageWithPayloadSize:responses[index]]; + id expected = [RMTStreamingOutputCallResponse messageWithPayloadSize:@256]; XCTAssertEqualObjects(response, expected); index += 1; RMTStreamingOutputCallRequest *request = - [RMTStreamingOutputCallRequest messageWithPayloadSize:requests[index] - requestedResponseSize:responses[index]]; + [RMTStreamingOutputCallRequest messageWithPayloadSize:@256 + requestedResponseSize:@0]; RMTEchoStatus *status = [RMTEchoStatus message]; status.code = 7; status.message = @"Error message!"; From e10c33381910c30cb2f94d63a258e9d06bfedc0c Mon Sep 17 00:00:00 2001 From: Muxi Yan Date: Fri, 11 Nov 2016 16:23:15 -0800 Subject: [PATCH 03/45] Send reset from the client when server closes stream unexpectedly --- src/core/ext/transport/chttp2/transport/parsing.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/core/ext/transport/chttp2/transport/parsing.c b/src/core/ext/transport/chttp2/transport/parsing.c index e1fc0ddee20..6e86431b1c9 100644 --- a/src/core/ext/transport/chttp2/transport/parsing.c +++ b/src/core/ext/transport/chttp2/transport/parsing.c @@ -253,8 +253,16 @@ void grpc_chttp2_publish_reads( } if (stream_parsing->received_close) { - grpc_chttp2_mark_stream_closed(exec_ctx, transport_global, stream_global, - 1, 0, GRPC_ERROR_NONE); + if (transport_global->is_client && !stream_global->write_closed) { + gpr_slice_buffer_add(&transport_global->qbuf, + grpc_chttp2_rst_stream_create(transport_parsing->incoming_stream_id, GRPC_CHTTP2_NO_ERROR, &stream_parsing->stats.outgoing)); + grpc_chttp2_initiate_write(exec_ctx, transport_global, false, "rst"); + grpc_chttp2_mark_stream_closed(exec_ctx, transport_global, stream_global, + 1, 1, GRPC_ERROR_NONE); + } else { + grpc_chttp2_mark_stream_closed(exec_ctx, transport_global, stream_global, + 1, 0, GRPC_ERROR_NONE); + } } } } From 2ac52edbfe5c82a4693ab6cc72af404d108f6b01 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Tue, 1 Nov 2016 11:48:46 -0700 Subject: [PATCH 04/45] Ensure something executes the new rst_stream code --- .../server_hanging_response_1_header | Bin 0 -> 18 bytes .../server_hanging_response_2_header2 | Bin 0 -> 27 bytes ..._client_examples_of_bad_closing_streams.py | 49 ++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 test/core/end2end/fuzzers/client_fuzzer_corpus/server_hanging_response_1_header create mode 100644 test/core/end2end/fuzzers/client_fuzzer_corpus/server_hanging_response_2_header2 create mode 100755 test/core/end2end/fuzzers/generate_client_examples_of_bad_closing_streams.py diff --git a/test/core/end2end/fuzzers/client_fuzzer_corpus/server_hanging_response_1_header b/test/core/end2end/fuzzers/client_fuzzer_corpus/server_hanging_response_1_header new file mode 100644 index 0000000000000000000000000000000000000000..d2abd17464c60ac237e196886d309bba43a7014b GIT binary patch literal 18 ScmZQzU|?Z@0!CIKgAo7#ZU77b literal 0 HcmV?d00001 diff --git a/test/core/end2end/fuzzers/client_fuzzer_corpus/server_hanging_response_2_header2 b/test/core/end2end/fuzzers/client_fuzzer_corpus/server_hanging_response_2_header2 new file mode 100644 index 0000000000000000000000000000000000000000..752af468ba6f8c6b1a9bd95cc082ebed73cc3b6a GIT binary patch literal 27 VcmZQzU|?Z@0!9#v5rkPm1ONc+01^NI literal 0 HcmV?d00001 diff --git a/test/core/end2end/fuzzers/generate_client_examples_of_bad_closing_streams.py b/test/core/end2end/fuzzers/generate_client_examples_of_bad_closing_streams.py new file mode 100755 index 00000000000..d80c1e0442b --- /dev/null +++ b/test/core/end2end/fuzzers/generate_client_examples_of_bad_closing_streams.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python2.7 +# 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. + +import os +import sys + +os.chdir(os.path.dirname(sys.argv[0])) + +streams = { + 'server_hanging_response_1_header': ( + [0,0,0,4,0,0,0,0,0] + # settings frame + [0,0,0,1,5,0,0,0,1] # trailers + ), + 'server_hanging_response_2_header2': ( + [0,0,0,4,0,0,0,0,0] + # settings frame + [0,0,0,1,4,0,0,0,1] + # headers + [0,0,0,1,5,0,0,0,1] # trailers + ), +} + +for name, stream in streams.items(): + open('client_fuzzer_corpus/%s' % name, 'w').write(bytearray(stream)) From 10790401dd7f1faa4aa2ab5d4801b91d788d3e2d Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Tue, 1 Nov 2016 13:25:04 -0700 Subject: [PATCH 05/45] Missed file --- tools/run_tests/tests.json | 44 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tools/run_tests/tests.json b/tools/run_tests/tests.json index 88869e6497e..5ed5a0ebbcf 100644 --- a/tools/run_tests/tests.json +++ b/tools/run_tests/tests.json @@ -62714,6 +62714,50 @@ ], "uses_polling": false }, + { + "args": [ + "test/core/end2end/fuzzers/client_fuzzer_corpus/server_hanging_response_1_header" + ], + "ci_platforms": [ + "linux" + ], + "cpu_cost": 0.1, + "exclude_configs": [ + "tsan" + ], + "exclude_iomgrs": [ + "uv" + ], + "flaky": false, + "language": "c", + "name": "client_fuzzer_one_entry", + "platforms": [ + "linux" + ], + "uses_polling": false + }, + { + "args": [ + "test/core/end2end/fuzzers/client_fuzzer_corpus/server_hanging_response_2_header2" + ], + "ci_platforms": [ + "linux" + ], + "cpu_cost": 0.1, + "exclude_configs": [ + "tsan" + ], + "exclude_iomgrs": [ + "uv" + ], + "flaky": false, + "language": "c", + "name": "client_fuzzer_one_entry", + "platforms": [ + "linux" + ], + "uses_polling": false + }, { "args": [ "test/core/end2end/fuzzers/client_fuzzer_corpus/settings_frame_1.bin" From 72541d08fd71c6fdf0cac94a83929231de93cc6e Mon Sep 17 00:00:00 2001 From: Muxi Yan Date: Fri, 11 Nov 2016 17:14:44 -0800 Subject: [PATCH 06/45] undo test hack --- src/core/ext/transport/chttp2/transport/writing.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/ext/transport/chttp2/transport/writing.c b/src/core/ext/transport/chttp2/transport/writing.c index cef4fe3ee6a..e0d87725e9d 100644 --- a/src/core/ext/transport/chttp2/transport/writing.c +++ b/src/core/ext/transport/chttp2/transport/writing.c @@ -301,10 +301,10 @@ static void finalize_outbuf(grpc_exec_ctx *exec_ctx, &transport_writing->outbuf); } if (!transport_writing->is_client && !stream_writing->read_closed) { -/* gpr_slice_buffer_add(&transport_writing->outbuf, + gpr_slice_buffer_add(&transport_writing->outbuf, grpc_chttp2_rst_stream_create( stream_writing->id, GRPC_CHTTP2_NO_ERROR, - &stream_writing->stats)); */ + &stream_writing->stats)); } stream_writing->send_trailing_metadata = NULL; stream_writing->sent_trailing_metadata = 1; From e4a013d6545fe9ab14e8317fe5a88bedb553059c Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Mon, 21 Nov 2016 17:12:59 -0800 Subject: [PATCH 07/45] Re-enable Node 7 artifact build --- tools/run_tests/build_artifact_node.bat | 2 +- tools/run_tests/build_artifact_node.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/run_tests/build_artifact_node.bat b/tools/run_tests/build_artifact_node.bat index 57d55ef19ec..2e0ecd21d0f 100644 --- a/tools/run_tests/build_artifact_node.bat +++ b/tools/run_tests/build_artifact_node.bat @@ -27,7 +27,7 @@ @rem (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE @rem OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -set node_versions=0.12.0 1.0.0 1.1.0 2.0.0 3.0.0 4.0.0 5.0.0 6.0.0 +set node_versions=0.12.0 1.0.0 1.1.0 2.0.0 3.0.0 4.0.0 5.0.0 6.0.0 7.0.0 set PATH=%PATH%;C:\Program Files\nodejs\;%APPDATA%\npm diff --git a/tools/run_tests/build_artifact_node.sh b/tools/run_tests/build_artifact_node.sh index 9d06472aa49..778a5c95d4a 100755 --- a/tools/run_tests/build_artifact_node.sh +++ b/tools/run_tests/build_artifact_node.sh @@ -42,7 +42,7 @@ mkdir -p artifacts npm update -node_versions=( 0.12.0 1.0.0 1.1.0 2.0.0 3.0.0 4.0.0 5.0.0 6.0.0 ) +node_versions=( 0.12.0 1.0.0 1.1.0 2.0.0 3.0.0 4.0.0 5.0.0 6.0.0 7.0.0 ) for version in ${node_versions[@]} do From 03fc19876de683e3c8b4ca3ddc71af6c9756b07a Mon Sep 17 00:00:00 2001 From: Alexander Polcyn Date: Tue, 22 Nov 2016 14:24:49 -0800 Subject: [PATCH 08/45] wait for write loop to finish at end of ruby read loop, on client side calls --- src/ruby/lib/grpc/generic/bidi_call.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ruby/lib/grpc/generic/bidi_call.rb b/src/ruby/lib/grpc/generic/bidi_call.rb index 75ddff0bfd9..2b882313f2c 100644 --- a/src/ruby/lib/grpc/generic/bidi_call.rb +++ b/src/ruby/lib/grpc/generic/bidi_call.rb @@ -208,6 +208,10 @@ module GRPC GRPC.logger.debug('bidi-read-loop: finished') @reads_complete = true finished + # Make sure that the write loop is done done before finishing the call. + # Note that blocking is ok at this point because we've already received + # a status + @enq_th.join if is_client end end end From 3d48cf4ed30032d417c3b64df11b7fb45220388c Mon Sep 17 00:00:00 2001 From: Alexander Polcyn Date: Wed, 23 Nov 2016 09:50:22 -0800 Subject: [PATCH 09/45] dont break out of response stream iterator in benchmark client --- src/ruby/qps/client.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ruby/qps/client.rb b/src/ruby/qps/client.rb index 8aed866da5d..817192626be 100644 --- a/src/ruby/qps/client.rb +++ b/src/ruby/qps/client.rb @@ -134,6 +134,7 @@ class BenchmarkClient resp = stub.streaming_call(q.each_item) start = Time.now q.push(req) + pushed_sentinal = false resp.each do |r| @histogram.add((Time.now-start)*1e9) if !@done @@ -141,8 +142,9 @@ class BenchmarkClient start = Time.now q.push(req) else - q.push(self) - break + q.push(self) unless pushed_sentinal + # Continue polling on the responses to consume and release resources + pushed_sentinal = true end end end From 3f1db28ed01a87c4df0b7019b272c96b680ac5cb Mon Sep 17 00:00:00 2001 From: Muxi Yan Date: Wed, 23 Nov 2016 09:59:48 -0800 Subject: [PATCH 10/45] clang-format --- src/core/ext/transport/chttp2/transport/parsing.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/core/ext/transport/chttp2/transport/parsing.c b/src/core/ext/transport/chttp2/transport/parsing.c index 6e86431b1c9..cbbffc5a610 100644 --- a/src/core/ext/transport/chttp2/transport/parsing.c +++ b/src/core/ext/transport/chttp2/transport/parsing.c @@ -254,14 +254,17 @@ void grpc_chttp2_publish_reads( if (stream_parsing->received_close) { if (transport_global->is_client && !stream_global->write_closed) { - gpr_slice_buffer_add(&transport_global->qbuf, - grpc_chttp2_rst_stream_create(transport_parsing->incoming_stream_id, GRPC_CHTTP2_NO_ERROR, &stream_parsing->stats.outgoing)); + gpr_slice_buffer_add( + &transport_global->qbuf, + grpc_chttp2_rst_stream_create(transport_parsing->incoming_stream_id, + GRPC_CHTTP2_NO_ERROR, + &stream_parsing->stats.outgoing)); grpc_chttp2_initiate_write(exec_ctx, transport_global, false, "rst"); - grpc_chttp2_mark_stream_closed(exec_ctx, transport_global, stream_global, - 1, 1, GRPC_ERROR_NONE); + grpc_chttp2_mark_stream_closed(exec_ctx, transport_global, + stream_global, 1, 1, GRPC_ERROR_NONE); } else { - grpc_chttp2_mark_stream_closed(exec_ctx, transport_global, stream_global, - 1, 0, GRPC_ERROR_NONE); + grpc_chttp2_mark_stream_closed(exec_ctx, transport_global, + stream_global, 1, 0, GRPC_ERROR_NONE); } } } From 7f8a628f6ae1c67020c9137ea53d2f9f20b00a50 Mon Sep 17 00:00:00 2001 From: Alexander Polcyn Date: Wed, 23 Nov 2016 10:36:08 -0800 Subject: [PATCH 11/45] fix race between app and GC on ruby server shutdown --- src/ruby/ext/grpc/rb_server.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ruby/ext/grpc/rb_server.c b/src/ruby/ext/grpc/rb_server.c index bf26841fd22..6783fd3f88a 100644 --- a/src/ruby/ext/grpc/rb_server.c +++ b/src/ruby/ext/grpc/rb_server.c @@ -37,6 +37,7 @@ #include "rb_server.h" #include +#include #include #include #include "rb_call.h" @@ -59,11 +60,13 @@ typedef struct grpc_rb_server { /* The actual server */ grpc_server *wrapped; grpc_completion_queue *queue; + gpr_atm shutdown_started; } grpc_rb_server; static void destroy_server(grpc_rb_server *server, gpr_timespec deadline) { grpc_event ev; - if (server->wrapped != NULL) { + // This can be started by app or implicitly by GC. Avoid a race between these. + if (gpr_atm_full_fetch_add(&server->shutdown_started, (gpr_atm)1) == 0) { grpc_server_shutdown_and_notify(server->wrapped, server->queue, NULL); ev = rb_completion_queue_pluck(server->queue, NULL, deadline, NULL); if (ev.type == GRPC_QUEUE_TIMEOUT) { @@ -115,6 +118,7 @@ static const rb_data_type_t grpc_rb_server_data_type = { static VALUE grpc_rb_server_alloc(VALUE cls) { grpc_rb_server *wrapper = ALLOC(grpc_rb_server); wrapper->wrapped = NULL; + wrapper->shutdown_started = (gpr_atm)0; return TypedData_Wrap_Struct(cls, &grpc_rb_server_data_type, wrapper); } From 6ead007882233ec2664e66548adbb8c938c32752 Mon Sep 17 00:00:00 2001 From: Muxi Yan Date: Wed, 23 Nov 2016 12:09:09 -0800 Subject: [PATCH 12/45] Revert "Update messages.proto and add a new test" as the test is not supported in v1.0.x This reverts commit 59af1bf4689d2992d23e0493465caec455bd05b2. --- src/objective-c/tests/InteropTests.m | 45 ------------ .../tests/RemoteTestClient/messages.proto | 71 +++---------------- 2 files changed, 9 insertions(+), 107 deletions(-) diff --git a/src/objective-c/tests/InteropTests.m b/src/objective-c/tests/InteropTests.m index add63614e79..f04a7e6441f 100644 --- a/src/objective-c/tests/InteropTests.m +++ b/src/objective-c/tests/InteropTests.m @@ -321,51 +321,6 @@ [self waitForExpectationsWithTimeout:4 handler:nil]; } -- (void)testErroneousPingPongRPC { - XCTAssertNotNil(self.class.host); - __weak XCTestExpectation *expectation = [self expectationWithDescription:@"PingPong"]; - - GRXBufferedPipe *requestsBuffer = [[GRXBufferedPipe alloc] init]; - - __block int index = 0; - - RMTStreamingOutputCallRequest *request = - [RMTStreamingOutputCallRequest messageWithPayloadSize:@256 - requestedResponseSize:@256]; - - [requestsBuffer writeValue:request]; - - [_service fullDuplexCallWithRequestsWriter:requestsBuffer - eventHandler:^(BOOL done, - RMTStreamingOutputCallResponse *response, - NSError *error) { - if (index == 0) { - XCTAssertNil(error, @"Finished with unexpected error: %@", error); - XCTAssertNotNil(response, @"Event handler called without an event."); - XCTAssertFalse(done); - - id expected = [RMTStreamingOutputCallResponse messageWithPayloadSize:@256]; - XCTAssertEqualObjects(response, expected); - index += 1; - - RMTStreamingOutputCallRequest *request = - [RMTStreamingOutputCallRequest messageWithPayloadSize:@256 - requestedResponseSize:@0]; - RMTEchoStatus *status = [RMTEchoStatus message]; - status.code = 7; - status.message = @"Error message!"; - request.responseStatus = status; - [requestsBuffer writeValue:request]; - } else { - XCTAssertNil(response); - XCTAssertNotNil(error); - - [expectation fulfill]; - } - }]; - [self waitForExpectationsWithTimeout:4 handler:nil]; -} - #ifndef GRPC_COMPILE_WITH_CRONET // TODO(makdharma@): Fix this test - (void)testEmptyStreamRPC { diff --git a/src/objective-c/tests/RemoteTestClient/messages.proto b/src/objective-c/tests/RemoteTestClient/messages.proto index b8b8b668d04..85d93c2ff96 100644 --- a/src/objective-c/tests/RemoteTestClient/messages.proto +++ b/src/objective-c/tests/RemoteTestClient/messages.proto @@ -1,5 +1,4 @@ - -// Copyright 2015-2016, Google Inc. +// Copyright 2015, Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -36,45 +35,34 @@ package grpc.testing; option objc_class_prefix = "RMT"; -// TODO(dgq): Go back to using well-known types once -// https://github.com/grpc/grpc/issues/6980 has been fixed. -// import "google/protobuf/wrappers.proto"; -message BoolValue { - // The bool value. - bool value = 1; -} - -// DEPRECATED, don't use. To be removed shortly. // The type of payload that should be returned. enum PayloadType { // Compressable text format. COMPRESSABLE = 0; + + // Uncompressable binary format. + UNCOMPRESSABLE = 1; + + // Randomly chosen from all other formats defined in this enum. + RANDOM = 2; } // A block of data, to simply increase gRPC message size. message Payload { - // DEPRECATED, don't use. To be removed shortly. // The type of data in body. PayloadType type = 1; // Primary contents of payload. bytes body = 2; } -// A protobuf representation for grpc status. This is used by test -// clients to specify a status that the server should attempt to return. -message EchoStatus { - int32 code = 1; - string message = 2; -} - // Unary request. message SimpleRequest { - // DEPRECATED, don't use. To be removed shortly. // Desired payload type in the response from the server. // If response_type is RANDOM, server randomly chooses one from other formats. PayloadType response_type = 1; // Desired payload size in the response from the server. + // If response_type is COMPRESSABLE, this denotes the size before compression. int32 response_size = 2; // Optional input payload sent along with the request. @@ -85,18 +73,6 @@ message SimpleRequest { // Whether SimpleResponse should include OAuth scope. bool fill_oauth_scope = 5; - - // Whether to request the server to compress the response. This field is - // "nullable" in order to interoperate seamlessly with clients not able to - // implement the full compression tests by introspecting the call to verify - // the response's compression status. - BoolValue response_compressed = 6; - - // Whether server should return a given status - EchoStatus response_status = 7; - - // Whether the server should expect this request to be compressed. - BoolValue expect_compressed = 8; } // Unary response, as configured by the request. @@ -115,12 +91,6 @@ message StreamingInputCallRequest { // Optional input payload sent along with the request. Payload payload = 1; - // Whether the server should expect this request to be compressed. This field - // is "nullable" in order to interoperate seamlessly with servers not able to - // implement the full compression tests by introspecting the call to verify - // the request's compression status. - BoolValue expect_compressed = 2; - // Not expecting any payload from the response. } @@ -133,22 +103,16 @@ message StreamingInputCallResponse { // Configuration for a particular response. message ResponseParameters { // Desired payload sizes in responses from the server. + // If response_type is COMPRESSABLE, this denotes the size before compression. int32 size = 1; // Desired interval between consecutive responses in the response stream in // microseconds. int32 interval_us = 2; - - // Whether to request the server to compress the response. This field is - // "nullable" in order to interoperate seamlessly with clients not able to - // implement the full compression tests by introspecting the call to verify - // the response's compression status. - BoolValue compressed = 3; } // Server-streaming request. message StreamingOutputCallRequest { - // DEPRECATED, don't use. To be removed shortly. // Desired payload type in the response from the server. // If response_type is RANDOM, the payload from each response in the stream // might be of different types. This is to simulate a mixed type of payload @@ -160,9 +124,6 @@ message StreamingOutputCallRequest { // Optional input payload sent along with the request. Payload payload = 3; - - // Whether server should return a given status - EchoStatus response_status = 7; } // Server-streaming response, as configured by the request and parameters. @@ -170,17 +131,3 @@ message StreamingOutputCallResponse { // Payload to increase response size. Payload payload = 1; } - -// For reconnect interop test only. -// Client tells server what reconnection parameters it used. -message ReconnectParams { - int32 max_reconnect_backoff_ms = 1; -} - -// For reconnect interop test only. -// Server tells client whether its reconnects are following the spec and the -// reconnect backoffs it saw. -message ReconnectInfo { - bool passed = 1; - repeated int32 backoff_ms = 2; -} From 878ff0835dc94c99609d8a3c93fc346ea36e18e4 Mon Sep 17 00:00:00 2001 From: Muxi Yan Date: Wed, 23 Nov 2016 15:27:56 -0800 Subject: [PATCH 13/45] Revert "Missed file" This reverts commit 10790401dd7f1faa4aa2ab5d4801b91d788d3e2d. --- tools/run_tests/tests.json | 44 -------------------------------------- 1 file changed, 44 deletions(-) diff --git a/tools/run_tests/tests.json b/tools/run_tests/tests.json index 5ed5a0ebbcf..88869e6497e 100644 --- a/tools/run_tests/tests.json +++ b/tools/run_tests/tests.json @@ -62714,50 +62714,6 @@ ], "uses_polling": false }, - { - "args": [ - "test/core/end2end/fuzzers/client_fuzzer_corpus/server_hanging_response_1_header" - ], - "ci_platforms": [ - "linux" - ], - "cpu_cost": 0.1, - "exclude_configs": [ - "tsan" - ], - "exclude_iomgrs": [ - "uv" - ], - "flaky": false, - "language": "c", - "name": "client_fuzzer_one_entry", - "platforms": [ - "linux" - ], - "uses_polling": false - }, - { - "args": [ - "test/core/end2end/fuzzers/client_fuzzer_corpus/server_hanging_response_2_header2" - ], - "ci_platforms": [ - "linux" - ], - "cpu_cost": 0.1, - "exclude_configs": [ - "tsan" - ], - "exclude_iomgrs": [ - "uv" - ], - "flaky": false, - "language": "c", - "name": "client_fuzzer_one_entry", - "platforms": [ - "linux" - ], - "uses_polling": false - }, { "args": [ "test/core/end2end/fuzzers/client_fuzzer_corpus/settings_frame_1.bin" From d7425740045acb658c365f905c4988460628ef33 Mon Sep 17 00:00:00 2001 From: Muxi Yan Date: Wed, 23 Nov 2016 15:29:44 -0800 Subject: [PATCH 14/45] Fix breaking generate_project.py --- tools/run_tests/tests.json | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tools/run_tests/tests.json b/tools/run_tests/tests.json index 88869e6497e..55fc0e38d17 100644 --- a/tools/run_tests/tests.json +++ b/tools/run_tests/tests.json @@ -62714,6 +62714,44 @@ ], "uses_polling": false }, + { + "args": [ + "test/core/end2end/fuzzers/client_fuzzer_corpus/server_hanging_response_1_header" + ], + "ci_platforms": [ + "linux" + ], + "cpu_cost": 0.1, + "exclude_configs": [ + "tsan" + ], + "flaky": false, + "language": "c", + "name": "client_fuzzer_one_entry", + "platforms": [ + "linux" + ], + "uses_polling": false + }, + { + "args": [ + "test/core/end2end/fuzzers/client_fuzzer_corpus/server_hanging_response_2_header2" + ], + "ci_platforms": [ + "linux" + ], + "cpu_cost": 0.1, + "exclude_configs": [ + "tsan" + ], + "flaky": false, + "language": "c", + "name": "client_fuzzer_one_entry", + "platforms": [ + "linux" + ], + "uses_polling": false + }, { "args": [ "test/core/end2end/fuzzers/client_fuzzer_corpus/settings_frame_1.bin" From a7f03f64234c496750bac5c72a7d3601e5e2a8ba Mon Sep 17 00:00:00 2001 From: Muxi Yan Date: Mon, 28 Nov 2016 11:29:19 -0800 Subject: [PATCH 15/45] Advance objective c version to v1.0.2 --- gRPC-Core.podspec | 6 ++++-- gRPC-ProtoRPC.podspec | 4 ++-- gRPC-RxLibrary.podspec | 4 ++-- gRPC.podspec | 2 +- src/objective-c/!ProtoCompiler-gRPCPlugin.podspec | 7 +++++-- src/objective-c/GRPCClient/private/GRPCHost.m | 2 +- templates/gRPC-Core.podspec.template | 6 ++++-- 7 files changed, 19 insertions(+), 12 deletions(-) diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec index b81b2eacda9..d49addb1f54 100644 --- a/gRPC-Core.podspec +++ b/gRPC-Core.podspec @@ -35,7 +35,7 @@ Pod::Spec.new do |s| s.name = 'gRPC-Core' - version = '1.0.1' + version = '1.0.2' s.version = version s.summary = 'Core cross-platform gRPC library, written in C' s.homepage = 'http://www.grpc.io' @@ -44,7 +44,9 @@ Pod::Spec.new do |s| s.source = { :git => 'https://github.com/grpc/grpc.git', - :tag => "v#{version}", + # TODO(mxyan): Change back to "v#{version}" for next release + #:tag => "v#{version}", + :tag => "objective-c-v#{version}", # TODO(jcanizales): Depend explicitly on the nanopb pod, and disable submodules. :submodules => true, } diff --git a/gRPC-ProtoRPC.podspec b/gRPC-ProtoRPC.podspec index 61d4b62d391..62eaa2aaf7f 100644 --- a/gRPC-ProtoRPC.podspec +++ b/gRPC-ProtoRPC.podspec @@ -30,7 +30,7 @@ Pod::Spec.new do |s| s.name = 'gRPC-ProtoRPC' - version = '1.0.1' + version = '1.0.2' s.version = version s.summary = 'RPC library for Protocol Buffers, based on gRPC' s.homepage = 'http://www.grpc.io' @@ -39,7 +39,7 @@ Pod::Spec.new do |s| s.source = { :git => 'https://github.com/grpc/grpc.git', - :tag => "v#{version}", + :tag => "objective-c-v#{version}", } s.ios.deployment_target = '7.1' diff --git a/gRPC-RxLibrary.podspec b/gRPC-RxLibrary.podspec index d59385c039a..2e8fffd2f11 100644 --- a/gRPC-RxLibrary.podspec +++ b/gRPC-RxLibrary.podspec @@ -30,7 +30,7 @@ Pod::Spec.new do |s| s.name = 'gRPC-RxLibrary' - version = '1.0.1' + version = '1.0.2' s.version = version s.summary = 'Reactive Extensions library for iOS/OSX.' s.homepage = 'http://www.grpc.io' @@ -39,7 +39,7 @@ Pod::Spec.new do |s| s.source = { :git => 'https://github.com/grpc/grpc.git', - :tag => "v#{version}", + :tag => "objective-c-v#{version}", } s.ios.deployment_target = '7.1' diff --git a/gRPC.podspec b/gRPC.podspec index 76410b17d28..bbec1ffffa4 100644 --- a/gRPC.podspec +++ b/gRPC.podspec @@ -30,7 +30,7 @@ Pod::Spec.new do |s| s.name = 'gRPC' - version = '1.0.1' + version = '1.0.2' s.version = version s.summary = 'gRPC client library for iOS/OSX' s.homepage = 'http://www.grpc.io' diff --git a/src/objective-c/!ProtoCompiler-gRPCPlugin.podspec b/src/objective-c/!ProtoCompiler-gRPCPlugin.podspec index 6e594fd3edc..bcc2bb61265 100644 --- a/src/objective-c/!ProtoCompiler-gRPCPlugin.podspec +++ b/src/objective-c/!ProtoCompiler-gRPCPlugin.podspec @@ -36,7 +36,7 @@ Pod::Spec.new do |s| # exclamation mark ensures that other "regular" pods will be able to find it as it'll be installed # before them. s.name = '!ProtoCompiler-gRPCPlugin' - v = '1.0.1' + v = '1.0.2' s.version = v s.summary = 'The gRPC ProtoC plugin generates Objective-C files from .proto services.' s.description = <<-DESC @@ -84,7 +84,10 @@ Pod::Spec.new do |s| repo = 'grpc/grpc' file = "grpc_objective_c_plugin-#{v}-macos-x86_64.zip" s.source = { - :http => "https://github.com/#{repo}/releases/download/v#{v}/#{file}", + # TODO(mxyan): Change back to "https://github.com/#{repo}/releases/download/v#{v}/#{file}" for + # next release + # :http => "https://github.com/#{repo}/releases/download/v#{v}/#{file}", + :http => "https://github.com/#{repo}/releases/download/objective-c-v#{v}/#{file}", # TODO(jcanizales): Add sha1 or sha256 # :sha1 => '??', } diff --git a/src/objective-c/GRPCClient/private/GRPCHost.m b/src/objective-c/GRPCClient/private/GRPCHost.m index 4e09b39da56..70a5aad8f33 100644 --- a/src/objective-c/GRPCClient/private/GRPCHost.m +++ b/src/objective-c/GRPCClient/private/GRPCHost.m @@ -49,7 +49,7 @@ NS_ASSUME_NONNULL_BEGIN // TODO(jcanizales): Generate the version in a standalone header, from templates. Like // templates/src/core/surface/version.c.template . -#define GRPC_OBJC_VERSION_STRING @"1.0.1" +#define GRPC_OBJC_VERSION_STRING @"1.0.2" static NSMutableDictionary *kHostCache; diff --git a/templates/gRPC-Core.podspec.template b/templates/gRPC-Core.podspec.template index f3bb7347b4e..6b949b6c6fd 100644 --- a/templates/gRPC-Core.podspec.template +++ b/templates/gRPC-Core.podspec.template @@ -62,7 +62,7 @@ %> Pod::Spec.new do |s| s.name = 'gRPC-Core' - version = '1.0.1' + version = '1.0.2' s.version = version s.summary = 'Core cross-platform gRPC library, written in C' s.homepage = 'http://www.grpc.io' @@ -71,7 +71,9 @@ s.source = { :git => 'https://github.com/grpc/grpc.git', - :tag => "v#{version}", + # TODO(mxyan): Change back to "v#{version}" for next release + #:tag => "v#{version}", + :tag => "objective-c-v#{version}", # TODO(jcanizales): Depend explicitly on the nanopb pod, and disable submodules. :submodules => true, } From de30b0ae3688c8956ce520e4a6e67bdabe173490 Mon Sep 17 00:00:00 2001 From: Muxi Yan Date: Mon, 28 Nov 2016 12:03:07 -0800 Subject: [PATCH 16/45] missing file --- gRPC.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gRPC.podspec b/gRPC.podspec index bbec1ffffa4..e8b77094491 100644 --- a/gRPC.podspec +++ b/gRPC.podspec @@ -39,7 +39,7 @@ Pod::Spec.new do |s| s.source = { :git => 'https://github.com/grpc/grpc.git', - :tag => "v#{version}", + :tag => "objective-c-v#{version}", } s.ios.deployment_target = '7.1' From 6002b8ff63b46afc8abb34081423692e2f02d2b3 Mon Sep 17 00:00:00 2001 From: Alexander Polcyn Date: Mon, 28 Nov 2016 17:41:13 -0800 Subject: [PATCH 17/45] add ruby subclasses of bad status for each GPRC status code --- src/ruby/lib/grpc/errors.rb | 126 ++++++++++++++++++++++++++++- src/ruby/spec/error_sanity_spec.rb | 58 +++++++++++++ 2 files changed, 182 insertions(+), 2 deletions(-) create mode 100644 src/ruby/spec/error_sanity_spec.rb diff --git a/src/ruby/lib/grpc/errors.rb b/src/ruby/lib/grpc/errors.rb index 23b2bb7e127..022a14b0a51 100644 --- a/src/ruby/lib/grpc/errors.rb +++ b/src/ruby/lib/grpc/errors.rb @@ -35,6 +35,13 @@ module GRPC # either end of a GRPC connection. When raised, it indicates that a status # error should be returned to the other end of a GRPC connection; when # caught it means that this end received a status error. + # + # There is also subclass of BadStatus in this module for each GRPC status. + # E.g., the GRPC::Cancelled class corresponds to status CANCELLED. + # + # See + # https://github.com/grpc/grpc/blob/master/include/grpc/impl/codegen/status.h + # for detailed descriptions of each status code. class BadStatus < StandardError attr_reader :code, :details, :metadata @@ -57,7 +64,122 @@ module GRPC end end - # Cancelled is an exception class that indicates that an rpc was cancelled. - class Cancelled < StandardError + # GRPC status code corresponding to status OK + class Ok < BadStatus + def initialize(details = 'unknown cause', metadata = {}) + super(Core::StatusCodes::OK, details, metadata) + end + end + + # GRPC status code corresponding to status CANCELLED + class Cancelled < BadStatus + def initialize(details = 'unknown cause', metadata = {}) + super(Core::StatusCodes::CANCELLED, details, metadata) + end + end + + # GRPC status code corresponding to status UNKNOWN + class Unknown < BadStatus + def initialize(details = 'unknown cause', metadata = {}) + super(Core::StatusCodes::UNKNOWN, details, metadata) + end + end + + # GRPC status code corresponding to status INVALID_ARGUMENT + class InvalidArgument < BadStatus + def initialize(details = 'unknown cause', metadata = {}) + super(Core::StatusCodes::INVALID_ARGUMENT, details, metadata) + end + end + + # GRPC status code corresponding to status DEADLINE_EXCEEDED + class DeadlineExceeded < BadStatus + def initialize(details = 'unknown cause', metadata = {}) + super(Core::StatusCodes::DEADLINE_EXCEEDED, details, metadata) + end + end + + # GRPC status code corresponding to status NOT_FOUND + class NotFound < BadStatus + def initialize(details = 'unknown cause', metadata = {}) + super(Core::StatusCodes::NOT_FOUND, details, metadata) + end + end + + # GRPC status code corresponding to status ALREADY_EXISTS + class AlreadyExists < BadStatus + def initialize(details = 'unknown cause', metadata = {}) + super(Core::StatusCodes::ALREADY_EXISTS, details, metadata) + end + end + + # GRPC status code corresponding to status PERMISSION_DENIED + class PermissionDenied < BadStatus + def initialize(details = 'unknown cause', metadata = {}) + super(Core::StatusCodes::PERMISSION_DENIED, details, metadata) + end + end + + # GRPC status code corresponding to status UNAUTHENTICATED + class Unauthenticated < BadStatus + def initialize(details = 'unknown cause', metadata = {}) + super(Core::StatusCodes::UNAUTHENTICATED, details, metadata) + end + end + + # GRPC status code corresponding to status RESOURCE_EXHAUSTED + class ResourceExhausted < BadStatus + def initialize(details = 'unknown cause', metadata = {}) + super(Core::StatusCodes::RESOURCE_EXHAUSTED, details, metadata) + end + end + + # GRPC status code corresponding to status FAILED_PRECONDITION + class FailedPrecondition < BadStatus + def initialize(details = 'unknown cause', metadata = {}) + super(Core::StatusCodes::FAILED_PRECONDITION, details, metadata) + end + end + + # GRPC status code corresponding to status ABORTED + class Aborted < BadStatus + def initialize(details = 'unknown cause', metadata = {}) + super(Core::StatusCodes::ABORTED, details, metadata) + end + end + + # GRPC status code corresponding to status OUT_OF_RANGE + class OutOfRange < BadStatus + def initialize(details = 'unknown cause', metadata = {}) + super(Core::StatusCodes::OUT_OF_RANGE, details, metadata) + end + end + + # GRPC status code corresponding to status UNIMPLEMENTED + class Unimplemented < BadStatus + def initialize(details = 'unknown cause', metadata = {}) + super(Core::StatusCodes::UNIMPLEMENTED, details, metadata) + end + end + + # GRPC status code corresponding to status INTERNAL + class Internal < BadStatus + def initialize(details = 'unknown cause', metadata = {}) + super(Core::StatusCodes::INTERNAL, details, metadata) + end + end + + # GRPC status code corresponding to status UNAVAILABLE + class Unavailable < BadStatus + def initialize(details = 'unknown cause', metadata = {}) + super(Core::StatusCodes::UNAVAILABLE, details, metadata) + end + end + + # GRPC status code corresponding to status DATA_LOSS + class DataLoss < BadStatus + def initialize(details = 'unknown cause', metadata = {}) + super(Core::StatusCodes::DATA_LOSS, details, metadata) + end end end diff --git a/src/ruby/spec/error_sanity_spec.rb b/src/ruby/spec/error_sanity_spec.rb new file mode 100644 index 00000000000..97712104fe1 --- /dev/null +++ b/src/ruby/spec/error_sanity_spec.rb @@ -0,0 +1,58 @@ +# 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. + +require 'grpc' + +StatusCodes = GRPC::Core::StatusCodes + +describe StatusCodes do + # convert upper snake-case to camel case. + # e.g., DEADLINE_EXCEEDED -> DeadlineExceeded + def upper_snake_to_camel(name) + name.to_s.split('_').map(&:downcase).map(&:capitalize).join('') + end + + StatusCodes.constants.each do |status_name| + it 'there is a subclass of BadStatus corresponding to StatusCode: ' \ + "#{name} that has code: #{StatusCodes.const_get(status_name)}" do + camel_case = upper_snake_to_camel(status_name) + error_class = GRPC.const_get(camel_case) + # expect the error class to be a subclass of BadStatus + expect(error_class < GRPC::BadStatus) + + error_object = error_class.new + # check that the code matches the int value of the error's constant + expect(error_object.code).to eq(StatusCodes.const_get(status_name)) + + # check default parameters + expect(error_object.details).to eq('unknown cause') + expect(error_object.metadata).to eq({}) + end + end +end From c13e2f5b033567055136dea95d3f1b54ad4f8a2c Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Tue, 29 Nov 2016 18:16:57 -0800 Subject: [PATCH 18/45] Node: correctly bubble up errors caused by non-serializable writes --- src/node/src/client.js | 13 ++++++++++++- src/node/src/server.js | 7 ++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/node/src/client.js b/src/node/src/client.js index f75f951eb8d..56aa890779b 100644 --- a/src/node/src/client.js +++ b/src/node/src/client.js @@ -99,7 +99,18 @@ function ClientWritableStream(call, serialize) { function _write(chunk, encoding, callback) { /* jshint validthis: true */ var batch = {}; - var message = this.serialize(chunk); + var message; + try { + message = this.serialize(chunk); + } catch (e) { + /* Sending this error to the server and emitting it immediately on the + client may put the call in a slightly weird state on the client side, + but passing an object that causes a serialization failure is a misuse + of the API anyway, so that's OK. The primary purpose here is to give the + programmer a useful error and to stop the stream properly */ + this.call.cancelWithStatus(grpc.status.INTERNAL, "Serialization failure"); + callback(e); + } if (_.isFinite(encoding)) { /* Attach the encoding if it is a finite number. This is the closest we * can get to checking that it is valid flags */ diff --git a/src/node/src/server.js b/src/node/src/server.js index b3b414969ab..bd0a5122ad0 100644 --- a/src/node/src/server.js +++ b/src/node/src/server.js @@ -278,7 +278,12 @@ function _write(chunk, encoding, callback) { (new Metadata())._getCoreRepresentation(); this.call.metadataSent = true; } - var message = this.serialize(chunk); + var message; + try { + message = this.serialize(chunk); + } catch (e) { + callback(e); + } if (_.isFinite(encoding)) { /* Attach the encoding if it is a finite number. This is the closest we * can get to checking that it is valid flags */ From acacd0d6467109e452e7375f662240c26fca004f Mon Sep 17 00:00:00 2001 From: Alexander Polcyn Date: Tue, 29 Nov 2016 23:42:27 -0800 Subject: [PATCH 19/45] add factory method to bad status to create correct subclass --- src/ruby/lib/grpc/errors.rb | 43 ++++++++++++++++++++++++++++++ src/ruby/spec/error_sanity_spec.rb | 8 +++++- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/ruby/lib/grpc/errors.rb b/src/ruby/lib/grpc/errors.rb index 022a14b0a51..41c15de1d57 100644 --- a/src/ruby/lib/grpc/errors.rb +++ b/src/ruby/lib/grpc/errors.rb @@ -45,6 +45,8 @@ module GRPC class BadStatus < StandardError attr_reader :code, :details, :metadata + include GRPC::Core::StatusCodes + # @param code [Numeric] the status code # @param details [String] the details of the exception # @param metadata [Hash] the error's metadata @@ -62,6 +64,47 @@ module GRPC def to_status Struct::Status.new(code, details, @metadata) end + + def self.new_status_exception(code, details = 'unkown cause', metadata = {}) + case code + when OK + Ok.new(details, metadata) + when CANCELLED + Cancelled.new(details, metadata) + when UNKNOWN + Unknown.new(details, metadata) + when INVALID_ARGUMENT + InvalidArgument.new(details, metadata) + when DEADLINE_EXCEEDED + DeadlineExceeded.new(details, metadata) + when NOT_FOUND + NotFound.new(details, metadata) + when ALREADY_EXISTS + AlreadyExists.new(details, metadata) + when PERMISSION_DENIED + PermissionDenied.new(details, metadata) + when UNAUTHENTICATED + Unauthenticated.new(details, metadata) + when RESOURCE_EXHAUSTED + ResourceExhausted.new(details, metadata) + when FAILED_PRECONDITION + FailedPrecondition.new(details, metadata) + when ABORTED + Aborted.new(details, metadata) + when OUT_OF_RANGE + OutOfRange.new(details, metadata) + when UNIMPLEMENTED + Unimplemented.new(details, metadata) + when INTERNAL + Internal.new(details, metadata) + when UNAVAILABLE + Unavailable.new(details, metadata) + when DATA_LOSS + DataLoss.new(details, metadata) + else + fail 'unknown code' + end + end end # GRPC status code corresponding to status OK diff --git a/src/ruby/spec/error_sanity_spec.rb b/src/ruby/spec/error_sanity_spec.rb index 97712104fe1..ca2d80e6850 100644 --- a/src/ruby/spec/error_sanity_spec.rb +++ b/src/ruby/spec/error_sanity_spec.rb @@ -48,11 +48,17 @@ describe StatusCodes do error_object = error_class.new # check that the code matches the int value of the error's constant - expect(error_object.code).to eq(StatusCodes.const_get(status_name)) + status_code = StatusCodes.const_get(status_name) + expect(error_object.code).to eq(status_code) # check default parameters expect(error_object.details).to eq('unknown cause') expect(error_object.metadata).to eq({}) + + # check that the BadStatus factory for creates the correct + # exception too + from_factory = GRPC::BadStatus.new_status_exception(status_code) + expect(from_factory.is_a?(error_class)).to be(true) end end end From 174aa915bae4b379932e4887e26f2953db52b954 Mon Sep 17 00:00:00 2001 From: Alexander Polcyn Date: Wed, 30 Nov 2016 08:35:52 -0800 Subject: [PATCH 20/45] change client code to use specific exceptions and throw bad status if unkown code --- src/ruby/lib/grpc/errors.rb | 59 +++++++++-------------- src/ruby/lib/grpc/generic/active_call.rb | 3 +- src/ruby/lib/grpc/generic/service.rb | 3 +- src/ruby/pb/grpc/health/checker.rb | 4 +- src/ruby/pb/test/client.rb | 7 +-- src/ruby/spec/error_sanity_spec.rb | 2 +- src/ruby/spec/generic/client_stub_spec.rb | 9 ++-- src/ruby/spec/generic/rpc_server_spec.rb | 5 +- src/ruby/spec/pb/health/checker_spec.rb | 8 +-- 9 files changed, 43 insertions(+), 57 deletions(-) diff --git a/src/ruby/lib/grpc/errors.rb b/src/ruby/lib/grpc/errors.rb index 41c15de1d57..f6998e17c49 100644 --- a/src/ruby/lib/grpc/errors.rb +++ b/src/ruby/lib/grpc/errors.rb @@ -66,43 +66,30 @@ module GRPC end def self.new_status_exception(code, details = 'unkown cause', metadata = {}) - case code - when OK - Ok.new(details, metadata) - when CANCELLED - Cancelled.new(details, metadata) - when UNKNOWN - Unknown.new(details, metadata) - when INVALID_ARGUMENT - InvalidArgument.new(details, metadata) - when DEADLINE_EXCEEDED - DeadlineExceeded.new(details, metadata) - when NOT_FOUND - NotFound.new(details, metadata) - when ALREADY_EXISTS - AlreadyExists.new(details, metadata) - when PERMISSION_DENIED - PermissionDenied.new(details, metadata) - when UNAUTHENTICATED - Unauthenticated.new(details, metadata) - when RESOURCE_EXHAUSTED - ResourceExhausted.new(details, metadata) - when FAILED_PRECONDITION - FailedPrecondition.new(details, metadata) - when ABORTED - Aborted.new(details, metadata) - when OUT_OF_RANGE - OutOfRange.new(details, metadata) - when UNIMPLEMENTED - Unimplemented.new(details, metadata) - when INTERNAL - Internal.new(details, metadata) - when UNAVAILABLE - Unavailable.new(details, metadata) - when DATA_LOSS - DataLoss.new(details, metadata) + codes = {} + codes[OK] = Ok + codes[CANCELLED] = Cancelled + codes[UNKNOWN] = Unknown + codes[INVALID_ARGUMENT] = InvalidArgument + codes[DEADLINE_EXCEEDED] = DeadlineExceeded + codes[NOT_FOUND] = NotFound + codes[ALREADY_EXISTS] = AlreadyExists + codes[PERMISSION_DENIED] = PermissionDenied + codes[UNAUTHENTICATED] = Unauthenticated + codes[RESOURCE_EXHAUSTED] = ResourceExhausted + codes[FAILED_PRECONDITION] = FailedPrecondition + codes[ABORTED] = Aborted + codes[OUT_OF_RANGE] = OutOfRange + codes[UNIMPLEMENTED] = Unimplemented + codes[INTERNAL] = Internal + codes[UNIMPLEMENTED] = Unimplemented + codes[UNAVAILABLE] = Unavailable + codes[DATA_LOSS] = DataLoss + + if codes[code].nil? + BadStatus.new(code, details, metadata) else - fail 'unknown code' + codes[code].new(details, metadata) end end end diff --git a/src/ruby/lib/grpc/generic/active_call.rb b/src/ruby/lib/grpc/generic/active_call.rb index 5787f534635..01797d13e1b 100644 --- a/src/ruby/lib/grpc/generic/active_call.rb +++ b/src/ruby/lib/grpc/generic/active_call.rb @@ -43,7 +43,8 @@ class Struct GRPC.logger.debug("Failing with status #{status}") # raise BadStatus, propagating the metadata if present. md = status.metadata - fail GRPC::BadStatus.new(status.code, status.details, md) + fail GRPC::BadStatus.new_status_exception( + status.code, status.details, md) end status end diff --git a/src/ruby/lib/grpc/generic/service.rb b/src/ruby/lib/grpc/generic/service.rb index 7cb9f1cc99d..0dbadcc19ef 100644 --- a/src/ruby/lib/grpc/generic/service.rb +++ b/src/ruby/lib/grpc/generic/service.rb @@ -111,7 +111,8 @@ module GRPC marshal_class_method, unmarshal_class_method) define_method(name) do - fail GRPC::BadStatus, GRPC::Core::StatusCodes::UNIMPLEMENTED + fail GRPC::BadStatus.new_status_exception( + GRPC::Core::StatusCodes::UNIMPLEMENTED) end end diff --git a/src/ruby/pb/grpc/health/checker.rb b/src/ruby/pb/grpc/health/checker.rb index 4bce1744c48..6b2d852ebfd 100644 --- a/src/ruby/pb/grpc/health/checker.rb +++ b/src/ruby/pb/grpc/health/checker.rb @@ -52,7 +52,9 @@ module Grpc @status_mutex.synchronize do status = @statuses["#{req.service}"] end - fail GRPC::BadStatus, StatusCodes::NOT_FOUND if status.nil? + if status.nil? + fail GRPC::BadStatus.new_status_exception(StatusCodes::NOT_FOUND) + end HealthCheckResponse.new(status: status) end diff --git a/src/ruby/pb/test/client.rb b/src/ruby/pb/test/client.rb index b9af160e7a3..9ee5cdf9fd3 100755 --- a/src/ruby/pb/test/client.rb +++ b/src/ruby/pb/test/client.rb @@ -338,11 +338,8 @@ class NamedTests deadline = GRPC::Core::TimeConsts::from_relative_time(1) resps = @stub.full_duplex_call(enum.each_item, deadline: deadline) resps.each { } # wait to receive each request (or timeout) - fail 'Should have raised GRPC::BadStatus(DEADLINE_EXCEEDED)' - rescue GRPC::BadStatus => e - assert("#{__callee__}: status was wrong") do - e.code == GRPC::Core::StatusCodes::DEADLINE_EXCEEDED - end + fail 'Should have raised GRPC::DeadlineExceeded' + rescue GRPC::DeadlineExceeded end def empty_stream diff --git a/src/ruby/spec/error_sanity_spec.rb b/src/ruby/spec/error_sanity_spec.rb index ca2d80e6850..77e94a88160 100644 --- a/src/ruby/spec/error_sanity_spec.rb +++ b/src/ruby/spec/error_sanity_spec.rb @@ -40,7 +40,7 @@ describe StatusCodes do StatusCodes.constants.each do |status_name| it 'there is a subclass of BadStatus corresponding to StatusCode: ' \ - "#{name} that has code: #{StatusCodes.const_get(status_name)}" do + "#{status_name} that has code: #{StatusCodes.const_get(status_name)}" do camel_case = upper_snake_to_camel(status_name) error_class = GRPC.const_get(camel_case) # expect the error class to be a subclass of BadStatus diff --git a/src/ruby/spec/generic/client_stub_spec.rb b/src/ruby/spec/generic/client_stub_spec.rb index 9c4e9cbd078..08a171e2991 100644 --- a/src/ruby/spec/generic/client_stub_spec.rb +++ b/src/ruby/spec/generic/client_stub_spec.rb @@ -190,15 +190,14 @@ describe 'ClientStub' do end creds = GRPC::Core::CallCredentials.new(failing_auth) - error_occured = false + unauth_error_occured = false begin get_response(stub, credentials: creds) - rescue GRPC::BadStatus => e - error_occured = true - expect(e.code).to eq(GRPC::Core::StatusCodes::UNAUTHENTICATED) + rescue GRPC::Unauthenticated => e + unauth_error_occured = true expect(e.details.include?(error_message)).to be true end - expect(error_occured).to eq(true) + expect(unauth_error_occured).to eq(true) # Kill the server thread so tests can complete th.kill diff --git a/src/ruby/spec/generic/rpc_server_spec.rb b/src/ruby/spec/generic/rpc_server_spec.rb index 31157cf161e..1689d2df681 100644 --- a/src/ruby/spec/generic/rpc_server_spec.rb +++ b/src/ruby/spec/generic/rpc_server_spec.rb @@ -414,9 +414,8 @@ describe GRPC::RpcServer do stub = SlowStub.new(alt_host, :this_channel_is_insecure) begin stub.an_rpc(req) - rescue GRPC::BadStatus => e - one_failed_as_unavailable = - e.code == StatusCodes::RESOURCE_EXHAUSTED + rescue GRPC::ResourceExhausted + one_failed_as_unavailable = true end end end diff --git a/src/ruby/spec/pb/health/checker_spec.rb b/src/ruby/spec/pb/health/checker_spec.rb index 1b2fa968271..2cc58f845b7 100644 --- a/src/ruby/spec/pb/health/checker_spec.rb +++ b/src/ruby/spec/pb/health/checker_spec.rb @@ -119,7 +119,7 @@ describe Grpc::Health::Checker do subject.check(HCReq.new(service: t[:service]), nil) end expected_msg = /#{StatusCodes::NOT_FOUND}/ - expect(&blk).to raise_error GRPC::BadStatus, expected_msg + expect(&blk).to raise_error GRPC::NotFound, expected_msg end end end @@ -137,7 +137,7 @@ describe Grpc::Health::Checker do subject.check(HCReq.new(service: t[:service]), nil) end expected_msg = /#{StatusCodes::NOT_FOUND}/ - expect(&blk).to raise_error GRPC::BadStatus, expected_msg + expect(&blk).to raise_error GRPC::NotFound, expected_msg end end end @@ -158,7 +158,7 @@ describe Grpc::Health::Checker do subject.check(HCReq.new(service: t[:service]), nil) end expected_msg = /#{StatusCodes::NOT_FOUND}/ - expect(&blk).to raise_error GRPC::BadStatus, expected_msg + expect(&blk).to raise_error GRPC::NotFound, expected_msg end end end @@ -206,7 +206,7 @@ describe Grpc::Health::Checker do stub.check(HCReq.new(service: 'unknown')) end expected_msg = /#{StatusCodes::NOT_FOUND}/ - expect(&blk).to raise_error GRPC::BadStatus, expected_msg + expect(&blk).to raise_error GRPC::NotFound, expected_msg @srv.stop t.join end From 1625d12ea1004e4d051775462e7dbe4906fb4a64 Mon Sep 17 00:00:00 2001 From: Alex Polcyn Date: Wed, 30 Nov 2016 13:51:54 -0800 Subject: [PATCH 21/45] update ruby protbuf dep to 3.1.0 --- grpc.gemspec | 2 +- templates/grpc.gemspec.template | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/grpc.gemspec b/grpc.gemspec index 088b99b1e66..724922ea460 100755 --- a/grpc.gemspec +++ b/grpc.gemspec @@ -27,7 +27,7 @@ Gem::Specification.new do |s| s.require_paths = %w( src/ruby/bin src/ruby/lib src/ruby/pb ) s.platform = Gem::Platform::RUBY - s.add_dependency 'google-protobuf', '~> 3.0.2' + s.add_dependency 'google-protobuf', '~> 3.1.0' s.add_dependency 'googleauth', '~> 0.5.1' s.add_development_dependency 'bundler', '~> 1.9' diff --git a/templates/grpc.gemspec.template b/templates/grpc.gemspec.template index 62d61b75c1d..82fbb690088 100644 --- a/templates/grpc.gemspec.template +++ b/templates/grpc.gemspec.template @@ -29,7 +29,7 @@ s.require_paths = %w( src/ruby/bin src/ruby/lib src/ruby/pb ) s.platform = Gem::Platform::RUBY - s.add_dependency 'google-protobuf', '~> 3.0.2' + s.add_dependency 'google-protobuf', '~> 3.1.0' s.add_dependency 'googleauth', '~> 0.5.1' s.add_development_dependency 'bundler', '~> 1.9' From 16db6e1c040e67a6c491fbe37409e6a37e61200f Mon Sep 17 00:00:00 2001 From: Alex Polcyn Date: Wed, 30 Nov 2016 13:59:59 -0800 Subject: [PATCH 22/45] destroy byte buffer reader after use in ruby recv msg --- src/ruby/ext/grpc/rb_byte_buffer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ruby/ext/grpc/rb_byte_buffer.c b/src/ruby/ext/grpc/rb_byte_buffer.c index 61b7c30315d..28c6a0fd3a8 100644 --- a/src/ruby/ext/grpc/rb_byte_buffer.c +++ b/src/ruby/ext/grpc/rb_byte_buffer.c @@ -65,5 +65,6 @@ VALUE grpc_rb_byte_buffer_to_s(grpc_byte_buffer *buffer) { GPR_SLICE_LENGTH(next)); gpr_slice_unref(next); } + grpc_byte_buffer_reader_destroy(&reader); return rb_string; } From c00d0f79aa2e61fe0d61a5734fc01745e8edb047 Mon Sep 17 00:00:00 2001 From: Nathaniel Manista Date: Wed, 30 Nov 2016 23:16:42 +0000 Subject: [PATCH 23/45] Clarify grpc_call_start_batch error semantics --- include/grpc/grpc.h | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h index 6f7a67b715e..8cb278ff8d2 100644 --- a/include/grpc/grpc.h +++ b/include/grpc/grpc.h @@ -197,9 +197,15 @@ GRPCAPI grpc_call *grpc_channel_create_registered_call( completion of type 'tag' to the completion queue bound to the call. The order of ops specified in the batch has no significance. Only one operation of each type can be active at once in any given - batch. You must call grpc_completion_queue_next or - grpc_completion_queue_pluck on the completion queue associated with 'call' - for work to be performed. + batch. + If a call to grpc_call_start_batch returns GRPC_CALL_OK you must call + grpc_completion_queue_next or grpc_completion_queue_pluck on the completion + queue associated with 'call' for work to be performed. If a call to + grpc_call_start_batch returns any value other than GRPC_CALL_OK it is + guaranteed that no state associated with 'call' is changed and it is not + appropriate to call grpc_completion_queue_next or + grpc_completion_queue_pluck consequent to the failed grpc_call_start_batch + call. THREAD SAFETY: access to grpc_call_start_batch in multi-threaded environment needs to be synchronized. As an optimization, you may synchronize batches containing just send operations independently from batches containing just From 564d3a7aa3db9daffff13c2eacb3fc1157bc4e9d Mon Sep 17 00:00:00 2001 From: Nathaniel Manista Date: Wed, 30 Nov 2016 23:21:17 +0000 Subject: [PATCH 24/45] Lint fixes --- src/python/grpcio/grpc/_channel.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/python/grpcio/grpc/_channel.py b/src/python/grpcio/grpc/_channel.py index 3117dd1cb31..bee32c9629e 100644 --- a/src/python/grpcio/grpc/_channel.py +++ b/src/python/grpcio/grpc/_channel.py @@ -36,8 +36,8 @@ import time import grpc from grpc import _common from grpc import _grpcio_metadata -from grpc.framework.foundation import callable_util from grpc._cython import cygrpc +from grpc.framework.foundation import callable_util _USER_AGENT = 'Python-gRPC-{}'.format(_grpcio_metadata.__version__) @@ -358,7 +358,7 @@ class _Rendezvous(grpc.RpcError, grpc.Future, grpc.Call): if self._state.callbacks is None: return False else: - self._state.callbacks.append(lambda: callback()) + self._state.callbacks.append(callback) return True def initial_metadata(self): @@ -857,6 +857,7 @@ def _options(options): class Channel(grpc.Channel): + """A cygrpc.Channel-backed implementation of grpc.Channel.""" def __init__(self, target, options, credentials): """Constructor. From b292a8502ed00ab5b06e5f5920e0d1a4e1ef9562 Mon Sep 17 00:00:00 2001 From: Nathaniel Manista Date: Wed, 30 Nov 2016 23:21:53 +0000 Subject: [PATCH 25/45] Refactor channel call management The requirement that any created managed call must have operations performed on it is obstructing proper handling of the case of applications providing invalid invocation metadata. In such cases the RPC is "over before it starts" when the very first call to start_client_batch returns an error. --- src/python/grpcio/grpc/_channel.py | 77 ++++++++++++++++-------------- 1 file changed, 41 insertions(+), 36 deletions(-) diff --git a/src/python/grpcio/grpc/_channel.py b/src/python/grpcio/grpc/_channel.py index bee32c9629e..3ac735a4ec9 100644 --- a/src/python/grpcio/grpc/_channel.py +++ b/src/python/grpcio/grpc/_channel.py @@ -435,10 +435,10 @@ def _end_unary_response_blocking(state, with_call, deadline): class _UnaryUnaryMultiCallable(grpc.UnaryUnaryMultiCallable): def __init__( - self, channel, create_managed_call, method, request_serializer, + self, channel, managed_call, method, request_serializer, response_deserializer): self._channel = channel - self._create_managed_call = create_managed_call + self._managed_call = managed_call self._method = method self._request_serializer = request_serializer self._response_deserializer = response_deserializer @@ -490,23 +490,24 @@ class _UnaryUnaryMultiCallable(grpc.UnaryUnaryMultiCallable): if rendezvous: return rendezvous else: - call = self._create_managed_call( + call, drive_call = self._managed_call( None, 0, self._method, None, deadline_timespec) if credentials is not None: call.set_credentials(credentials._credentials) event_handler = _event_handler(state, call, self._response_deserializer) with state.condition: call.start_client_batch(cygrpc.Operations(operations), event_handler) + drive_call() return _Rendezvous(state, call, self._response_deserializer, deadline) class _UnaryStreamMultiCallable(grpc.UnaryStreamMultiCallable): def __init__( - self, channel, create_managed_call, method, request_serializer, + self, channel, managed_call, method, request_serializer, response_deserializer): self._channel = channel - self._create_managed_call = create_managed_call + self._managed_call = managed_call self._method = method self._request_serializer = request_serializer self._response_deserializer = response_deserializer @@ -518,7 +519,7 @@ class _UnaryStreamMultiCallable(grpc.UnaryStreamMultiCallable): raise rendezvous else: state = _RPCState(_UNARY_STREAM_INITIAL_DUE, None, None, None, None) - call = self._create_managed_call( + call, drive_call = self._managed_call( None, 0, self._method, None, deadline_timespec) if credentials is not None: call.set_credentials(credentials._credentials) @@ -536,16 +537,17 @@ class _UnaryStreamMultiCallable(grpc.UnaryStreamMultiCallable): cygrpc.operation_receive_status_on_client(_EMPTY_FLAGS), ) call.start_client_batch(cygrpc.Operations(operations), event_handler) + drive_call() return _Rendezvous(state, call, self._response_deserializer, deadline) class _StreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable): def __init__( - self, channel, create_managed_call, method, request_serializer, + self, channel, managed_call, method, request_serializer, response_deserializer): self._channel = channel - self._create_managed_call = create_managed_call + self._managed_call = managed_call self._method = method self._request_serializer = request_serializer self._response_deserializer = response_deserializer @@ -597,7 +599,7 @@ class _StreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable): self, request_iterator, timeout=None, metadata=None, credentials=None): deadline, deadline_timespec = _deadline(timeout) state = _RPCState(_STREAM_UNARY_INITIAL_DUE, None, None, None, None) - call = self._create_managed_call( + call, drive_call = self._managed_call( None, 0, self._method, None, deadline_timespec) if credentials is not None: call.set_credentials(credentials._credentials) @@ -614,6 +616,7 @@ class _StreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable): cygrpc.operation_receive_status_on_client(_EMPTY_FLAGS), ) call.start_client_batch(cygrpc.Operations(operations), event_handler) + drive_call() _consume_request_iterator( request_iterator, state, call, self._request_serializer) return _Rendezvous(state, call, self._response_deserializer, deadline) @@ -622,10 +625,10 @@ class _StreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable): class _StreamStreamMultiCallable(grpc.StreamStreamMultiCallable): def __init__( - self, channel, create_managed_call, method, request_serializer, + self, channel, managed_call, method, request_serializer, response_deserializer): self._channel = channel - self._create_managed_call = create_managed_call + self._managed_call = managed_call self._method = method self._request_serializer = request_serializer self._response_deserializer = response_deserializer @@ -634,7 +637,7 @@ class _StreamStreamMultiCallable(grpc.StreamStreamMultiCallable): self, request_iterator, timeout=None, metadata=None, credentials=None): deadline, deadline_timespec = _deadline(timeout) state = _RPCState(_STREAM_STREAM_INITIAL_DUE, None, None, None, None) - call = self._create_managed_call( + call, drive_call = self._managed_call( None, 0, self._method, None, deadline_timespec) if credentials is not None: call.set_credentials(credentials._credentials) @@ -650,6 +653,7 @@ class _StreamStreamMultiCallable(grpc.StreamStreamMultiCallable): cygrpc.operation_receive_status_on_client(_EMPTY_FLAGS), ) call.start_client_batch(cygrpc.Operations(operations), event_handler) + drive_call() _consume_request_iterator( request_iterator, state, call, self._request_serializer) return _Rendezvous(state, call, self._response_deserializer, deadline) @@ -687,16 +691,13 @@ def _run_channel_spin_thread(state): channel_spin_thread.start() -def _create_channel_managed_call(state): - def create_channel_managed_call(parent, flags, method, host, deadline): - """Creates a managed cygrpc.Call. +def _channel_managed_call_management(state): + def create(parent, flags, method, host, deadline): + """Creates a managed cygrpc.Call and a function to call to drive it. - Callers of this function must conduct at least one operation on the returned - call. The tags associated with operations conducted on the returned call - must be no-argument callables that return None to indicate that this channel - should continue polling for events associated with the call and return the - call itself to indicate that no more events associated with the call will be - generated. + If operations are successfully added to the returned cygrpc.Call, the + returned function must be called. If operations are not successfully added + to the returned cygrpc.Call, the returned function must not be called. Args: parent: A cygrpc.Call to be used as the parent of the created call. @@ -706,18 +707,22 @@ def _create_channel_managed_call(state): deadline: A cygrpc.Timespec to be the deadline of the created call. Returns: - A cygrpc.Call with which to conduct an RPC. + A cygrpc.Call with which to conduct an RPC and a function to call if + operations are successfully started on the call. """ - with state.lock: - call = state.channel.create_call( - parent, flags, state.completion_queue, method, host, deadline) - if state.managed_calls is None: - state.managed_calls = set((call,)) - _run_channel_spin_thread(state) - else: - state.managed_calls.add(call) - return call - return create_channel_managed_call + call = state.channel.create_call( + parent, flags, state.completion_queue, method, host, deadline) + + def drive(): + with state.lock: + if state.managed_calls is None: + state.managed_calls = set((call,)) + _run_channel_spin_thread(state) + else: + state.managed_calls.add(call) + + return call, drive + return create class _ChannelConnectivityState(object): @@ -881,25 +886,25 @@ class Channel(grpc.Channel): def unary_unary( self, method, request_serializer=None, response_deserializer=None): return _UnaryUnaryMultiCallable( - self._channel, _create_channel_managed_call(self._call_state), + self._channel, _channel_managed_call_management(self._call_state), _common.encode(method), request_serializer, response_deserializer) def unary_stream( self, method, request_serializer=None, response_deserializer=None): return _UnaryStreamMultiCallable( - self._channel, _create_channel_managed_call(self._call_state), + self._channel, _channel_managed_call_management(self._call_state), _common.encode(method), request_serializer, response_deserializer) def stream_unary( self, method, request_serializer=None, response_deserializer=None): return _StreamUnaryMultiCallable( - self._channel, _create_channel_managed_call(self._call_state), + self._channel, _channel_managed_call_management(self._call_state), _common.encode(method), request_serializer, response_deserializer) def stream_stream( self, method, request_serializer=None, response_deserializer=None): return _StreamStreamMultiCallable( - self._channel, _create_channel_managed_call(self._call_state), + self._channel, _channel_managed_call_management(self._call_state), _common.encode(method), request_serializer, response_deserializer) def __del__(self): From d2537c1aa9deb04937b64d23dcd71456c1eb7729 Mon Sep 17 00:00:00 2001 From: Alexander Polcyn Date: Wed, 30 Nov 2016 14:34:49 -0800 Subject: [PATCH 26/45] turn on Thread.abort_on_exception in ruby unit tests by default --- src/ruby/spec/spec_helper.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ruby/spec/spec_helper.rb b/src/ruby/spec/spec_helper.rb index c891c1bf5e4..c2be0afa726 100644 --- a/src/ruby/spec/spec_helper.rb +++ b/src/ruby/spec/spec_helper.rb @@ -67,3 +67,5 @@ RSpec.configure do |config| end RSpec::Expectations.configuration.warn_about_potential_false_positives = false + +Thread.abort_on_exception = true From dbda92064f3fca42bba45771fd2939ccbcefaefd Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Thu, 1 Dec 2016 10:30:35 -0800 Subject: [PATCH 27/45] Also propagate serialization errors in unary server responses --- src/node/src/server.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/node/src/server.js b/src/node/src/server.js index bd0a5122ad0..51bb99ee537 100644 --- a/src/node/src/server.js +++ b/src/node/src/server.js @@ -127,7 +127,13 @@ function sendUnaryResponse(call, value, serialize, metadata, flags) { (new Metadata())._getCoreRepresentation(); call.metadataSent = true; } - var message = serialize(value); + var message; + try { + message = serialize(value); + } catch (e) { + e.code = grpc.status.INTERNAL; + handleError(e); + } message.grpcWriteFlags = flags; end_batch[grpc.opType.SEND_MESSAGE] = message; end_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = status; @@ -282,7 +288,9 @@ function _write(chunk, encoding, callback) { try { message = this.serialize(chunk); } catch (e) { + e.code = grpc.status.INTERNAL; callback(e); + return; } if (_.isFinite(encoding)) { /* Attach the encoding if it is a finite number. This is the closest we From 45d4561cd0088fc49606a209ab4b8476311c75af Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Thu, 1 Dec 2016 10:59:52 -0800 Subject: [PATCH 28/45] Add missing return --- src/node/src/server.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/node/src/server.js b/src/node/src/server.js index 51bb99ee537..da9c6b2d7ff 100644 --- a/src/node/src/server.js +++ b/src/node/src/server.js @@ -133,6 +133,7 @@ function sendUnaryResponse(call, value, serialize, metadata, flags) { } catch (e) { e.code = grpc.status.INTERNAL; handleError(e); + return; } message.grpcWriteFlags = flags; end_batch[grpc.opType.SEND_MESSAGE] = message; From a868c0424e492cfa135390dacb76c30c525dd794 Mon Sep 17 00:00:00 2001 From: Alex Polcyn Date: Mon, 5 Dec 2016 18:49:08 +0000 Subject: [PATCH 29/45] keep old behavior to not destroy ruby server if not instantiated --- src/ruby/ext/grpc/rb_server.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/ruby/ext/grpc/rb_server.c b/src/ruby/ext/grpc/rb_server.c index 6783fd3f88a..55c745965b3 100644 --- a/src/ruby/ext/grpc/rb_server.c +++ b/src/ruby/ext/grpc/rb_server.c @@ -67,17 +67,19 @@ static void destroy_server(grpc_rb_server *server, gpr_timespec deadline) { grpc_event ev; // This can be started by app or implicitly by GC. Avoid a race between these. if (gpr_atm_full_fetch_add(&server->shutdown_started, (gpr_atm)1) == 0) { - grpc_server_shutdown_and_notify(server->wrapped, server->queue, NULL); - ev = rb_completion_queue_pluck(server->queue, NULL, deadline, NULL); - if (ev.type == GRPC_QUEUE_TIMEOUT) { - grpc_server_cancel_all_calls(server->wrapped); - rb_completion_queue_pluck(server->queue, NULL, - gpr_inf_future(GPR_CLOCK_REALTIME), NULL); + if (server->wrapped != NULL) { + grpc_server_shutdown_and_notify(server->wrapped, server->queue, NULL); + ev = rb_completion_queue_pluck(server->queue, NULL, deadline, NULL); + if (ev.type == GRPC_QUEUE_TIMEOUT) { + grpc_server_cancel_all_calls(server->wrapped); + rb_completion_queue_pluck(server->queue, NULL, + gpr_inf_future(GPR_CLOCK_REALTIME), NULL); + } + grpc_server_destroy(server->wrapped); + grpc_rb_completion_queue_destroy(server->queue); + server->wrapped = NULL; + server->queue = NULL; } - grpc_server_destroy(server->wrapped); - grpc_rb_completion_queue_destroy(server->queue); - server->wrapped = NULL; - server->queue = NULL; } } From a0804ef0d2fe1661eb7511e6800cca6e68b13e68 Mon Sep 17 00:00:00 2001 From: Masood Malekghassemi Date: Mon, 5 Dec 2016 15:54:56 -0800 Subject: [PATCH 30/45] Fix test runner failures for Python on Windows --- tools/run_tests/run_tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py index cd49dfa9075..0424cf3d6fd 100755 --- a/tools/run_tests/run_tests.py +++ b/tools/run_tests/run_tests.py @@ -435,8 +435,8 @@ class PythonLanguage(object): return [self.config.job_spec( config.run, timeout_seconds=5*60, - environ=dict(environment.items() + - [('GRPC_PYTHON_TESTRUNNER_FILTER', suite_name)]), + environ=dict(list(environment.items()) + + [('GRPC_PYTHON_TESTRUNNER_FILTER', str(suite_name))]), shortname='%s.test.%s' % (config.name, suite_name),) for suite_name in tests_json for config in self.pythons] From 53360f2d1c89b3b69c06ace8e865515a031da1c4 Mon Sep 17 00:00:00 2001 From: Masood Malekghassemi Date: Tue, 12 Jul 2016 14:02:12 +0200 Subject: [PATCH 31/45] Backport Python features to 1.0.x Backports per-object grpc_init/deinit and separated-file grpc protoc codegen (#7538, #8246, #8920). --- src/compiler/python_generator.cc | 768 +++++++++++------- src/compiler/python_generator.h | 5 +- .../grpcio/grpc/_cython/_cygrpc/call.pyx.pxi | 2 + .../grpc/_cython/_cygrpc/channel.pyx.pxi | 2 + .../_cython/_cygrpc/completion_queue.pyx.pxi | 2 + .../grpc/_cython/_cygrpc/credentials.pyx.pxi | 14 + .../grpc/_cython/_cygrpc/records.pyx.pxi | 12 + .../grpc/_cython/_cygrpc/server.pyx.pxi | 2 + src/python/grpcio/grpc/_cython/cygrpc.pyx | 4 - src/python/grpcio_health_checking/.gitignore | 1 + src/python/grpcio_tests/.gitignore | 1 + .../protoc_plugin/_split_definitions_test.py | 304 +++++++ .../protos/invocation_testing/__init__.py | 30 + .../protos/invocation_testing/same.proto | 39 + .../split_messages/__init__.py | 30 + .../split_messages/messages.proto | 35 + .../split_services/__init__.py | 30 + .../split_services/services.proto | 38 + src/python/grpcio_tests/tests/tests.json | 40 +- 19 files changed, 1026 insertions(+), 333 deletions(-) create mode 100644 src/python/grpcio_tests/tests/protoc_plugin/_split_definitions_test.py create mode 100644 src/python/grpcio_tests/tests/protoc_plugin/protos/invocation_testing/__init__.py create mode 100644 src/python/grpcio_tests/tests/protoc_plugin/protos/invocation_testing/same.proto create mode 100644 src/python/grpcio_tests/tests/protoc_plugin/protos/invocation_testing/split_messages/__init__.py create mode 100644 src/python/grpcio_tests/tests/protoc_plugin/protos/invocation_testing/split_messages/messages.proto create mode 100644 src/python/grpcio_tests/tests/protoc_plugin/protos/invocation_testing/split_services/__init__.py create mode 100644 src/python/grpcio_tests/tests/protoc_plugin/protos/invocation_testing/split_services/services.proto diff --git a/src/compiler/python_generator.cc b/src/compiler/python_generator.cc index 0e7b9fbf4dd..b0a60092ab1 100644 --- a/src/compiler/python_generator.cc +++ b/src/compiler/python_generator.cc @@ -35,6 +35,8 @@ #include #include #include +#include +#include #include #include #include @@ -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(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 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 ListToDict( - const initializer_list& values) { - assert(values.size() % 2 == 0); - map 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 DescriptorVector; +typedef map StringMap; +typedef vector 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,27 +116,80 @@ 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 GetGrpcServices(); -bool GetModuleAndMessagePath(const Descriptor* type, - const ServiceDescriptor* service, - grpc::string* out) { + 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 + void PrintAllComments(const DescriptorType* descriptor); + + bool GetModuleAndMessagePath(const Descriptor* type, grpc::string* out); +}; + +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 message_path; + DescriptorVector message_path; do { message_path.push_back(path_elem_type); path_elem_type = path_elem_type->containing_type(); - } while (path_elem_type); // implicit nullptr comparison; don't be explicit + } while (path_elem_type); // implicit nullptr comparison; don't be explicit grpc::string file_name = type->file()->name(); static const int proto_suffix_length = strlen(".proto"); if (!(file_name.size() > static_cast(proto_suffix_length) && 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 -static void PrintAllComments(const DescriptorType* desc, Printer* printer) { - std::vector 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" - "\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); + 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); 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" - "\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); + 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); 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, " - "pool_size=None, default_timeout=None, maximum_timeout=None):\n", - "Service", service->name()); + 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" - "\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 method_implementation_constructors; - map input_message_modules_and_classes; - map output_message_modules_and_classes; + 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"); + 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,94 +332,99 @@ 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 = - input_message_modules_and_classes.begin(); + 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(); + input_message_modules_and_classes.end(); name_and_input_module_class_pair++) { IndentScope raii_indent(out); - 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); + 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); } out->Print("}\n"); out->Print("response_serializers = {\n"); - for (auto name_and_output_module_class_pair = - output_message_modules_and_classes.begin(); + 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(); + output_message_modules_and_classes.end(); name_and_output_module_class_pair++) { IndentScope raii_indent(out); - out->Print("(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): " - "$OutputTypeModuleAndClass$.SerializeToString,\n", - "PackageQualifiedServiceName", package_qualified_service_name, - "MethodName", name_and_output_module_class_pair->first, - "OutputTypeModuleAndClass", - name_and_output_module_class_pair->second); + out->Print( + "(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): " + "$OutputTypeModuleAndClass$.SerializeToString,\n", + "PackageQualifiedServiceName", package_qualified_service_name, + "MethodName", name_and_output_module_class_pair->first, + "OutputTypeModuleAndClass", + name_and_output_module_class_pair->second); } out->Print("}\n"); out->Print("method_implementations = {\n"); - for (auto name_and_implementation_constructor = - method_implementation_constructors.begin(); - name_and_implementation_constructor != - method_implementation_constructors.end(); - name_and_implementation_constructor++) { + for (StringMap::iterator name_and_implementation_constructor = + method_implementation_constructors.begin(); + name_and_implementation_constructor != + method_implementation_constructors.end(); + name_and_implementation_constructor++) { IndentScope raii_descriptions_indent(out); const grpc::string method_name = name_and_implementation_constructor->first; - 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); + 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); } out->Print("}\n"); - 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, " - "options=server_options)\n"); + 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, " + "options=server_options)\n"); } return true; } -bool PrintBetaStubFactory(const grpc::string& package_qualified_service_name, - const ServiceDescriptor* service, Printer* out) { - map 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" - "\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 method_cardinalities; - map input_message_modules_and_classes; - map output_message_modules_and_classes; + 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"); + 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,65 +436,70 @@ 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 = - input_message_modules_and_classes.begin(); + 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(); + input_message_modules_and_classes.end(); name_and_input_module_class_pair++) { IndentScope raii_indent(out); - 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); + 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); } out->Print("}\n"); out->Print("response_deserializers = {\n"); - for (auto name_and_output_module_class_pair = - output_message_modules_and_classes.begin(); + 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(); + output_message_modules_and_classes.end(); name_and_output_module_class_pair++) { IndentScope raii_indent(out); - out->Print("(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): " - "$OutputTypeModuleAndClass$.FromString,\n", - "PackageQualifiedServiceName", package_qualified_service_name, - "MethodName", name_and_output_module_class_pair->first, - "OutputTypeModuleAndClass", - name_and_output_module_class_pair->second); + out->Print( + "(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): " + "$OutputTypeModuleAndClass$.FromString,\n", + "PackageQualifiedServiceName", package_qualified_service_name, + "MethodName", name_and_output_module_class_pair->first, + "OutputTypeModuleAndClass", + name_and_output_module_class_pair->second); } 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(" - "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$\', " + "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$\', " "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"); { @@ -494,65 +509,63 @@ bool PrintStub(const grpc::string& package_qualified_service_name, out->Print("Args:\n"); { IndentScope raii_args_indent(out); - out->Print("channel: A grpc.Channel.\n"); + out->Print("channel: A grpc.Channel.\n"); } out->Print("\"\"\"\n"); for (int i = 0; i < service->method_count(); ++i) { - auto method = service->method(i); - auto multi_callable_constructor = - grpc::string(method->client_streaming() ? "stream" : "unary") + - "_" + - grpc::string(method->server_streaming() ? "stream" : "unary"); - grpc::string request_module_and_class; - if (!GetModuleAndMessagePath(method->input_type(), service, - &request_module_and_class)) { - return false; - } - grpc::string response_module_and_class; - if (!GetModuleAndMessagePath(method->output_type(), service, - &response_module_and_class)) { + 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 request_module_and_class; + if (!GetModuleAndMessagePath(method->input_type(), + &request_module_and_class)) { + return false; + } + grpc::string response_module_and_class; + 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); - { + } + out->Print("self.$Method$ = channel.$MultiCallableConstructor$(\n", + "Method", method->name(), "MultiCallableConstructor", + multi_callable_constructor); + { IndentScope raii_first_attribute_indent(out); IndentScope raii_second_attribute_indent(out); - out->Print( - "'/$PackageQualifiedService$/$Method$',\n", - "PackageQualifiedService", package_qualified_service_name, - "Method", method->name()); - out->Print( - "request_serializer=$RequestModuleAndClass$.SerializeToString,\n", - "RequestModuleAndClass", request_module_and_class); - out->Print( + out->Print("'/$PackageQualifiedService$/$Method$',\n", + "PackageQualifiedService", package_qualified_service_name, + "Method", method->name()); + out->Print( + "request_serializer=$RequestModuleAndClass$.SerializeToString,\n", + "RequestModuleAndClass", request_module_and_class); + out->Print( "response_deserializer=$ResponseModuleAndClass$.FromString,\n", - "ResponseModuleAndClass", response_module_and_class); - out->Print(")\n"); - } + "ResponseModuleAndClass", response_module_and_class); + out->Print(")\n"); + } } } } 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,11 +575,12 @@ 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()); + "Service", service->name()); { IndentScope raii_class_indent(out); out->Print("rpc_method_handlers = {\n"); @@ -574,35 +588,38 @@ 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, - &request_module_and_class)) { - return false; - } - grpc::string response_module_and_class; - if (!GetModuleAndMessagePath(method->output_type(), service, - &response_module_and_class)) { + grpc::string request_module_and_class; + if (!GetModuleAndMessagePath(method->input_type(), + &request_module_and_class)) { return false; - } - out->Print("'$Method$': grpc.$MethodHandlerConstructor$(\n", - "Method", method->name(), - "MethodHandlerConstructor", method_handler_constructor); - { + } + grpc::string response_module_and_class; + if (!GetModuleAndMessagePath(method->output_type(), + &response_module_and_class)) { + return false; + } + 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", - "RequestModuleAndClass", request_module_and_class); - out->Print("response_serializer=$ResponseModuleAndClass$.SerializeToString,\n", - "ResponseModuleAndClass", response_module_and_class); - } - out->Print("),\n"); + IndentScope raii_call_second_indent(out); + out->Print("servicer.$Method$,\n", "Method", method->name()); + out->Print( + "request_deserializer=$RequestModuleAndClass$.FromString,\n", + "RequestModuleAndClass", request_module_and_class); + out->Print( + "response_serializer=$ResponseModuleAndClass$.SerializeToString," + "\n", + "ResponseModuleAndClass", response_module_and_class); + } + out->Print("),\n"); } } out->Print("}\n"); @@ -611,56 +628,193 @@ bool PrintAddServicerToServer(const grpc::string& package_qualified_service_name IndentScope raii_call_first_indent(out); IndentScope raii_call_second_indent(out); out->Print("'$PackageQualifiedServiceName$', rpc_method_handlers)\n", - "PackageQualifiedServiceName", package_qualified_service_name); + "PackageQualifiedServiceName", package_qualified_service_name); } out->Print("server.add_generic_rpc_handlers((generic_handler,))\n"); } 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; +} + +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 GetServices(const FileDescriptor* file, - const GeneratorConfiguration& config) { +pair 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)) { - return make_pair(false, ""); - } - auto package = file->package(); - if (!package.empty()) { - package = package.append("."); - } - 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))) { + Printer out_printer(&output_stream, '$'); + out = &out_printer; + + if (generate_in_pb2_grpc) { + if (!PrintPreamble()) { + return make_pair(false, ""); + } + if (!PrintGAServices()) { return make_pair(false, ""); } + } 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 output; + std::unique_ptr 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(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 diff --git a/src/compiler/python_generator.h b/src/compiler/python_generator.h index 7ed99eff0bf..6a95255d40e 100644 --- a/src/compiler/python_generator.h +++ b/src/compiler/python_generator.h @@ -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 GetServices( - const grpc::protobuf::FileDescriptor* file, - const GeneratorConfiguration& config); - } // namespace grpc_python_generator #endif // GRPC_INTERNAL_COMPILER_PYTHON_GENERATOR_H diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/call.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/call.pyx.pxi index ba60986143c..cc3bd7a0672 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/call.pyx.pxi +++ b/src/python/grpcio/grpc/_cython/_cygrpc/call.pyx.pxi @@ -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 diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx.pxi index 54164014313..3df937eb14f 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx.pxi +++ b/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx.pxi @@ -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() diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx.pxi index 5955021ceb7..a258ba40639 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx.pxi +++ b/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx.pxi @@ -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() diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi index 035ac49a8bf..04872b9c09a 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi +++ b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi @@ -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, diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi index 54b3d00dfc7..834a44123d4 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi +++ b/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi @@ -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) diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi index 4f2d51b03f5..ca2b8311147 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi +++ b/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi @@ -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() diff --git a/src/python/grpcio/grpc/_cython/cygrpc.pyx b/src/python/grpcio/grpc/_cython/cygrpc.pyx index a9520b9c0fa..08089994a95 100644 --- a/src/python/grpcio/grpc/_cython/cygrpc.pyx +++ b/src/python/grpcio/grpc/_cython/cygrpc.pyx @@ -55,12 +55,8 @@ cdef extern from "Python.h": def _initialize(): - grpc_init() grpc_set_ssl_roots_override_callback( ssl_roots_override_callback) - if Py_AtExit(grpc_shutdown) != 0: - raise ImportError('failed to register gRPC library shutdown callbacks') - _initialize() diff --git a/src/python/grpcio_health_checking/.gitignore b/src/python/grpcio_health_checking/.gitignore index 85af4668866..432c3194f04 100644 --- a/src/python/grpcio_health_checking/.gitignore +++ b/src/python/grpcio_health_checking/.gitignore @@ -1,5 +1,6 @@ *.proto *_pb2.py +*_pb2_grpc.py build/ grpcio_health_checking.egg-info/ dist/ diff --git a/src/python/grpcio_tests/.gitignore b/src/python/grpcio_tests/.gitignore index fc620135dc7..dcba283a8ca 100644 --- a/src/python/grpcio_tests/.gitignore +++ b/src/python/grpcio_tests/.gitignore @@ -1,4 +1,5 @@ proto/ src/ *_pb2.py +*_pb2_grpc.py *.egg-info/ diff --git a/src/python/grpcio_tests/tests/protoc_plugin/_split_definitions_test.py b/src/python/grpcio_tests/tests/protoc_plugin/_split_definitions_test.py new file mode 100644 index 00000000000..64fd97256eb --- /dev/null +++ b/src/python/grpcio_tests/tests/protoc_plugin/_split_definitions_test.py @@ -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) diff --git a/src/python/grpcio_tests/tests/protoc_plugin/protos/invocation_testing/__init__.py b/src/python/grpcio_tests/tests/protoc_plugin/protos/invocation_testing/__init__.py new file mode 100644 index 00000000000..2f88fa04122 --- /dev/null +++ b/src/python/grpcio_tests/tests/protoc_plugin/protos/invocation_testing/__init__.py @@ -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. + + diff --git a/src/python/grpcio_tests/tests/protoc_plugin/protos/invocation_testing/same.proto b/src/python/grpcio_tests/tests/protoc_plugin/protos/invocation_testing/same.proto new file mode 100644 index 00000000000..269e2fd2c7e --- /dev/null +++ b/src/python/grpcio_tests/tests/protoc_plugin/protos/invocation_testing/same.proto @@ -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); +} diff --git a/src/python/grpcio_tests/tests/protoc_plugin/protos/invocation_testing/split_messages/__init__.py b/src/python/grpcio_tests/tests/protoc_plugin/protos/invocation_testing/split_messages/__init__.py new file mode 100644 index 00000000000..2f88fa04122 --- /dev/null +++ b/src/python/grpcio_tests/tests/protoc_plugin/protos/invocation_testing/split_messages/__init__.py @@ -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. + + diff --git a/src/python/grpcio_tests/tests/protoc_plugin/protos/invocation_testing/split_messages/messages.proto b/src/python/grpcio_tests/tests/protoc_plugin/protos/invocation_testing/split_messages/messages.proto new file mode 100644 index 00000000000..de22dae0492 --- /dev/null +++ b/src/python/grpcio_tests/tests/protoc_plugin/protos/invocation_testing/split_messages/messages.proto @@ -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 {} diff --git a/src/python/grpcio_tests/tests/protoc_plugin/protos/invocation_testing/split_services/__init__.py b/src/python/grpcio_tests/tests/protoc_plugin/protos/invocation_testing/split_services/__init__.py new file mode 100644 index 00000000000..2f88fa04122 --- /dev/null +++ b/src/python/grpcio_tests/tests/protoc_plugin/protos/invocation_testing/split_services/__init__.py @@ -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. + + diff --git a/src/python/grpcio_tests/tests/protoc_plugin/protos/invocation_testing/split_services/services.proto b/src/python/grpcio_tests/tests/protoc_plugin/protos/invocation_testing/split_services/services.proto new file mode 100644 index 00000000000..af999cd48db --- /dev/null +++ b/src/python/grpcio_tests/tests/protoc_plugin/protos/invocation_testing/split_services/services.proto @@ -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); +} diff --git a/src/python/grpcio_tests/tests/tests.json b/src/python/grpcio_tests/tests/tests.json index dcaef0db1fa..d0cf2b5779e 100644 --- a/src/python/grpcio_tests/tests/tests.json +++ b/src/python/grpcio_tests/tests/tests.json @@ -4,40 +4,44 @@ "_api_test.ChannelTest", "_auth_test.AccessTokenCallCredentialsTest", "_auth_test.GoogleCallCredentialsTest", - "_beta_features_test.BetaFeaturesTest", - "_beta_features_test.ContextManagementAndLifecycleTest", + "_beta_features_test.BetaFeaturesTest", + "_beta_features_test.ContextManagementAndLifecycleTest", "_cancel_many_calls_test.CancelManyCallsTest", "_channel_connectivity_test.ChannelConnectivityTest", "_channel_ready_future_test.ChannelReadyFutureTest", - "_channel_test.ChannelTest", + "_channel_test.ChannelTest", "_compression_test.CompressionTest", "_connectivity_channel_test.ConnectivityStatesTest", "_credentials_test.CredentialsTest", "_empty_message_test.EmptyMessageTest", "_exit_test.ExitTest", - "_face_interface_test.DynamicInvokerBlockingInvocationInlineServiceTest", - "_face_interface_test.DynamicInvokerFutureInvocationAsynchronousEventServiceTest", - "_face_interface_test.GenericInvokerBlockingInvocationInlineServiceTest", - "_face_interface_test.GenericInvokerFutureInvocationAsynchronousEventServiceTest", - "_face_interface_test.MultiCallableInvokerBlockingInvocationInlineServiceTest", + "_face_interface_test.DynamicInvokerBlockingInvocationInlineServiceTest", + "_face_interface_test.DynamicInvokerFutureInvocationAsynchronousEventServiceTest", + "_face_interface_test.GenericInvokerBlockingInvocationInlineServiceTest", + "_face_interface_test.GenericInvokerFutureInvocationAsynchronousEventServiceTest", + "_face_interface_test.MultiCallableInvokerBlockingInvocationInlineServiceTest", "_face_interface_test.MultiCallableInvokerFutureInvocationAsynchronousEventServiceTest", "_health_servicer_test.HealthServicerTest", "_implementations_test.CallCredentialsTest", - "_implementations_test.ChannelCredentialsTest", - "_insecure_interop_test.InsecureInteropTest", - "_logging_pool_test.LoggingPoolTest", + "_implementations_test.ChannelCredentialsTest", + "_insecure_interop_test.InsecureInteropTest", + "_logging_pool_test.LoggingPoolTest", "_metadata_code_details_test.MetadataCodeDetailsTest", "_metadata_test.MetadataTest", - "_not_found_test.NotFoundTest", + "_not_found_test.NotFoundTest", "_python_plugin_test.PythonPluginTest", "_read_some_but_not_all_responses_test.ReadSomeButNotAllResponsesTest", "_rpc_test.RPCTest", - "_sanity_test.Sanity", - "_secure_interop_test.SecureInteropTest", + "_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", - "cygrpc_test.InsecureServerInsecureClient", - "cygrpc_test.SecureServerSecureClient", + "_utilities_test.ChannelConnectivityTest", + "beta_python_plugin_test.PythonPluginTest", + "cygrpc_test.InsecureServerInsecureClient", + "cygrpc_test.SecureServerSecureClient", "cygrpc_test.TypeSmokeTest" ] From b2b6a9eb5761944fdc4f689b291b60028c92c082 Mon Sep 17 00:00:00 2001 From: Nathaniel Manista Date: Fri, 9 Dec 2016 22:25:36 +0000 Subject: [PATCH 32/45] ServicerContext methods doc string fix and tweak ServicerContext.set_code takes a (grpc.)StatusCode, not an integer. --- src/python/grpcio/grpc/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/python/grpcio/grpc/__init__.py b/src/python/grpcio/grpc/__init__.py index 126ad556bb7..d4e3152c591 100644 --- a/src/python/grpcio/grpc/__init__.py +++ b/src/python/grpcio/grpc/__init__.py @@ -767,8 +767,8 @@ class ServicerContext(six.with_metaclass(abc.ABCMeta, RpcContext)): gRPC runtime to determine the status code of the RPC. Args: - code: The integer status code of the RPC to be transmitted to the - invocation side of the RPC. + code: A StatusCode value to be transmitted to the invocation side of the + RPC as the status code of the RPC. """ raise NotImplementedError() @@ -780,8 +780,8 @@ class ServicerContext(six.with_metaclass(abc.ABCMeta, RpcContext)): details to transmit. Args: - details: The details string of the RPC to be transmitted to - the invocation side of the RPC. + details: A string to be transmitted to the invocation side of the RPC as + the status details of the RPC. """ raise NotImplementedError() From 6ba93f2612cf6e8010ee90d8b17a4478efde65e2 Mon Sep 17 00:00:00 2001 From: Masood Malekghassemi Date: Wed, 7 Dec 2016 18:25:19 -0800 Subject: [PATCH 33/45] Up-version Python for backports --- build.yaml | 1 + src/python/grpcio/grpc_version.py | 2 +- src/python/grpcio_health_checking/grpc_version.py | 2 +- src/python/grpcio_tests/grpc_version.py | 2 +- tools/distrib/python/grpcio_tools/grpc_version.py | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/build.yaml b/build.yaml index 12d7f855de7..d725ff38905 100644 --- a/build.yaml +++ b/build.yaml @@ -7,6 +7,7 @@ settings: '#3': Use "-preN" suffixes to identify pre-release versions '#4': Per-language overrides are possible with (eg) ruby_version tag here '#5': See the expand_version.py for all the quirks here + python_version: 1.0.2 version: 1.0.1 filegroups: - name: census diff --git a/src/python/grpcio/grpc_version.py b/src/python/grpcio/grpc_version.py index 5e35a7770de..219045a721d 100644 --- a/src/python/grpcio/grpc_version.py +++ b/src/python/grpcio/grpc_version.py @@ -29,4 +29,4 @@ # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio/grpc_version.py.template`!!! -VERSION='1.0.1' +VERSION='1.0.2' diff --git a/src/python/grpcio_health_checking/grpc_version.py b/src/python/grpcio_health_checking/grpc_version.py index 14cb881237c..4cb8c3d82a5 100644 --- a/src/python/grpcio_health_checking/grpc_version.py +++ b/src/python/grpcio_health_checking/grpc_version.py @@ -29,4 +29,4 @@ # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_health_checking/grpc_version.py.template`!!! -VERSION='1.0.1' +VERSION='1.0.2' diff --git a/src/python/grpcio_tests/grpc_version.py b/src/python/grpcio_tests/grpc_version.py index 0f39afb37f5..66b0398c7ab 100644 --- a/src/python/grpcio_tests/grpc_version.py +++ b/src/python/grpcio_tests/grpc_version.py @@ -29,4 +29,4 @@ # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_tests/grpc_version.py.template`!!! -VERSION='1.0.1' +VERSION='1.0.2' diff --git a/tools/distrib/python/grpcio_tools/grpc_version.py b/tools/distrib/python/grpcio_tools/grpc_version.py index 4f7fe9d5874..6dd6fae0d71 100644 --- a/tools/distrib/python/grpcio_tools/grpc_version.py +++ b/tools/distrib/python/grpcio_tools/grpc_version.py @@ -29,4 +29,4 @@ # AUTO-GENERATED FROM `$REPO_ROOT/templates/tools/distrib/python/grpcio_tools/grpc_version.py.template`!!! -VERSION='1.0.1' +VERSION='1.0.2' From 4625b8bb94cfebdbac7f2fd8b2c7ba3b2b9d1574 Mon Sep 17 00:00:00 2001 From: Noah Eisen Date: Fri, 9 Dec 2016 15:04:49 -0800 Subject: [PATCH 34/45] Fix go docker --- tools/dockerfile/interoptest/grpc_interop_go/build_interop.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/dockerfile/interoptest/grpc_interop_go/build_interop.sh b/tools/dockerfile/interoptest/grpc_interop_go/build_interop.sh index 858ee0a68cd..46aabc6b384 100755 --- a/tools/dockerfile/interoptest/grpc_interop_go/build_interop.sh +++ b/tools/dockerfile/interoptest/grpc_interop_go/build_interop.sh @@ -37,7 +37,7 @@ set -e git clone --recursive /var/local/jenkins/grpc-go src/google.golang.org/grpc # Get all gRPC Go dependencies -(cd src/google.golang.org/grpc && go get -t .) +(cd src/google.golang.org/grpc && make deps && make testdeps) # copy service account keys if available cp -r /var/local/jenkins/service_account $HOME || true From 8b223e2986fe92e93aa93b8524d25fbc6d3989b2 Mon Sep 17 00:00:00 2001 From: Nathaniel Manista Date: Fri, 9 Dec 2016 23:25:56 +0000 Subject: [PATCH 35/45] Correct Python cancel_after_begin interop test It was a mistake that requests might be sent; the test specification calls for no requests to be sent. It was a mistake that the response future's cancelled() method was called; the cancelled() method returns something more like "was this object's cancel() method called earlier?" than "did the RPC terminate with status code CANCELLED?". Since it's something that we'd well enough like to work I've retained the cancelled() call with a different failure message. --- .../grpcio_tests/tests/interop/methods.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/python/grpcio_tests/tests/interop/methods.py b/src/python/grpcio_tests/tests/interop/methods.py index 7edd75c56c9..c4a0c084303 100644 --- a/src/python/grpcio_tests/tests/interop/methods.py +++ b/src/python/grpcio_tests/tests/interop/methods.py @@ -159,16 +159,6 @@ def _server_streaming(stub): raise ValueError( 'response body of invalid size %d!' % len(response.payload.body)) -def _cancel_after_begin(stub): - sizes = (27182, 8, 1828, 45904,) - payloads = (messages_pb2.Payload(body=b'\x00' * size) for size in sizes) - requests = (messages_pb2.StreamingInputCallRequest(payload=payload) - for payload in payloads) - response_future = stub.StreamingInputCall.future(requests) - response_future.cancel() - if not response_future.cancelled(): - raise ValueError('expected call to be cancelled') - class _Pipe(object): @@ -232,6 +222,16 @@ def _ping_pong(stub): 'response body of invalid size %d!' % len(response.payload.body)) +def _cancel_after_begin(stub): + with _Pipe() as pipe: + response_future = stub.StreamingInputCall.future(pipe) + response_future.cancel() + if not response_future.cancelled(): + raise ValueError('expected cancelled method to return True') + if response_future.code() is not grpc.StatusCode.CANCELLED: + raise ValueError('expected status code CANCELLED') + + def _cancel_after_first_response(stub): request_response_sizes = (31415, 9, 2653, 58979,) request_payload_sizes = (27182, 8, 1828, 45904,) From e1fd78f110d067c5091906664db05b7d80ee2991 Mon Sep 17 00:00:00 2001 From: Nathaniel Manista Date: Fri, 9 Dec 2016 23:27:58 +0000 Subject: [PATCH 36/45] Drop unnecessary sleep in interop test --- src/python/grpcio_tests/tests/interop/methods.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/python/grpcio_tests/tests/interop/methods.py b/src/python/grpcio_tests/tests/interop/methods.py index c4a0c084303..16afe4c6bfc 100644 --- a/src/python/grpcio_tests/tests/interop/methods.py +++ b/src/python/grpcio_tests/tests/interop/methods.py @@ -33,7 +33,6 @@ import enum import json import os import threading -import time from oauth2client import client as oauth2client_client @@ -269,7 +268,6 @@ def _timeout_on_sleeping_server(stub): response_type=messages_pb2.COMPRESSABLE, payload=messages_pb2.Payload(body=b'\x00' * request_payload_size)) pipe.add(request) - time.sleep(0.1) try: next(response_iterator) except grpc.RpcError as rpc_error: From 2562b532e0833cfe006cac0af2d9c54645a2d216 Mon Sep 17 00:00:00 2001 From: Masood Malekghassemi Date: Fri, 9 Dec 2016 16:26:07 -0800 Subject: [PATCH 37/45] Backport Python setuptools unpinning This should allow our tests to build on newer virtualenvs. --- tools/run_tests/build_python.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/run_tests/build_python.sh b/tools/run_tests/build_python.sh index d2ff1c6b53c..0a73353ce5e 100755 --- a/tools/run_tests/build_python.sh +++ b/tools/run_tests/build_python.sh @@ -170,8 +170,7 @@ pip_install_dir() { } $VENV_PYTHON -m pip install --upgrade pip -# TODO(https://github.com/pypa/setuptools/issues/709) get the latest setuptools -$VENV_PYTHON -m pip install setuptools==25.1.1 +$VENV_PYTHON -m pip install setuptools $VENV_PYTHON -m pip install cython pip_install_dir $ROOT $VENV_PYTHON $ROOT/tools/distrib/python/make_grpcio_tools.py From 7ba3527ffe6900a3c68059eec2220a17f6bd819c Mon Sep 17 00:00:00 2001 From: Masood Malekghassemi Date: Mon, 12 Dec 2016 13:19:31 -0800 Subject: [PATCH 38/45] Un-namespace Python packages Setuptools was updated and our hacky namespace-package-chickens came back to roost. This removes the unsupported namespace package hacks. --- examples/python/multiplex/run_codegen.py | 2 +- examples/python/route_guide/run_codegen.py | 2 +- .../grpcio_health_checking/grpc/__init__.py | 30 ------------------- .../{grpc/health => grpc_health}/__init__.py | 0 .../health => grpc_health}/v1/__init__.py | 0 .../{grpc/health => grpc_health}/v1/health.py | 2 +- .../grpcio_health_checking/health_commands.py | 4 +-- src/python/grpcio_health_checking/setup.py | 1 - src/python/grpcio_tests/commands.py | 4 +-- src/python/grpcio_tests/setup.py | 4 +-- .../health_check/_health_servicer_test.py | 6 ++-- .../protoc_plugin/_split_definitions_test.py | 2 +- .../python/grpcio_tools/grpc/__init__.py | 30 ------------------- .../{grpc/tools => grpc_tools}/__init__.py | 0 .../tools => grpc_tools}/_protoc_compiler.pyx | 2 +- .../{grpc/tools => grpc_tools}/command.py | 2 +- .../{grpc/tools => grpc_tools}/main.cc | 2 +- .../{grpc/tools => grpc_tools}/main.h | 0 .../{grpc/tools => grpc_tools}/protoc.py | 2 +- tools/distrib/python/grpcio_tools/setup.py | 9 +++--- 20 files changed, 21 insertions(+), 83 deletions(-) delete mode 100644 src/python/grpcio_health_checking/grpc/__init__.py rename src/python/grpcio_health_checking/{grpc/health => grpc_health}/__init__.py (100%) rename src/python/grpcio_health_checking/{grpc/health => grpc_health}/v1/__init__.py (100%) rename src/python/grpcio_health_checking/{grpc/health => grpc_health}/v1/health.py (98%) delete mode 100644 tools/distrib/python/grpcio_tools/grpc/__init__.py rename tools/distrib/python/grpcio_tools/{grpc/tools => grpc_tools}/__init__.py (100%) rename tools/distrib/python/grpcio_tools/{grpc/tools => grpc_tools}/_protoc_compiler.pyx (97%) rename tools/distrib/python/grpcio_tools/{grpc/tools => grpc_tools}/command.py (98%) rename tools/distrib/python/grpcio_tools/{grpc/tools => grpc_tools}/main.cc (98%) rename tools/distrib/python/grpcio_tools/{grpc/tools => grpc_tools}/main.h (100%) rename tools/distrib/python/grpcio_tools/{grpc/tools => grpc_tools}/protoc.py (98%) diff --git a/examples/python/multiplex/run_codegen.py b/examples/python/multiplex/run_codegen.py index 7922a0f5c7f..89ac9c8fae5 100755 --- a/examples/python/multiplex/run_codegen.py +++ b/examples/python/multiplex/run_codegen.py @@ -29,7 +29,7 @@ """Generates protocol messages and gRPC stubs.""" -from grpc.tools import protoc +from grpc_tools import protoc protoc.main( ( diff --git a/examples/python/route_guide/run_codegen.py b/examples/python/route_guide/run_codegen.py index c7c60085809..3751e019c97 100644 --- a/examples/python/route_guide/run_codegen.py +++ b/examples/python/route_guide/run_codegen.py @@ -29,7 +29,7 @@ """Runs protoc with the gRPC plugin to generate messages and gRPC stubs.""" -from grpc.tools import protoc +from grpc_tools import protoc protoc.main( ( diff --git a/src/python/grpcio_health_checking/grpc/__init__.py b/src/python/grpcio_health_checking/grpc/__init__.py deleted file mode 100644 index fcc7048815f..00000000000 --- a/src/python/grpcio_health_checking/grpc/__init__.py +++ /dev/null @@ -1,30 +0,0 @@ -# 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. - -__import__('pkg_resources').declare_namespace(__name__) diff --git a/src/python/grpcio_health_checking/grpc/health/__init__.py b/src/python/grpcio_health_checking/grpc_health/__init__.py similarity index 100% rename from src/python/grpcio_health_checking/grpc/health/__init__.py rename to src/python/grpcio_health_checking/grpc_health/__init__.py diff --git a/src/python/grpcio_health_checking/grpc/health/v1/__init__.py b/src/python/grpcio_health_checking/grpc_health/v1/__init__.py similarity index 100% rename from src/python/grpcio_health_checking/grpc/health/v1/__init__.py rename to src/python/grpcio_health_checking/grpc_health/v1/__init__.py diff --git a/src/python/grpcio_health_checking/grpc/health/v1/health.py b/src/python/grpcio_health_checking/grpc_health/v1/health.py similarity index 98% rename from src/python/grpcio_health_checking/grpc/health/v1/health.py rename to src/python/grpcio_health_checking/grpc_health/v1/health.py index 8108ac10962..0df679b0e22 100644 --- a/src/python/grpcio_health_checking/grpc/health/v1/health.py +++ b/src/python/grpcio_health_checking/grpc_health/v1/health.py @@ -33,7 +33,7 @@ import threading import grpc -from grpc.health.v1 import health_pb2 +from grpc_health.v1 import health_pb2 class HealthServicer(health_pb2.HealthServicer): diff --git a/src/python/grpcio_health_checking/health_commands.py b/src/python/grpcio_health_checking/health_commands.py index 66df25da63f..0c420a655f5 100644 --- a/src/python/grpcio_health_checking/health_commands.py +++ b/src/python/grpcio_health_checking/health_commands.py @@ -54,7 +54,7 @@ class CopyProtoModules(setuptools.Command): if os.path.isfile(HEALTH_PROTO): shutil.copyfile( HEALTH_PROTO, - os.path.join(ROOT_DIR, 'grpc/health/v1/health.proto')) + os.path.join(ROOT_DIR, 'grpc_health/v1/health.proto')) class BuildPackageProtos(setuptools.Command): @@ -74,5 +74,5 @@ class BuildPackageProtos(setuptools.Command): # directory is provided as an 'include' directory. We assume it's the '' key # to `self.distribution.package_dir` (and get a key error if it's not # there). - from grpc.tools import command + from grpc_tools import command command.build_package_protos(self.distribution.package_dir['']) diff --git a/src/python/grpcio_health_checking/setup.py b/src/python/grpcio_health_checking/setup.py index 8c92ee16a93..e88f389ba8a 100644 --- a/src/python/grpcio_health_checking/setup.py +++ b/src/python/grpcio_health_checking/setup.py @@ -66,7 +66,6 @@ setuptools.setup( license='3-clause BSD', package_dir=PACKAGE_DIRECTORIES, packages=setuptools.find_packages('.'), - namespace_packages=['grpc'], install_requires=INSTALL_REQUIRES, setup_requires=SETUP_REQUIRES, cmdclass=COMMAND_CLASS diff --git a/src/python/grpcio_tests/commands.py b/src/python/grpcio_tests/commands.py index 5ee551cfe1e..e822971fe09 100644 --- a/src/python/grpcio_tests/commands.py +++ b/src/python/grpcio_tests/commands.py @@ -100,7 +100,7 @@ class BuildProtoModules(setuptools.Command): pass def run(self): - import grpc.tools.protoc as protoc + import grpc_tools.protoc as protoc include_regex = re.compile(self.include) exclude_regex = re.compile(self.exclude) if self.exclude else None @@ -116,7 +116,7 @@ class BuildProtoModules(setuptools.Command): # but we currently have name conflicts in src/proto for path in paths: command = [ - 'grpc.tools.protoc', + 'grpc_tools.protoc', '-I {}'.format(PROTO_STEM), '--python_out={}'.format(PROTO_STEM), '--grpc_python_out={}'.format(PROTO_STEM), diff --git a/src/python/grpcio_tests/setup.py b/src/python/grpcio_tests/setup.py index 73842066020..cd9194a7a85 100644 --- a/src/python/grpcio_tests/setup.py +++ b/src/python/grpcio_tests/setup.py @@ -35,7 +35,7 @@ import sys import setuptools -import grpc.tools.command +import grpc_tools.command PY3 = sys.version_info.major == 3 @@ -68,7 +68,7 @@ COMMAND_CLASS = { # Run `preprocess` *before* doing any packaging! 'preprocess': commands.GatherProto, - 'build_package_protos': grpc.tools.command.BuildPackageProtos, + 'build_package_protos': grpc_tools.command.BuildPackageProtos, 'build_py': commands.BuildPy, 'run_interop': commands.RunInterop, 'test_lite': commands.TestLite diff --git a/src/python/grpcio_tests/tests/health_check/_health_servicer_test.py b/src/python/grpcio_tests/tests/health_check/_health_servicer_test.py index 80300d13df7..5dde72b1698 100644 --- a/src/python/grpcio_tests/tests/health_check/_health_servicer_test.py +++ b/src/python/grpcio_tests/tests/health_check/_health_servicer_test.py @@ -27,14 +27,14 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -"""Tests of grpc.health.v1.health.""" +"""Tests of grpc_health.v1.health.""" import unittest import grpc from grpc.framework.foundation import logging_pool -from grpc.health.v1 import health -from grpc.health.v1 import health_pb2 +from grpc_health.v1 import health +from grpc_health.v1 import health_pb2 from tests.unit.framework.common import test_constants diff --git a/src/python/grpcio_tests/tests/protoc_plugin/_split_definitions_test.py b/src/python/grpcio_tests/tests/protoc_plugin/_split_definitions_test.py index 64fd97256eb..f8ae05bb7a9 100644 --- a/src/python/grpcio_tests/tests/protoc_plugin/_split_definitions_test.py +++ b/src/python/grpcio_tests/tests/protoc_plugin/_split_definitions_test.py @@ -44,7 +44,7 @@ import threading import unittest import grpc -from grpc.tools import protoc +from grpc_tools import protoc from tests.unit.framework.common import test_constants _MESSAGES_IMPORT = b'import "messages.proto";' diff --git a/tools/distrib/python/grpcio_tools/grpc/__init__.py b/tools/distrib/python/grpcio_tools/grpc/__init__.py deleted file mode 100644 index 70ac5edd483..00000000000 --- a/tools/distrib/python/grpcio_tools/grpc/__init__.py +++ /dev/null @@ -1,30 +0,0 @@ -# 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__('pkg_resources').declare_namespace(__name__) diff --git a/tools/distrib/python/grpcio_tools/grpc/tools/__init__.py b/tools/distrib/python/grpcio_tools/grpc_tools/__init__.py similarity index 100% rename from tools/distrib/python/grpcio_tools/grpc/tools/__init__.py rename to tools/distrib/python/grpcio_tools/grpc_tools/__init__.py diff --git a/tools/distrib/python/grpcio_tools/grpc/tools/_protoc_compiler.pyx b/tools/distrib/python/grpcio_tools/grpc_tools/_protoc_compiler.pyx similarity index 97% rename from tools/distrib/python/grpcio_tools/grpc/tools/_protoc_compiler.pyx rename to tools/distrib/python/grpcio_tools/grpc_tools/_protoc_compiler.pyx index a6530127c04..81034fad5e0 100644 --- a/tools/distrib/python/grpcio_tools/grpc/tools/_protoc_compiler.pyx +++ b/tools/distrib/python/grpcio_tools/grpc_tools/_protoc_compiler.pyx @@ -29,7 +29,7 @@ from libc cimport stdlib -cdef extern from "grpc/tools/main.h": +cdef extern from "grpc_tools/main.h": int protoc_main(int argc, char *argv[]) def run_main(list args not None): diff --git a/tools/distrib/python/grpcio_tools/grpc/tools/command.py b/tools/distrib/python/grpcio_tools/grpc_tools/command.py similarity index 98% rename from tools/distrib/python/grpcio_tools/grpc/tools/command.py rename to tools/distrib/python/grpcio_tools/grpc_tools/command.py index 25200998357..befb1284da0 100644 --- a/tools/distrib/python/grpcio_tools/grpc/tools/command.py +++ b/tools/distrib/python/grpcio_tools/grpc_tools/command.py @@ -32,7 +32,7 @@ import sys import setuptools -from grpc.tools import protoc +from grpc_tools import protoc def build_package_protos(package_root): diff --git a/tools/distrib/python/grpcio_tools/grpc/tools/main.cc b/tools/distrib/python/grpcio_tools/grpc_tools/main.cc similarity index 98% rename from tools/distrib/python/grpcio_tools/grpc/tools/main.cc rename to tools/distrib/python/grpcio_tools/grpc_tools/main.cc index 83918395135..0c2fa3180a9 100644 --- a/tools/distrib/python/grpcio_tools/grpc/tools/main.cc +++ b/tools/distrib/python/grpcio_tools/grpc_tools/main.cc @@ -32,7 +32,7 @@ #include "src/compiler/python_generator.h" -#include "grpc/tools/main.h" +#include "grpc_tools/main.h" int protoc_main(int argc, char* argv[]) { google::protobuf::compiler::CommandLineInterface cli; diff --git a/tools/distrib/python/grpcio_tools/grpc/tools/main.h b/tools/distrib/python/grpcio_tools/grpc_tools/main.h similarity index 100% rename from tools/distrib/python/grpcio_tools/grpc/tools/main.h rename to tools/distrib/python/grpcio_tools/grpc_tools/main.h diff --git a/tools/distrib/python/grpcio_tools/grpc/tools/protoc.py b/tools/distrib/python/grpcio_tools/grpc_tools/protoc.py similarity index 98% rename from tools/distrib/python/grpcio_tools/grpc/tools/protoc.py rename to tools/distrib/python/grpcio_tools/grpc_tools/protoc.py index e1256a7dd9b..7d5892dc4b7 100644 --- a/tools/distrib/python/grpcio_tools/grpc/tools/protoc.py +++ b/tools/distrib/python/grpcio_tools/grpc_tools/protoc.py @@ -32,7 +32,7 @@ import pkg_resources import sys -from grpc.tools import _protoc_compiler +from grpc_tools import _protoc_compiler def main(command_arguments): """Run the protocol buffer compiler with the given command-line arguments. diff --git a/tools/distrib/python/grpcio_tools/setup.py b/tools/distrib/python/grpcio_tools/setup.py index 762d6948ccd..814bf9651b1 100644 --- a/tools/distrib/python/grpcio_tools/setup.py +++ b/tools/distrib/python/grpcio_tools/setup.py @@ -103,7 +103,7 @@ PROTO_FILES = [ CC_INCLUDE = os.path.normpath(protoc_lib_deps.CC_INCLUDE) PROTO_INCLUDE = os.path.normpath(protoc_lib_deps.PROTO_INCLUDE) -GRPC_PYTHON_TOOLS_PACKAGE = 'grpc.tools' +GRPC_PYTHON_TOOLS_PACKAGE = 'grpc_tools' GRPC_PYTHON_PROTO_RESOURCES_NAME = '_proto' DEFINE_MACROS = () @@ -149,14 +149,14 @@ def package_data(): def protoc_ext_module(): plugin_sources = [ - os.path.join('grpc', 'tools', 'main.cc'), + os.path.join('grpc_tools', 'main.cc'), os.path.join('grpc_root', 'src', 'compiler', 'python_generator.cc')] + [ os.path.join(CC_INCLUDE, cc_file) for cc_file in CC_FILES] plugin_ext = extension.Extension( - name='grpc.tools._protoc_compiler', + name='grpc_tools._protoc_compiler', sources=( - [os.path.join('grpc', 'tools', '_protoc_compiler.pyx')] + + [os.path.join('grpc_tools', '_protoc_compiler.pyx')] + plugin_sources), include_dirs=[ '.', @@ -183,7 +183,6 @@ setuptools.setup( protoc_ext_module(), ]), packages=setuptools.find_packages('.'), - namespace_packages=['grpc'], install_requires=[ 'protobuf>=3.0.0', 'grpcio>={version}'.format(version=grpc_version.VERSION), From f096f540eeb057103eb5c2330a4165e0aa251ff1 Mon Sep 17 00:00:00 2001 From: Masood Malekghassemi Date: Mon, 12 Dec 2016 15:34:34 -0800 Subject: [PATCH 39/45] Recover 'namespace'd Python distribution packages Uses dynamic loading to paper-over the negative effects of losing namespace packages in the previous commit. --- src/python/grpcio/grpc/__init__.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/python/grpcio/grpc/__init__.py b/src/python/grpcio/grpc/__init__.py index d4e3152c591..f801dd632a0 100644 --- a/src/python/grpcio/grpc/__init__.py +++ b/src/python/grpcio/grpc/__init__.py @@ -31,6 +31,7 @@ import abc import enum +import sys import six @@ -1293,3 +1294,24 @@ __all__ = ( 'secure_channel', 'server', ) + + +############################### Extension Shims ################################ + + +# Here to maintain backwards compatibility; avoid using these in new code! +try: + import grpc_tools + sys.modules.update({'grpc.tools': grpc_tools}) +except ImportError: + pass +try: + import grpc_health + sys.modules.update({'grpc.health': grpc_health}) +except ImportError: + pass +try: + import grpc_reflection + sys.modules.update({'grpc.reflection': grpc_reflection}) +except ImportError: + pass From e136c1a77309274328760fb73b7faa8420f47c6b Mon Sep 17 00:00:00 2001 From: Masood Malekghassemi Date: Mon, 12 Dec 2016 15:58:36 -0800 Subject: [PATCH 40/45] Up-version Python --- build.yaml | 2 +- src/python/grpcio/grpc_version.py | 2 +- src/python/grpcio_health_checking/grpc_version.py | 2 +- src/python/grpcio_tests/grpc_version.py | 2 +- tools/distrib/python/grpcio_tools/grpc_version.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build.yaml b/build.yaml index d725ff38905..79bb8c94715 100644 --- a/build.yaml +++ b/build.yaml @@ -7,7 +7,7 @@ settings: '#3': Use "-preN" suffixes to identify pre-release versions '#4': Per-language overrides are possible with (eg) ruby_version tag here '#5': See the expand_version.py for all the quirks here - python_version: 1.0.2 + python_version: 1.0.3 version: 1.0.1 filegroups: - name: census diff --git a/src/python/grpcio/grpc_version.py b/src/python/grpcio/grpc_version.py index 219045a721d..775ece397bb 100644 --- a/src/python/grpcio/grpc_version.py +++ b/src/python/grpcio/grpc_version.py @@ -29,4 +29,4 @@ # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio/grpc_version.py.template`!!! -VERSION='1.0.2' +VERSION='1.0.3' diff --git a/src/python/grpcio_health_checking/grpc_version.py b/src/python/grpcio_health_checking/grpc_version.py index 4cb8c3d82a5..17ef5873f14 100644 --- a/src/python/grpcio_health_checking/grpc_version.py +++ b/src/python/grpcio_health_checking/grpc_version.py @@ -29,4 +29,4 @@ # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_health_checking/grpc_version.py.template`!!! -VERSION='1.0.2' +VERSION='1.0.3' diff --git a/src/python/grpcio_tests/grpc_version.py b/src/python/grpcio_tests/grpc_version.py index 66b0398c7ab..c6226160b89 100644 --- a/src/python/grpcio_tests/grpc_version.py +++ b/src/python/grpcio_tests/grpc_version.py @@ -29,4 +29,4 @@ # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_tests/grpc_version.py.template`!!! -VERSION='1.0.2' +VERSION='1.0.3' diff --git a/tools/distrib/python/grpcio_tools/grpc_version.py b/tools/distrib/python/grpcio_tools/grpc_version.py index 6dd6fae0d71..a96d2ef8b26 100644 --- a/tools/distrib/python/grpcio_tools/grpc_version.py +++ b/tools/distrib/python/grpcio_tools/grpc_version.py @@ -29,4 +29,4 @@ # AUTO-GENERATED FROM `$REPO_ROOT/templates/tools/distrib/python/grpcio_tools/grpc_version.py.template`!!! -VERSION='1.0.2' +VERSION='1.0.3' From 91764921bc25f695bf2ce75904fe7a69f552203e Mon Sep 17 00:00:00 2001 From: Nathaniel Manista Date: Tue, 13 Dec 2016 02:53:20 +0000 Subject: [PATCH 41/45] Use LONG_TIMEOUT for calls that do not time out --- .../grpcio_tests/tests/unit/_channel_ready_future_test.py | 2 +- src/python/grpcio_tests/tests/unit/beta/_utilities_test.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/python/grpcio_tests/tests/unit/_channel_ready_future_test.py b/src/python/grpcio_tests/tests/unit/_channel_ready_future_test.py index e8982ed2ded..3d1519a5372 100644 --- a/src/python/grpcio_tests/tests/unit/_channel_ready_future_test.py +++ b/src/python/grpcio_tests/tests/unit/_channel_ready_future_test.py @@ -86,7 +86,7 @@ class ChannelReadyFutureTest(unittest.TestCase): ready_future = grpc.channel_ready_future(channel) ready_future.add_done_callback(callback.accept_value) - self.assertIsNone(ready_future.result(test_constants.SHORT_TIMEOUT)) + self.assertIsNone(ready_future.result(test_constants.LONG_TIMEOUT)) value_passed_to_callback = callback.block_until_called() self.assertIs(ready_future, value_passed_to_callback) self.assertFalse(ready_future.cancelled()) diff --git a/src/python/grpcio_tests/tests/unit/beta/_utilities_test.py b/src/python/grpcio_tests/tests/unit/beta/_utilities_test.py index 90fe10c77ce..b955756b6b9 100644 --- a/src/python/grpcio_tests/tests/unit/beta/_utilities_test.py +++ b/src/python/grpcio_tests/tests/unit/beta/_utilities_test.py @@ -88,7 +88,7 @@ class ChannelConnectivityTest(unittest.TestCase): ready_future = utilities.channel_ready_future(channel) ready_future.add_done_callback(callback.accept_value) self.assertIsNone( - ready_future.result(test_constants.SHORT_TIMEOUT)) + ready_future.result(test_constants.LONG_TIMEOUT)) value_passed_to_callback = callback.block_until_called() self.assertIs(ready_future, value_passed_to_callback) self.assertFalse(ready_future.cancelled()) From 1de3914d69f90969114dc6e73d34d1e6d4bc2ae6 Mon Sep 17 00:00:00 2001 From: Nathaniel Manista Date: Tue, 13 Dec 2016 02:56:41 +0000 Subject: [PATCH 42/45] Style fix: pass keyword arguments by keyword --- .../grpcio_tests/tests/unit/_channel_ready_future_test.py | 4 ++-- src/python/grpcio_tests/tests/unit/beta/_utilities_test.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/python/grpcio_tests/tests/unit/_channel_ready_future_test.py b/src/python/grpcio_tests/tests/unit/_channel_ready_future_test.py index 3d1519a5372..20ba13fc4eb 100644 --- a/src/python/grpcio_tests/tests/unit/_channel_ready_future_test.py +++ b/src/python/grpcio_tests/tests/unit/_channel_ready_future_test.py @@ -66,7 +66,7 @@ class ChannelReadyFutureTest(unittest.TestCase): ready_future = grpc.channel_ready_future(channel) ready_future.add_done_callback(callback.accept_value) with self.assertRaises(grpc.FutureTimeoutError): - ready_future.result(test_constants.SHORT_TIMEOUT) + ready_future.result(timeout=test_constants.SHORT_TIMEOUT) self.assertFalse(ready_future.cancelled()) self.assertFalse(ready_future.done()) self.assertTrue(ready_future.running()) @@ -86,7 +86,7 @@ class ChannelReadyFutureTest(unittest.TestCase): ready_future = grpc.channel_ready_future(channel) ready_future.add_done_callback(callback.accept_value) - self.assertIsNone(ready_future.result(test_constants.LONG_TIMEOUT)) + self.assertIsNone(ready_future.result(timeout=test_constants.LONG_TIMEOUT)) value_passed_to_callback = callback.block_until_called() self.assertIs(ready_future, value_passed_to_callback) self.assertFalse(ready_future.cancelled()) diff --git a/src/python/grpcio_tests/tests/unit/beta/_utilities_test.py b/src/python/grpcio_tests/tests/unit/beta/_utilities_test.py index b955756b6b9..9cce96cc85c 100644 --- a/src/python/grpcio_tests/tests/unit/beta/_utilities_test.py +++ b/src/python/grpcio_tests/tests/unit/beta/_utilities_test.py @@ -66,7 +66,7 @@ class ChannelConnectivityTest(unittest.TestCase): ready_future = utilities.channel_ready_future(channel) ready_future.add_done_callback(callback.accept_value) with self.assertRaises(future.TimeoutError): - ready_future.result(test_constants.SHORT_TIMEOUT) + ready_future.result(timeout=test_constants.SHORT_TIMEOUT) self.assertFalse(ready_future.cancelled()) self.assertFalse(ready_future.done()) self.assertTrue(ready_future.running()) @@ -88,7 +88,7 @@ class ChannelConnectivityTest(unittest.TestCase): ready_future = utilities.channel_ready_future(channel) ready_future.add_done_callback(callback.accept_value) self.assertIsNone( - ready_future.result(test_constants.LONG_TIMEOUT)) + ready_future.result(timeout=test_constants.LONG_TIMEOUT)) value_passed_to_callback = callback.block_until_called() self.assertIs(ready_future, value_passed_to_callback) self.assertFalse(ready_future.cancelled()) From 94b82355cb07c5a8db4c0f797a1760b2addb451c Mon Sep 17 00:00:00 2001 From: Masood Malekghassemi Date: Tue, 13 Dec 2016 13:26:47 -0800 Subject: [PATCH 43/45] Patch overlooked strings from Python un-namespacing --- src/python/grpcio_health_checking/MANIFEST.in | 2 +- tools/distrib/python/grpcio_tools/MANIFEST.in | 2 +- tools/distrib/python/grpcio_tools/grpc_tools/command.py | 2 +- tools/distrib/python/grpcio_tools/grpc_tools/protoc.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/python/grpcio_health_checking/MANIFEST.in b/src/python/grpcio_health_checking/MANIFEST.in index 7407f646d16..5255e4c4036 100644 --- a/src/python/grpcio_health_checking/MANIFEST.in +++ b/src/python/grpcio_health_checking/MANIFEST.in @@ -1,4 +1,4 @@ include grpc_version.py include health_commands.py -graft grpc +graft grpc_health global-exclude *.pyc diff --git a/tools/distrib/python/grpcio_tools/MANIFEST.in b/tools/distrib/python/grpcio_tools/MANIFEST.in index 7712834d642..11ce367747a 100644 --- a/tools/distrib/python/grpcio_tools/MANIFEST.in +++ b/tools/distrib/python/grpcio_tools/MANIFEST.in @@ -2,6 +2,6 @@ include grpc_version.py include protoc_deps.py include protoc_lib_deps.py include README.rst -graft grpc +graft grpc_tools graft grpc_root graft third_party diff --git a/tools/distrib/python/grpcio_tools/grpc_tools/command.py b/tools/distrib/python/grpcio_tools/grpc_tools/command.py index befb1284da0..e490940e7fd 100644 --- a/tools/distrib/python/grpcio_tools/grpc_tools/command.py +++ b/tools/distrib/python/grpcio_tools/grpc_tools/command.py @@ -45,7 +45,7 @@ def build_package_protos(package_root): for proto_file in proto_files: command = [ - 'grpc.tools.protoc', + 'grpc_tools.protoc', '--proto_path={}'.format(inclusion_root), '--python_out={}'.format(inclusion_root), '--grpc_python_out={}'.format(inclusion_root), diff --git a/tools/distrib/python/grpcio_tools/grpc_tools/protoc.py b/tools/distrib/python/grpcio_tools/grpc_tools/protoc.py index 7d5892dc4b7..63fddb2f064 100644 --- a/tools/distrib/python/grpcio_tools/grpc_tools/protoc.py +++ b/tools/distrib/python/grpcio_tools/grpc_tools/protoc.py @@ -45,5 +45,5 @@ def main(command_arguments): return _protoc_compiler.run_main(command_arguments) if __name__ == '__main__': - proto_include = pkg_resources.resource_filename('grpc.tools', '_proto') + proto_include = pkg_resources.resource_filename('grpc_tools', '_proto') sys.exit(main(sys.argv + ['-I{}'.format(proto_include)])) From 9fb054a5377b97feb0ef6bf0d128a2c675e210d2 Mon Sep 17 00:00:00 2001 From: Masood Malekghassemi Date: Tue, 13 Dec 2016 13:32:17 -0800 Subject: [PATCH 44/45] Upversion Python --- build.yaml | 2 +- src/python/grpcio/grpc_version.py | 2 +- src/python/grpcio_health_checking/grpc_version.py | 2 +- src/python/grpcio_tests/grpc_version.py | 2 +- tools/distrib/python/grpcio_tools/grpc_version.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build.yaml b/build.yaml index 79bb8c94715..be77738002c 100644 --- a/build.yaml +++ b/build.yaml @@ -7,7 +7,7 @@ settings: '#3': Use "-preN" suffixes to identify pre-release versions '#4': Per-language overrides are possible with (eg) ruby_version tag here '#5': See the expand_version.py for all the quirks here - python_version: 1.0.3 + python_version: 1.0.4 version: 1.0.1 filegroups: - name: census diff --git a/src/python/grpcio/grpc_version.py b/src/python/grpcio/grpc_version.py index 775ece397bb..1c0183ee479 100644 --- a/src/python/grpcio/grpc_version.py +++ b/src/python/grpcio/grpc_version.py @@ -29,4 +29,4 @@ # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio/grpc_version.py.template`!!! -VERSION='1.0.3' +VERSION='1.0.4' diff --git a/src/python/grpcio_health_checking/grpc_version.py b/src/python/grpcio_health_checking/grpc_version.py index 17ef5873f14..60e94ffcfa7 100644 --- a/src/python/grpcio_health_checking/grpc_version.py +++ b/src/python/grpcio_health_checking/grpc_version.py @@ -29,4 +29,4 @@ # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_health_checking/grpc_version.py.template`!!! -VERSION='1.0.3' +VERSION='1.0.4' diff --git a/src/python/grpcio_tests/grpc_version.py b/src/python/grpcio_tests/grpc_version.py index c6226160b89..bdc1c36b705 100644 --- a/src/python/grpcio_tests/grpc_version.py +++ b/src/python/grpcio_tests/grpc_version.py @@ -29,4 +29,4 @@ # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_tests/grpc_version.py.template`!!! -VERSION='1.0.3' +VERSION='1.0.4' diff --git a/tools/distrib/python/grpcio_tools/grpc_version.py b/tools/distrib/python/grpcio_tools/grpc_version.py index a96d2ef8b26..547a4b5ae26 100644 --- a/tools/distrib/python/grpcio_tools/grpc_version.py +++ b/tools/distrib/python/grpcio_tools/grpc_version.py @@ -29,4 +29,4 @@ # AUTO-GENERATED FROM `$REPO_ROOT/templates/tools/distrib/python/grpcio_tools/grpc_version.py.template`!!! -VERSION='1.0.3' +VERSION='1.0.4' From 61b8c8920698b949872480540912ba2bf022c9df Mon Sep 17 00:00:00 2001 From: ncteisen Date: Wed, 7 Dec 2016 15:28:35 -0800 Subject: [PATCH 45/45] Add check on return value from start_client_batch --- src/python/grpcio/grpc/_channel.py | 46 ++++- src/python/grpcio_tests/tests/tests.json | 1 + .../tests/unit/_invalid_metadata_test.py | 179 ++++++++++++++++++ 3 files changed, 220 insertions(+), 6 deletions(-) create mode 100644 src/python/grpcio_tests/tests/unit/_invalid_metadata_test.py diff --git a/src/python/grpcio/grpc/_channel.py b/src/python/grpcio/grpc/_channel.py index 3ac735a4ec9..1298cfbb6f6 100644 --- a/src/python/grpcio/grpc/_channel.py +++ b/src/python/grpcio/grpc/_channel.py @@ -99,6 +99,22 @@ def _wait_once_until(condition, until): else: condition.wait(timeout=remaining) +_INTERNAL_CALL_ERROR_MESSAGE_FORMAT = ( + 'Internal gRPC call error %d. ' + + 'Please report to https://github.com/grpc/grpc/issues') + +def _check_call_error(call_error, metadata): + if call_error == cygrpc.CallError.invalid_metadata: + raise ValueError('metadata was invalid: %s' % metadata) + elif call_error != cygrpc.CallError.ok: + raise ValueError(_INTERNAL_CALL_ERROR_MESSAGE_FORMAT % call_error) + +def _call_error_set_RPCstate(state, call_error, metadata): + if call_error == cygrpc.CallError.invalid_metadata: + _abort(state, grpc.StatusCode.INTERNAL, 'metadata was invalid: %s' % metadata) + else: + _abort(state, grpc.StatusCode.INTERNAL, + _INTERNAL_CALL_ERROR_MESSAGE_FORMAT % call_error) class _RPCState(object): @@ -472,7 +488,8 @@ class _UnaryUnaryMultiCallable(grpc.UnaryUnaryMultiCallable): None, 0, completion_queue, self._method, None, deadline_timespec) if credentials is not None: call.set_credentials(credentials._credentials) - call.start_client_batch(cygrpc.Operations(operations), None) + call_error = call.start_client_batch(cygrpc.Operations(operations), None) + _check_call_error(call_error, metadata) _handle_event(completion_queue.poll(), state, self._response_deserializer) return state, deadline @@ -496,7 +513,11 @@ class _UnaryUnaryMultiCallable(grpc.UnaryUnaryMultiCallable): call.set_credentials(credentials._credentials) event_handler = _event_handler(state, call, self._response_deserializer) with state.condition: - call.start_client_batch(cygrpc.Operations(operations), event_handler) + call_error = call.start_client_batch(cygrpc.Operations(operations), + event_handler) + if call_error != cygrpc.CallError.ok: + _call_error_set_RPCstate(state, call_error, metadata) + return _Rendezvous(state, None, None, deadline) drive_call() return _Rendezvous(state, call, self._response_deserializer, deadline) @@ -536,7 +557,11 @@ class _UnaryStreamMultiCallable(grpc.UnaryStreamMultiCallable): cygrpc.operation_send_close_from_client(_EMPTY_FLAGS), cygrpc.operation_receive_status_on_client(_EMPTY_FLAGS), ) - call.start_client_batch(cygrpc.Operations(operations), event_handler) + call_error = call.start_client_batch(cygrpc.Operations(operations), + event_handler) + if call_error != cygrpc.CallError.ok: + _call_error_set_RPCstate(state, call_error, metadata) + return _Rendezvous(state, None, None, deadline) drive_call() return _Rendezvous(state, call, self._response_deserializer, deadline) @@ -571,7 +596,8 @@ class _StreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable): cygrpc.operation_receive_message(_EMPTY_FLAGS), cygrpc.operation_receive_status_on_client(_EMPTY_FLAGS), ) - call.start_client_batch(cygrpc.Operations(operations), None) + call_error = call.start_client_batch(cygrpc.Operations(operations), None) + _check_call_error(call_error, metadata) _consume_request_iterator( request_iterator, state, call, self._request_serializer) while True: @@ -615,7 +641,11 @@ class _StreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable): cygrpc.operation_receive_message(_EMPTY_FLAGS), cygrpc.operation_receive_status_on_client(_EMPTY_FLAGS), ) - call.start_client_batch(cygrpc.Operations(operations), event_handler) + call_error = call.start_client_batch(cygrpc.Operations(operations), + event_handler) + if call_error != cygrpc.CallError.ok: + _call_error_set_RPCstate(state, call_error, metadata) + return _Rendezvous(state, None, None, deadline) drive_call() _consume_request_iterator( request_iterator, state, call, self._request_serializer) @@ -652,7 +682,11 @@ class _StreamStreamMultiCallable(grpc.StreamStreamMultiCallable): _common.cygrpc_metadata(metadata), _EMPTY_FLAGS), cygrpc.operation_receive_status_on_client(_EMPTY_FLAGS), ) - call.start_client_batch(cygrpc.Operations(operations), event_handler) + call_error = call.start_client_batch(cygrpc.Operations(operations), + event_handler) + if call_error != cygrpc.CallError.ok: + _call_error_set_RPCstate(state, call_error, metadata) + return _Rendezvous(state, None, None, deadline) drive_call() _consume_request_iterator( request_iterator, state, call, self._request_serializer) diff --git a/src/python/grpcio_tests/tests/tests.json b/src/python/grpcio_tests/tests/tests.json index d0cf2b5779e..0eea66da400 100644 --- a/src/python/grpcio_tests/tests/tests.json +++ b/src/python/grpcio_tests/tests/tests.json @@ -25,6 +25,7 @@ "_implementations_test.CallCredentialsTest", "_implementations_test.ChannelCredentialsTest", "_insecure_interop_test.InsecureInteropTest", + "_invalid_metadata_test.InvalidMetadataTest" "_logging_pool_test.LoggingPoolTest", "_metadata_code_details_test.MetadataCodeDetailsTest", "_metadata_test.MetadataTest", diff --git a/src/python/grpcio_tests/tests/unit/_invalid_metadata_test.py b/src/python/grpcio_tests/tests/unit/_invalid_metadata_test.py new file mode 100644 index 00000000000..0411c58900a --- /dev/null +++ b/src/python/grpcio_tests/tests/unit/_invalid_metadata_test.py @@ -0,0 +1,179 @@ +# 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. + +"""Test of RPCs made against gRPC Python's application-layer API.""" + +import unittest + +import grpc + +from tests.unit.framework.common import test_constants + +_SERIALIZE_REQUEST = lambda bytestring: bytestring * 2 +_DESERIALIZE_REQUEST = lambda bytestring: bytestring[len(bytestring) // 2:] +_SERIALIZE_RESPONSE = lambda bytestring: bytestring * 3 +_DESERIALIZE_RESPONSE = lambda bytestring: bytestring[:len(bytestring) // 3] + +_UNARY_UNARY = '/test/UnaryUnary' +_UNARY_STREAM = '/test/UnaryStream' +_STREAM_UNARY = '/test/StreamUnary' +_STREAM_STREAM = '/test/StreamStream' + + +def _unary_unary_multi_callable(channel): + return channel.unary_unary(_UNARY_UNARY) + + +def _unary_stream_multi_callable(channel): + return channel.unary_stream( + _UNARY_STREAM, + request_serializer=_SERIALIZE_REQUEST, + response_deserializer=_DESERIALIZE_RESPONSE) + + +def _stream_unary_multi_callable(channel): + return channel.stream_unary( + _STREAM_UNARY, + request_serializer=_SERIALIZE_REQUEST, + response_deserializer=_DESERIALIZE_RESPONSE) + + +def _stream_stream_multi_callable(channel): + return channel.stream_stream(_STREAM_STREAM) + + +class InvalidMetadataTest(unittest.TestCase): + + def setUp(self): + self._channel = grpc.insecure_channel('localhost:8080') + self._unary_unary = _unary_unary_multi_callable(self._channel) + self._unary_stream = _unary_stream_multi_callable(self._channel) + self._stream_unary = _stream_unary_multi_callable(self._channel) + self._stream_stream = _stream_stream_multi_callable(self._channel) + + def testUnaryRequestBlockingUnaryResponse(self): + request = b'\x07\x08' + metadata = (('InVaLiD', 'UnaryRequestBlockingUnaryResponse'),) + expected_error_details = "metadata was invalid: %s" % metadata + with self.assertRaises(ValueError) as exception_context: + self._unary_unary(request, metadata=metadata) + self.assertEqual( + expected_error_details, exception_context.exception.message) + + def testUnaryRequestBlockingUnaryResponseWithCall(self): + request = b'\x07\x08' + metadata = (('InVaLiD', 'UnaryRequestBlockingUnaryResponseWithCall'),) + expected_error_details = "metadata was invalid: %s" % metadata + with self.assertRaises(ValueError) as exception_context: + self._unary_unary.with_call(request, metadata=metadata) + self.assertEqual( + expected_error_details, exception_context.exception.message) + + def testUnaryRequestFutureUnaryResponse(self): + request = b'\x07\x08' + metadata = (('InVaLiD', 'UnaryRequestFutureUnaryResponse'),) + expected_error_details = "metadata was invalid: %s" % metadata + response_future = self._unary_unary.future(request, metadata=metadata) + with self.assertRaises(grpc.RpcError) as exception_context: + response_future.result() + self.assertEqual( + exception_context.exception.details(), expected_error_details) + self.assertEqual( + exception_context.exception.code(), grpc.StatusCode.INTERNAL) + self.assertEqual(response_future.details(), expected_error_details) + self.assertEqual(response_future.code(), grpc.StatusCode.INTERNAL) + + def testUnaryRequestStreamResponse(self): + request = b'\x37\x58' + metadata = (('InVaLiD', 'UnaryRequestStreamResponse'),) + expected_error_details = "metadata was invalid: %s" % metadata + response_iterator = self._unary_stream(request, metadata=metadata) + with self.assertRaises(grpc.RpcError) as exception_context: + next(response_iterator) + self.assertEqual( + exception_context.exception.details(), expected_error_details) + self.assertEqual( + exception_context.exception.code(), grpc.StatusCode.INTERNAL) + self.assertEqual(response_iterator.details(), expected_error_details) + self.assertEqual(response_iterator.code(), grpc.StatusCode.INTERNAL) + + def testStreamRequestBlockingUnaryResponse(self): + request_iterator = (b'\x07\x08' for _ in range(test_constants.STREAM_LENGTH)) + metadata = (('InVaLiD', 'StreamRequestBlockingUnaryResponse'),) + expected_error_details = "metadata was invalid: %s" % metadata + with self.assertRaises(ValueError) as exception_context: + self._stream_unary(request_iterator, metadata=metadata) + self.assertEqual( + expected_error_details, exception_context.exception.message) + + def testStreamRequestBlockingUnaryResponseWithCall(self): + request_iterator = ( + b'\x07\x08' for _ in range(test_constants.STREAM_LENGTH)) + metadata = (('InVaLiD', 'StreamRequestBlockingUnaryResponseWithCall'),) + expected_error_details = "metadata was invalid: %s" % metadata + multi_callable = _stream_unary_multi_callable(self._channel) + with self.assertRaises(ValueError) as exception_context: + multi_callable.with_call(request_iterator, metadata=metadata) + self.assertEqual( + expected_error_details, exception_context.exception.message) + + def testStreamRequestFutureUnaryResponse(self): + request_iterator = ( + b'\x07\x08' for _ in range(test_constants.STREAM_LENGTH)) + metadata = (('InVaLiD', 'StreamRequestFutureUnaryResponse'),) + expected_error_details = "metadata was invalid: %s" % metadata + response_future = self._stream_unary.future( + request_iterator, metadata=metadata) + with self.assertRaises(grpc.RpcError) as exception_context: + response_future.result() + self.assertEqual( + exception_context.exception.details(), expected_error_details) + self.assertEqual( + exception_context.exception.code(), grpc.StatusCode.INTERNAL) + self.assertEqual(response_future.details(), expected_error_details) + self.assertEqual(response_future.code(), grpc.StatusCode.INTERNAL) + + def testStreamRequestStreamResponse(self): + request_iterator = ( + b'\x07\x08' for _ in range(test_constants.STREAM_LENGTH)) + metadata = (('InVaLiD', 'StreamRequestStreamResponse'),) + expected_error_details = "metadata was invalid: %s" % metadata + response_iterator = self._stream_stream(request_iterator, metadata=metadata) + with self.assertRaises(grpc.RpcError) as exception_context: + next(response_iterator) + self.assertEqual( + exception_context.exception.details(), expected_error_details) + self.assertEqual( + exception_context.exception.code(), grpc.StatusCode.INTERNAL) + self.assertEqual(response_iterator.details(), expected_error_details) + self.assertEqual(response_iterator.code(), grpc.StatusCode.INTERNAL) + + +if __name__ == '__main__': + unittest.main(verbosity=2)