Skip to content

Commit bf92597

Browse files
gh-95432: Add doctests for the sqlite3 docs (#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
1 parent af368a7 commit bf92597

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
@@ -367,22 +369,27 @@ Module functions
367369
to disable the feature again.
368370

369371
Register an :func:`unraisable hook handler <sys.unraisablehook>` for an
370-
improved debug experience::
372+
improved debug experience:
373+
374+
.. testsetup:: sqlite3.trace
375+
376+
import sqlite3
377+
378+
.. doctest:: sqlite3.trace
371379

372-
>>> import sqlite3
373380
>>> sqlite3.enable_callback_tracebacks(True)
374-
>>> cx = sqlite3.connect(":memory:")
375-
>>> cx.set_trace_callback(lambda stmt: 5/0)
376-
>>> cx.execute("select 1")
377-
Exception ignored in: <function <lambda> at 0x10b4e3ee0>
378-
Traceback (most recent call last):
379-
File "<stdin>", line 1, in <lambda>
380-
ZeroDivisionError: division by zero
381+
>>> con = sqlite3.connect(":memory:")
382+
>>> def evil_trace(stmt):
383+
... 5/0
384+
>>> con.set_trace_callback(evil_trace)
385+
>>> def debug(unraisable):
386+
... print(f"{unraisable.exc_value!r} in callback {unraisable.object.__name__}")
387+
... print(f"Error message: {unraisable.err_msg}")
381388
>>> import sys
382-
>>> sys.unraisablehook = lambda unraisable: print(unraisable)
383-
>>> cx.execute("select 1")
384-
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>)
385-
<sqlite3.Cursor object at 0x10b1fe840>
389+
>>> sys.unraisablehook = debug
390+
>>> cur = con.execute("select 1")
391+
ZeroDivisionError('division by zero') in callback evil_trace
392+
Error message: None
386393

387394
.. function:: register_adapter(type, adapter, /)
388395

@@ -939,12 +946,12 @@ Connection objects
939946
Useful when saving an in-memory database for later restoration.
940947
Similar to the ``.dump`` command in the :program:`sqlite3` shell.
941948

942-
Example::
949+
Example:
943950

944-
# Convert file existing_db.db to SQL dump file dump.sql
945-
import sqlite3
951+
.. testcode::
946952

947-
con = sqlite3.connect('existing_db.db')
953+
# Convert file example.db to SQL dump file dump.sql
954+
con = sqlite3.connect('example.db')
948955
with open('dump.sql', 'w') as f:
949956
for line in con.iterdump():
950957
f.write('%s\n' % line)
@@ -987,27 +994,32 @@ Connection objects
987994
The number of seconds to sleep between successive attempts
988995
to back up remaining pages.
989996

990-
Example 1, copy an existing database into another::
997+
Example 1, copy an existing database into another:
991998

992-
import sqlite3
999+
.. testcode::
9931000

9941001
def progress(status, remaining, total):
9951002
print(f'Copied {total-remaining} of {total} pages...')
9961003

997-
con = sqlite3.connect('existing_db.db')
998-
bck = sqlite3.connect('backup.db')
999-
with bck:
1000-
con.backup(bck, pages=1, progress=progress)
1001-
bck.close()
1002-
con.close()
1004+
src = sqlite3.connect('example.db')
1005+
dst = sqlite3.connect('backup.db')
1006+
with dst:
1007+
src.backup(dst, pages=1, progress=progress)
1008+
dst.close()
1009+
src.close()
10031010

1004-
Example 2, copy an existing database into a transient copy::
1011+
.. testoutput::
1012+
:hide:
10051013

1006-
import sqlite3
1014+
Copied 0 of 0 pages...
10071015

1008-
source = sqlite3.connect('existing_db.db')
1009-
dest = sqlite3.connect(':memory:')
1010-
source.backup(dest)
1016+
Example 2, copy an existing database into a transient copy:
1017+
1018+
.. testcode::
1019+
1020+
src = sqlite3.connect('example.db')
1021+
dst = sqlite3.connect(':memory:')
1022+
src.backup(dst)
10111023

10121024
.. versionadded:: 3.7
10131025

@@ -1023,12 +1035,20 @@ Connection objects
10231035
:raises ProgrammingError:
10241036
If *category* is not recognised by the underlying SQLite library.
10251037

1026-
Example, query the maximum length of an SQL statement::
1038+
Example, query the maximum length of an SQL statement
1039+
for :class:`Connection` ``con`` (the default is 1000000000):
1040+
1041+
.. testsetup:: sqlite3.limits
10271042

10281043
import sqlite3
10291044
con = sqlite3.connect(":memory:")
1030-
lim = con.getlimit(sqlite3.SQLITE_LIMIT_SQL_LENGTH)
1031-
print(f"SQLITE_LIMIT_SQL_LENGTH={lim}")
1045+
con.setlimit(sqlite3.SQLITE_LIMIT_SQL_LENGTH, 1_000_000_000)
1046+
con.setlimit(sqlite3.SQLITE_LIMIT_ATTACHED, 10)
1047+
1048+
.. doctest:: sqlite3.limits
1049+
1050+
>>> con.getlimit(sqlite3.SQLITE_LIMIT_SQL_LENGTH)
1051+
1000000000
10321052

10331053
.. versionadded:: 3.11
10341054

@@ -1052,11 +1072,15 @@ Connection objects
10521072
:raises ProgrammingError:
10531073
If *category* is not recognised by the underlying SQLite library.
10541074

1055-
Example, limit the number of attached databases to 1::
1075+
Example, limit the number of attached databases to 1
1076+
for :class:`Connection` ``con`` (the default limit is 10):
10561077

1057-
import sqlite3
1058-
con = sqlite3.connect(":memory:")
1059-
con.setlimit(sqlite3.SQLITE_LIMIT_ATTACHED, 1)
1078+
.. doctest:: sqlite3.limits
1079+
1080+
>>> con.setlimit(sqlite3.SQLITE_LIMIT_ATTACHED, 1)
1081+
10
1082+
>>> con.getlimit(sqlite3.SQLITE_LIMIT_ATTACHED)
1083+
1
10601084

10611085
.. versionadded:: 3.11
10621086

@@ -1132,11 +1156,25 @@ Cursor objects
11321156

11331157
Cursor objects are :term:`iterators <iterator>`,
11341158
meaning that if you :meth:`~Cursor.execute` a ``SELECT`` query,
1135-
you can simply iterate over the cursor to fetch the resulting rows::
1159+
you can simply iterate over the cursor to fetch the resulting rows:
1160+
1161+
.. testsetup:: sqlite3.cursor
1162+
1163+
import sqlite3
1164+
con = sqlite3.connect(":memory:", isolation_level=None)
1165+
cur = con.execute("CREATE TABLE data(t)")
1166+
cur.execute("INSERT INTO data VALUES(1)")
11361167

1137-
for row in cur.execute("select * from data"):
1168+
.. testcode:: sqlite3.cursor
1169+
1170+
for row in cur.execute("SELECT t FROM data"):
11381171
print(row)
11391172

1173+
.. testoutput:: sqlite3.cursor
1174+
:hide:
1175+
1176+
(1,)
1177+
11401178
.. _database cursor: https://en.wikipedia.org/wiki/Cursor_(databases)
11411179

11421180
.. class:: Cursor
@@ -1172,14 +1210,16 @@ Cursor objects
11721210
:term:`iterator` yielding parameters instead of a sequence.
11731211
Uses the same implicit transaction handling as :meth:`~Cursor.execute`.
11741212

1175-
Example::
1213+
Example:
1214+
1215+
.. testcode:: sqlite3.cursor
11761216

1177-
data = [
1178-
("row1",),
1179-
("row2",),
1180-
]
1181-
# cur is an sqlite3.Cursor object
1182-
cur.executemany("insert into t values(?)", data)
1217+
rows = [
1218+
("row1",),
1219+
("row2",),
1220+
]
1221+
# cur is an sqlite3.Cursor object
1222+
cur.executemany("insert into data values(?)", rows)
11831223

11841224
.. method:: executescript(sql_script, /)
11851225

@@ -1191,7 +1231,9 @@ Cursor objects
11911231

11921232
*sql_script* must be a :class:`string <str>`.
11931233

1194-
Example::
1234+
Example:
1235+
1236+
.. testcode:: sqlite3.cursor
11951237

11961238
# cur is an sqlite3.Cursor object
11971239
cur.executescript("""
@@ -1288,7 +1330,9 @@ Cursor objects
12881330
Read-only attribute that provides the SQLite database :class:`Connection`
12891331
belonging to the cursor. A :class:`Cursor` object created by
12901332
calling :meth:`con.cursor() <Connection.cursor>` will have a
1291-
:attr:`connection` attribute that refers to *con*::
1333+
:attr:`connection` attribute that refers to *con*:
1334+
1335+
.. doctest::
12921336

12931337
>>> con = sqlite3.connect(":memory:")
12941338
>>> cur = con.cursor()
@@ -1323,7 +1367,9 @@ Row objects
13231367
.. versionchanged:: 3.5
13241368
Added support of slicing.
13251369

1326-
Example::
1370+
Example:
1371+
1372+
.. doctest::
13271373

13281374
>>> con = sqlite3.connect(":memory:")
13291375
>>> con.row_factory = sqlite3.Row
@@ -1700,7 +1746,7 @@ and constructs a :class:`!Point` object from it.
17001746
Converter functions are **always** passed a :class:`bytes` object,
17011747
no matter the underlying SQLite data type.
17021748

1703-
::
1749+
.. testcode::
17041750

17051751
def convert_point(s):
17061752
x, y = map(float, s.split(b";"))
@@ -1728,7 +1774,7 @@ Adapter and converter recipes
17281774

17291775
This section shows recipes for common adapters and converters.
17301776

1731-
.. code-block::
1777+
.. testcode::
17321778

17331779
import datetime
17341780
import sqlite3
@@ -1741,7 +1787,7 @@ This section shows recipes for common adapters and converters.
17411787
"""Adapt datetime.datetime to timezone-naive ISO 8601 date."""
17421788
return val.isoformat()
17431789

1744-
def adapt_datetime_epoch(val)
1790+
def adapt_datetime_epoch(val):
17451791
"""Adapt datetime.datetime to Unix timestamp."""
17461792
return int(val.timestamp())
17471793

@@ -1815,23 +1861,38 @@ How to work with SQLite URIs
18151861

18161862
Some useful URI tricks include:
18171863

1818-
* Open a database in read-only mode::
1864+
* Open a database in read-only mode:
18191865

1820-
con = sqlite3.connect("file:template.db?mode=ro", uri=True)
1866+
.. doctest::
1867+
1868+
>>> con = sqlite3.connect("file:tutorial.db?mode=ro", uri=True)
1869+
>>> con.execute("CREATE TABLE readonly(data)")
1870+
Traceback (most recent call last):
1871+
OperationalError: attempt to write a readonly database
18211872

18221873
* Do not implicitly create a new database file if it does not already exist;
1823-
will raise :exc:`~sqlite3.OperationalError` if unable to create a new file::
1874+
will raise :exc:`~sqlite3.OperationalError` if unable to create a new file:
1875+
1876+
.. doctest::
1877+
1878+
>>> con = sqlite3.connect("file:nosuchdb.db?mode=rw", uri=True)
1879+
Traceback (most recent call last):
1880+
OperationalError: unable to open database file
1881+
18241882

1825-
con = sqlite3.connect("file:nosuchdb.db?mode=rw", uri=True)
1883+
* Create a shared named in-memory database:
1884+
1885+
.. testcode::
18261886

1827-
* Create a shared named in-memory database::
1887+
db = "file:mem1?mode=memory&cache=shared"
1888+
con1 = sqlite3.connect(db, uri=True)
1889+
con2 = sqlite3.connect(db, uri=True)
1890+
with con1:
1891+
con1.execute("CREATE TABLE shared(data)")
1892+
con1.execute("INSERT INTO shared VALUES(28)")
1893+
res = con2.execute("SELECT data FROM shared")
1894+
assert res.fetchone() == (28,)
18281895

1829-
con1 = sqlite3.connect("file:mem1?mode=memory&cache=shared", uri=True)
1830-
con2 = sqlite3.connect("file:mem1?mode=memory&cache=shared", uri=True)
1831-
con1.execute("create table t(t)")
1832-
con1.execute("insert into t values(28)")
1833-
con1.commit()
1834-
rows = con2.execute("select * from t").fetchall()
18351896

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

0 commit comments

Comments
 (0)