Skip to content

FIX: Context manager commits on clean exit, rolls back on exception#639

Open
bewithgaurav wants to merge 3 commits into
mainfrom
bewithgaurav/fix-635-context-manager-commit
Open

FIX: Context manager commits on clean exit, rolls back on exception#639
bewithgaurav wants to merge 3 commits into
mainfrom
bewithgaurav/fix-635-context-manager-commit

Conversation

@bewithgaurav

@bewithgaurav bewithgaurav commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

Work Item / Issue Reference

AB#45829

GitHub Issue: #635


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 called close(), which always rolled back uncommitted changes regardless of whether the block exited cleanly or via exception. Now:

  • Clean exit + autocommit=False: commits the transaction
  • Exception + autocommit=False: rolls back the transaction
  • autocommit=True: no explicit commit/rollback (same as before)
  • Connection always closed on exit

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.

@github-actions github-actions Bot added the pr-size: large Substantial code update label Jun 22, 2026
Comment thread tests/test_024_context_manager_transaction.py Fixed
@bewithgaurav bewithgaurav force-pushed the bewithgaurav/fix-635-context-manager-commit branch from 7a7d0d5 to 9489153 Compare June 22, 2026 07:25
return self

def __exit__(self, *args: Any) -> None:
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why *argsexc_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.

@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown

📊 Code Coverage Report

🔥 Diff Coverage

35%


🎯 Overall Coverage

80%


📈 Total Lines Covered: 6749 out of 8352
📁 Project: mssql-python


Diff Coverage

Diff: main...HEAD, staged and unstaged changes

  • mssql_python/connection.py (35.0%): Missing lines 1723,1729-1737,1740-1742

Summary

  • Total: 20 lines
  • Missing: 13 lines
  • Coverage: 35%

mssql_python/connection.py

Lines 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

⚙️ Build Summary 📋 Coverage Details

View Azure DevOps Build

Browse Full Coverage Report

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>
@bewithgaurav bewithgaurav force-pushed the bewithgaurav/fix-635-context-manager-commit branch from 9489153 to b3c915a Compare June 22, 2026 07:41
@bewithgaurav bewithgaurav marked this pull request as ready for review June 22, 2026 08:33
Copilot AI review requested due to automatic review settings June 22, 2026 08:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 via pytest.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.

Comment thread mssql_python/connection.py Outdated
Comment thread tests/test_024_context_manager_transaction.py Outdated
Comment thread tests/test_024_context_manager_transaction.py Outdated
Comment thread tests/test_024_context_manager_transaction.py Outdated
bewithgaurav and others added 2 commits June 22, 2026 14:32
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr-size: large Substantial code update

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants