mirror of
https://github.com/python/cpython.git
synced 2025-10-03 13:45:29 +00:00
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 bf9259776d
)
Co-authored-by: Erlend E. Aasland <erlend.aasland@protonmail.com>
This commit is contained in:
parent
ca7e78dc3a
commit
2ba877258a
1 changed files with 125 additions and 64 deletions
|
@ -343,7 +343,9 @@ Module functions
|
||||||
other than checking that there are no unclosed string literals
|
other than checking that there are no unclosed string literals
|
||||||
and the statement is terminated by a semicolon.
|
and the statement is terminated by a semicolon.
|
||||||
|
|
||||||
For example::
|
For example:
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
|
||||||
>>> sqlite3.complete_statement("SELECT foo FROM bar;")
|
>>> sqlite3.complete_statement("SELECT foo FROM bar;")
|
||||||
True
|
True
|
||||||
|
@ -364,22 +366,27 @@ Module functions
|
||||||
to disable the feature again.
|
to disable the feature again.
|
||||||
|
|
||||||
Register an :func:`unraisable hook handler <sys.unraisablehook>` for an
|
Register an :func:`unraisable hook handler <sys.unraisablehook>` for an
|
||||||
improved debug experience::
|
improved debug experience:
|
||||||
|
|
||||||
|
.. testsetup:: sqlite3.trace
|
||||||
|
|
||||||
|
import sqlite3
|
||||||
|
|
||||||
|
.. doctest:: sqlite3.trace
|
||||||
|
|
||||||
>>> import sqlite3
|
|
||||||
>>> sqlite3.enable_callback_tracebacks(True)
|
>>> sqlite3.enable_callback_tracebacks(True)
|
||||||
>>> cx = sqlite3.connect(":memory:")
|
>>> con = sqlite3.connect(":memory:")
|
||||||
>>> cx.set_trace_callback(lambda stmt: 5/0)
|
>>> def evil_trace(stmt):
|
||||||
>>> cx.execute("select 1")
|
... 5/0
|
||||||
Exception ignored in: <function <lambda> at 0x10b4e3ee0>
|
>>> con.set_trace_callback(evil_trace)
|
||||||
Traceback (most recent call last):
|
>>> def debug(unraisable):
|
||||||
File "<stdin>", line 1, in <lambda>
|
... print(f"{unraisable.exc_value!r} in callback {unraisable.object.__name__}")
|
||||||
ZeroDivisionError: division by zero
|
... print(f"Error message: {unraisable.err_msg}")
|
||||||
>>> import sys
|
>>> import sys
|
||||||
>>> sys.unraisablehook = lambda unraisable: print(unraisable)
|
>>> sys.unraisablehook = debug
|
||||||
>>> cx.execute("select 1")
|
>>> cur = con.execute("select 1")
|
||||||
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>)
|
ZeroDivisionError('division by zero') in callback evil_trace
|
||||||
<sqlite3.Cursor object at 0x10b1fe840>
|
Error message: None
|
||||||
|
|
||||||
.. function:: register_adapter(type, adapter, /)
|
.. function:: register_adapter(type, adapter, /)
|
||||||
|
|
||||||
|
@ -926,12 +933,12 @@ Connection objects
|
||||||
Useful when saving an in-memory database for later restoration.
|
Useful when saving an in-memory database for later restoration.
|
||||||
Similar to the ``.dump`` command in the :program:`sqlite3` shell.
|
Similar to the ``.dump`` command in the :program:`sqlite3` shell.
|
||||||
|
|
||||||
Example::
|
Example:
|
||||||
|
|
||||||
# Convert file existing_db.db to SQL dump file dump.sql
|
.. testcode::
|
||||||
import sqlite3
|
|
||||||
|
|
||||||
con = sqlite3.connect('existing_db.db')
|
# Convert file example.db to SQL dump file dump.sql
|
||||||
|
con = sqlite3.connect('example.db')
|
||||||
with open('dump.sql', 'w') as f:
|
with open('dump.sql', 'w') as f:
|
||||||
for line in con.iterdump():
|
for line in con.iterdump():
|
||||||
f.write('%s\n' % line)
|
f.write('%s\n' % line)
|
||||||
|
@ -974,27 +981,32 @@ Connection objects
|
||||||
The number of seconds to sleep between successive attempts
|
The number of seconds to sleep between successive attempts
|
||||||
to back up remaining pages.
|
to back up remaining pages.
|
||||||
|
|
||||||
Example 1, copy an existing database into another::
|
Example 1, copy an existing database into another:
|
||||||
|
|
||||||
import sqlite3
|
.. testcode::
|
||||||
|
|
||||||
def progress(status, remaining, total):
|
def progress(status, remaining, total):
|
||||||
print(f'Copied {total-remaining} of {total} pages...')
|
print(f'Copied {total-remaining} of {total} pages...')
|
||||||
|
|
||||||
con = sqlite3.connect('existing_db.db')
|
src = sqlite3.connect('example.db')
|
||||||
bck = sqlite3.connect('backup.db')
|
dst = sqlite3.connect('backup.db')
|
||||||
with bck:
|
with dst:
|
||||||
con.backup(bck, pages=1, progress=progress)
|
src.backup(dst, pages=1, progress=progress)
|
||||||
bck.close()
|
dst.close()
|
||||||
con.close()
|
src.close()
|
||||||
|
|
||||||
Example 2, copy an existing database into a transient copy::
|
.. testoutput::
|
||||||
|
:hide:
|
||||||
|
|
||||||
import sqlite3
|
Copied 0 of 0 pages...
|
||||||
|
|
||||||
source = sqlite3.connect('existing_db.db')
|
Example 2, copy an existing database into a transient copy:
|
||||||
dest = sqlite3.connect(':memory:')
|
|
||||||
source.backup(dest)
|
.. testcode::
|
||||||
|
|
||||||
|
src = sqlite3.connect('example.db')
|
||||||
|
dst = sqlite3.connect(':memory:')
|
||||||
|
src.backup(dst)
|
||||||
|
|
||||||
.. versionadded:: 3.7
|
.. versionadded:: 3.7
|
||||||
|
|
||||||
|
@ -1010,12 +1022,20 @@ Connection objects
|
||||||
:raises ProgrammingError:
|
:raises ProgrammingError:
|
||||||
If *category* is not recognised by the underlying SQLite library.
|
If *category* is not recognised by the underlying SQLite library.
|
||||||
|
|
||||||
Example, query the maximum length of an SQL statement::
|
Example, query the maximum length of an SQL statement
|
||||||
|
for :class:`Connection` ``con`` (the default is 1000000000):
|
||||||
|
|
||||||
|
.. testsetup:: sqlite3.limits
|
||||||
|
|
||||||
import sqlite3
|
import sqlite3
|
||||||
con = sqlite3.connect(":memory:")
|
con = sqlite3.connect(":memory:")
|
||||||
lim = con.getlimit(sqlite3.SQLITE_LIMIT_SQL_LENGTH)
|
con.setlimit(sqlite3.SQLITE_LIMIT_SQL_LENGTH, 1_000_000_000)
|
||||||
print(f"SQLITE_LIMIT_SQL_LENGTH={lim}")
|
con.setlimit(sqlite3.SQLITE_LIMIT_ATTACHED, 10)
|
||||||
|
|
||||||
|
.. doctest:: sqlite3.limits
|
||||||
|
|
||||||
|
>>> con.getlimit(sqlite3.SQLITE_LIMIT_SQL_LENGTH)
|
||||||
|
1000000000
|
||||||
|
|
||||||
.. versionadded:: 3.11
|
.. versionadded:: 3.11
|
||||||
|
|
||||||
|
@ -1039,11 +1059,15 @@ Connection objects
|
||||||
:raises ProgrammingError:
|
:raises ProgrammingError:
|
||||||
If *category* is not recognised by the underlying SQLite library.
|
If *category* is not recognised by the underlying SQLite library.
|
||||||
|
|
||||||
Example, limit the number of attached databases to 1::
|
Example, limit the number of attached databases to 1
|
||||||
|
for :class:`Connection` ``con`` (the default limit is 10):
|
||||||
|
|
||||||
import sqlite3
|
.. doctest:: sqlite3.limits
|
||||||
con = sqlite3.connect(":memory:")
|
|
||||||
con.setlimit(sqlite3.SQLITE_LIMIT_ATTACHED, 1)
|
>>> con.setlimit(sqlite3.SQLITE_LIMIT_ATTACHED, 1)
|
||||||
|
10
|
||||||
|
>>> con.getlimit(sqlite3.SQLITE_LIMIT_ATTACHED)
|
||||||
|
1
|
||||||
|
|
||||||
.. versionadded:: 3.11
|
.. versionadded:: 3.11
|
||||||
|
|
||||||
|
@ -1119,11 +1143,25 @@ Cursor objects
|
||||||
|
|
||||||
Cursor objects are :term:`iterators <iterator>`,
|
Cursor objects are :term:`iterators <iterator>`,
|
||||||
meaning that if you :meth:`~Cursor.execute` a ``SELECT`` query,
|
meaning that if you :meth:`~Cursor.execute` a ``SELECT`` query,
|
||||||
you can simply iterate over the cursor to fetch the resulting rows::
|
you can simply iterate over the cursor to fetch the resulting rows:
|
||||||
|
|
||||||
for row in cur.execute("select * from data"):
|
.. testsetup:: sqlite3.cursor
|
||||||
|
|
||||||
|
import sqlite3
|
||||||
|
con = sqlite3.connect(":memory:", isolation_level=None)
|
||||||
|
cur = con.execute("CREATE TABLE data(t)")
|
||||||
|
cur.execute("INSERT INTO data VALUES(1)")
|
||||||
|
|
||||||
|
.. testcode:: sqlite3.cursor
|
||||||
|
|
||||||
|
for row in cur.execute("SELECT t FROM data"):
|
||||||
print(row)
|
print(row)
|
||||||
|
|
||||||
|
.. testoutput:: sqlite3.cursor
|
||||||
|
:hide:
|
||||||
|
|
||||||
|
(1,)
|
||||||
|
|
||||||
.. _database cursor: https://en.wikipedia.org/wiki/Cursor_(databases)
|
.. _database cursor: https://en.wikipedia.org/wiki/Cursor_(databases)
|
||||||
|
|
||||||
.. class:: Cursor
|
.. class:: Cursor
|
||||||
|
@ -1159,14 +1197,16 @@ Cursor objects
|
||||||
:term:`iterator` yielding parameters instead of a sequence.
|
:term:`iterator` yielding parameters instead of a sequence.
|
||||||
Uses the same implicit transaction handling as :meth:`~Cursor.execute`.
|
Uses the same implicit transaction handling as :meth:`~Cursor.execute`.
|
||||||
|
|
||||||
Example::
|
Example:
|
||||||
|
|
||||||
data = [
|
.. testcode:: sqlite3.cursor
|
||||||
|
|
||||||
|
rows = [
|
||||||
("row1",),
|
("row1",),
|
||||||
("row2",),
|
("row2",),
|
||||||
]
|
]
|
||||||
# cur is an sqlite3.Cursor object
|
# cur is an sqlite3.Cursor object
|
||||||
cur.executemany("insert into t values(?)", data)
|
cur.executemany("insert into data values(?)", rows)
|
||||||
|
|
||||||
.. method:: executescript(sql_script, /)
|
.. method:: executescript(sql_script, /)
|
||||||
|
|
||||||
|
@ -1178,7 +1218,9 @@ Cursor objects
|
||||||
|
|
||||||
*sql_script* must be a :class:`string <str>`.
|
*sql_script* must be a :class:`string <str>`.
|
||||||
|
|
||||||
Example::
|
Example:
|
||||||
|
|
||||||
|
.. testcode:: sqlite3.cursor
|
||||||
|
|
||||||
# cur is an sqlite3.Cursor object
|
# cur is an sqlite3.Cursor object
|
||||||
cur.executescript("""
|
cur.executescript("""
|
||||||
|
@ -1275,7 +1317,9 @@ Cursor objects
|
||||||
Read-only attribute that provides the SQLite database :class:`Connection`
|
Read-only attribute that provides the SQLite database :class:`Connection`
|
||||||
belonging to the cursor. A :class:`Cursor` object created by
|
belonging to the cursor. A :class:`Cursor` object created by
|
||||||
calling :meth:`con.cursor() <Connection.cursor>` will have a
|
calling :meth:`con.cursor() <Connection.cursor>` will have a
|
||||||
:attr:`connection` attribute that refers to *con*::
|
:attr:`connection` attribute that refers to *con*:
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
|
||||||
>>> con = sqlite3.connect(":memory:")
|
>>> con = sqlite3.connect(":memory:")
|
||||||
>>> cur = con.cursor()
|
>>> cur = con.cursor()
|
||||||
|
@ -1310,7 +1354,9 @@ Row objects
|
||||||
.. versionchanged:: 3.5
|
.. versionchanged:: 3.5
|
||||||
Added support of slicing.
|
Added support of slicing.
|
||||||
|
|
||||||
Example::
|
Example:
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
|
||||||
>>> con = sqlite3.connect(":memory:")
|
>>> con = sqlite3.connect(":memory:")
|
||||||
>>> con.row_factory = sqlite3.Row
|
>>> con.row_factory = sqlite3.Row
|
||||||
|
@ -1662,7 +1708,7 @@ and constructs a :class:`!Point` object from it.
|
||||||
Converter functions are **always** passed a :class:`bytes` object,
|
Converter functions are **always** passed a :class:`bytes` object,
|
||||||
no matter the underlying SQLite data type.
|
no matter the underlying SQLite data type.
|
||||||
|
|
||||||
::
|
.. testcode::
|
||||||
|
|
||||||
def convert_point(s):
|
def convert_point(s):
|
||||||
x, y = map(float, s.split(b";"))
|
x, y = map(float, s.split(b";"))
|
||||||
|
@ -1690,7 +1736,7 @@ Adapter and converter recipes
|
||||||
|
|
||||||
This section shows recipes for common adapters and converters.
|
This section shows recipes for common adapters and converters.
|
||||||
|
|
||||||
.. code-block::
|
.. testcode::
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
@ -1703,7 +1749,7 @@ This section shows recipes for common adapters and converters.
|
||||||
"""Adapt datetime.datetime to timezone-naive ISO 8601 date."""
|
"""Adapt datetime.datetime to timezone-naive ISO 8601 date."""
|
||||||
return val.isoformat()
|
return val.isoformat()
|
||||||
|
|
||||||
def adapt_datetime_epoch(val)
|
def adapt_datetime_epoch(val):
|
||||||
"""Adapt datetime.datetime to Unix timestamp."""
|
"""Adapt datetime.datetime to Unix timestamp."""
|
||||||
return int(val.timestamp())
|
return int(val.timestamp())
|
||||||
|
|
||||||
|
@ -1777,23 +1823,38 @@ How to work with SQLite URIs
|
||||||
|
|
||||||
Some useful URI tricks include:
|
Some useful URI tricks include:
|
||||||
|
|
||||||
* Open a database in read-only mode::
|
* Open a database in read-only mode:
|
||||||
|
|
||||||
con = sqlite3.connect("file:template.db?mode=ro", uri=True)
|
.. doctest::
|
||||||
|
|
||||||
|
>>> con = sqlite3.connect("file:tutorial.db?mode=ro", uri=True)
|
||||||
|
>>> con.execute("CREATE TABLE readonly(data)")
|
||||||
|
Traceback (most recent call last):
|
||||||
|
OperationalError: attempt to write a readonly database
|
||||||
|
|
||||||
* Do not implicitly create a new database file if it does not already exist;
|
* Do not implicitly create a new database file if it does not already exist;
|
||||||
will raise :exc:`~sqlite3.OperationalError` if unable to create a new file::
|
will raise :exc:`~sqlite3.OperationalError` if unable to create a new file:
|
||||||
|
|
||||||
con = sqlite3.connect("file:nosuchdb.db?mode=rw", uri=True)
|
.. doctest::
|
||||||
|
|
||||||
* Create a shared named in-memory database::
|
>>> con = sqlite3.connect("file:nosuchdb.db?mode=rw", uri=True)
|
||||||
|
Traceback (most recent call last):
|
||||||
|
OperationalError: unable to open database file
|
||||||
|
|
||||||
|
|
||||||
|
* Create a shared named in-memory database:
|
||||||
|
|
||||||
|
.. testcode::
|
||||||
|
|
||||||
|
db = "file:mem1?mode=memory&cache=shared"
|
||||||
|
con1 = sqlite3.connect(db, uri=True)
|
||||||
|
con2 = sqlite3.connect(db, uri=True)
|
||||||
|
with con1:
|
||||||
|
con1.execute("CREATE TABLE shared(data)")
|
||||||
|
con1.execute("INSERT INTO shared VALUES(28)")
|
||||||
|
res = con2.execute("SELECT data FROM shared")
|
||||||
|
assert res.fetchone() == (28,)
|
||||||
|
|
||||||
con1 = sqlite3.connect("file:mem1?mode=memory&cache=shared", uri=True)
|
|
||||||
con2 = sqlite3.connect("file:mem1?mode=memory&cache=shared", uri=True)
|
|
||||||
con1.execute("create table t(t)")
|
|
||||||
con1.execute("insert into t values(28)")
|
|
||||||
con1.commit()
|
|
||||||
rows = con2.execute("select * from t").fetchall()
|
|
||||||
|
|
||||||
More information about this feature, including a list of parameters,
|
More information about this feature, including a list of parameters,
|
||||||
can be found in the `SQLite URI documentation`_.
|
can be found in the `SQLite URI documentation`_.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue