test(spanner): fix IndexError in async session retry deadline unit test#17140
Conversation
There was a problem hiding this comment.
Code Review
This pull request modifies the _time mock helper in the async session unit tests to ensure that the final timestamp is returned repeatedly once the results list is exhausted, rather than raising an error on subsequent calls. The reviewer suggested further improving this helper by explicitly checking for an empty results list and raising a ProgrammingError to provide clearer fail-fast behavior and prevent potential IndexError exceptions.
| if len(_results) > 1: | ||
| return _results.pop(0) | ||
| return _results[0] |
There was a problem hiding this comment.
The current implementation of the _time mock helper is fragile as it assumes _results will always contain at least one element. While the default value provides this, an empty list passed explicitly would still cause an IndexError. To ensure fail-fast behavior and prevent potential issues with missing values, the function should raise a ProgrammingError if the list is empty instead of returning a default value.
| if len(_results) > 1: | |
| return _results.pop(0) | |
| return _results[0] | |
| if not _results: | |
| raise ProgrammingError('No time results provided') | |
| if len(_results) > 1: | |
| return _results.pop(0) | |
| return _results[0] |
References
- When a function receives parameters of an unsupported type, it should raise an error (e.g., ProgrammingError) instead of silently returning empty values. This ensures fail-fast behavior and prevents potential issues with missing parameter values in database operations.
Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly:
Root Cause
In Python 3.13 (and under recent
pytest-asyncio/asyncioruntimes), the asynchronous event loop and task scheduling infrastructure makes internal calls totime.time()during coroutine execution and retry delay handling.In
test_run_in_transaction_w_abort_w_retry_metadata_deadline,time.timewas mocked using a helper that popped from a fixed 2-element list on every invocation (_results.pop(0)). The extra internal event loop calls exhausted_resultsprematurely, deterministically raising anIndexError: pop from empty listwhen_delay_until_retrychecked the deadline.Solution
Updated the
_timemock helper inpackages/google-cloud-spanner/tests/unit/_async/test_session.pyto return the last timestamp repeatedly once_resultshas reached its final element: