FIX: Context manager commits on clean exit, rolls back on exception#639
FIX: Context manager commits on clean exit, rolls back on exception#639bewithgaurav wants to merge 3 commits into
Conversation
7a7d0d5 to
9489153
Compare
| return self | ||
|
|
||
| def __exit__(self, *args: Any) -> None: | ||
| def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: |
There was a problem hiding this comment.
why *args → exc_type, exc_val, exc_tb:
the old __exit__ didn't inspect its arguments at all (just called close()), so *args was fine. now we branch on exc_type to decide commit vs rollback, so we use the canonical __exit__(self, exc_type, exc_val, exc_tb) signature from the Python data model.
this is what sqlite3, psycopg2, and every other DB-API driver uses. no behavioral change from the rename itself - just makes the branching logic readable.
📊 Code Coverage Report
Diff CoverageDiff: main...HEAD, staged and unstaged changes
Summary
mssql_python/connection.pyLines 1719-1727 1719 (rollback or close) are suppressed so the original user exception
1720 propagates unchanged.
1721 """
1722 if self._closed:
! 1723 return
1724 try:
1725 if not self.autocommit:
1726 if exc_type is None:
1727 self.commit()Lines 1725-1746 1725 if not self.autocommit:
1726 if exc_type is None:
1727 self.commit()
1728 else:
! 1729 self.rollback()
! 1730 except Exception:
! 1731 try:
! 1732 self.close()
! 1733 except Exception:
! 1734 pass
! 1735 if exc_type is None:
! 1736 raise
! 1737 return
1738 try:
1739 self.close()
! 1740 except Exception:
! 1741 if exc_type is None:
! 1742 raise
1743
1744 def __del__(self) -> None:
1745 """
1746 Destructor to ensure the connection is closed when the connection object📋 Files Needing Attention📉 Files with overall lowest coverage (click to expand)mssql_python.pybind.logger_bridge.cpp: 59.2%
mssql_python.pybind.ddbc_bindings.h: 59.9%
mssql_python.pybind.logger_bridge.hpp: 70.8%
mssql_python.pybind.connection.connection.cpp: 76.2%
mssql_python.pybind.ddbc_bindings.cpp: 76.3%
mssql_python.row.py: 76.9%
mssql_python.__init__.py: 77.3%
mssql_python.ddbc_bindings.py: 79.6%
mssql_python.connection.py: 83.4%
mssql_python.logging.py: 85.5%🔗 Quick Links
|
Fixes #635. The __exit__ implementation previously only called close(), which always rolled back uncommitted changes. This contradicted the documented behavior in the wiki, the DevBlogs post, and the docstrings. The fix adds commit-on-success / rollback-on-exception semantics to __exit__, matching the standard pattern used by sqlite3, psycopg2, and other Python DB-API drivers. close() remains unchanged; its internal rollback is a harmless no-op on an already-resolved transaction. 24 subprocess-isolated tests cover: clean commit, exception rollback, autocommit modes, manual commit/rollback inside block, DDL+DML, pending results, doomed transactions, KeyboardInterrupt, generator abandonment, killed SPID, large transactions, and double-exit idempotency. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
9489153 to
b3c915a
Compare
There was a problem hiding this comment.
Pull request overview
This pull request updates the Connection context manager behavior to match documented transaction semantics: commit on clean exit and rollback on exception when autocommit=False, while always closing the connection on exit.
Changes:
- Implemented commit-on-success / rollback-on-exception logic in
Connection.__exit__. - Added a new subprocess-isolated test suite to validate context manager transaction behavior and crash-safety edge cases.
- Marked resource-intensive context-manager tests as
@pytest.mark.stress(excluded by default viapytest.ini).
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
mssql_python/connection.py |
Updates Connection.__exit__ to commit/rollback based on exit condition and autocommit, then close the connection. |
tests/test_024_context_manager_transaction.py |
Adds subprocess-based tests covering commit/rollback semantics, exception propagation, and various hardening scenarios. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…eoutcomments - Wrap final self.close() in try/except so cleanup failures don't mask the user's original exception on the exception path - Remove hardcoded fallback connection string from tests (tests now properly skip when DB_CONNECTION_STRING is unset) - Replace signal.Signals._value2member_map_ (private API) with try/except ValueError for stable cross-version behavior - Bump subprocess timeout to 120s for 10k-insert stress test Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Work Item / Issue Reference
Summary
Fixes the Connection context manager (
__exit__) to implement commit-on-success / rollback-on-exception semantics, matching the documented behavior in the wiki, the DevBlogs post, and the docstrings.Previously
__exit__only calledclose(), which always rolled back uncommitted changes regardless of whether the block exited cleanly or via exception. Now:autocommit=False: commits the transactionautocommit=False: rolls back the transactionautocommit=True: no explicit commit/rollback (same as before)close()is unchanged. Its internal rollback becomes a harmless no-op on an already-resolved transaction.24 subprocess-isolated tests cover: commit/rollback semantics, autocommit toggling, manual commit/rollback inside block, DDL+DML, pending cursor results, doomed transactions (XACT_ABORT), KeyboardInterrupt, generator abandonment, killed SPID, 10k-row transactions, double-exit idempotency, and GC safety. Resource-intensive tests (rapid connection cycling, 10k inserts) are marked
@pytest.mark.stress.