Skip to content

google-cloud-ndb: DeprecationWarning from the three-arg generator.throw() in tasklets #18158

Description

@skippdot

Environment details

  • API: google-cloud-ndb
  • OS type and version: macOS 15 (Darwin 25.5.0); also reproduced on Ubuntu CI
  • Python version: 3.12, 3.13, 3.14 — any version since 3.12
  • google-cloud-ndb version: 2.5.1

Steps to reproduce

_advance_tasklet throws exceptions into the wrapped generator using the three-argument form of generator.throw(), deprecated in Python 3.12:

https://github.com/googleapis/google-cloud-python/blob/main/packages/google-cloud-ndb/google/cloud/ndb/tasklets.py#L318

if error:
    traceback = error.__traceback__
    yielded = self.generator.throw(type(error), error, traceback)

Every exception that crosses a tasklet boundary emits a DeprecationWarning. Any suite that exercises error paths fills up with them — ours emits 34 from 8 tests, which drowns out warnings that actually matter.

Code example

Needs only google-cloud-ndb and the standard library — no Datastore connection:

import warnings
from unittest import mock

from google.cloud.ndb import context as context_module, tasklets


@tasklets.tasklet
def inner():
    raise ValueError("boom")
    yield


@tasklets.tasklet
def outer():
    yield inner()


client = mock.Mock(
    project="testing", database=None, namespace=None,
    spec=("project", "database", "namespace"),
)
with context_module.Context(client).use():
    with warnings.catch_warnings(record=True) as caught:
        warnings.simplefilter("always")
        try:
            outer().result()
        except ValueError:
            pass
        for w in caught:
            print(f"{w.category.__name__}: {w.message}")

Stack trace

DeprecationWarning: the (type, exc, tb) signature of throw() is deprecated,
use the single-arg signature instead.
  google/cloud/ndb/tasklets.py:318: yielded = self.generator.throw(type(error), error, traceback)

Suggested fix

The single-argument form reads the traceback off the exception itself, so the local becomes redundant:

if error:
    yielded = self.generator.throw(error)

I applied exactly this against 2.5.1 and verified that:

  • the DeprecationWarning is gone
  • the exception still reaches the caller unchanged
  • __traceback__ is still populated on the delivered exception
  • a 50-test suite that drives ndb through a Datastore stub still passes

Right now this is only noise, but the Python docs say the old signature "may be removed in a future version" — once it is, every exception crossing a tasklet boundary would raise TypeError instead of propagating, so the fix is worth landing before that.

Happy to open a PR if it helps.

Metadata

Metadata

Assignees

No one assigned

    Labels

    priority: p2Moderately-important priority. Fix may not be included in next release.type: bugError or flaw in code with unintended results or allowing sub-optimal usage patterns.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions