sqliter-py 0.2.0__py3-none-any.whl → 0.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of sqliter-py might be problematic. Click here for more details.
- sqliter/__init__.py +5 -1
- sqliter/constants.py +18 -1
- sqliter/exceptions.py +39 -12
- sqliter/helpers.py +35 -0
- sqliter/model/__init__.py +3 -2
- sqliter/model/model.py +119 -13
- sqliter/query/__init__.py +5 -1
- sqliter/query/query.py +444 -77
- sqliter/sqliter.py +360 -42
- sqliter_py-0.4.0.dist-info/METADATA +196 -0
- sqliter_py-0.4.0.dist-info/RECORD +13 -0
- sqliter_py-0.4.0.dist-info/licenses/LICENSE.txt +20 -0
- sqliter_py-0.2.0.dist-info/METADATA +0 -351
- sqliter_py-0.2.0.dist-info/RECORD +0 -11
- {sqliter_py-0.2.0.dist-info → sqliter_py-0.4.0.dist-info}/WHEEL +0 -0
sqliter/sqliter.py
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Core module for SQLiter, providing the main database interaction class.
|
|
2
|
+
|
|
3
|
+
This module defines the SqliterDB class, which serves as the primary
|
|
4
|
+
interface for all database operations in SQLiter. It handles connection
|
|
5
|
+
management, table creation, and CRUD operations, bridging the gap between
|
|
6
|
+
Pydantic models and SQLite database interactions.
|
|
7
|
+
"""
|
|
2
8
|
|
|
3
9
|
from __future__ import annotations
|
|
4
10
|
|
|
11
|
+
import logging
|
|
5
12
|
import sqlite3
|
|
6
|
-
from typing import TYPE_CHECKING, Optional
|
|
13
|
+
from typing import TYPE_CHECKING, Any, Optional
|
|
7
14
|
|
|
8
15
|
from typing_extensions import Self
|
|
9
16
|
|
|
@@ -14,8 +21,11 @@ from sqliter.exceptions import (
|
|
|
14
21
|
RecordInsertionError,
|
|
15
22
|
RecordNotFoundError,
|
|
16
23
|
RecordUpdateError,
|
|
24
|
+
SqlExecutionError,
|
|
17
25
|
TableCreationError,
|
|
26
|
+
TableDeletionError,
|
|
18
27
|
)
|
|
28
|
+
from sqliter.helpers import infer_sqlite_type
|
|
19
29
|
from sqliter.query.query import QueryBuilder
|
|
20
30
|
|
|
21
31
|
if TYPE_CHECKING: # pragma: no cover
|
|
@@ -25,16 +35,145 @@ if TYPE_CHECKING: # pragma: no cover
|
|
|
25
35
|
|
|
26
36
|
|
|
27
37
|
class SqliterDB:
|
|
28
|
-
"""
|
|
38
|
+
"""Main class for interacting with SQLite databases.
|
|
39
|
+
|
|
40
|
+
This class provides methods for connecting to a SQLite database,
|
|
41
|
+
creating tables, and performing CRUD operations.
|
|
42
|
+
|
|
43
|
+
Arguements:
|
|
44
|
+
db_filename (str): The filename of the SQLite database.
|
|
45
|
+
auto_commit (bool): Whether to automatically commit transactions.
|
|
46
|
+
debug (bool): Whether to enable debug logging.
|
|
47
|
+
logger (Optional[logging.Logger]): Custom logger for debug output.
|
|
48
|
+
"""
|
|
29
49
|
|
|
30
|
-
def __init__(
|
|
31
|
-
|
|
32
|
-
|
|
50
|
+
def __init__(
|
|
51
|
+
self,
|
|
52
|
+
db_filename: Optional[str] = None,
|
|
53
|
+
*,
|
|
54
|
+
memory: bool = False,
|
|
55
|
+
auto_commit: bool = True,
|
|
56
|
+
debug: bool = False,
|
|
57
|
+
logger: Optional[logging.Logger] = None,
|
|
58
|
+
reset: bool = False,
|
|
59
|
+
) -> None:
|
|
60
|
+
"""Initialize a new SqliterDB instance.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
db_filename: The filename of the SQLite database.
|
|
64
|
+
memory: If True, create an in-memory database.
|
|
65
|
+
auto_commit: Whether to automatically commit transactions.
|
|
66
|
+
debug: Whether to enable debug logging.
|
|
67
|
+
logger: Custom logger for debug output.
|
|
68
|
+
reset: Whether to reset the database on initialization. This will
|
|
69
|
+
basically drop all existing tables.
|
|
70
|
+
|
|
71
|
+
Raises:
|
|
72
|
+
ValueError: If no filename is provided for a non-memory database.
|
|
73
|
+
"""
|
|
74
|
+
if memory:
|
|
75
|
+
self.db_filename = ":memory:"
|
|
76
|
+
elif db_filename:
|
|
77
|
+
self.db_filename = db_filename
|
|
78
|
+
else:
|
|
79
|
+
err = (
|
|
80
|
+
"Database name must be provided if not using an in-memory "
|
|
81
|
+
"database."
|
|
82
|
+
)
|
|
83
|
+
raise ValueError(err)
|
|
33
84
|
self.auto_commit = auto_commit
|
|
85
|
+
self.debug = debug
|
|
86
|
+
self.logger = logger
|
|
34
87
|
self.conn: Optional[sqlite3.Connection] = None
|
|
88
|
+
self.reset = reset
|
|
89
|
+
|
|
90
|
+
if self.debug:
|
|
91
|
+
self._setup_logger()
|
|
92
|
+
|
|
93
|
+
if self.reset:
|
|
94
|
+
self._reset_database()
|
|
95
|
+
|
|
96
|
+
def _reset_database(self) -> None:
|
|
97
|
+
"""Drop all user-created tables in the database."""
|
|
98
|
+
with self.connect() as conn:
|
|
99
|
+
cursor = conn.cursor()
|
|
100
|
+
|
|
101
|
+
# Get all table names, excluding SQLite system tables
|
|
102
|
+
cursor.execute(
|
|
103
|
+
"SELECT name FROM sqlite_master WHERE type='table' "
|
|
104
|
+
"AND name NOT LIKE 'sqlite_%';"
|
|
105
|
+
)
|
|
106
|
+
tables = cursor.fetchall()
|
|
107
|
+
|
|
108
|
+
# Drop each user-created table
|
|
109
|
+
for table in tables:
|
|
110
|
+
cursor.execute(f"DROP TABLE IF EXISTS {table[0]}")
|
|
111
|
+
|
|
112
|
+
conn.commit()
|
|
113
|
+
|
|
114
|
+
if self.debug and self.logger:
|
|
115
|
+
self.logger.debug(
|
|
116
|
+
"Database reset: %s user-created tables dropped.", len(tables)
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
def _setup_logger(self) -> None:
|
|
120
|
+
"""Set up the logger for debug output.
|
|
121
|
+
|
|
122
|
+
This method configures a logger for the SqliterDB instance, either
|
|
123
|
+
using an existing logger or creating a new one specifically for
|
|
124
|
+
SQLiter.
|
|
125
|
+
"""
|
|
126
|
+
# Check if the root logger is already configured
|
|
127
|
+
root_logger = logging.getLogger()
|
|
128
|
+
|
|
129
|
+
if root_logger.hasHandlers():
|
|
130
|
+
# If the root logger has handlers, use it without modifying the root
|
|
131
|
+
# configuration
|
|
132
|
+
self.logger = root_logger.getChild("sqliter")
|
|
133
|
+
else:
|
|
134
|
+
# If no root logger is configured, set up a new logger specific to
|
|
135
|
+
# SqliterDB
|
|
136
|
+
self.logger = logging.getLogger("sqliter")
|
|
137
|
+
|
|
138
|
+
handler = logging.StreamHandler() # Output to console
|
|
139
|
+
formatter = logging.Formatter(
|
|
140
|
+
"%(levelname)-8s%(message)s"
|
|
141
|
+
) # Custom format
|
|
142
|
+
handler.setFormatter(formatter)
|
|
143
|
+
self.logger.addHandler(handler)
|
|
144
|
+
|
|
145
|
+
self.logger.setLevel(logging.DEBUG)
|
|
146
|
+
self.logger.propagate = False
|
|
147
|
+
|
|
148
|
+
def _log_sql(self, sql: str, values: list[Any]) -> None:
|
|
149
|
+
"""Log the SQL query and its values if debug mode is enabled.
|
|
150
|
+
|
|
151
|
+
The values are inserted into the SQL query string to replace the
|
|
152
|
+
placeholders.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
sql: The SQL query string.
|
|
156
|
+
values: The list of values to be inserted into the query.
|
|
157
|
+
"""
|
|
158
|
+
if self.debug and self.logger:
|
|
159
|
+
formatted_sql = sql
|
|
160
|
+
for value in values:
|
|
161
|
+
if isinstance(value, str):
|
|
162
|
+
formatted_sql = formatted_sql.replace("?", f"'{value}'", 1)
|
|
163
|
+
else:
|
|
164
|
+
formatted_sql = formatted_sql.replace("?", str(value), 1)
|
|
165
|
+
|
|
166
|
+
self.logger.debug("Executing SQL: %s", formatted_sql)
|
|
35
167
|
|
|
36
168
|
def connect(self) -> sqlite3.Connection:
|
|
37
|
-
"""
|
|
169
|
+
"""Establish a connection to the SQLite database.
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
The SQLite connection object.
|
|
173
|
+
|
|
174
|
+
Raises:
|
|
175
|
+
DatabaseConnectionError: If unable to connect to the database.
|
|
176
|
+
"""
|
|
38
177
|
if not self.conn:
|
|
39
178
|
try:
|
|
40
179
|
self.conn = sqlite3.connect(self.db_filename)
|
|
@@ -43,41 +182,88 @@ class SqliterDB:
|
|
|
43
182
|
return self.conn
|
|
44
183
|
|
|
45
184
|
def close(self) -> None:
|
|
46
|
-
"""Close the connection
|
|
185
|
+
"""Close the database connection.
|
|
186
|
+
|
|
187
|
+
This method commits any pending changes if auto_commit is True,
|
|
188
|
+
then closes the connection. If the connection is already closed or does
|
|
189
|
+
not exist, this method silently does nothing.
|
|
190
|
+
"""
|
|
47
191
|
if self.conn:
|
|
48
192
|
self._maybe_commit()
|
|
49
193
|
self.conn.close()
|
|
50
194
|
self.conn = None
|
|
51
195
|
|
|
52
196
|
def commit(self) -> None:
|
|
53
|
-
"""Commit
|
|
197
|
+
"""Commit the current transaction.
|
|
198
|
+
|
|
199
|
+
This method explicitly commits any pending changes to the database.
|
|
200
|
+
"""
|
|
54
201
|
if self.conn:
|
|
55
202
|
self.conn.commit()
|
|
56
203
|
|
|
57
|
-
def create_table(
|
|
58
|
-
|
|
204
|
+
def create_table(
|
|
205
|
+
self,
|
|
206
|
+
model_class: type[BaseDBModel],
|
|
207
|
+
*,
|
|
208
|
+
exists_ok: bool = True,
|
|
209
|
+
force: bool = False,
|
|
210
|
+
) -> None:
|
|
211
|
+
"""Create a table in the database based on the given model class.
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
model_class: The Pydantic model class representing the table.
|
|
215
|
+
exists_ok: If True, do not raise an error if the table already
|
|
216
|
+
exists. Default is True which is the original behavior.
|
|
217
|
+
force: If True, drop the table if it exists before creating.
|
|
218
|
+
Defaults to False.
|
|
219
|
+
|
|
220
|
+
Raises:
|
|
221
|
+
TableCreationError: If there's an error creating the table.
|
|
222
|
+
ValueError: If the primary key field is not found in the model.
|
|
223
|
+
"""
|
|
59
224
|
table_name = model_class.get_table_name()
|
|
60
225
|
primary_key = model_class.get_primary_key()
|
|
61
|
-
|
|
226
|
+
create_pk = model_class.should_create_pk()
|
|
62
227
|
|
|
63
|
-
|
|
64
|
-
f"
|
|
65
|
-
|
|
228
|
+
if force:
|
|
229
|
+
drop_table_sql = f"DROP TABLE IF EXISTS {table_name}"
|
|
230
|
+
self._execute_sql(drop_table_sql)
|
|
66
231
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
)
|
|
73
|
-
"""
|
|
232
|
+
fields = []
|
|
233
|
+
|
|
234
|
+
# Always add the primary key field first
|
|
235
|
+
if create_pk:
|
|
236
|
+
fields.append(f"{primary_key} INTEGER PRIMARY KEY AUTOINCREMENT")
|
|
74
237
|
else:
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
238
|
+
field_info = model_class.model_fields.get(primary_key)
|
|
239
|
+
if field_info is not None:
|
|
240
|
+
sqlite_type = infer_sqlite_type(field_info.annotation)
|
|
241
|
+
fields.append(f"{primary_key} {sqlite_type} PRIMARY KEY")
|
|
242
|
+
else:
|
|
243
|
+
err = (
|
|
244
|
+
f"Primary key field '{primary_key}' not found in model "
|
|
245
|
+
"fields."
|
|
79
246
|
)
|
|
80
|
-
|
|
247
|
+
raise ValueError(err)
|
|
248
|
+
|
|
249
|
+
# Add remaining fields
|
|
250
|
+
for field_name, field_info in model_class.model_fields.items():
|
|
251
|
+
if field_name != primary_key:
|
|
252
|
+
sqlite_type = infer_sqlite_type(field_info.annotation)
|
|
253
|
+
fields.append(f"{field_name} {sqlite_type}")
|
|
254
|
+
|
|
255
|
+
create_str = (
|
|
256
|
+
"CREATE TABLE IF NOT EXISTS" if exists_ok else "CREATE TABLE"
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
create_table_sql = f"""
|
|
260
|
+
{create_str} {table_name} (
|
|
261
|
+
{", ".join(fields)}
|
|
262
|
+
)
|
|
263
|
+
"""
|
|
264
|
+
|
|
265
|
+
if self.debug:
|
|
266
|
+
self._log_sql(create_table_sql, [])
|
|
81
267
|
|
|
82
268
|
try:
|
|
83
269
|
with self.connect() as conn:
|
|
@@ -87,26 +273,81 @@ class SqliterDB:
|
|
|
87
273
|
except sqlite3.Error as exc:
|
|
88
274
|
raise TableCreationError(table_name) from exc
|
|
89
275
|
|
|
276
|
+
def _execute_sql(self, sql: str) -> None:
|
|
277
|
+
"""Execute an SQL statement.
|
|
278
|
+
|
|
279
|
+
Args:
|
|
280
|
+
sql: The SQL statement to execute.
|
|
281
|
+
|
|
282
|
+
Raises:
|
|
283
|
+
SqlExecutionError: If the SQL execution fails.
|
|
284
|
+
"""
|
|
285
|
+
if self.debug:
|
|
286
|
+
self._log_sql(sql, [])
|
|
287
|
+
|
|
288
|
+
try:
|
|
289
|
+
with self.connect() as conn:
|
|
290
|
+
cursor = conn.cursor()
|
|
291
|
+
cursor.execute(sql)
|
|
292
|
+
conn.commit()
|
|
293
|
+
except (sqlite3.Error, sqlite3.Warning) as exc:
|
|
294
|
+
raise SqlExecutionError(sql) from exc
|
|
295
|
+
|
|
296
|
+
def drop_table(self, model_class: type[BaseDBModel]) -> None:
|
|
297
|
+
"""Drop the table associated with the given model class.
|
|
298
|
+
|
|
299
|
+
Args:
|
|
300
|
+
model_class: The model class for which to drop the table.
|
|
301
|
+
|
|
302
|
+
Raises:
|
|
303
|
+
TableDeletionError: If there's an error dropping the table.
|
|
304
|
+
"""
|
|
305
|
+
table_name = model_class.get_table_name()
|
|
306
|
+
drop_table_sql = f"DROP TABLE IF EXISTS {table_name}"
|
|
307
|
+
|
|
308
|
+
if self.debug:
|
|
309
|
+
self._log_sql(drop_table_sql, [])
|
|
310
|
+
|
|
311
|
+
try:
|
|
312
|
+
with self.connect() as conn:
|
|
313
|
+
cursor = conn.cursor()
|
|
314
|
+
cursor.execute(drop_table_sql)
|
|
315
|
+
self.commit()
|
|
316
|
+
except sqlite3.Error as exc:
|
|
317
|
+
raise TableDeletionError(table_name) from exc
|
|
318
|
+
|
|
90
319
|
def _maybe_commit(self) -> None:
|
|
91
|
-
"""Commit changes if auto_commit is
|
|
320
|
+
"""Commit changes if auto_commit is enabled.
|
|
321
|
+
|
|
322
|
+
This method is called after operations that modify the database,
|
|
323
|
+
committing changes only if auto_commit is set to True.
|
|
324
|
+
"""
|
|
92
325
|
if self.auto_commit and self.conn:
|
|
93
326
|
self.conn.commit()
|
|
94
327
|
|
|
95
328
|
def insert(self, model_instance: BaseDBModel) -> None:
|
|
96
|
-
"""Insert a new record into the
|
|
329
|
+
"""Insert a new record into the database.
|
|
330
|
+
|
|
331
|
+
Args:
|
|
332
|
+
model_instance: An instance of a Pydantic model to be inserted.
|
|
333
|
+
|
|
334
|
+
Raises:
|
|
335
|
+
RecordInsertionError: If there's an error inserting the record.
|
|
336
|
+
"""
|
|
97
337
|
model_class = type(model_instance)
|
|
98
338
|
table_name = model_class.get_table_name()
|
|
99
339
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
340
|
+
data = model_instance.model_dump()
|
|
341
|
+
fields = ", ".join(data.keys())
|
|
342
|
+
placeholders = ", ".join(
|
|
343
|
+
["?" if value is not None else "NULL" for value in data.values()]
|
|
104
344
|
)
|
|
345
|
+
values = tuple(value for value in data.values() if value is not None)
|
|
105
346
|
|
|
106
347
|
insert_sql = f"""
|
|
107
348
|
INSERT INTO {table_name} ({fields})
|
|
108
349
|
VALUES ({placeholders})
|
|
109
|
-
|
|
350
|
+
""" # noqa: S608
|
|
110
351
|
|
|
111
352
|
try:
|
|
112
353
|
with self.connect() as conn:
|
|
@@ -119,7 +360,18 @@ class SqliterDB:
|
|
|
119
360
|
def get(
|
|
120
361
|
self, model_class: type[BaseDBModel], primary_key_value: str
|
|
121
362
|
) -> BaseDBModel | None:
|
|
122
|
-
"""Retrieve a record
|
|
363
|
+
"""Retrieve a single record from the database by its primary key.
|
|
364
|
+
|
|
365
|
+
Args:
|
|
366
|
+
model_class: The Pydantic model class representing the table.
|
|
367
|
+
primary_key_value: The value of the primary key to look up.
|
|
368
|
+
|
|
369
|
+
Returns:
|
|
370
|
+
An instance of the model class if found, None otherwise.
|
|
371
|
+
|
|
372
|
+
Raises:
|
|
373
|
+
RecordFetchError: If there's an error fetching the record.
|
|
374
|
+
"""
|
|
123
375
|
table_name = model_class.get_table_name()
|
|
124
376
|
primary_key = model_class.get_primary_key()
|
|
125
377
|
|
|
@@ -147,7 +399,15 @@ class SqliterDB:
|
|
|
147
399
|
return None
|
|
148
400
|
|
|
149
401
|
def update(self, model_instance: BaseDBModel) -> None:
|
|
150
|
-
"""Update an existing record
|
|
402
|
+
"""Update an existing record in the database.
|
|
403
|
+
|
|
404
|
+
Args:
|
|
405
|
+
model_instance: An instance of a Pydantic model to be updated.
|
|
406
|
+
|
|
407
|
+
Raises:
|
|
408
|
+
RecordUpdateError: If there's an error updating the record.
|
|
409
|
+
RecordNotFoundError: If the record to update is not found.
|
|
410
|
+
"""
|
|
151
411
|
model_class = type(model_instance)
|
|
152
412
|
table_name = model_class.get_table_name()
|
|
153
413
|
primary_key = model_class.get_primary_key()
|
|
@@ -187,7 +447,17 @@ class SqliterDB:
|
|
|
187
447
|
def delete(
|
|
188
448
|
self, model_class: type[BaseDBModel], primary_key_value: str
|
|
189
449
|
) -> None:
|
|
190
|
-
"""Delete a record by its primary key.
|
|
450
|
+
"""Delete a record from the database by its primary key.
|
|
451
|
+
|
|
452
|
+
Args:
|
|
453
|
+
model_class: The Pydantic model class representing the table.
|
|
454
|
+
primary_key_value: The value of the primary key of the record to
|
|
455
|
+
delete.
|
|
456
|
+
|
|
457
|
+
Raises:
|
|
458
|
+
RecordDeletionError: If there's an error deleting the record.
|
|
459
|
+
RecordNotFoundError: If the record to delete is not found.
|
|
460
|
+
"""
|
|
191
461
|
table_name = model_class.get_table_name()
|
|
192
462
|
primary_key = model_class.get_primary_key()
|
|
193
463
|
|
|
@@ -206,13 +476,44 @@ class SqliterDB:
|
|
|
206
476
|
except sqlite3.Error as exc:
|
|
207
477
|
raise RecordDeletionError(table_name) from exc
|
|
208
478
|
|
|
209
|
-
def select(
|
|
210
|
-
|
|
211
|
-
|
|
479
|
+
def select(
|
|
480
|
+
self,
|
|
481
|
+
model_class: type[BaseDBModel],
|
|
482
|
+
fields: Optional[list[str]] = None,
|
|
483
|
+
exclude: Optional[list[str]] = None,
|
|
484
|
+
) -> QueryBuilder:
|
|
485
|
+
"""Create a QueryBuilder instance for selecting records.
|
|
486
|
+
|
|
487
|
+
Args:
|
|
488
|
+
model_class: The Pydantic model class representing the table.
|
|
489
|
+
fields: Optional list of fields to include in the query.
|
|
490
|
+
exclude: Optional list of fields to exclude from the query.
|
|
491
|
+
|
|
492
|
+
Returns:
|
|
493
|
+
A QueryBuilder instance for further query construction.
|
|
494
|
+
"""
|
|
495
|
+
query_builder = QueryBuilder(self, model_class, fields)
|
|
496
|
+
|
|
497
|
+
# If exclude is provided, apply the exclude method
|
|
498
|
+
if exclude:
|
|
499
|
+
query_builder.exclude(exclude)
|
|
500
|
+
|
|
501
|
+
return query_builder
|
|
212
502
|
|
|
213
503
|
# --- Context manager methods ---
|
|
214
504
|
def __enter__(self) -> Self:
|
|
215
|
-
"""Enter the runtime context for the
|
|
505
|
+
"""Enter the runtime context for the SqliterDB instance.
|
|
506
|
+
|
|
507
|
+
This method is called when entering a 'with' statement. It ensures
|
|
508
|
+
that a database connection is established.
|
|
509
|
+
|
|
510
|
+
Note that this method should never be called explicitly, but will be
|
|
511
|
+
called by the 'with' statement when entering the context.
|
|
512
|
+
|
|
513
|
+
Returns:
|
|
514
|
+
The SqliterDB instance.
|
|
515
|
+
|
|
516
|
+
"""
|
|
216
517
|
self.connect()
|
|
217
518
|
return self
|
|
218
519
|
|
|
@@ -222,7 +523,24 @@ class SqliterDB:
|
|
|
222
523
|
exc_value: Optional[BaseException],
|
|
223
524
|
traceback: Optional[TracebackType],
|
|
224
525
|
) -> None:
|
|
225
|
-
"""Exit the runtime context
|
|
526
|
+
"""Exit the runtime context for the SqliterDB instance.
|
|
527
|
+
|
|
528
|
+
This method is called when exiting a 'with' statement. It handles
|
|
529
|
+
committing or rolling back transactions based on whether an exception
|
|
530
|
+
occurred, and closes the database connection.
|
|
531
|
+
|
|
532
|
+
Args:
|
|
533
|
+
exc_type: The type of the exception that caused the context to be
|
|
534
|
+
exited, or None if no exception was raised.
|
|
535
|
+
exc_value: The instance of the exception that caused the context
|
|
536
|
+
to be exited, or None if no exception was raised.
|
|
537
|
+
traceback: A traceback object encoding the stack trace, or None
|
|
538
|
+
if no exception was raised.
|
|
539
|
+
|
|
540
|
+
Note that this method should never be called explicitly, but will be
|
|
541
|
+
called by the 'with' statement when exiting the context.
|
|
542
|
+
|
|
543
|
+
"""
|
|
226
544
|
if self.conn:
|
|
227
545
|
try:
|
|
228
546
|
if exc_type:
|