diff --git a/packages/google-cloud-ndb/google/cloud/ndb/tasklets.py b/packages/google-cloud-ndb/google/cloud/ndb/tasklets.py index f3dd8632dd95..ee03fa281927 100644 --- a/packages/google-cloud-ndb/google/cloud/ndb/tasklets.py +++ b/packages/google-cloud-ndb/google/cloud/ndb/tasklets.py @@ -314,8 +314,7 @@ def _advance_tasklet(self, send_value=None, error=None): with self.context.use(): # Send the next value or exception into the generator if error: - traceback = error.__traceback__ - yielded = self.generator.throw(type(error), error, traceback) + yielded = self.generator.throw(error) else: # send_value will be None if this is the first time diff --git a/packages/google-cloud-ndb/tests/unit/test_tasklets.py b/packages/google-cloud-ndb/tests/unit/test_tasklets.py index 9c9d9b6a6683..c8aba2fd9278 100644 --- a/packages/google-cloud-ndb/tests/unit/test_tasklets.py +++ b/packages/google-cloud-ndb/tests/unit/test_tasklets.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import warnings from unittest import mock import pytest @@ -363,6 +364,39 @@ def generator_function(dependency): with pytest.raises(Exception): future.result() + @staticmethod + def test__advance_tasklet_dependency_raises_preserves_traceback(in_context): + """Regression test: the error reaches the generator with its traceback + and without a DeprecationWarning from the legacy throw() signature.""" + + def generator_function(dependency): + try: + yield dependency + except Exception as caught: + raise tasklets.Return(caught.__traceback__ is not None) + + error = Exception("Spurious error.") + dependency = tasklets.Future() + generator = generator_function(dependency) + future = tasklets._TaskletFuture(generator, in_context) + future._advance_tasklet() + + try: + raise error + except Exception: + pass # give the exception a traceback, as a real failure would have + + with warnings.catch_warnings(record=True) as caught_warnings: + warnings.simplefilter("always") + dependency.set_exception(error) + + assert future.result() is True + assert not [ + warning + for warning in caught_warnings + if issubclass(warning.category, DeprecationWarning) + ] + @staticmethod def test__advance_tasklet_dependency_raises_with_try_except(in_context): def generator_function(dependency, error_handler):