fix(python): prevent BlobFile finalizer panics on Tokio workers (#4291)

## Cause

`io.RawIOBase.__del__` checks `closed` and calls `close()` during GC.
Both PyO3 methods entered `block_on`, which panics when GC runs on a
LanceDB Tokio worker.

## Fix

Track Python blob handle closure atomically so `closed` is synchronous.
`close()` marks the handle closed immediately and, when called on a
Tokio thread, schedules the underlying async cleanup without blocking
that thread. Reads and seeks after close fail immediately; ordinary
callers still wait for cleanup.

## Validation

- Rebuilt the Python extension with `uv run --extra tests --extra dev
maturin develop --extras tests,dev`.
- Ran the blob test file and remote blob handle test: 69 passed, 1
skipped.
- Ran `cargo fmt --all`, `ruff format .`, and `ruff check .`.

Fixes #4283

<!-- lance-gatekeeper-fix:v1 agent=15a7bd545e63dea4b69faa0429f0a684
generation=1 -->

Co-authored-by: Gatefixer <313497061+lancedb-gatefixer[bot]@users.noreply.github.com>
This commit is contained in:
lancedb-gatefixer[bot]
2026-09-23 14:24:04 -07:00
committed by GitHub
parent 1842c88f2c
commit 75e71176cb
4 changed files with 129 additions and 6 deletions
+4
View File
@@ -2470,6 +2470,10 @@ def test_remote_blob_files_are_lazy_seekable_handles():
assert alpha.read_range(1, 3) == b"lph"
gamma.seek(2)
assert gamma.read() == b"mma"
alpha.close()
assert alpha.closed
with pytest.raises(RuntimeError, match="already closed"):
alpha.read_range(0, 1)
def test_remote_blob_fetch_accepts_query_table():