google-cloud-ndb: DeprecationWarning from the three-arg generator.throw() in tasklets
type: bugpriority: p2
#### 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://docs.python.org/3/reference/expressions.html#generator.throw):
https://github.com/googleapis/google-cloud-python/blob/main/packages/google-cloud-ndb/google/cloud/ndb/tasklets.py#L318
```python
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:
```python
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:
```python
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.
0 条评论