From c74bcbcd1269fe91209c501f2847cde629ac6ff6 Mon Sep 17 00:00:00 2001 From: Brendan Collins Date: Tue, 16 Jun 2026 08:04:24 -0700 Subject: [PATCH] Make test_chunks_is_lazy counter thread-safe for free-threaded CI (#3363) The 3.14t full-suite job fails on test_chunks_is_lazy_does_not_call_internal_reader with 'expected 16 per-chunk decodes after compute, got 15'. compute() runs the 16 per-chunk reads through dask's threaded scheduler; under PYTHON_GIL=0 the bare counter['calls'] += 1 in the counting wrapper races and loses an update, so 16 real decodes count as 15. Guard the increment with a threading.Lock. Test-instrumentation fix only; the chunked decode already runs 16 times correctly. --- xrspatial/geotiff/tests/vrt/test_window.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/xrspatial/geotiff/tests/vrt/test_window.py b/xrspatial/geotiff/tests/vrt/test_window.py index 79073da14..0ce5b00ff 100644 --- a/xrspatial/geotiff/tests/vrt/test_window.py +++ b/xrspatial/geotiff/tests/vrt/test_window.py @@ -19,6 +19,7 @@ import glob import os import tempfile +import threading import uuid import warnings from pathlib import Path @@ -872,10 +873,17 @@ def test_chunks_is_lazy_does_not_call_internal_reader(monkeypatch, lazy_chunks_t vrt_path, _ = lazy_chunks_two_by_two_vrt from xrspatial.geotiff import _vrt as vrt_module counter = {'calls': 0} + # ``compute()`` runs the 16 per-chunk reads through dask's threaded + # scheduler, so the counter is incremented from multiple worker threads + # concurrently. Guard the read-modify-write with a lock: a bare + # ``counter['calls'] += 1`` loses updates under the free-threaded + # interpreter (PYTHON_GIL=0), undercounting the decodes (issue #3363). + counter_lock = threading.Lock() real_read = vrt_module.read_vrt def counting_read(*args, **kwargs): - counter['calls'] += 1 + with counter_lock: + counter['calls'] += 1 return real_read(*args, **kwargs) monkeypatch.setattr(vrt_module, 'read_vrt', counting_read) result = _read_vrt(vrt_path, chunks=(64, 64))