FIX: prevent AttributeError in __del__ on partially-initialized Cursor#646
Open
subrata-ms wants to merge 2 commits into
Open
FIX: prevent AttributeError in __del__ on partially-initialized Cursor#646subrata-ms wants to merge 2 commits into
subrata-ms wants to merge 2 commits into
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR hardens mssql_python.cursor.Cursor lifecycle behavior to avoid unraisable exceptions during garbage collection when a Cursor is only partially initialized, and adds regression tests to prevent recurrence of the CI PytestUnraisableExceptionWarning seen in issue #642.
Changes:
- Establishes
Cursorcleanup invariants earlier in__init__(closed=False,hstmt=None) soclose()/__del__can run safely even after initialization failures. - Makes
Cursor.close()tolerant of instances missing theclosedattribute (e.g., objects created viaCursor.__new__). - Fixes
__del__finalization check to usesys.is_finalizing()and guards debug logging during interpreter shutdown; adds lifecycle regression tests.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
mssql_python/cursor.py |
Makes cursor initialization/cleanup resilient to partial construction and interpreter shutdown edge cases. |
tests/test_005_connection_cursor_lifecycle.py |
Adds regression tests for half-initialized cursor GC paths and failed cursor construction scenarios. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+481
to
+489
| with pytest.raises(RuntimeError, match="simulated HSTMT"): | ||
| conn.cursor() | ||
|
|
||
| # Force GC of any cursor instance __new__'d during the failed | ||
| # construction; no unraisable AttributeError must escape. | ||
| with warnings.catch_warnings(record=True) as caught: | ||
| warnings.simplefilter("always") | ||
| gc.collect() | ||
|
|
Comment on lines
+406
to
+409
| """Regression: if ``Cursor.__init__`` raises before reaching | ||
| ``self.closed = False`` (e.g. ``_initialize_cursor`` failure on a bad | ||
| HSTMT alloc), the half-initialized instance eventually hits ``__del__``. | ||
|
|
Contributor
|
@subrata-ms generally looks good to me. The copilot comment on the test is interesting. |
saurabh500
approved these changes
Jun 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Work Item / Issue Reference
Summary
This pull request improves the robustness of the
Cursorclass initialization and cleanup logic, ensuring that partially-initialized or half-constructed cursor objects are handled safely and do not cause unraisable exceptions during garbage collection. It also adds regression tests to prevent recurrence of related bugs.Initialization and cleanup robustness:
__init__method incursor.pynow setsself.closed = Falseandself.hstmt = Noneas the very first statements, before any code that might raise an exception, ensuring that even partially-initializedCursorinstances have a consistent state for cleanup. [1] [2]close()method now safely checks for the existence of theclosedattribute usinggetattr(self, "closed", True), preventingAttributeErrorif the attribute is missing (e.g., in half-initialized objects).__del__method now uses the correctsys.is_finalizing()function (instead of the incorrectsys._is_finalizing()) and guards logging calls to prevent unraisable exceptions during interpreter shutdown.Testing and regression prevention:
test_cursor_del_half_initialized_cursor_no_errorsandtest_cursor_init_failure_leaves_consistent_stateto ensure that half-initialized cursors do not raise unraisable exceptions during garbage collection and that failed initialization leaves the cursor and connection in a consistent, recoverable state.