Skip to content

Commit c36efcf

Browse files
committed
feat(context-manager): support to context manager for transactions
1 parent 57696f9 commit c36efcf

File tree

2 files changed

+76
-0
lines changed

2 files changed

+76
-0
lines changed

src/sqlitecloud/dbapi2.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,28 @@ def _apply_adapter(self, value: Any) -> SQLiteTypes:
330330

331331
return value
332332

333+
def __enter__(self):
334+
"""
335+
Context manager to handle transactions.
336+
337+
In sqlite3 module the control of the autocommit mode is governed by
338+
the `isolation_level` of the connection. To follow this behavior, the
339+
context manager does't start a new transaction implicitly. Instead,
340+
it handles the commit or rollback of transactions that are explicitly opened.
341+
"""
342+
return self
343+
344+
def __exit__(self, exc_type, exc_value, traceback):
345+
if exc_type is None:
346+
self.commit()
347+
else:
348+
self.rollback()
349+
logging.error(
350+
f"Rolling back transaction - error '{exc_value}'",
351+
exc_info=True,
352+
extra={"traceback": traceback},
353+
)
354+
333355
def __del__(self) -> None:
334356
self.close()
335357

src/tests/integration/test_sqlite3_parity.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,3 +1105,57 @@ def test_parse_colnames_and_decltypes_when_both_are_not_specified(
11051105

11061106
assert result[0] == 12
11071107
assert result[1] == 25
1108+
1109+
@pytest.mark.parametrize(
1110+
"connection",
1111+
[
1112+
"sqlitecloud_dbapi2_connection",
1113+
"sqlite3_connection",
1114+
],
1115+
)
1116+
def test_transaction_context_manager_on_success(self, connection, request):
1117+
connection = request.getfixturevalue(connection)
1118+
1119+
connection.execute("BEGIN")
1120+
with connection:
1121+
cursor = connection.execute(
1122+
"INSERT INTO albums (Title, ArtistId) VALUES ('Test Album 1', 1)"
1123+
)
1124+
id1 = cursor.lastrowid
1125+
cursor = connection.execute(
1126+
"INSERT INTO albums (Title, ArtistId) VALUES ('Test Album 2', 1)"
1127+
)
1128+
id2 = cursor.lastrowid
1129+
1130+
cursor = connection.execute(
1131+
"SELECT * FROM albums WHERE AlbumId IN (?, ?)", (id1, id2)
1132+
)
1133+
result = cursor.fetchall()
1134+
1135+
assert len(result) == 2
1136+
1137+
@pytest.mark.parametrize(
1138+
"connection",
1139+
[
1140+
"sqlitecloud_dbapi2_connection",
1141+
"sqlite3_connection",
1142+
],
1143+
)
1144+
def test_transaction_context_manager_on_failure(self, connection, request):
1145+
connection = request.getfixturevalue(connection)
1146+
1147+
try:
1148+
connection.execute("BEGIN")
1149+
with connection:
1150+
cursor = connection.execute(
1151+
"INSERT INTO albums (Title, ArtistId) VALUES ('Test Album 1', 1)"
1152+
)
1153+
id1 = cursor.lastrowid
1154+
connection.execute("INVALID COMMAND")
1155+
except Exception:
1156+
assert True
1157+
1158+
cursor = connection.execute("SELECT * FROM albums WHERE AlbumId = ?", (id1,))
1159+
result = cursor.fetchone()
1160+
1161+
assert result is None

0 commit comments

Comments
 (0)