Skip to content

Commit dfb0e36

Browse files
authored
fix(ndb): use the single-argument generator.throw() signature (#18159)
Fixes #18158 ## Summary `_TaskletFuture._advance_tasklet` throws exceptions into the wrapped generator using the three-argument form of `generator.throw()`, [deprecated in Python 3.12](https://docs.python.org/3/reference/expressions.html#generator.throw): ```python if error: traceback = error.__traceback__ yielded = self.generator.throw(type(error), error, traceback) ``` Every exception that crosses a tasklet boundary emits a `DeprecationWarning`. This library's own unit suite raises **70 warnings** today; with this change it raises **36**, so 34 of them came from this single line. Today that is only noise, but the Python docs say the old signature "may be removed in a future version". `noxfile.py` already lists `3.15` in `ALL_INTERPRETERS`, so it is worth landing before that removal makes every tasklet-boundary exception raise `TypeError` instead of propagating. ## Changes The single-argument form reads the traceback off the exception itself, so the local becomes redundant: ```python if error: yielded = self.generator.throw(error) ``` Also adds a regression test asserting both halves of the contract: the exception still arrives with its `__traceback__` intact, and no `DeprecationWarning` is emitted. I verified the test fails against the old line and passes against the new one, so it genuinely guards the behaviour rather than just tracking it. ## Verification - `pytest tests/unit` — 1833 passed, 1 skipped (1832 before, plus the new test); warnings drop from 70 to 36 - `ruff format --check` and `flake8 google tests` — clean - Reproduced the original warning with only `google-cloud-ndb` and the standard library; see #18158 for the standalone snippet
1 parent 88b10ac commit dfb0e36

2 files changed

Lines changed: 35 additions & 2 deletions

File tree

packages/google-cloud-ndb/google/cloud/ndb/tasklets.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -314,8 +314,7 @@ def _advance_tasklet(self, send_value=None, error=None):
314314
with self.context.use():
315315
# Send the next value or exception into the generator
316316
if error:
317-
traceback = error.__traceback__
318-
yielded = self.generator.throw(type(error), error, traceback)
317+
yielded = self.generator.throw(error)
319318

320319
else:
321320
# send_value will be None if this is the first time

packages/google-cloud-ndb/tests/unit/test_tasklets.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
import warnings
1516
from unittest import mock
1617

1718
import pytest
@@ -363,6 +364,39 @@ def generator_function(dependency):
363364
with pytest.raises(Exception):
364365
future.result()
365366

367+
@staticmethod
368+
def test__advance_tasklet_dependency_raises_preserves_traceback(in_context):
369+
"""Regression test: the error reaches the generator with its traceback
370+
and without a DeprecationWarning from the legacy throw() signature."""
371+
372+
def generator_function(dependency):
373+
try:
374+
yield dependency
375+
except Exception as caught:
376+
raise tasklets.Return(caught.__traceback__ is not None)
377+
378+
error = Exception("Spurious error.")
379+
dependency = tasklets.Future()
380+
generator = generator_function(dependency)
381+
future = tasklets._TaskletFuture(generator, in_context)
382+
future._advance_tasklet()
383+
384+
try:
385+
raise error
386+
except Exception:
387+
pass # give the exception a traceback, as a real failure would have
388+
389+
with warnings.catch_warnings(record=True) as caught_warnings:
390+
warnings.simplefilter("always")
391+
dependency.set_exception(error)
392+
393+
assert future.result() is True
394+
assert not [
395+
warning
396+
for warning in caught_warnings
397+
if issubclass(warning.category, DeprecationWarning)
398+
]
399+
366400
@staticmethod
367401
def test__advance_tasklet_dependency_raises_with_try_except(in_context):
368402
def generator_function(dependency, error_handler):

0 commit comments

Comments
 (0)