FIX: Context manager commits on clean exit, rolls back on exception#639
Merged
Conversation
7a7d0d5 to
9489153
Compare
bewithgaurav
commented
Jun 22, 2026
📊 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-1737 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 logger.warning(
1735 "Failed to close connection after failed "
1736 "commit/rollback in context manager.",
1737 exc_info=True,Lines 1735-1750 1735 "Failed to close connection after failed "
1736 "commit/rollback in context manager.",
1737 exc_info=True,
1738 )
! 1739 if exc_type is None:
! 1740 raise
! 1741 return
1742 try:
1743 self.close()
! 1744 except Exception:
! 1745 if exc_type is None:
! 1746 raise
1747 logger.warning(
1748 "Failed to close connection in context manager.",
1749 exc_info=True,
1750 )📋 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.2%
mssql_python.__init__.py: 77.3%
mssql_python.row.py: 77.6%
mssql_python.ddbc_bindings.py: 79.6%
mssql_python.connection.py: 83.6%
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
Contributor
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>
sumitmsft
reviewed
Jul 6, 2026
sumitmsft
reviewed
Jul 6, 2026
sumitmsft
previously approved these changes
Jul 6, 2026
sumitmsft
left a comment
Contributor
There was a problem hiding this comment.
Approved with minor comments
Log warnings when close() fails on exception-exit paths so failures aren't silently swallowed. Consistent with __del__ pattern. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
jahnvi480
approved these changes
Jul 8, 2026
gargsaumya
approved these changes
Jul 9, 2026
Merged
bewithgaurav
pushed a commit
that referenced
this pull request
Jul 10, 2026
Release mssql-python v1.11.0. [AB#46332](https://sqlclientdrivers.visualstudio.com/c6d89619-62de-46a0-8b46-70b92a84d85e/_workitems/edit/46332) ### Summary Version bump to 1.11.0. Updates `mssql_python/__init__.py`, `setup.py`, and `PyPI_Description.md`. Bundled `mssql_py_core` bumped from 0.1.5 to 0.1.6. #### Bug Fixes - **SSH-tunnel / in-process forwarder deadlock** — Release the GIL around blocking ODBC round-trips in the teardown path and `SQLDescribeParam`, so `close()` and parametrized queries with `None` values no longer deadlock in-process TCP forwarder setups (#604, issue #565). - **BINARY/VARBINARY NULL parameters in temp tables and table variables** — Proactively resolve unknown NULL parameter types before binding and emit actionable `setinputsizes()` guidance on `SQL_VARCHAR` fallback (#654, issue #627). - **Context manager transaction semantics** — `Connection.__exit__` now commits on clean exit and rolls back on exception when `autocommit=False`, instead of always rolling back (#639, issue #635). - **macOS Apple Silicon import failure** — `configure_dylibs.sh` rewrites bundled ODBC dylib dependencies to `@loader_path` for every architecture in the universal2 wheel, fixing `import mssql_python` on clean Apple Silicon machines (#661, issue #656). - **Service Principal bulk copy freeze** — Fixed a GIL-deadlock in the Rust core that froze bulk copy under `ActiveDirectoryServicePrincipal` auth; picked up via the `mssql_py_core` 0.1.6 bump (#666, issue #662). #### Version Bump - `mssql_python/__init__.py`: `__version__ = "1.11.0"` - `setup.py`: `version="1.11.0"` - `PyPI_Description.md`: `## What's new in v1.11.0` section refreshed.
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
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.