Skip to content

Commit 2ba8772

Browse files
gh-95432: Add doctests for the sqlite3 docs (GH-96225)
As a consequence of the added test, this commit also includes fixes for broken examples. - Add separate namespace for trace tests bco. module level callback - Move more backup and cursor examples under separate namespaces (cherry picked from commit bf92597) Co-authored-by: Erlend E. Aasland <[email protected]>
1 parent ca7e78d commit 2ba8772

File tree

1 file changed

+125
-64
lines changed

1 file changed

+125
-64
lines changed

Doc/library/sqlite3.rst

Lines changed: 125 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,9 @@ Module functions
343343
other than checking that there are no unclosed string literals
344344
and the statement is terminated by a semicolon.
345345

346-
For example::
346+
For example:
347+
348+
.. doctest::
347349

348350
>>> sqlite3.complete_statement("SELECT foo FROM bar;")
349351
True
@@ -364,22 +366,27 @@ Module functions
364366
to disable the feature again.
365367

366368
Register an :func:`unraisable hook handler <sys.unraisablehook>` for an
367-
improved debug experience::
369+
improved debug experience:
370+
371+
.. testsetup:: sqlite3.trace
372+
373+
import sqlite3
374+
375+
.. doctest:: sqlite3.trace
368376

369-
>>> import sqlite3
370377
>>> sqlite3.enable_callback_tracebacks(True)
371-
>>> cx = sqlite3.connect(":memory:")
372-
>>> cx.set_trace_callback(lambda stmt: 5/0)
373-
>>> cx.execute("select 1")
374-
Exception ignored in: <function <lambda> at 0x10b4e3ee0>
375-
Traceback (most recent call last):
376-
File "<stdin>", line 1, in <lambda>
377-
ZeroDivisionError: division by zero
378+
>>> con = sqlite3.connect(":memory:")
379+
>>> def evil_trace(stmt):
380+
... 5/0
381+
>>> con.set_trace_callback(evil_trace)
382+
>>> def debug(unraisable):
383+
... print(f"{unraisable.exc_value!r} in callback {unraisable.object.__name__}")
384+
... print(f"Error message: {unraisable.err_msg}")
378385
>>> import sys
379-
>>> sys.unraisablehook = lambda unraisable: print(unraisable)
380-
>>> cx.execute("select 1")
381-
UnraisableHookArgs(exc_type=<class 'ZeroDivisionError'>, exc_value=ZeroDivisionError('division by zero'), exc_traceback=<traceback object at 0x10b559900>, err_msg=None, object=<function <lambda> at 0x10b4e3ee0>)
382-
<sqlite3.Cursor object at 0x10b1fe840>
386+
>>> sys.unraisablehook = debug
387+
>>> cur = con.execute("select 1")
388+
ZeroDivisionError('division by zero') in callback evil_trace
389+
Error message: None
383390

384391
.. function:: register_adapter(type, adapter, /)
385392

@@ -926,12 +933,12 @@ Connection objects
926933
Useful when saving an in-memory database for later restoration.
927934
Similar to the ``.dump`` command in the :program:`sqlite3` shell.
928935

929-
Example::
936+
Example:
930937

931-
# Convert file existing_db.db to SQL dump file dump.sql
932-
import sqlite3
938+
.. testcode::
933939

934-
con = sqlite3.connect('existing_db.db')
940+
# Convert file example.db to SQL dump file dump.sql
941+
con = sqlite3.connect('example.db')
935942
with open('dump.sql', 'w') as f:
936943
for line in con.iterdump():
937944
f.write('%s\n' % line)
@@ -974,27 +981,32 @@ Connection objects
974981
The number of seconds to sleep between successive attempts
975982
to back up remaining pages.
976983

977-
Example 1, copy an existing database into another::
984+
Example 1, copy an existing database into another:
978985

979-
import sqlite3
986+
.. testcode::
980987

981988
def progress(status, remaining, total):
982989
print(f'Copied {total-remaining} of {total} pages...')
983990

984-
con = sqlite3.connect('existing_db.db')
985-
bck = sqlite3.connect('backup.db')
986-
with bck:
987-
con.backup(bck, pages=1, progress=progress)
988-
bck.close()
989-
con.close()
991+
src = sqlite3.connect('example.db')
992+
dst = sqlite3.connect('backup.db')
993+
with dst:
994+
src.backup(dst, pages=1, progress=progress)
995+
dst.close()
996+
src.close()
990997

991-
Example 2, copy an existing database into a transient copy::
998+
.. testoutput::
999+
:hide:
9921000

993-
import sqlite3
1001+
Copied 0 of 0 pages...
9941002

995-
source = sqlite3.connect('existing_db.db')
996-
dest = sqlite3.connect(':memory:')
997-
source.backup(dest)
1003+
Example 2, copy an existing database into a transient copy:
1004+
1005+
.. testcode::
1006+
1007+
src = sqlite3.connect('example.db')
1008+
dst = sqlite3.connect(':memory:')
1009+
src.backup(dst)
9981010

9991011
.. versionadded:: 3.7
10001012

@@ -1010,12 +1022,20 @@ Connection objects
10101022
:raises ProgrammingError:
10111023
If *category* is not recognised by the underlying SQLite library.
10121024

1013-
Example, query the maximum length of an SQL statement::
1025+
Example, query the maximum length of an SQL statement
1026+
for :class:`Connection` ``con`` (the default is 1000000000):
1027+
1028+
.. testsetup:: sqlite3.limits
10141029

10151030
import sqlite3
10161031
con = sqlite3.connect(":memory:")
1017-
lim = con.getlimit(sqlite3.SQLITE_LIMIT_SQL_LENGTH)
1018-
print(f"SQLITE_LIMIT_SQL_LENGTH={lim}")
1032+
con.setlimit(sqlite3.SQLITE_LIMIT_SQL_LENGTH, 1_000_000_000)
1033+
con.setlimit(sqlite3.SQLITE_LIMIT_ATTACHED, 10)
1034+
1035+
.. doctest:: sqlite3.limits
1036+
1037+
>>> con.getlimit(sqlite3.SQLITE_LIMIT_SQL_LENGTH)
1038+
1000000000
10191039

10201040
.. versionadded:: 3.11
10211041

@@ -1039,11 +1059,15 @@ Connection objects
10391059
:raises ProgrammingError:
10401060
If *category* is not recognised by the underlying SQLite library.
10411061

1042-
Example, limit the number of attached databases to 1::
1062+
Example, limit the number of attached databases to 1
1063+
for :class:`Connection` ``con`` (the default limit is 10):
10431064

1044-
import sqlite3
1045-
con = sqlite3.connect(":memory:")
1046-
con.setlimit(sqlite3.SQLITE_LIMIT_ATTACHED, 1)
1065+
.. doctest:: sqlite3.limits
1066+
1067+
>>> con.setlimit(sqlite3.SQLITE_LIMIT_ATTACHED, 1)
1068+
10
1069+
>>> con.getlimit(sqlite3.SQLITE_LIMIT_ATTACHED)
1070+
1
10471071

10481072
.. versionadded:: 3.11
10491073

@@ -1119,11 +1143,25 @@ Cursor objects
11191143

11201144
Cursor objects are :term:`iterators <iterator>`,
11211145
meaning that if you :meth:`~Cursor.execute` a ``SELECT`` query,
1122-
you can simply iterate over the cursor to fetch the resulting rows::
1146+
you can simply iterate over the cursor to fetch the resulting rows:
1147+
1148+
.. testsetup:: sqlite3.cursor
1149+
1150+
import sqlite3
1151+
con = sqlite3.connect(":memory:", isolation_level=None)
1152+
cur = con.execute("CREATE TABLE data(t)")
1153+
cur.execute("INSERT INTO data VALUES(1)")
11231154

1124-
for row in cur.execute("select * from data"):
1155+
.. testcode:: sqlite3.cursor
1156+
1157+
for row in cur.execute("SELECT t FROM data"):
11251158
print(row)
11261159

1160+
.. testoutput:: sqlite3.cursor
1161+
:hide:
1162+
1163+
(1,)
1164+
11271165
.. _database cursor: https://en.wikipedia.org/wiki/Cursor_(databases)
11281166

11291167
.. class:: Cursor
@@ -1159,14 +1197,16 @@ Cursor objects
11591197
:term:`iterator` yielding parameters instead of a sequence.
11601198
Uses the same implicit transaction handling as :meth:`~Cursor.execute`.
11611199

1162-
Example::
1200+
Example:
1201+
1202+
.. testcode:: sqlite3.cursor
11631203

1164-
data = [
1165-
("row1",),
1166-
("row2",),
1167-
]
1168-
# cur is an sqlite3.Cursor object
1169-
cur.executemany("insert into t values(?)", data)
1204+
rows = [
1205+
("row1",),
1206+
("row2",),
1207+
]
1208+
# cur is an sqlite3.Cursor object
1209+
cur.executemany("insert into data values(?)", rows)
11701210

11711211
.. method:: executescript(sql_script, /)
11721212

@@ -1178,7 +1218,9 @@ Cursor objects
11781218

11791219
*sql_script* must be a :class:`string <str>`.
11801220

1181-
Example::
1221+
Example:
1222+
1223+
.. testcode:: sqlite3.cursor
11821224

11831225
# cur is an sqlite3.Cursor object
11841226
cur.executescript("""
@@ -1275,7 +1317,9 @@ Cursor objects
12751317
Read-only attribute that provides the SQLite database :class:`Connection`
12761318
belonging to the cursor. A :class:`Cursor` object created by
12771319
calling :meth:`con.cursor() <Connection.cursor>` will have a
1278-
:attr:`connection` attribute that refers to *con*::
1320+
:attr:`connection` attribute that refers to *con*:
1321+
1322+
.. doctest::
12791323

12801324
>>> con = sqlite3.connect(":memory:")
12811325
>>> cur = con.cursor()
@@ -1310,7 +1354,9 @@ Row objects
13101354
.. versionchanged:: 3.5
13111355
Added support of slicing.
13121356

1313-
Example::
1357+
Example:
1358+
1359+
.. doctest::
13141360

13151361
>>> con = sqlite3.connect(":memory:")
13161362
>>> con.row_factory = sqlite3.Row
@@ -1662,7 +1708,7 @@ and constructs a :class:`!Point` object from it.
16621708
Converter functions are **always** passed a :class:`bytes` object,
16631709
no matter the underlying SQLite data type.
16641710

1665-
::
1711+
.. testcode::
16661712

16671713
def convert_point(s):
16681714
x, y = map(float, s.split(b";"))
@@ -1690,7 +1736,7 @@ Adapter and converter recipes
16901736

16911737
This section shows recipes for common adapters and converters.
16921738

1693-
.. code-block::
1739+
.. testcode::
16941740

16951741
import datetime
16961742
import sqlite3
@@ -1703,7 +1749,7 @@ This section shows recipes for common adapters and converters.
17031749
"""Adapt datetime.datetime to timezone-naive ISO 8601 date."""
17041750
return val.isoformat()
17051751

1706-
def adapt_datetime_epoch(val)
1752+
def adapt_datetime_epoch(val):
17071753
"""Adapt datetime.datetime to Unix timestamp."""
17081754
return int(val.timestamp())
17091755

@@ -1777,23 +1823,38 @@ How to work with SQLite URIs
17771823

17781824
Some useful URI tricks include:
17791825

1780-
* Open a database in read-only mode::
1826+
* Open a database in read-only mode:
17811827

1782-
con = sqlite3.connect("file:template.db?mode=ro", uri=True)
1828+
.. doctest::
1829+
1830+
>>> con = sqlite3.connect("file:tutorial.db?mode=ro", uri=True)
1831+
>>> con.execute("CREATE TABLE readonly(data)")
1832+
Traceback (most recent call last):
1833+
OperationalError: attempt to write a readonly database
17831834

17841835
* Do not implicitly create a new database file if it does not already exist;
1785-
will raise :exc:`~sqlite3.OperationalError` if unable to create a new file::
1836+
will raise :exc:`~sqlite3.OperationalError` if unable to create a new file:
1837+
1838+
.. doctest::
1839+
1840+
>>> con = sqlite3.connect("file:nosuchdb.db?mode=rw", uri=True)
1841+
Traceback (most recent call last):
1842+
OperationalError: unable to open database file
1843+
17861844

1787-
con = sqlite3.connect("file:nosuchdb.db?mode=rw", uri=True)
1845+
* Create a shared named in-memory database:
1846+
1847+
.. testcode::
17881848

1789-
* Create a shared named in-memory database::
1849+
db = "file:mem1?mode=memory&cache=shared"
1850+
con1 = sqlite3.connect(db, uri=True)
1851+
con2 = sqlite3.connect(db, uri=True)
1852+
with con1:
1853+
con1.execute("CREATE TABLE shared(data)")
1854+
con1.execute("INSERT INTO shared VALUES(28)")
1855+
res = con2.execute("SELECT data FROM shared")
1856+
assert res.fetchone() == (28,)
17901857

1791-
con1 = sqlite3.connect("file:mem1?mode=memory&cache=shared", uri=True)
1792-
con2 = sqlite3.connect("file:mem1?mode=memory&cache=shared", uri=True)
1793-
con1.execute("create table t(t)")
1794-
con1.execute("insert into t values(28)")
1795-
con1.commit()
1796-
rows = con2.execute("select * from t").fetchall()
17971858

17981859
More information about this feature, including a list of parameters,
17991860
can be found in the `SQLite URI documentation`_.

0 commit comments

Comments
 (0)