Merge pull request #15690 from grpc/revert-15539-recv_trailing_metadata_ready

Revert "move recv_trailing_metadata into its own callback, don't use on_complete for recv_ops"
diff --git a/.pylintrc b/.pylintrc
index 453b45a..90e8989 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -72,6 +72,13 @@
 	# TODO(https://github.com/grpc/grpc/issues/261): Maybe we could have
 	# this one if we extracted just a few more helper functions...
 	too-many-nested-blocks,
-	# NOTE(nathaniel): I have disputed the premise of this inspection from
-	# the beginning and will continue to do so until it goes away for good.
+	# TODO(https://github.com/grpc/grpc/issues/261): Disable unnecessary
+	# super-init requirement for abstract class implementations for now.
+	super-init-not-called,
+	# NOTE(nathaniel): A single statement that always returns program
+	# control is better than two statements the first of which sometimes
+	# returns program control and the second of which always returns
+	# program control. Probably generally, but definitely in the cases of
+	# if:/else: and for:/else:.
 	useless-else-on-loop,
+	no-else-return,
diff --git a/.pylintrc-tests b/.pylintrc-tests
index b358b2c..ebe9d50 100644
--- a/.pylintrc-tests
+++ b/.pylintrc-tests
@@ -103,6 +103,13 @@
 	# TODO(https://github.com/grpc/grpc/issues/261): Maybe we could have
 	# this one if we extracted just a few more helper functions...
 	too-many-nested-blocks,
-	# NOTE(nathaniel): I have disputed the premise of this inspection from
-	# the beginning and will continue to do so until it goes away for good.
+	# TODO(https://github.com/grpc/grpc/issues/261): Disable unnecessary
+	# super-init requirement for abstract class implementations for now.
+	super-init-not-called,
+	# NOTE(nathaniel): A single statement that always returns program
+	# control is better than two statements the first of which sometimes
+	# returns program control and the second of which always returns
+	# program control. Probably generally, but definitely in the cases of
+	# if:/else: and for:/else:.
 	useless-else-on-loop,
+	no-else-return,
diff --git a/include/grpcpp/impl/codegen/call.h b/include/grpcpp/impl/codegen/call.h
index 28cc4a9..e324f6b 100644
--- a/include/grpcpp/impl/codegen/call.h
+++ b/include/grpcpp/impl/codegen/call.h
@@ -573,10 +573,13 @@
       binary_error_details =
           grpc::string(iter->second.begin(), iter->second.length());
     }
-    *recv_status_ = Status(static_cast<StatusCode>(status_code_),
-                           grpc::string(GRPC_SLICE_START_PTR(error_message_),
-                                        GRPC_SLICE_END_PTR(error_message_)),
-                           binary_error_details);
+    *recv_status_ =
+        Status(static_cast<StatusCode>(status_code_),
+               GRPC_SLICE_IS_EMPTY(error_message_)
+                   ? grpc::string()
+                   : grpc::string(GRPC_SLICE_START_PTR(error_message_),
+                                  GRPC_SLICE_END_PTR(error_message_)),
+               binary_error_details);
     client_context_->set_debug_error_string(
         debug_error_string_ != nullptr ? debug_error_string_ : "");
     g_core_codegen_interface->grpc_slice_unref(error_message_);
diff --git a/src/python/grpcio/grpc/__init__.py b/src/python/grpcio/grpc/__init__.py
index 0f31119..d1477fb 100644
--- a/src/python/grpcio/grpc/__init__.py
+++ b/src/python/grpcio/grpc/__init__.py
@@ -1482,7 +1482,7 @@
       A ServerCredentials for use with an SSL-enabled Server. Typically, this
       object is an argument to add_secure_port() method during server setup.
     """
-    if len(private_key_certificate_chain_pairs) == 0:
+    if not private_key_certificate_chain_pairs:
         raise ValueError(
             'At least one private key-certificate chain pair is required!')
     elif require_client_auth and root_certificates is None:
@@ -1512,15 +1512,15 @@
       A ServerCertificateConfiguration that can be returned in the certificate
         configuration fetching callback.
     """
-    if len(private_key_certificate_chain_pairs) == 0:
-        raise ValueError(
-            'At least one private key-certificate chain pair is required!')
-    else:
+    if private_key_certificate_chain_pairs:
         return ServerCertificateConfiguration(
             _cygrpc.server_certificate_config_ssl(root_certificates, [
                 _cygrpc.SslPemKeyCertPair(key, pem)
                 for key, pem in private_key_certificate_chain_pairs
             ]))
+    else:
+        raise ValueError(
+            'At least one private key-certificate chain pair is required!')
 
 
 def dynamic_ssl_server_credentials(initial_certificate_configuration,
diff --git a/src/python/grpcio/grpc/_channel.py b/src/python/grpcio/grpc/_channel.py
index 9fe946b..e924699 100644
--- a/src/python/grpcio/grpc/_channel.py
+++ b/src/python/grpcio/grpc/_channel.py
@@ -192,7 +192,7 @@
             with state.condition:
                 if state.code is None and not state.cancelled:
                     if serialized_request is None:
-                        code = grpc.StatusCode.INTERNAL  # pylint: disable=redefined-variable-type
+                        code = grpc.StatusCode.INTERNAL
                         details = 'Exception serializing request!'
                         call.cancel(
                             _common.STATUS_CODE_TO_CYGRPC_STATUS_CODE[code],
@@ -813,10 +813,7 @@
                     _common.CYGRPC_CONNECTIVITY_STATE_TO_CHANNEL_CONNECTIVITY[
                         connectivity])
                 if not state.delivering:
-                    # NOTE(nathaniel): The field is only ever used as a
-                    # sequence so it's fine that both lists and tuples are
-                    # assigned to it.
-                    callbacks = _deliveries(state)  # pylint: disable=redefined-variable-type
+                    callbacks = _deliveries(state)
                     if callbacks:
                         _spawn_delivery(state, callbacks)
 
diff --git a/src/python/grpcio/grpc/_interceptor.py b/src/python/grpcio/grpc/_interceptor.py
index f465e35..6b7a912 100644
--- a/src/python/grpcio/grpc/_interceptor.py
+++ b/src/python/grpcio/grpc/_interceptor.py
@@ -100,6 +100,12 @@
     def cancelled(self):
         return False
 
+    def is_active(self):
+        return False
+
+    def time_remaining(self):
+        return None
+
     def running(self):
         return False
 
@@ -115,6 +121,9 @@
     def traceback(self, ignored_timeout=None):
         return self._traceback
 
+    def add_callback(self, callback):
+        return False
+
     def add_done_callback(self, fn):
         fn(self)
 
@@ -288,11 +297,11 @@
         self._channel = channel
         self._interceptor = interceptor
 
-    def subscribe(self, *args, **kwargs):
-        self._channel.subscribe(*args, **kwargs)
+    def subscribe(self, callback, try_to_connect=False):
+        self._channel.subscribe(callback, try_to_connect=try_to_connect)
 
-    def unsubscribe(self, *args, **kwargs):
-        self._channel.unsubscribe(*args, **kwargs)
+    def unsubscribe(self, callback):
+        self._channel.unsubscribe(callback)
 
     def unary_unary(self,
                     method,
diff --git a/src/python/grpcio/grpc/_server.py b/src/python/grpcio/grpc/_server.py
index d12a242..2761022 100644
--- a/src/python/grpcio/grpc/_server.py
+++ b/src/python/grpcio/grpc/_server.py
@@ -330,6 +330,8 @@
             self._state.request = None
             return request
 
+        raise AssertionError()  # should never run
+
     def _next(self):
         with self._state.condition:
             self._raise_or_start_receive_message()
diff --git a/src/python/grpcio/grpc/_utilities.py b/src/python/grpcio/grpc/_utilities.py
index 25bd1ce..d90b34b 100644
--- a/src/python/grpcio/grpc/_utilities.py
+++ b/src/python/grpcio/grpc/_utilities.py
@@ -116,6 +116,8 @@
             callable_util.call_logging_exceptions(
                 done_callback, _DONE_CALLBACK_EXCEPTION_LOG_MESSAGE, self)
 
+        return True
+
     def cancelled(self):
         with self._condition:
             return self._cancelled
diff --git a/src/python/grpcio/grpc/beta/_server_adaptations.py b/src/python/grpcio/grpc/beta/_server_adaptations.py
index ccafec8..80ac65b 100644
--- a/src/python/grpcio/grpc/beta/_server_adaptations.py
+++ b/src/python/grpcio/grpc/beta/_server_adaptations.py
@@ -305,6 +305,7 @@
                                         response_serializer, None, None, None,
                                         _adapt_stream_stream_event(
                                             implementation.stream_stream_event))
+    raise ValueError()
 
 
 def _flatten_method_pair_map(method_pair_map):
diff --git a/src/python/grpcio/grpc/beta/utilities.py b/src/python/grpcio/grpc/beta/utilities.py
index b5d8aac..fe3ce60 100644
--- a/src/python/grpcio/grpc/beta/utilities.py
+++ b/src/python/grpcio/grpc/beta/utilities.py
@@ -85,6 +85,8 @@
             callable_util.call_logging_exceptions(
                 done_callback, _DONE_CALLBACK_EXCEPTION_LOG_MESSAGE, self)
 
+        return True
+
     def cancelled(self):
         with self._condition:
             return self._cancelled
diff --git a/src/python/grpcio/grpc/framework/foundation/stream_util.py b/src/python/grpcio/grpc/framework/foundation/stream_util.py
index ed0448a..1faaf29 100644
--- a/src/python/grpcio/grpc/framework/foundation/stream_util.py
+++ b/src/python/grpcio/grpc/framework/foundation/stream_util.py
@@ -47,10 +47,10 @@
         self._values = []
         self._active = True
 
-    def consume(self, stock_reply):
+    def consume(self, value):
         with self._condition:
             if self._active:
-                self._values.append(stock_reply)
+                self._values.append(value)
                 self._condition.notify()
 
     def terminate(self):
@@ -58,10 +58,10 @@
             self._active = False
             self._condition.notify()
 
-    def consume_and_terminate(self, stock_reply):
+    def consume_and_terminate(self, value):
         with self._condition:
             if self._active:
-                self._values.append(stock_reply)
+                self._values.append(value)
                 self._active = False
                 self._condition.notify()
 
diff --git a/src/python/grpcio_testing/grpc_testing/__init__.py b/src/python/grpcio_testing/grpc_testing/__init__.py
index e87d0ff..65fdd1b 100644
--- a/src/python/grpcio_testing/grpc_testing/__init__.py
+++ b/src/python/grpcio_testing/grpc_testing/__init__.py
@@ -14,9 +14,9 @@
 """Objects for use in testing gRPC Python-using application code."""
 
 import abc
+import six
 
 from google.protobuf import descriptor
-import six
 
 import grpc
 
diff --git a/src/python/grpcio_testing/grpc_testing/_server/_handler.py b/src/python/grpcio_testing/grpc_testing/_server/_handler.py
index d4f50f6..0e3404b 100644
--- a/src/python/grpcio_testing/grpc_testing/_server/_handler.py
+++ b/src/python/grpcio_testing/grpc_testing/_server/_handler.py
@@ -105,10 +105,10 @@
                 self._expiration_future.cancel()
             self._condition.notify_all()
 
-    def add_termination_callback(self, termination_callback):
+    def add_termination_callback(self, callback):
         with self._condition:
             if self._code is None:
-                self._termination_callbacks.append(termination_callback)
+                self._termination_callbacks.append(callback)
                 return True
             else:
                 return False
diff --git a/src/python/grpcio_tests/tests/_loader.py b/src/python/grpcio_tests/tests/_loader.py
index be0af646..80c107a 100644
--- a/src/python/grpcio_tests/tests/_loader.py
+++ b/src/python/grpcio_tests/tests/_loader.py
@@ -48,12 +48,13 @@
         # measure unnecessarily suffers)
         coverage_context = coverage.Coverage(data_suffix=True)
         coverage_context.start()
-        modules = [importlib.import_module(name) for name in names]
-        for module in modules:
-            self.visit_module(module)
-        for module in modules:
+        imported_modules = tuple(
+            importlib.import_module(name) for name in names)
+        for imported_module in imported_modules:
+            self.visit_module(imported_module)
+        for imported_module in imported_modules:
             try:
-                package_paths = module.__path__
+                package_paths = imported_module.__path__
             except AttributeError:
                 continue
             self.walk_packages(package_paths)
diff --git a/src/python/grpcio_tests/tests/_result.py b/src/python/grpcio_tests/tests/_result.py
index b105f18..e5378b7 100644
--- a/src/python/grpcio_tests/tests/_result.py
+++ b/src/python/grpcio_tests/tests/_result.py
@@ -144,10 +144,6 @@
         super(AugmentedResult, self).startTestRun()
         self.cases = dict()
 
-    def stopTestRun(self):
-        """See unittest.TestResult.stopTestRun."""
-        super(AugmentedResult, self).stopTestRun()
-
     def startTest(self, test):
         """See unittest.TestResult.startTest."""
         super(AugmentedResult, self).startTest(test)
@@ -155,19 +151,19 @@
         self.cases[case_id] = CaseResult(
             id=case_id, name=test.id(), kind=CaseResult.Kind.RUNNING)
 
-    def addError(self, test, error):
+    def addError(self, test, err):
         """See unittest.TestResult.addError."""
-        super(AugmentedResult, self).addError(test, error)
+        super(AugmentedResult, self).addError(test, err)
         case_id = self.id_map(test)
         self.cases[case_id] = self.cases[case_id].updated(
-            kind=CaseResult.Kind.ERROR, traceback=error)
+            kind=CaseResult.Kind.ERROR, traceback=err)
 
-    def addFailure(self, test, error):
+    def addFailure(self, test, err):
         """See unittest.TestResult.addFailure."""
-        super(AugmentedResult, self).addFailure(test, error)
+        super(AugmentedResult, self).addFailure(test, err)
         case_id = self.id_map(test)
         self.cases[case_id] = self.cases[case_id].updated(
-            kind=CaseResult.Kind.FAILURE, traceback=error)
+            kind=CaseResult.Kind.FAILURE, traceback=err)
 
     def addSuccess(self, test):
         """See unittest.TestResult.addSuccess."""
@@ -183,12 +179,12 @@
         self.cases[case_id] = self.cases[case_id].updated(
             kind=CaseResult.Kind.SKIP, skip_reason=reason)
 
-    def addExpectedFailure(self, test, error):
+    def addExpectedFailure(self, test, err):
         """See unittest.TestResult.addExpectedFailure."""
-        super(AugmentedResult, self).addExpectedFailure(test, error)
+        super(AugmentedResult, self).addExpectedFailure(test, err)
         case_id = self.id_map(test)
         self.cases[case_id] = self.cases[case_id].updated(
-            kind=CaseResult.Kind.EXPECTED_FAILURE, traceback=error)
+            kind=CaseResult.Kind.EXPECTED_FAILURE, traceback=err)
 
     def addUnexpectedSuccess(self, test):
         """See unittest.TestResult.addUnexpectedSuccess."""
@@ -249,13 +245,6 @@
         self.coverage_context.save()
         self.coverage_context = None
 
-    def stopTestRun(self):
-        """See unittest.TestResult.stopTestRun."""
-        super(CoverageResult, self).stopTestRun()
-        # TODO(atash): Dig deeper into why the following line fails to properly
-        # combine coverage data from the Cython plugin.
-        #coverage.Coverage().combine()
-
 
 class _Colors(object):
     """Namespaced constants for terminal color magic numbers."""
@@ -295,16 +284,16 @@
         self.out.write(summary(self))
         self.out.flush()
 
-    def addError(self, test, error):
+    def addError(self, test, err):
         """See unittest.TestResult.addError."""
-        super(TerminalResult, self).addError(test, error)
+        super(TerminalResult, self).addError(test, err)
         self.out.write(
             _Colors.FAIL + 'ERROR         {}\n'.format(test.id()) + _Colors.END)
         self.out.flush()
 
-    def addFailure(self, test, error):
+    def addFailure(self, test, err):
         """See unittest.TestResult.addFailure."""
-        super(TerminalResult, self).addFailure(test, error)
+        super(TerminalResult, self).addFailure(test, err)
         self.out.write(
             _Colors.FAIL + 'FAILURE       {}\n'.format(test.id()) + _Colors.END)
         self.out.flush()
@@ -323,9 +312,9 @@
             _Colors.INFO + 'SKIP          {}\n'.format(test.id()) + _Colors.END)
         self.out.flush()
 
-    def addExpectedFailure(self, test, error):
+    def addExpectedFailure(self, test, err):
         """See unittest.TestResult.addExpectedFailure."""
-        super(TerminalResult, self).addExpectedFailure(test, error)
+        super(TerminalResult, self).addExpectedFailure(test, err)
         self.out.write(
             _Colors.INFO + 'FAILURE_OK    {}\n'.format(test.id()) + _Colors.END)
         self.out.flush()
diff --git a/src/python/grpcio_tests/tests/interop/methods.py b/src/python/grpcio_tests/tests/interop/methods.py
index b728ffd..cda15a6 100644
--- a/src/python/grpcio_tests/tests/interop/methods.py
+++ b/src/python/grpcio_tests/tests/interop/methods.py
@@ -144,8 +144,8 @@
 def _empty_unary(stub):
     response = stub.EmptyCall(empty_pb2.Empty())
     if not isinstance(response, empty_pb2.Empty):
-        raise TypeError('response is of type "%s", not empty_pb2.Empty!',
-                        type(response))
+        raise TypeError(
+            'response is of type "%s", not empty_pb2.Empty!' % type(response))
 
 
 def _large_unary(stub):
diff --git a/src/python/grpcio_tests/tests/unit/_junkdrawer/__init__.py b/src/python/grpcio_tests/tests/unit/_junkdrawer/__init__.py
deleted file mode 100644
index 5fb4f3c..0000000
--- a/src/python/grpcio_tests/tests/unit/_junkdrawer/__init__.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright 2015 gRPC authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
diff --git a/src/python/grpcio_tests/tests/unit/_junkdrawer/stock_pb2.py b/src/python/grpcio_tests/tests/unit/_junkdrawer/stock_pb2.py
deleted file mode 100644
index 2bf1e1c..0000000
--- a/src/python/grpcio_tests/tests/unit/_junkdrawer/stock_pb2.py
+++ /dev/null
@@ -1,164 +0,0 @@
-# Copyright 2015 gRPC authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# TODO(nathaniel): Remove this from source control after having made
-# generation from the stock.proto source part of GRPC's build-and-test
-# process.
-
-# Generated by the protocol buffer compiler.  DO NOT EDIT!
-# source: stock.proto
-
-import sys
-_b = sys.version_info[0] < 3 and (lambda x: x) or (lambda x: x.encode('latin1'))
-from google.protobuf import descriptor as _descriptor
-from google.protobuf import message as _message
-from google.protobuf import reflection as _reflection
-from google.protobuf import symbol_database as _symbol_database
-from google.protobuf import descriptor_pb2
-# @@protoc_insertion_point(imports)
-
-_sym_db = _symbol_database.Default()
-
-DESCRIPTOR = _descriptor.FileDescriptor(
-    name='stock.proto',
-    package='stock',
-    serialized_pb=_b(
-        '\n\x0bstock.proto\x12\x05stock\">\n\x0cStockRequest\x12\x0e\n\x06symbol\x18\x01 \x01(\t\x12\x1e\n\x13num_trades_to_watch\x18\x02 \x01(\x05:\x01\x30\"+\n\nStockReply\x12\r\n\x05price\x18\x01 \x01(\x02\x12\x0e\n\x06symbol\x18\x02 \x01(\t2\x96\x02\n\x05Stock\x12=\n\x11GetLastTradePrice\x12\x13.stock.StockRequest\x1a\x11.stock.StockReply\"\x00\x12I\n\x19GetLastTradePriceMultiple\x12\x13.stock.StockRequest\x1a\x11.stock.StockReply\"\x00(\x01\x30\x01\x12?\n\x11WatchFutureTrades\x12\x13.stock.StockRequest\x1a\x11.stock.StockReply\"\x00\x30\x01\x12\x42\n\x14GetHighestTradePrice\x12\x13.stock.StockRequest\x1a\x11.stock.StockReply\"\x00(\x01'
-    ))
-_sym_db.RegisterFileDescriptor(DESCRIPTOR)
-
-_STOCKREQUEST = _descriptor.Descriptor(
-    name='StockRequest',
-    full_name='stock.StockRequest',
-    filename=None,
-    file=DESCRIPTOR,
-    containing_type=None,
-    fields=[
-        _descriptor.FieldDescriptor(
-            name='symbol',
-            full_name='stock.StockRequest.symbol',
-            index=0,
-            number=1,
-            type=9,
-            cpp_type=9,
-            label=1,
-            has_default_value=False,
-            default_value=_b("").decode('utf-8'),
-            message_type=None,
-            enum_type=None,
-            containing_type=None,
-            is_extension=False,
-            extension_scope=None,
-            options=None),
-        _descriptor.FieldDescriptor(
-            name='num_trades_to_watch',
-            full_name='stock.StockRequest.num_trades_to_watch',
-            index=1,
-            number=2,
-            type=5,
-            cpp_type=1,
-            label=1,
-            has_default_value=True,
-            default_value=0,
-            message_type=None,
-            enum_type=None,
-            containing_type=None,
-            is_extension=False,
-            extension_scope=None,
-            options=None),
-    ],
-    extensions=[],
-    nested_types=[],
-    enum_types=[],
-    options=None,
-    is_extendable=False,
-    extension_ranges=[],
-    oneofs=[],
-    serialized_start=22,
-    serialized_end=84,)
-
-_STOCKREPLY = _descriptor.Descriptor(
-    name='StockReply',
-    full_name='stock.StockReply',
-    filename=None,
-    file=DESCRIPTOR,
-    containing_type=None,
-    fields=[
-        _descriptor.FieldDescriptor(
-            name='price',
-            full_name='stock.StockReply.price',
-            index=0,
-            number=1,
-            type=2,
-            cpp_type=6,
-            label=1,
-            has_default_value=False,
-            default_value=0,
-            message_type=None,
-            enum_type=None,
-            containing_type=None,
-            is_extension=False,
-            extension_scope=None,
-            options=None),
-        _descriptor.FieldDescriptor(
-            name='symbol',
-            full_name='stock.StockReply.symbol',
-            index=1,
-            number=2,
-            type=9,
-            cpp_type=9,
-            label=1,
-            has_default_value=False,
-            default_value=_b("").decode('utf-8'),
-            message_type=None,
-            enum_type=None,
-            containing_type=None,
-            is_extension=False,
-            extension_scope=None,
-            options=None),
-    ],
-    extensions=[],
-    nested_types=[],
-    enum_types=[],
-    options=None,
-    is_extendable=False,
-    extension_ranges=[],
-    oneofs=[],
-    serialized_start=86,
-    serialized_end=129,)
-
-DESCRIPTOR.message_types_by_name['StockRequest'] = _STOCKREQUEST
-DESCRIPTOR.message_types_by_name['StockReply'] = _STOCKREPLY
-
-StockRequest = _reflection.GeneratedProtocolMessageType(
-    'StockRequest',
-    (_message.Message,),
-    dict(
-        DESCRIPTOR=_STOCKREQUEST,
-        __module__='stock_pb2'
-        # @@protoc_insertion_point(class_scope:stock.StockRequest)
-    ))
-_sym_db.RegisterMessage(StockRequest)
-
-StockReply = _reflection.GeneratedProtocolMessageType(
-    'StockReply',
-    (_message.Message,),
-    dict(
-        DESCRIPTOR=_STOCKREPLY,
-        __module__='stock_pb2'
-        # @@protoc_insertion_point(class_scope:stock.StockReply)
-    ))
-_sym_db.RegisterMessage(StockReply)
-
-# @@protoc_insertion_point(module_scope)
diff --git a/test/cpp/end2end/end2end_test.cc b/test/cpp/end2end/end2end_test.cc
index 60238e9..3c1d48c 100644
--- a/test/cpp/end2end/end2end_test.cc
+++ b/test/cpp/end2end/end2end_test.cc
@@ -1209,8 +1209,13 @@
   std::vector<ErrorStatus> expected_status;
   expected_status.emplace_back();
   expected_status.back().set_code(13);  // INTERNAL
+  // No Error message or details
+
+  expected_status.emplace_back();
+  expected_status.back().set_code(13);  // INTERNAL
   expected_status.back().set_error_message("text error message");
   expected_status.back().set_binary_error_details("text error details");
+
   expected_status.emplace_back();
   expected_status.back().set_code(13);  // INTERNAL
   expected_status.back().set_error_message("text error message");
diff --git a/tools/distrib/pylint_code.sh b/tools/distrib/pylint_code.sh
index 013b666..82a818c 100755
--- a/tools/distrib/pylint_code.sh
+++ b/tools/distrib/pylint_code.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# Copyright 2017 gRPC authors.
+# Copyright 2017 The gRPC Authors
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -35,14 +35,15 @@
 PYTHON=$VIRTUALENV/bin/python
 
 $PYTHON -m pip install --upgrade pip==10.0.1
-$PYTHON -m pip install pylint==1.6.5
+$PYTHON -m pip install pylint==1.9.2
 
+EXIT=0
 for dir in "${DIRS[@]}"; do
-  $PYTHON -m pylint --rcfile=.pylintrc -rn "$dir" || exit $?
+  $PYTHON -m pylint --rcfile=.pylintrc -rn "$dir" || EXIT=1
 done
 
 for dir in "${TEST_DIRS[@]}"; do
-  $PYTHON -m pylint --rcfile=.pylintrc-tests -rn "$dir" || exit $?
+  $PYTHON -m pylint --rcfile=.pylintrc-tests -rn "$dir" || EXIT=1
 done
 
-exit 0
+exit $EXIT