Python-checkins
Threads by month
- ----- 2024 -----
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2004 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2003 -----
- December
- November
- October
- September
- August
August 2022
- 1 participants
- 535 discussions
https://github.com/python/cpython/commit/0cd33e11fe6c77c423dbf3a7a9920daff0…
commit: 0cd33e11fe6c77c423dbf3a7a9920daff012f006
branch: main
author: Anh71me <iyumelive(a)gmail.com>
committer: gvanrossum <gvanrossum(a)gmail.com>
date: 2022-08-31T16:02:24-07:00
summary:
GH-96079 Fix missing field name for _AnnotatedAlias (#96080)
files:
A Misc/NEWS.d/next/Library/2022-08-31-11-10-21.gh-issue-96079.uqrXdJ.rst
M Lib/test/test_typing.py
M Lib/typing.py
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index 9239673c248..015fa80942a 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -7143,6 +7143,7 @@ def test_special_attrs(self):
typing.Self: 'Self',
# Subscribed special forms
typing.Annotated[Any, "Annotation"]: 'Annotated',
+ typing.Annotated[int, 'Annotation']: 'Annotated',
typing.ClassVar[Any]: 'ClassVar',
typing.Concatenate[Any, SpecialAttrsP]: 'Concatenate',
typing.Final[Any]: 'Final',
diff --git a/Lib/typing.py b/Lib/typing.py
index 84fe007a9ee..95bd61c7f8c 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -2101,7 +2101,7 @@ def __init__(self, origin, metadata):
if isinstance(origin, _AnnotatedAlias):
metadata = origin.__metadata__ + metadata
origin = origin.__origin__
- super().__init__(origin, origin)
+ super().__init__(origin, origin, name='Annotated')
self.__metadata__ = metadata
def copy_with(self, params):
@@ -2134,6 +2134,9 @@ def __getattr__(self, attr):
return 'Annotated'
return super().__getattr__(attr)
+ def __mro_entries__(self, bases):
+ return (self.__origin__,)
+
class Annotated:
"""Add context specific metadata to a type.
diff --git a/Misc/NEWS.d/next/Library/2022-08-31-11-10-21.gh-issue-96079.uqrXdJ.rst b/Misc/NEWS.d/next/Library/2022-08-31-11-10-21.gh-issue-96079.uqrXdJ.rst
new file mode 100644
index 00000000000..4cb8d276cbe
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-08-31-11-10-21.gh-issue-96079.uqrXdJ.rst
@@ -0,0 +1 @@
+In :mod:`typing`, fix missing field ``name`` and incorrect ``__module__`` in _AnnotatedAlias.
1
0
31 Aug '22
https://github.com/python/cpython/commit/615537e62f0a49f6888ac27046bd8de965…
commit: 615537e62f0a49f6888ac27046bd8de965512d9d
branch: main
author: Piotr Kaznowski <piotr(a)kazno.dev>
committer: rhettinger <rhettinger(a)users.noreply.github.com>
date: 2022-08-31T16:23:52-05:00
summary:
gh-96408: Document difference between set-like view and sets. (GH-96439)
files:
M Doc/library/stdtypes.rst
diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst
index 2c021866e29..f68cf46a6c6 100644
--- a/Doc/library/stdtypes.rst
+++ b/Doc/library/stdtypes.rst
@@ -4694,7 +4694,9 @@ values are hashable, so that ``(key, value)`` pairs are unique and hashable,
then the items view is also set-like. (Values views are not treated as set-like
since the entries are generally not unique.) For set-like views, all of the
operations defined for the abstract base class :class:`collections.abc.Set` are
-available (for example, ``==``, ``<``, or ``^``).
+available (for example, ``==``, ``<``, or ``^``). While using set operators,
+set-like views accept any iterable as the other operand, unlike sets which only
+accept sets as the input.
An example of dictionary view usage::
@@ -4726,6 +4728,8 @@ An example of dictionary view usage::
{'bacon'}
>>> keys ^ {'sausage', 'juice'}
{'juice', 'sausage', 'bacon', 'spam'}
+ >>> keys | ['juice', 'juice', 'juice']
+ {'juice', 'sausage', 'bacon', 'spam', 'eggs'}
>>> # get back a read-only proxy for the original dictionary
>>> values.mapping
1
0
https://github.com/python/cpython/commit/29f1b0bb1ff73dcc28f0ca7e11794141b6…
commit: 29f1b0bb1ff73dcc28f0ca7e11794141b6de58c9
branch: main
author: Vinay Sajip <vinay_sajip(a)yahoo.co.uk>
committer: vsajip <vinay_sajip(a)yahoo.co.uk>
date: 2022-08-31T10:50:29+01:00
summary:
gh-89258: Add a getChildren() method to logging.Logger. (GH-96444)
Co-authored-by: Éric <merwok(a)netwok.org>
files:
A Misc/NEWS.d/next/Library/2022-08-29-07-04-03.gh-issue-89258.ri7ncj.rst
M Doc/library/logging.rst
M Lib/logging/__init__.py
M Lib/test/test_logging.py
diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst
index 319340a39350..c3806b6f5bf8 100644
--- a/Doc/library/logging.rst
+++ b/Doc/library/logging.rst
@@ -170,6 +170,18 @@ is the module's name in the Python package namespace.
.. versionadded:: 3.2
+ .. method:: Logger.getChildren()
+
+ Returns a set of loggers which are immediate children of this logger. So for
+ example ``logging.getLogger().getChildren()`` might return a set containing
+ loggers named ``foo`` and ``bar``, but a logger named ``foo.bar`` wouldn't be
+ included in the set. Likewise, ``logging.getLogger('foo').getChildren()`` might
+ return a set including a logger named ``foo.bar``, but it wouldn't include one
+ named ``foo.bar.baz``.
+
+ .. versionadded:: 3.12
+
+
.. method:: Logger.debug(msg, *args, **kwargs)
Logs a message with level :const:`DEBUG` on this logger. The *msg* is the
diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py
index c3208a21f499..86e1efe6e653 100644
--- a/Lib/logging/__init__.py
+++ b/Lib/logging/__init__.py
@@ -1828,6 +1828,25 @@ def getChild(self, suffix):
suffix = '.'.join((self.name, suffix))
return self.manager.getLogger(suffix)
+ def getChildren(self):
+
+ def _hierlevel(logger):
+ if logger is logger.manager.root:
+ return 0
+ return 1 + logger.name.count('.')
+
+ d = self.manager.loggerDict
+ _acquireLock()
+ try:
+ # exclude PlaceHolders - the last check is to ensure that lower-level
+ # descendants aren't returned - if there are placeholders, a logger's
+ # parent field might point to a grandparent or ancestor thereof.
+ return set(item for item in d.values()
+ if isinstance(item, Logger) and item.parent is self and
+ _hierlevel(item) == 1 + _hierlevel(item.parent))
+ finally:
+ _releaseLock()
+
def __repr__(self):
level = getLevelName(self.getEffectiveLevel())
return '<%s %s (%s)>' % (self.__class__.__name__, self.name, level)
diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
index a67ed07f12c8..0c852fc1eda2 100644
--- a/Lib/test/test_logging.py
+++ b/Lib/test/test_logging.py
@@ -3717,6 +3717,20 @@ def test_child_loggers(self):
self.assertIs(c2, logging.getLogger('abc.def.ghi'))
self.assertIs(c2, c3)
+ def test_get_children(self):
+ r = logging.getLogger()
+ l1 = logging.getLogger('foo')
+ l2 = logging.getLogger('foo.bar')
+ l3 = logging.getLogger('foo.bar.baz.bozz')
+ l4 = logging.getLogger('bar')
+ kids = r.getChildren()
+ expected = {l1, l4}
+ self.assertEqual(expected, kids & expected) # might be other kids for root
+ self.assertNotIn(l2, expected)
+ kids = l1.getChildren()
+ self.assertEqual({l2}, kids)
+ kids = l2.getChildren()
+ self.assertEqual(set(), kids)
class DerivedLogRecord(logging.LogRecord):
pass
diff --git a/Misc/NEWS.d/next/Library/2022-08-29-07-04-03.gh-issue-89258.ri7ncj.rst b/Misc/NEWS.d/next/Library/2022-08-29-07-04-03.gh-issue-89258.ri7ncj.rst
new file mode 100644
index 000000000000..74300c108c89
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-08-29-07-04-03.gh-issue-89258.ri7ncj.rst
@@ -0,0 +1,2 @@
+Added a :meth:`~logging.Logger.getChildren` method to
+:class:`logging.Logger`, to get the immediate child loggers of a logger.
1
0
[3.10] gh-96414: Inline code examples in sqlite3 docs (GH-96442). (#96453)
by erlend-aasland 31 Aug '22
by erlend-aasland 31 Aug '22
31 Aug '22
https://github.com/python/cpython/commit/2ecc195498f3b1256fabc2b66e0d8f6d67…
commit: 2ecc195498f3b1256fabc2b66e0d8f6d671fa1d7
branch: 3.10
author: Erlend E. Aasland <erlend.aasland(a)protonmail.com>
committer: erlend-aasland <erlend.aasland(a)protonmail.com>
date: 2022-08-31T10:10:55+02:00
summary:
[3.10] gh-96414: Inline code examples in sqlite3 docs (GH-96442). (#96453)
* [3.10] gh-96414: Inline code examples in sqlite3 docs (GH-96442).
(cherry picked from commit f7e7bf161aaec5a5cffdcec7c97e1f09e445421b)
Co-authored-by: Erlend E. Aasland <erlend.aasland(a)protonmail.com>
files:
D Doc/includes/sqlite3/adapter_point_1.py
D Doc/includes/sqlite3/adapter_point_2.py
D Doc/includes/sqlite3/collation_reverse.py
D Doc/includes/sqlite3/converter_point.py
D Doc/includes/sqlite3/ctx_manager.py
D Doc/includes/sqlite3/execute_1.py
D Doc/includes/sqlite3/load_extension.py
D Doc/includes/sqlite3/md5func.py
D Doc/includes/sqlite3/mysumaggr.py
D Doc/includes/sqlite3/row_factory.py
D Doc/includes/sqlite3/shortcut_methods.py
D Doc/includes/sqlite3/text_factory.py
M Doc/library/sqlite3.rst
M Doc/tools/susp-ignored.csv
diff --git a/Doc/includes/sqlite3/adapter_point_1.py b/Doc/includes/sqlite3/adapter_point_1.py
deleted file mode 100644
index 77daf8f16d22..000000000000
--- a/Doc/includes/sqlite3/adapter_point_1.py
+++ /dev/null
@@ -1,18 +0,0 @@
-import sqlite3
-
-class Point:
- def __init__(self, x, y):
- self.x, self.y = x, y
-
- def __conform__(self, protocol):
- if protocol is sqlite3.PrepareProtocol:
- return "%f;%f" % (self.x, self.y)
-
-con = sqlite3.connect(":memory:")
-cur = con.cursor()
-
-p = Point(4.0, -3.2)
-cur.execute("select ?", (p,))
-print(cur.fetchone()[0])
-
-con.close()
diff --git a/Doc/includes/sqlite3/adapter_point_2.py b/Doc/includes/sqlite3/adapter_point_2.py
deleted file mode 100644
index cb86331692b6..000000000000
--- a/Doc/includes/sqlite3/adapter_point_2.py
+++ /dev/null
@@ -1,19 +0,0 @@
-import sqlite3
-
-class Point:
- def __init__(self, x, y):
- self.x, self.y = x, y
-
-def adapt_point(point):
- return "%f;%f" % (point.x, point.y)
-
-sqlite3.register_adapter(Point, adapt_point)
-
-con = sqlite3.connect(":memory:")
-cur = con.cursor()
-
-p = Point(4.0, -3.2)
-cur.execute("select ?", (p,))
-print(cur.fetchone()[0])
-
-con.close()
diff --git a/Doc/includes/sqlite3/collation_reverse.py b/Doc/includes/sqlite3/collation_reverse.py
deleted file mode 100644
index 3504a350a04e..000000000000
--- a/Doc/includes/sqlite3/collation_reverse.py
+++ /dev/null
@@ -1,20 +0,0 @@
-import sqlite3
-
-def collate_reverse(string1, string2):
- if string1 == string2:
- return 0
- elif string1 < string2:
- return 1
- else:
- return -1
-
-con = sqlite3.connect(":memory:")
-con.create_collation("reverse", collate_reverse)
-
-cur = con.cursor()
-cur.execute("create table test(x)")
-cur.executemany("insert into test(x) values (?)", [("a",), ("b",)])
-cur.execute("select x from test order by x collate reverse")
-for row in cur:
- print(row)
-con.close()
diff --git a/Doc/includes/sqlite3/converter_point.py b/Doc/includes/sqlite3/converter_point.py
deleted file mode 100644
index 147807a2225f..000000000000
--- a/Doc/includes/sqlite3/converter_point.py
+++ /dev/null
@@ -1,40 +0,0 @@
-import sqlite3
-
-class Point:
- def __init__(self, x, y):
- self.x, self.y = x, y
-
- def __repr__(self):
- return f"Point({self.x}, {self.y})"
-
-def adapt_point(point):
- return f"{point.x};{point.y}".encode("utf-8")
-
-def convert_point(s):
- x, y = list(map(float, s.split(b";")))
- return Point(x, y)
-
-# Register the adapter and converter
-sqlite3.register_adapter(Point, adapt_point)
-sqlite3.register_converter("point", convert_point)
-
-# 1) Parse using declared types
-p = Point(4.0, -3.2)
-con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_DECLTYPES)
-cur = con.execute("create table test(p point)")
-
-cur.execute("insert into test(p) values (?)", (p,))
-cur.execute("select p from test")
-print("with declared types:", cur.fetchone()[0])
-cur.close()
-con.close()
-
-# 2) Parse using column names
-con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_COLNAMES)
-cur = con.execute("create table test(p)")
-
-cur.execute("insert into test(p) values (?)", (p,))
-cur.execute('select p as "p [point]" from test')
-print("with column names:", cur.fetchone()[0])
-cur.close()
-con.close()
diff --git a/Doc/includes/sqlite3/ctx_manager.py b/Doc/includes/sqlite3/ctx_manager.py
deleted file mode 100644
index 2e1175ef44c6..000000000000
--- a/Doc/includes/sqlite3/ctx_manager.py
+++ /dev/null
@@ -1,20 +0,0 @@
-import sqlite3
-
-con = sqlite3.connect(":memory:")
-con.execute("create table lang (id integer primary key, name varchar unique)")
-
-# Successful, con.commit() is called automatically afterwards
-with con:
- con.execute("insert into lang(name) values (?)", ("Python",))
-
-# con.rollback() is called after the with block finishes with an exception, the
-# exception is still raised and must be caught
-try:
- with con:
- con.execute("insert into lang(name) values (?)", ("Python",))
-except sqlite3.IntegrityError:
- print("couldn't add Python twice")
-
-# Connection object used as context manager only commits or rollbacks transactions,
-# so the connection object should be closed manually
-con.close()
diff --git a/Doc/includes/sqlite3/execute_1.py b/Doc/includes/sqlite3/execute_1.py
deleted file mode 100644
index ee0000e2b94a..000000000000
--- a/Doc/includes/sqlite3/execute_1.py
+++ /dev/null
@@ -1,22 +0,0 @@
-import sqlite3
-
-con = sqlite3.connect(":memory:")
-cur = con.cursor()
-cur.execute("create table lang (name, first_appeared)")
-
-# This is the qmark style:
-cur.execute("insert into lang values (?, ?)", ("C", 1972))
-
-# The qmark style used with executemany():
-lang_list = [
- ("Fortran", 1957),
- ("Python", 1991),
- ("Go", 2009),
-]
-cur.executemany("insert into lang values (?, ?)", lang_list)
-
-# And this is the named style:
-cur.execute("select * from lang where first_appeared=:year", {"year": 1972})
-print(cur.fetchall())
-
-con.close()
diff --git a/Doc/includes/sqlite3/load_extension.py b/Doc/includes/sqlite3/load_extension.py
deleted file mode 100644
index 624cfe262f38..000000000000
--- a/Doc/includes/sqlite3/load_extension.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import sqlite3
-
-con = sqlite3.connect(":memory:")
-
-# enable extension loading
-con.enable_load_extension(True)
-
-# Load the fulltext search extension
-con.execute("select load_extension('./fts3.so')")
-
-# alternatively you can load the extension using an API call:
-# con.load_extension("./fts3.so")
-
-# disable extension loading again
-con.enable_load_extension(False)
-
-# example from SQLite wiki
-con.execute("create virtual table recipe using fts3(name, ingredients)")
-con.executescript("""
- insert into recipe (name, ingredients) values ('broccoli stew', 'broccoli peppers cheese tomatoes');
- insert into recipe (name, ingredients) values ('pumpkin stew', 'pumpkin onions garlic celery');
- insert into recipe (name, ingredients) values ('broccoli pie', 'broccoli cheese onions flour');
- insert into recipe (name, ingredients) values ('pumpkin pie', 'pumpkin sugar flour butter');
- """)
-for row in con.execute("select rowid, name, ingredients from recipe where name match 'pie'"):
- print(row)
-
-con.close()
diff --git a/Doc/includes/sqlite3/md5func.py b/Doc/includes/sqlite3/md5func.py
deleted file mode 100644
index 16dc348bf001..000000000000
--- a/Doc/includes/sqlite3/md5func.py
+++ /dev/null
@@ -1,13 +0,0 @@
-import sqlite3
-import hashlib
-
-def md5sum(t):
- return hashlib.md5(t).hexdigest()
-
-con = sqlite3.connect(":memory:")
-con.create_function("md5", 1, md5sum)
-cur = con.cursor()
-cur.execute("select md5(?)", (b"foo",))
-print(cur.fetchone()[0])
-
-con.close()
diff --git a/Doc/includes/sqlite3/mysumaggr.py b/Doc/includes/sqlite3/mysumaggr.py
deleted file mode 100644
index 11f96395b6c4..000000000000
--- a/Doc/includes/sqlite3/mysumaggr.py
+++ /dev/null
@@ -1,22 +0,0 @@
-import sqlite3
-
-class MySum:
- def __init__(self):
- self.count = 0
-
- def step(self, value):
- self.count += value
-
- def finalize(self):
- return self.count
-
-con = sqlite3.connect(":memory:")
-con.create_aggregate("mysum", 1, MySum)
-cur = con.cursor()
-cur.execute("create table test(i)")
-cur.execute("insert into test(i) values (1)")
-cur.execute("insert into test(i) values (2)")
-cur.execute("select mysum(i) from test")
-print(cur.fetchone()[0])
-
-con.close()
diff --git a/Doc/includes/sqlite3/row_factory.py b/Doc/includes/sqlite3/row_factory.py
deleted file mode 100644
index 9de6e7b1b905..000000000000
--- a/Doc/includes/sqlite3/row_factory.py
+++ /dev/null
@@ -1,15 +0,0 @@
-import sqlite3
-
-def dict_factory(cursor, row):
- d = {}
- for idx, col in enumerate(cursor.description):
- d[col[0]] = row[idx]
- return d
-
-con = sqlite3.connect(":memory:")
-con.row_factory = dict_factory
-cur = con.cursor()
-cur.execute("select 1 as a")
-print(cur.fetchone()["a"])
-
-con.close()
diff --git a/Doc/includes/sqlite3/shortcut_methods.py b/Doc/includes/sqlite3/shortcut_methods.py
deleted file mode 100644
index 48ea6fad15a8..000000000000
--- a/Doc/includes/sqlite3/shortcut_methods.py
+++ /dev/null
@@ -1,24 +0,0 @@
-import sqlite3
-
-langs = [
- ("C++", 1985),
- ("Objective-C", 1984),
-]
-
-con = sqlite3.connect(":memory:")
-
-# Create the table
-con.execute("create table lang(name, first_appeared)")
-
-# Fill the table
-con.executemany("insert into lang(name, first_appeared) values (?, ?)", langs)
-
-# Print the table contents
-for row in con.execute("select name, first_appeared from lang"):
- print(row)
-
-print("I just deleted", con.execute("delete from lang").rowcount, "rows")
-
-# close is not a shortcut method and it's not called automatically,
-# so the connection object should be closed manually
-con.close()
diff --git a/Doc/includes/sqlite3/text_factory.py b/Doc/includes/sqlite3/text_factory.py
deleted file mode 100644
index c0d87cd55911..000000000000
--- a/Doc/includes/sqlite3/text_factory.py
+++ /dev/null
@@ -1,29 +0,0 @@
-import sqlite3
-
-con = sqlite3.connect(":memory:")
-cur = con.cursor()
-
-AUSTRIA = "Österreich"
-
-# by default, rows are returned as str
-cur.execute("select ?", (AUSTRIA,))
-row = cur.fetchone()
-assert row[0] == AUSTRIA
-
-# but we can make sqlite3 always return bytestrings ...
-con.text_factory = bytes
-cur.execute("select ?", (AUSTRIA,))
-row = cur.fetchone()
-assert type(row[0]) is bytes
-# the bytestrings will be encoded in UTF-8, unless you stored garbage in the
-# database ...
-assert row[0] == AUSTRIA.encode("utf-8")
-
-# we can also implement a custom text_factory ...
-# here we implement one that appends "foo" to all strings
-con.text_factory = lambda x: x.decode("utf-8") + "foo"
-cur.execute("select ?", ("bar",))
-row = cur.fetchone()
-assert row[0] == "barfoo"
-
-con.close()
diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst
index 27645b05364e..b24fa48ef829 100644
--- a/Doc/library/sqlite3.rst
+++ b/Doc/library/sqlite3.rst
@@ -546,7 +546,25 @@ Connection objects
Example:
- .. literalinclude:: ../includes/sqlite3/row_factory.py
+ .. testcode::
+
+ def dict_factory(cursor, row):
+ d = {}
+ for idx, col in enumerate(cursor.description):
+ d[col[0]] = row[idx]
+ return d
+
+ con = sqlite3.connect(":memory:")
+ con.row_factory = dict_factory
+ cur = con.execute("SELECT 1 AS a")
+ print(cur.fetchone()["a"])
+
+ con.close()
+
+ .. testoutput::
+ :hide:
+
+ 1
If returning a tuple doesn't suffice and you want name-based access to
columns, you should consider setting :attr:`row_factory` to the
@@ -567,7 +585,35 @@ Connection objects
Example:
- .. literalinclude:: ../includes/sqlite3/text_factory.py
+ .. testcode::
+
+ con = sqlite3.connect(":memory:")
+ cur = con.cursor()
+
+ AUSTRIA = "Österreich"
+
+ # by default, rows are returned as str
+ cur.execute("SELECT ?", (AUSTRIA,))
+ row = cur.fetchone()
+ assert row[0] == AUSTRIA
+
+ # but we can make sqlite3 always return bytestrings ...
+ con.text_factory = bytes
+ cur.execute("SELECT ?", (AUSTRIA,))
+ row = cur.fetchone()
+ assert type(row[0]) is bytes
+ # the bytestrings will be encoded in UTF-8, unless you stored garbage in the
+ # database ...
+ assert row[0] == AUSTRIA.encode("utf-8")
+
+ # we can also implement a custom text_factory ...
+ # here we implement one that appends "foo" to all strings
+ con.text_factory = lambda x: x.decode("utf-8") + "foo"
+ cur.execute("SELECT ?", ("bar",))
+ row = cur.fetchone()
+ assert row[0] == "barfoo"
+
+ con.close()
.. attribute:: total_changes
@@ -648,7 +694,16 @@ Connection objects
Example:
- .. literalinclude:: ../includes/sqlite3/md5func.py
+ .. doctest::
+
+ >>> import hashlib
+ >>> def md5sum(t):
+ ... return hashlib.md5(t).hexdigest()
+ >>> con = sqlite3.connect(":memory:")
+ >>> con.create_function("md5", 1, md5sum)
+ >>> for row in con.execute("SELECT md5(?)", (b"foo",)):
+ ... print(row)
+ ('acbd18db4cc2f85cedef654fccc4a4d8',)
.. method:: create_aggregate(name, /, n_arg, aggregate_class)
@@ -677,7 +732,32 @@ Connection objects
Example:
- .. literalinclude:: ../includes/sqlite3/mysumaggr.py
+ .. testcode::
+
+ class MySum:
+ def __init__(self):
+ self.count = 0
+
+ def step(self, value):
+ self.count += value
+
+ def finalize(self):
+ return self.count
+
+ con = sqlite3.connect(":memory:")
+ con.create_aggregate("mysum", 1, MySum)
+ cur = con.execute("CREATE TABLE test(i)")
+ cur.execute("INSERT INTO test(i) VALUES(1)")
+ cur.execute("INSERT INTO test(i) VALUES(2)")
+ cur.execute("SELECT mysum(i) FROM test")
+ print(cur.fetchone()[0])
+
+ con.close()
+
+ .. testoutput::
+ :hide:
+
+ 3
.. method:: create_collation(name, callable)
@@ -692,7 +772,31 @@ Connection objects
The following example shows a reverse sorting collation:
- .. literalinclude:: ../includes/sqlite3/collation_reverse.py
+ .. testcode::
+
+ def collate_reverse(string1, string2):
+ if string1 == string2:
+ return 0
+ elif string1 < string2:
+ return 1
+ else:
+ return -1
+
+ con = sqlite3.connect(":memory:")
+ con.create_collation("reverse", collate_reverse)
+
+ cur = con.execute("CREATE TABLE test(x)")
+ cur.executemany("INSERT INTO test(x) VALUES(?)", [("a",), ("b",)])
+ cur.execute("SELECT x FROM test ORDER BY x COLLATE reverse")
+ for row in cur:
+ print(row)
+ con.close()
+
+ .. testoutput::
+ :hide:
+
+ ('b',)
+ ('a',)
Remove a collation function by setting *callable* to ``None``.
@@ -788,7 +892,43 @@ Connection objects
.. versionchanged:: 3.10
Added the ``sqlite3.enable_load_extension`` auditing event.
- .. literalinclude:: ../includes/sqlite3/load_extension.py
+ .. testsetup:: sqlite3.loadext
+
+ import sqlite3
+ con = sqlite3.connect(":memory:")
+
+ .. testcode:: sqlite3.loadext
+ :skipif: True # not testable at the moment
+
+ con.enable_load_extension(True)
+
+ # Load the fulltext search extension
+ con.execute("select load_extension('./fts3.so')")
+
+ # alternatively you can load the extension using an API call:
+ # con.load_extension("./fts3.so")
+
+ # disable extension loading again
+ con.enable_load_extension(False)
+
+ # example from SQLite wiki
+ con.execute("CREATE VIRTUAL TABLE recipe USING fts3(name, ingredients)")
+ con.executescript("""
+ INSERT INTO recipe (name, ingredients) VALUES('broccoli stew', 'broccoli peppers cheese tomatoes');
+ INSERT INTO recipe (name, ingredients) VALUES('pumpkin stew', 'pumpkin onions garlic celery');
+ INSERT INTO recipe (name, ingredients) VALUES('broccoli pie', 'broccoli cheese onions flour');
+ INSERT INTO recipe (name, ingredients) VALUES('pumpkin pie', 'pumpkin sugar flour butter');
+ """)
+ for row in con.execute("SELECT rowid, name, ingredients FROM recipe WHERE name MATCH 'pie'"):
+ print(row)
+
+ con.close()
+
+ .. testoutput:: sqlite3.loadext
+ :hide:
+
+ (2, 'broccoli pie', 'broccoli cheese onions flour')
+ (3, 'pumpkin pie', 'pumpkin sugar flour butter')
.. method:: load_extension(path, /)
@@ -1326,7 +1466,30 @@ placeholders (named style). For the qmark style, ``parameters`` must be a
keys for all named parameters. Any extra items are ignored. Here's an example of
both styles:
-.. literalinclude:: ../includes/sqlite3/execute_1.py
+.. testcode::
+
+ con = sqlite3.connect(":memory:")
+ cur = con.execute("CREATE TABLE lang(name, first_appeared)")
+
+ # This is the qmark style:
+ cur.execute("INSERT INTO lang VALUES(?, ?)", ("C", 1972))
+
+ # The qmark style used with executemany():
+ lang_list = [
+ ("Fortran", 1957),
+ ("Python", 1991),
+ ("Go", 2009),
+ ]
+ cur.executemany("INSERT INTO lang VALUES(?, ?)", lang_list)
+
+ # And this is the named style:
+ cur.execute("SELECT * FROM lang WHERE first_appeared = :year", {"year": 1972})
+ print(cur.fetchall())
+
+.. testoutput::
+ :hide:
+
+ [('C', 1972)]
.. _sqlite3-adapters:
@@ -1360,7 +1523,26 @@ This can be implemented by adding a ``__conform__(self, protocol)``
method which returns the adapted value.
The object passed to *protocol* will be of type :class:`PrepareProtocol`.
-.. literalinclude:: ../includes/sqlite3/adapter_point_1.py
+.. testcode::
+
+ class Point:
+ def __init__(self, x, y):
+ self.x, self.y = x, y
+
+ def __conform__(self, protocol):
+ if protocol is sqlite3.PrepareProtocol:
+ return f"{self.x};{self.y}"
+
+ con = sqlite3.connect(":memory:")
+ cur = con.cursor()
+
+ cur.execute("SELECT ?", (Point(4.0, -3.2),))
+ print(cur.fetchone()[0])
+
+.. testoutput::
+ :hide:
+
+ 4.0;-3.2
How to register adapter callables
@@ -1370,7 +1552,27 @@ The other possibility is to create a function that converts the Python object
to an SQLite-compatible type.
This function can then be registered using :func:`register_adapter`.
-.. literalinclude:: ../includes/sqlite3/adapter_point_2.py
+.. testcode::
+
+ class Point:
+ def __init__(self, x, y):
+ self.x, self.y = x, y
+
+ def adapt_point(point):
+ return f"{point.x};{point.y}"
+
+ sqlite3.register_adapter(Point, adapt_point)
+
+ con = sqlite3.connect(":memory:")
+ cur = con.cursor()
+
+ cur.execute("SELECT ?", (Point(1.0, 2.5),))
+ print(cur.fetchone()[0])
+
+.. testoutput::
+ :hide:
+
+ 1.0;2.5
.. _sqlite3-converters:
@@ -1412,7 +1614,50 @@ of :func:`connect`. There are three options:
The following example illustrates the implicit and explicit approaches:
-.. literalinclude:: ../includes/sqlite3/converter_point.py
+.. testcode::
+
+ class Point:
+ def __init__(self, x, y):
+ self.x, self.y = x, y
+
+ def __repr__(self):
+ return f"Point({self.x}, {self.y})"
+
+ def adapt_point(point):
+ return f"{point.x};{point.y}".encode("utf-8")
+
+ def convert_point(s):
+ x, y = list(map(float, s.split(b";")))
+ return Point(x, y)
+
+ # Register the adapter and converter
+ sqlite3.register_adapter(Point, adapt_point)
+ sqlite3.register_converter("point", convert_point)
+
+ # 1) Parse using declared types
+ p = Point(4.0, -3.2)
+ con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_DECLTYPES)
+ cur = con.execute("CREATE TABLE test(p point)")
+
+ cur.execute("INSERT INTO test(p) VALUES(?)", (p,))
+ cur.execute("SELECT p FROM test")
+ print("with declared types:", cur.fetchone()[0])
+ cur.close()
+ con.close()
+
+ # 2) Parse using column names
+ con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_COLNAMES)
+ cur = con.execute("CREATE TABLE test(p)")
+
+ cur.execute("INSERT INTO test(p) VALUES(?)", (p,))
+ cur.execute('SELECT p AS "p [point]" FROM test')
+ print("with column names:", cur.fetchone()[0])
+
+.. testoutput::
+ :hide:
+
+ with declared types: Point(4.0, -3.2)
+ with column names: Point(4.0, -3.2)
.. _sqlite3-adapter-converter-recipes:
@@ -1474,7 +1719,33 @@ objects are created implicitly and these shortcut methods return the cursor
objects. This way, you can execute a ``SELECT`` statement and iterate over it
directly using only a single call on the :class:`Connection` object.
-.. literalinclude:: ../includes/sqlite3/shortcut_methods.py
+.. testcode::
+
+ # Create and fill the table.
+ con = sqlite3.connect(":memory:")
+ con.execute("CREATE TABLE lang(name, first_appeared)")
+ data = [
+ ("C++", 1985),
+ ("Objective-C", 1984),
+ ]
+ con.executemany("INSERT INTO lang(name, first_appeared) VALUES(?, ?)", data)
+
+ # Print the table contents
+ for row in con.execute("SELECT name, first_appeared FROM lang"):
+ print(row)
+
+ print("I just deleted", con.execute("DELETE FROM lang").rowcount, "rows")
+
+ # close() is not a shortcut method and it's not called automatically;
+ # the connection object should be closed manually
+ con.close()
+
+.. testoutput::
+ :hide:
+
+ ('C++', 1985)
+ ('Objective-C', 1984)
+ I just deleted 2 rows
.. _sqlite3-connection-context-manager:
@@ -1499,7 +1770,31 @@ the context manager is a no-op.
The context manager neither implicitly opens a new transaction
nor closes the connection.
-.. literalinclude:: ../includes/sqlite3/ctx_manager.py
+.. testcode::
+
+ con = sqlite3.connect(":memory:")
+ con.execute("CREATE TABLE lang(id INTEGER PRIMARY KEY, name VARCHAR UNIQUE)")
+
+ # Successful, con.commit() is called automatically afterwards
+ with con:
+ con.execute("INSERT INTO lang(name) VALUES(?)", ("Python",))
+
+ # con.rollback() is called after the with block finishes with an exception,
+ # the exception is still raised and must be caught
+ try:
+ with con:
+ con.execute("INSERT INTO lang(name) VALUES(?)", ("Python",))
+ except sqlite3.IntegrityError:
+ print("couldn't add Python twice")
+
+ # Connection object used as context manager only commits or rollbacks transactions,
+ # so the connection object should be closed manually
+ con.close()
+
+.. testoutput::
+ :hide:
+
+ couldn't add Python twice
.. _sqlite3-uri-tricks:
diff --git a/Doc/tools/susp-ignored.csv b/Doc/tools/susp-ignored.csv
index a3380344db3b..75049783e19c 100644
--- a/Doc/tools/susp-ignored.csv
+++ b/Doc/tools/susp-ignored.csv
@@ -212,7 +212,7 @@ library/smtplib,,:port,method must support that as well as a regular host:port
library/socket,,::,'5aef:2b::8'
library/socket,,:can,"return (can_id, can_dlc, data[:can_dlc])"
library/socket,,:len,fds.frombytes(cmsg_data[:len(cmsg_data) - (len(cmsg_data) % fds.itemsize)])
-library/sqlite3,,:year,"cur.execute(""select * from lang where first_appeared=:year"", {""year"": 1972})"
+library/sqlite3,,:year,"cur.execute(""SELECT * FROM lang WHERE first_appeared = :year"", {""year"": 1972})"
library/sqlite3,,:memory,
library/sqlite3,,:mem1,"db = ""file:mem1?mode=memory&cache=shared"""
library/sqlite3,,:nosuchdb,">>> con = sqlite3.connect(""file:nosuchdb.db?mode=rw"", uri=True)"
1
0
https://github.com/python/cpython/commit/d4d5e605cd74b198a6e21d5bf8fa053de2…
commit: d4d5e605cd74b198a6e21d5bf8fa053de2069aae
branch: 3.11
author: Miss Islington (bot) <31488909+miss-islington(a)users.noreply.github.com>
committer: miss-islington <31488909+miss-islington(a)users.noreply.github.com>
date: 2022-08-30T23:03:33-07:00
summary:
gh-96414: Inline code examples in sqlite3 docs (GH-96442)
(cherry picked from commit f7e7bf161aaec5a5cffdcec7c97e1f09e445421b)
Co-authored-by: Erlend E. Aasland <erlend.aasland(a)protonmail.com>
files:
D Doc/includes/sqlite3/adapter_point_1.py
D Doc/includes/sqlite3/adapter_point_2.py
D Doc/includes/sqlite3/blob.py
D Doc/includes/sqlite3/collation_reverse.py
D Doc/includes/sqlite3/converter_point.py
D Doc/includes/sqlite3/ctx_manager.py
D Doc/includes/sqlite3/execute_1.py
D Doc/includes/sqlite3/load_extension.py
D Doc/includes/sqlite3/md5func.py
D Doc/includes/sqlite3/mysumaggr.py
D Doc/includes/sqlite3/row_factory.py
D Doc/includes/sqlite3/shortcut_methods.py
D Doc/includes/sqlite3/sumintwindow.py
D Doc/includes/sqlite3/text_factory.py
M Doc/library/sqlite3.rst
diff --git a/Doc/includes/sqlite3/adapter_point_1.py b/Doc/includes/sqlite3/adapter_point_1.py
deleted file mode 100644
index 77daf8f16d22..000000000000
--- a/Doc/includes/sqlite3/adapter_point_1.py
+++ /dev/null
@@ -1,18 +0,0 @@
-import sqlite3
-
-class Point:
- def __init__(self, x, y):
- self.x, self.y = x, y
-
- def __conform__(self, protocol):
- if protocol is sqlite3.PrepareProtocol:
- return "%f;%f" % (self.x, self.y)
-
-con = sqlite3.connect(":memory:")
-cur = con.cursor()
-
-p = Point(4.0, -3.2)
-cur.execute("select ?", (p,))
-print(cur.fetchone()[0])
-
-con.close()
diff --git a/Doc/includes/sqlite3/adapter_point_2.py b/Doc/includes/sqlite3/adapter_point_2.py
deleted file mode 100644
index cb86331692b6..000000000000
--- a/Doc/includes/sqlite3/adapter_point_2.py
+++ /dev/null
@@ -1,19 +0,0 @@
-import sqlite3
-
-class Point:
- def __init__(self, x, y):
- self.x, self.y = x, y
-
-def adapt_point(point):
- return "%f;%f" % (point.x, point.y)
-
-sqlite3.register_adapter(Point, adapt_point)
-
-con = sqlite3.connect(":memory:")
-cur = con.cursor()
-
-p = Point(4.0, -3.2)
-cur.execute("select ?", (p,))
-print(cur.fetchone()[0])
-
-con.close()
diff --git a/Doc/includes/sqlite3/blob.py b/Doc/includes/sqlite3/blob.py
deleted file mode 100644
index ff58d6c352b6..000000000000
--- a/Doc/includes/sqlite3/blob.py
+++ /dev/null
@@ -1,19 +0,0 @@
-import sqlite3
-
-con = sqlite3.connect(":memory:")
-con.execute("create table test(blob_col blob)")
-con.execute("insert into test(blob_col) values (zeroblob(13))")
-
-# Write to our blob, using two write operations:
-with con.blobopen("test", "blob_col", 1) as blob:
- blob.write(b"hello, ")
- blob.write(b"world.")
- # Modify the first and last bytes of our blob
- blob[0] = ord("H")
- blob[-1] = ord("!")
-
-# Read the contents of our blob
-with con.blobopen("test", "blob_col", 1) as blob:
- greeting = blob.read()
-
-print(greeting) # outputs "b'Hello, world!'"
diff --git a/Doc/includes/sqlite3/collation_reverse.py b/Doc/includes/sqlite3/collation_reverse.py
deleted file mode 100644
index 3504a350a04e..000000000000
--- a/Doc/includes/sqlite3/collation_reverse.py
+++ /dev/null
@@ -1,20 +0,0 @@
-import sqlite3
-
-def collate_reverse(string1, string2):
- if string1 == string2:
- return 0
- elif string1 < string2:
- return 1
- else:
- return -1
-
-con = sqlite3.connect(":memory:")
-con.create_collation("reverse", collate_reverse)
-
-cur = con.cursor()
-cur.execute("create table test(x)")
-cur.executemany("insert into test(x) values (?)", [("a",), ("b",)])
-cur.execute("select x from test order by x collate reverse")
-for row in cur:
- print(row)
-con.close()
diff --git a/Doc/includes/sqlite3/converter_point.py b/Doc/includes/sqlite3/converter_point.py
deleted file mode 100644
index 147807a2225f..000000000000
--- a/Doc/includes/sqlite3/converter_point.py
+++ /dev/null
@@ -1,40 +0,0 @@
-import sqlite3
-
-class Point:
- def __init__(self, x, y):
- self.x, self.y = x, y
-
- def __repr__(self):
- return f"Point({self.x}, {self.y})"
-
-def adapt_point(point):
- return f"{point.x};{point.y}".encode("utf-8")
-
-def convert_point(s):
- x, y = list(map(float, s.split(b";")))
- return Point(x, y)
-
-# Register the adapter and converter
-sqlite3.register_adapter(Point, adapt_point)
-sqlite3.register_converter("point", convert_point)
-
-# 1) Parse using declared types
-p = Point(4.0, -3.2)
-con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_DECLTYPES)
-cur = con.execute("create table test(p point)")
-
-cur.execute("insert into test(p) values (?)", (p,))
-cur.execute("select p from test")
-print("with declared types:", cur.fetchone()[0])
-cur.close()
-con.close()
-
-# 2) Parse using column names
-con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_COLNAMES)
-cur = con.execute("create table test(p)")
-
-cur.execute("insert into test(p) values (?)", (p,))
-cur.execute('select p as "p [point]" from test')
-print("with column names:", cur.fetchone()[0])
-cur.close()
-con.close()
diff --git a/Doc/includes/sqlite3/ctx_manager.py b/Doc/includes/sqlite3/ctx_manager.py
deleted file mode 100644
index 2e1175ef44c6..000000000000
--- a/Doc/includes/sqlite3/ctx_manager.py
+++ /dev/null
@@ -1,20 +0,0 @@
-import sqlite3
-
-con = sqlite3.connect(":memory:")
-con.execute("create table lang (id integer primary key, name varchar unique)")
-
-# Successful, con.commit() is called automatically afterwards
-with con:
- con.execute("insert into lang(name) values (?)", ("Python",))
-
-# con.rollback() is called after the with block finishes with an exception, the
-# exception is still raised and must be caught
-try:
- with con:
- con.execute("insert into lang(name) values (?)", ("Python",))
-except sqlite3.IntegrityError:
- print("couldn't add Python twice")
-
-# Connection object used as context manager only commits or rollbacks transactions,
-# so the connection object should be closed manually
-con.close()
diff --git a/Doc/includes/sqlite3/execute_1.py b/Doc/includes/sqlite3/execute_1.py
deleted file mode 100644
index ee0000e2b94a..000000000000
--- a/Doc/includes/sqlite3/execute_1.py
+++ /dev/null
@@ -1,22 +0,0 @@
-import sqlite3
-
-con = sqlite3.connect(":memory:")
-cur = con.cursor()
-cur.execute("create table lang (name, first_appeared)")
-
-# This is the qmark style:
-cur.execute("insert into lang values (?, ?)", ("C", 1972))
-
-# The qmark style used with executemany():
-lang_list = [
- ("Fortran", 1957),
- ("Python", 1991),
- ("Go", 2009),
-]
-cur.executemany("insert into lang values (?, ?)", lang_list)
-
-# And this is the named style:
-cur.execute("select * from lang where first_appeared=:year", {"year": 1972})
-print(cur.fetchall())
-
-con.close()
diff --git a/Doc/includes/sqlite3/load_extension.py b/Doc/includes/sqlite3/load_extension.py
deleted file mode 100644
index 624cfe262f38..000000000000
--- a/Doc/includes/sqlite3/load_extension.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import sqlite3
-
-con = sqlite3.connect(":memory:")
-
-# enable extension loading
-con.enable_load_extension(True)
-
-# Load the fulltext search extension
-con.execute("select load_extension('./fts3.so')")
-
-# alternatively you can load the extension using an API call:
-# con.load_extension("./fts3.so")
-
-# disable extension loading again
-con.enable_load_extension(False)
-
-# example from SQLite wiki
-con.execute("create virtual table recipe using fts3(name, ingredients)")
-con.executescript("""
- insert into recipe (name, ingredients) values ('broccoli stew', 'broccoli peppers cheese tomatoes');
- insert into recipe (name, ingredients) values ('pumpkin stew', 'pumpkin onions garlic celery');
- insert into recipe (name, ingredients) values ('broccoli pie', 'broccoli cheese onions flour');
- insert into recipe (name, ingredients) values ('pumpkin pie', 'pumpkin sugar flour butter');
- """)
-for row in con.execute("select rowid, name, ingredients from recipe where name match 'pie'"):
- print(row)
-
-con.close()
diff --git a/Doc/includes/sqlite3/md5func.py b/Doc/includes/sqlite3/md5func.py
deleted file mode 100644
index 16dc348bf001..000000000000
--- a/Doc/includes/sqlite3/md5func.py
+++ /dev/null
@@ -1,13 +0,0 @@
-import sqlite3
-import hashlib
-
-def md5sum(t):
- return hashlib.md5(t).hexdigest()
-
-con = sqlite3.connect(":memory:")
-con.create_function("md5", 1, md5sum)
-cur = con.cursor()
-cur.execute("select md5(?)", (b"foo",))
-print(cur.fetchone()[0])
-
-con.close()
diff --git a/Doc/includes/sqlite3/mysumaggr.py b/Doc/includes/sqlite3/mysumaggr.py
deleted file mode 100644
index 11f96395b6c4..000000000000
--- a/Doc/includes/sqlite3/mysumaggr.py
+++ /dev/null
@@ -1,22 +0,0 @@
-import sqlite3
-
-class MySum:
- def __init__(self):
- self.count = 0
-
- def step(self, value):
- self.count += value
-
- def finalize(self):
- return self.count
-
-con = sqlite3.connect(":memory:")
-con.create_aggregate("mysum", 1, MySum)
-cur = con.cursor()
-cur.execute("create table test(i)")
-cur.execute("insert into test(i) values (1)")
-cur.execute("insert into test(i) values (2)")
-cur.execute("select mysum(i) from test")
-print(cur.fetchone()[0])
-
-con.close()
diff --git a/Doc/includes/sqlite3/row_factory.py b/Doc/includes/sqlite3/row_factory.py
deleted file mode 100644
index 9de6e7b1b905..000000000000
--- a/Doc/includes/sqlite3/row_factory.py
+++ /dev/null
@@ -1,15 +0,0 @@
-import sqlite3
-
-def dict_factory(cursor, row):
- d = {}
- for idx, col in enumerate(cursor.description):
- d[col[0]] = row[idx]
- return d
-
-con = sqlite3.connect(":memory:")
-con.row_factory = dict_factory
-cur = con.cursor()
-cur.execute("select 1 as a")
-print(cur.fetchone()["a"])
-
-con.close()
diff --git a/Doc/includes/sqlite3/shortcut_methods.py b/Doc/includes/sqlite3/shortcut_methods.py
deleted file mode 100644
index 48ea6fad15a8..000000000000
--- a/Doc/includes/sqlite3/shortcut_methods.py
+++ /dev/null
@@ -1,24 +0,0 @@
-import sqlite3
-
-langs = [
- ("C++", 1985),
- ("Objective-C", 1984),
-]
-
-con = sqlite3.connect(":memory:")
-
-# Create the table
-con.execute("create table lang(name, first_appeared)")
-
-# Fill the table
-con.executemany("insert into lang(name, first_appeared) values (?, ?)", langs)
-
-# Print the table contents
-for row in con.execute("select name, first_appeared from lang"):
- print(row)
-
-print("I just deleted", con.execute("delete from lang").rowcount, "rows")
-
-# close is not a shortcut method and it's not called automatically,
-# so the connection object should be closed manually
-con.close()
diff --git a/Doc/includes/sqlite3/sumintwindow.py b/Doc/includes/sqlite3/sumintwindow.py
deleted file mode 100644
index 0e915d6cc6ae..000000000000
--- a/Doc/includes/sqlite3/sumintwindow.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# Example taken from https://www.sqlite.org/windowfunctions.html#udfwinfunc
-import sqlite3
-
-
-class WindowSumInt:
- def __init__(self):
- self.count = 0
-
- def step(self, value):
- """Adds a row to the current window."""
- self.count += value
-
- def value(self):
- """Returns the current value of the aggregate."""
- return self.count
-
- def inverse(self, value):
- """Removes a row from the current window."""
- self.count -= value
-
- def finalize(self):
- """Returns the final value of the aggregate.
-
- Any clean-up actions should be placed here.
- """
- return self.count
-
-
-con = sqlite3.connect(":memory:")
-cur = con.execute("create table test(x, y)")
-values = [
- ("a", 4),
- ("b", 5),
- ("c", 3),
- ("d", 8),
- ("e", 1),
-]
-cur.executemany("insert into test values(?, ?)", values)
-con.create_window_function("sumint", 1, WindowSumInt)
-cur.execute("""
- select x, sumint(y) over (
- order by x rows between 1 preceding and 1 following
- ) as sum_y
- from test order by x
-""")
-print(cur.fetchall())
diff --git a/Doc/includes/sqlite3/text_factory.py b/Doc/includes/sqlite3/text_factory.py
deleted file mode 100644
index c0d87cd55911..000000000000
--- a/Doc/includes/sqlite3/text_factory.py
+++ /dev/null
@@ -1,29 +0,0 @@
-import sqlite3
-
-con = sqlite3.connect(":memory:")
-cur = con.cursor()
-
-AUSTRIA = "Österreich"
-
-# by default, rows are returned as str
-cur.execute("select ?", (AUSTRIA,))
-row = cur.fetchone()
-assert row[0] == AUSTRIA
-
-# but we can make sqlite3 always return bytestrings ...
-con.text_factory = bytes
-cur.execute("select ?", (AUSTRIA,))
-row = cur.fetchone()
-assert type(row[0]) is bytes
-# the bytestrings will be encoded in UTF-8, unless you stored garbage in the
-# database ...
-assert row[0] == AUSTRIA.encode("utf-8")
-
-# we can also implement a custom text_factory ...
-# here we implement one that appends "foo" to all strings
-con.text_factory = lambda x: x.decode("utf-8") + "foo"
-cur.execute("select ?", ("bar",))
-row = cur.fetchone()
-assert row[0] == "barfoo"
-
-con.close()
diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst
index 2c58ef71c33f..fc288f6f7399 100644
--- a/Doc/library/sqlite3.rst
+++ b/Doc/library/sqlite3.rst
@@ -588,7 +588,25 @@ Connection objects
Example:
- .. literalinclude:: ../includes/sqlite3/row_factory.py
+ .. testcode::
+
+ def dict_factory(cursor, row):
+ d = {}
+ for idx, col in enumerate(cursor.description):
+ d[col[0]] = row[idx]
+ return d
+
+ con = sqlite3.connect(":memory:")
+ con.row_factory = dict_factory
+ cur = con.execute("SELECT 1 AS a")
+ print(cur.fetchone()["a"])
+
+ con.close()
+
+ .. testoutput::
+ :hide:
+
+ 1
If returning a tuple doesn't suffice and you want name-based access to
columns, you should consider setting :attr:`row_factory` to the
@@ -609,7 +627,35 @@ Connection objects
Example:
- .. literalinclude:: ../includes/sqlite3/text_factory.py
+ .. testcode::
+
+ con = sqlite3.connect(":memory:")
+ cur = con.cursor()
+
+ AUSTRIA = "Österreich"
+
+ # by default, rows are returned as str
+ cur.execute("SELECT ?", (AUSTRIA,))
+ row = cur.fetchone()
+ assert row[0] == AUSTRIA
+
+ # but we can make sqlite3 always return bytestrings ...
+ con.text_factory = bytes
+ cur.execute("SELECT ?", (AUSTRIA,))
+ row = cur.fetchone()
+ assert type(row[0]) is bytes
+ # the bytestrings will be encoded in UTF-8, unless you stored garbage in the
+ # database ...
+ assert row[0] == AUSTRIA.encode("utf-8")
+
+ # we can also implement a custom text_factory ...
+ # here we implement one that appends "foo" to all strings
+ con.text_factory = lambda x: x.decode("utf-8") + "foo"
+ cur.execute("SELECT ?", ("bar",))
+ row = cur.fetchone()
+ assert row[0] == "barfoo"
+
+ con.close()
.. attribute:: total_changes
@@ -725,7 +771,16 @@ Connection objects
Example:
- .. literalinclude:: ../includes/sqlite3/md5func.py
+ .. doctest::
+
+ >>> import hashlib
+ >>> def md5sum(t):
+ ... return hashlib.md5(t).hexdigest()
+ >>> con = sqlite3.connect(":memory:")
+ >>> con.create_function("md5", 1, md5sum)
+ >>> for row in con.execute("SELECT md5(?)", (b"foo",)):
+ ... print(row)
+ ('acbd18db4cc2f85cedef654fccc4a4d8',)
.. method:: create_aggregate(name, /, n_arg, aggregate_class)
@@ -754,7 +809,32 @@ Connection objects
Example:
- .. literalinclude:: ../includes/sqlite3/mysumaggr.py
+ .. testcode::
+
+ class MySum:
+ def __init__(self):
+ self.count = 0
+
+ def step(self, value):
+ self.count += value
+
+ def finalize(self):
+ return self.count
+
+ con = sqlite3.connect(":memory:")
+ con.create_aggregate("mysum", 1, MySum)
+ cur = con.execute("CREATE TABLE test(i)")
+ cur.execute("INSERT INTO test(i) VALUES(1)")
+ cur.execute("INSERT INTO test(i) VALUES(2)")
+ cur.execute("SELECT mysum(i) FROM test")
+ print(cur.fetchone()[0])
+
+ con.close()
+
+ .. testoutput::
+ :hide:
+
+ 3
.. method:: create_window_function(name, num_params, aggregate_class, /)
@@ -792,8 +872,56 @@ Connection objects
Example:
- .. literalinclude:: ../includes/sqlite3/sumintwindow.py
+ .. testcode::
+
+ # Example taken from https://www.sqlite.org/windowfunctions.html#udfwinfunc
+ class WindowSumInt:
+ def __init__(self):
+ self.count = 0
+ def step(self, value):
+ """Add a row to the current window."""
+ self.count += value
+
+ def value(self):
+ """Return the current value of the aggregate."""
+ return self.count
+
+ def inverse(self, value):
+ """Remove a row from the current window."""
+ self.count -= value
+
+ def finalize(self):
+ """Return the final value of the aggregate.
+
+ Any clean-up actions should be placed here.
+ """
+ return self.count
+
+
+ con = sqlite3.connect(":memory:")
+ cur = con.execute("CREATE TABLE test(x, y)")
+ values = [
+ ("a", 4),
+ ("b", 5),
+ ("c", 3),
+ ("d", 8),
+ ("e", 1),
+ ]
+ cur.executemany("INSERT INTO test VALUES(?, ?)", values)
+ con.create_window_function("sumint", 1, WindowSumInt)
+ cur.execute("""
+ SELECT x, sumint(y) OVER (
+ ORDER BY x ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING
+ ) AS sum_y
+ FROM test ORDER BY x
+ """)
+ print(cur.fetchall())
+
+ .. testoutput::
+ :hide:
+
+ [('a', 9), ('b', 12), ('c', 16), ('d', 12), ('e', 9)]
.. method:: create_collation(name, callable)
@@ -807,7 +935,31 @@ Connection objects
The following example shows a reverse sorting collation:
- .. literalinclude:: ../includes/sqlite3/collation_reverse.py
+ .. testcode::
+
+ def collate_reverse(string1, string2):
+ if string1 == string2:
+ return 0
+ elif string1 < string2:
+ return 1
+ else:
+ return -1
+
+ con = sqlite3.connect(":memory:")
+ con.create_collation("reverse", collate_reverse)
+
+ cur = con.execute("CREATE TABLE test(x)")
+ cur.executemany("INSERT INTO test(x) VALUES(?)", [("a",), ("b",)])
+ cur.execute("SELECT x FROM test ORDER BY x COLLATE reverse")
+ for row in cur:
+ print(row)
+ con.close()
+
+ .. testoutput::
+ :hide:
+
+ ('b',)
+ ('a',)
Remove a collation function by setting *callable* to ``None``.
@@ -912,7 +1064,43 @@ Connection objects
.. versionchanged:: 3.10
Added the ``sqlite3.enable_load_extension`` auditing event.
- .. literalinclude:: ../includes/sqlite3/load_extension.py
+ .. testsetup:: sqlite3.loadext
+
+ import sqlite3
+ con = sqlite3.connect(":memory:")
+
+ .. testcode:: sqlite3.loadext
+ :skipif: True # not testable at the moment
+
+ con.enable_load_extension(True)
+
+ # Load the fulltext search extension
+ con.execute("select load_extension('./fts3.so')")
+
+ # alternatively you can load the extension using an API call:
+ # con.load_extension("./fts3.so")
+
+ # disable extension loading again
+ con.enable_load_extension(False)
+
+ # example from SQLite wiki
+ con.execute("CREATE VIRTUAL TABLE recipe USING fts3(name, ingredients)")
+ con.executescript("""
+ INSERT INTO recipe (name, ingredients) VALUES('broccoli stew', 'broccoli peppers cheese tomatoes');
+ INSERT INTO recipe (name, ingredients) VALUES('pumpkin stew', 'pumpkin onions garlic celery');
+ INSERT INTO recipe (name, ingredients) VALUES('broccoli pie', 'broccoli cheese onions flour');
+ INSERT INTO recipe (name, ingredients) VALUES('pumpkin pie', 'pumpkin sugar flour butter');
+ """)
+ for row in con.execute("SELECT rowid, name, ingredients FROM recipe WHERE name MATCH 'pie'"):
+ print(row)
+
+ con.close()
+
+ .. testoutput:: sqlite3.loadext
+ :hide:
+
+ (2, 'broccoli pie', 'broccoli cheese onions flour')
+ (3, 'pumpkin pie', 'pumpkin sugar flour butter')
.. method:: load_extension(path, /)
@@ -1387,7 +1575,30 @@ Blob objects
Use the :class:`Blob` as a :term:`context manager` to ensure that the blob
handle is closed after use.
- .. literalinclude:: ../includes/sqlite3/blob.py
+ .. testcode::
+
+ con = sqlite3.connect(":memory:")
+ con.execute("CREATE TABLE test(blob_col blob)")
+ con.execute("INSERT INTO test(blob_col) VALUES(zeroblob(13))")
+
+ # Write to our blob, using two write operations:
+ with con.blobopen("test", "blob_col", 1) as blob:
+ blob.write(b"hello, ")
+ blob.write(b"world.")
+ # Modify the first and last bytes of our blob
+ blob[0] = ord("H")
+ blob[-1] = ord("!")
+
+ # Read the contents of our blob
+ with con.blobopen("test", "blob_col", 1) as blob:
+ greeting = blob.read()
+
+ print(greeting) # outputs "b'Hello, world!'"
+
+ .. testoutput::
+ :hide:
+
+ b'Hello, world!'
.. method:: close()
@@ -1640,7 +1851,30 @@ placeholders (named style). For the qmark style, ``parameters`` must be a
keys for all named parameters. Any extra items are ignored. Here's an example of
both styles:
-.. literalinclude:: ../includes/sqlite3/execute_1.py
+.. testcode::
+
+ con = sqlite3.connect(":memory:")
+ cur = con.execute("CREATE TABLE lang(name, first_appeared)")
+
+ # This is the qmark style:
+ cur.execute("INSERT INTO lang VALUES(?, ?)", ("C", 1972))
+
+ # The qmark style used with executemany():
+ lang_list = [
+ ("Fortran", 1957),
+ ("Python", 1991),
+ ("Go", 2009),
+ ]
+ cur.executemany("INSERT INTO lang VALUES(?, ?)", lang_list)
+
+ # And this is the named style:
+ cur.execute("SELECT * FROM lang WHERE first_appeared = :year", {"year": 1972})
+ print(cur.fetchall())
+
+.. testoutput::
+ :hide:
+
+ [('C', 1972)]
.. _sqlite3-adapters:
@@ -1674,7 +1908,26 @@ This can be implemented by adding a ``__conform__(self, protocol)``
method which returns the adapted value.
The object passed to *protocol* will be of type :class:`PrepareProtocol`.
-.. literalinclude:: ../includes/sqlite3/adapter_point_1.py
+.. testcode::
+
+ class Point:
+ def __init__(self, x, y):
+ self.x, self.y = x, y
+
+ def __conform__(self, protocol):
+ if protocol is sqlite3.PrepareProtocol:
+ return f"{self.x};{self.y}"
+
+ con = sqlite3.connect(":memory:")
+ cur = con.cursor()
+
+ cur.execute("SELECT ?", (Point(4.0, -3.2),))
+ print(cur.fetchone()[0])
+
+.. testoutput::
+ :hide:
+
+ 4.0;-3.2
How to register adapter callables
@@ -1684,7 +1937,27 @@ The other possibility is to create a function that converts the Python object
to an SQLite-compatible type.
This function can then be registered using :func:`register_adapter`.
-.. literalinclude:: ../includes/sqlite3/adapter_point_2.py
+.. testcode::
+
+ class Point:
+ def __init__(self, x, y):
+ self.x, self.y = x, y
+
+ def adapt_point(point):
+ return f"{point.x};{point.y}"
+
+ sqlite3.register_adapter(Point, adapt_point)
+
+ con = sqlite3.connect(":memory:")
+ cur = con.cursor()
+
+ cur.execute("SELECT ?", (Point(1.0, 2.5),))
+ print(cur.fetchone()[0])
+
+.. testoutput::
+ :hide:
+
+ 1.0;2.5
.. _sqlite3-converters:
@@ -1726,7 +1999,50 @@ of :func:`connect`. There are three options:
The following example illustrates the implicit and explicit approaches:
-.. literalinclude:: ../includes/sqlite3/converter_point.py
+.. testcode::
+
+ class Point:
+ def __init__(self, x, y):
+ self.x, self.y = x, y
+
+ def __repr__(self):
+ return f"Point({self.x}, {self.y})"
+
+ def adapt_point(point):
+ return f"{point.x};{point.y}".encode("utf-8")
+
+ def convert_point(s):
+ x, y = list(map(float, s.split(b";")))
+ return Point(x, y)
+
+ # Register the adapter and converter
+ sqlite3.register_adapter(Point, adapt_point)
+ sqlite3.register_converter("point", convert_point)
+
+ # 1) Parse using declared types
+ p = Point(4.0, -3.2)
+ con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_DECLTYPES)
+ cur = con.execute("CREATE TABLE test(p point)")
+
+ cur.execute("INSERT INTO test(p) VALUES(?)", (p,))
+ cur.execute("SELECT p FROM test")
+ print("with declared types:", cur.fetchone()[0])
+ cur.close()
+ con.close()
+
+ # 2) Parse using column names
+ con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_COLNAMES)
+ cur = con.execute("CREATE TABLE test(p)")
+
+ cur.execute("INSERT INTO test(p) VALUES(?)", (p,))
+ cur.execute('SELECT p AS "p [point]" FROM test')
+ print("with column names:", cur.fetchone()[0])
+
+.. testoutput::
+ :hide:
+
+ with declared types: Point(4.0, -3.2)
+ with column names: Point(4.0, -3.2)
.. _sqlite3-adapter-converter-recipes:
@@ -1788,7 +2104,33 @@ objects are created implicitly and these shortcut methods return the cursor
objects. This way, you can execute a ``SELECT`` statement and iterate over it
directly using only a single call on the :class:`Connection` object.
-.. literalinclude:: ../includes/sqlite3/shortcut_methods.py
+.. testcode::
+
+ # Create and fill the table.
+ con = sqlite3.connect(":memory:")
+ con.execute("CREATE TABLE lang(name, first_appeared)")
+ data = [
+ ("C++", 1985),
+ ("Objective-C", 1984),
+ ]
+ con.executemany("INSERT INTO lang(name, first_appeared) VALUES(?, ?)", data)
+
+ # Print the table contents
+ for row in con.execute("SELECT name, first_appeared FROM lang"):
+ print(row)
+
+ print("I just deleted", con.execute("DELETE FROM lang").rowcount, "rows")
+
+ # close() is not a shortcut method and it's not called automatically;
+ # the connection object should be closed manually
+ con.close()
+
+.. testoutput::
+ :hide:
+
+ ('C++', 1985)
+ ('Objective-C', 1984)
+ I just deleted 2 rows
.. _sqlite3-connection-context-manager:
@@ -1813,7 +2155,31 @@ the context manager is a no-op.
The context manager neither implicitly opens a new transaction
nor closes the connection.
-.. literalinclude:: ../includes/sqlite3/ctx_manager.py
+.. testcode::
+
+ con = sqlite3.connect(":memory:")
+ con.execute("CREATE TABLE lang(id INTEGER PRIMARY KEY, name VARCHAR UNIQUE)")
+
+ # Successful, con.commit() is called automatically afterwards
+ with con:
+ con.execute("INSERT INTO lang(name) VALUES(?)", ("Python",))
+
+ # con.rollback() is called after the with block finishes with an exception,
+ # the exception is still raised and must be caught
+ try:
+ with con:
+ con.execute("INSERT INTO lang(name) VALUES(?)", ("Python",))
+ except sqlite3.IntegrityError:
+ print("couldn't add Python twice")
+
+ # Connection object used as context manager only commits or rollbacks transactions,
+ # so the connection object should be closed manually
+ con.close()
+
+.. testoutput::
+ :hide:
+
+ couldn't add Python twice
.. _sqlite3-uri-tricks:
1
0
https://github.com/python/cpython/commit/f7e7bf161aaec5a5cffdcec7c97e1f09e4…
commit: f7e7bf161aaec5a5cffdcec7c97e1f09e445421b
branch: main
author: Erlend E. Aasland <erlend.aasland(a)protonmail.com>
committer: erlend-aasland <erlend.aasland(a)protonmail.com>
date: 2022-08-31T07:54:54+02:00
summary:
gh-96414: Inline code examples in sqlite3 docs (#96442)
files:
D Doc/includes/sqlite3/adapter_point_1.py
D Doc/includes/sqlite3/adapter_point_2.py
D Doc/includes/sqlite3/blob.py
D Doc/includes/sqlite3/collation_reverse.py
D Doc/includes/sqlite3/converter_point.py
D Doc/includes/sqlite3/ctx_manager.py
D Doc/includes/sqlite3/execute_1.py
D Doc/includes/sqlite3/load_extension.py
D Doc/includes/sqlite3/md5func.py
D Doc/includes/sqlite3/mysumaggr.py
D Doc/includes/sqlite3/row_factory.py
D Doc/includes/sqlite3/shortcut_methods.py
D Doc/includes/sqlite3/sumintwindow.py
D Doc/includes/sqlite3/text_factory.py
M Doc/library/sqlite3.rst
diff --git a/Doc/includes/sqlite3/adapter_point_1.py b/Doc/includes/sqlite3/adapter_point_1.py
deleted file mode 100644
index 77daf8f16d22..000000000000
--- a/Doc/includes/sqlite3/adapter_point_1.py
+++ /dev/null
@@ -1,18 +0,0 @@
-import sqlite3
-
-class Point:
- def __init__(self, x, y):
- self.x, self.y = x, y
-
- def __conform__(self, protocol):
- if protocol is sqlite3.PrepareProtocol:
- return "%f;%f" % (self.x, self.y)
-
-con = sqlite3.connect(":memory:")
-cur = con.cursor()
-
-p = Point(4.0, -3.2)
-cur.execute("select ?", (p,))
-print(cur.fetchone()[0])
-
-con.close()
diff --git a/Doc/includes/sqlite3/adapter_point_2.py b/Doc/includes/sqlite3/adapter_point_2.py
deleted file mode 100644
index cb86331692b6..000000000000
--- a/Doc/includes/sqlite3/adapter_point_2.py
+++ /dev/null
@@ -1,19 +0,0 @@
-import sqlite3
-
-class Point:
- def __init__(self, x, y):
- self.x, self.y = x, y
-
-def adapt_point(point):
- return "%f;%f" % (point.x, point.y)
-
-sqlite3.register_adapter(Point, adapt_point)
-
-con = sqlite3.connect(":memory:")
-cur = con.cursor()
-
-p = Point(4.0, -3.2)
-cur.execute("select ?", (p,))
-print(cur.fetchone()[0])
-
-con.close()
diff --git a/Doc/includes/sqlite3/blob.py b/Doc/includes/sqlite3/blob.py
deleted file mode 100644
index ff58d6c352b6..000000000000
--- a/Doc/includes/sqlite3/blob.py
+++ /dev/null
@@ -1,19 +0,0 @@
-import sqlite3
-
-con = sqlite3.connect(":memory:")
-con.execute("create table test(blob_col blob)")
-con.execute("insert into test(blob_col) values (zeroblob(13))")
-
-# Write to our blob, using two write operations:
-with con.blobopen("test", "blob_col", 1) as blob:
- blob.write(b"hello, ")
- blob.write(b"world.")
- # Modify the first and last bytes of our blob
- blob[0] = ord("H")
- blob[-1] = ord("!")
-
-# Read the contents of our blob
-with con.blobopen("test", "blob_col", 1) as blob:
- greeting = blob.read()
-
-print(greeting) # outputs "b'Hello, world!'"
diff --git a/Doc/includes/sqlite3/collation_reverse.py b/Doc/includes/sqlite3/collation_reverse.py
deleted file mode 100644
index 3504a350a04e..000000000000
--- a/Doc/includes/sqlite3/collation_reverse.py
+++ /dev/null
@@ -1,20 +0,0 @@
-import sqlite3
-
-def collate_reverse(string1, string2):
- if string1 == string2:
- return 0
- elif string1 < string2:
- return 1
- else:
- return -1
-
-con = sqlite3.connect(":memory:")
-con.create_collation("reverse", collate_reverse)
-
-cur = con.cursor()
-cur.execute("create table test(x)")
-cur.executemany("insert into test(x) values (?)", [("a",), ("b",)])
-cur.execute("select x from test order by x collate reverse")
-for row in cur:
- print(row)
-con.close()
diff --git a/Doc/includes/sqlite3/converter_point.py b/Doc/includes/sqlite3/converter_point.py
deleted file mode 100644
index 147807a2225f..000000000000
--- a/Doc/includes/sqlite3/converter_point.py
+++ /dev/null
@@ -1,40 +0,0 @@
-import sqlite3
-
-class Point:
- def __init__(self, x, y):
- self.x, self.y = x, y
-
- def __repr__(self):
- return f"Point({self.x}, {self.y})"
-
-def adapt_point(point):
- return f"{point.x};{point.y}".encode("utf-8")
-
-def convert_point(s):
- x, y = list(map(float, s.split(b";")))
- return Point(x, y)
-
-# Register the adapter and converter
-sqlite3.register_adapter(Point, adapt_point)
-sqlite3.register_converter("point", convert_point)
-
-# 1) Parse using declared types
-p = Point(4.0, -3.2)
-con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_DECLTYPES)
-cur = con.execute("create table test(p point)")
-
-cur.execute("insert into test(p) values (?)", (p,))
-cur.execute("select p from test")
-print("with declared types:", cur.fetchone()[0])
-cur.close()
-con.close()
-
-# 2) Parse using column names
-con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_COLNAMES)
-cur = con.execute("create table test(p)")
-
-cur.execute("insert into test(p) values (?)", (p,))
-cur.execute('select p as "p [point]" from test')
-print("with column names:", cur.fetchone()[0])
-cur.close()
-con.close()
diff --git a/Doc/includes/sqlite3/ctx_manager.py b/Doc/includes/sqlite3/ctx_manager.py
deleted file mode 100644
index 2e1175ef44c6..000000000000
--- a/Doc/includes/sqlite3/ctx_manager.py
+++ /dev/null
@@ -1,20 +0,0 @@
-import sqlite3
-
-con = sqlite3.connect(":memory:")
-con.execute("create table lang (id integer primary key, name varchar unique)")
-
-# Successful, con.commit() is called automatically afterwards
-with con:
- con.execute("insert into lang(name) values (?)", ("Python",))
-
-# con.rollback() is called after the with block finishes with an exception, the
-# exception is still raised and must be caught
-try:
- with con:
- con.execute("insert into lang(name) values (?)", ("Python",))
-except sqlite3.IntegrityError:
- print("couldn't add Python twice")
-
-# Connection object used as context manager only commits or rollbacks transactions,
-# so the connection object should be closed manually
-con.close()
diff --git a/Doc/includes/sqlite3/execute_1.py b/Doc/includes/sqlite3/execute_1.py
deleted file mode 100644
index ee0000e2b94a..000000000000
--- a/Doc/includes/sqlite3/execute_1.py
+++ /dev/null
@@ -1,22 +0,0 @@
-import sqlite3
-
-con = sqlite3.connect(":memory:")
-cur = con.cursor()
-cur.execute("create table lang (name, first_appeared)")
-
-# This is the qmark style:
-cur.execute("insert into lang values (?, ?)", ("C", 1972))
-
-# The qmark style used with executemany():
-lang_list = [
- ("Fortran", 1957),
- ("Python", 1991),
- ("Go", 2009),
-]
-cur.executemany("insert into lang values (?, ?)", lang_list)
-
-# And this is the named style:
-cur.execute("select * from lang where first_appeared=:year", {"year": 1972})
-print(cur.fetchall())
-
-con.close()
diff --git a/Doc/includes/sqlite3/load_extension.py b/Doc/includes/sqlite3/load_extension.py
deleted file mode 100644
index 624cfe262f38..000000000000
--- a/Doc/includes/sqlite3/load_extension.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import sqlite3
-
-con = sqlite3.connect(":memory:")
-
-# enable extension loading
-con.enable_load_extension(True)
-
-# Load the fulltext search extension
-con.execute("select load_extension('./fts3.so')")
-
-# alternatively you can load the extension using an API call:
-# con.load_extension("./fts3.so")
-
-# disable extension loading again
-con.enable_load_extension(False)
-
-# example from SQLite wiki
-con.execute("create virtual table recipe using fts3(name, ingredients)")
-con.executescript("""
- insert into recipe (name, ingredients) values ('broccoli stew', 'broccoli peppers cheese tomatoes');
- insert into recipe (name, ingredients) values ('pumpkin stew', 'pumpkin onions garlic celery');
- insert into recipe (name, ingredients) values ('broccoli pie', 'broccoli cheese onions flour');
- insert into recipe (name, ingredients) values ('pumpkin pie', 'pumpkin sugar flour butter');
- """)
-for row in con.execute("select rowid, name, ingredients from recipe where name match 'pie'"):
- print(row)
-
-con.close()
diff --git a/Doc/includes/sqlite3/md5func.py b/Doc/includes/sqlite3/md5func.py
deleted file mode 100644
index 16dc348bf001..000000000000
--- a/Doc/includes/sqlite3/md5func.py
+++ /dev/null
@@ -1,13 +0,0 @@
-import sqlite3
-import hashlib
-
-def md5sum(t):
- return hashlib.md5(t).hexdigest()
-
-con = sqlite3.connect(":memory:")
-con.create_function("md5", 1, md5sum)
-cur = con.cursor()
-cur.execute("select md5(?)", (b"foo",))
-print(cur.fetchone()[0])
-
-con.close()
diff --git a/Doc/includes/sqlite3/mysumaggr.py b/Doc/includes/sqlite3/mysumaggr.py
deleted file mode 100644
index 11f96395b6c4..000000000000
--- a/Doc/includes/sqlite3/mysumaggr.py
+++ /dev/null
@@ -1,22 +0,0 @@
-import sqlite3
-
-class MySum:
- def __init__(self):
- self.count = 0
-
- def step(self, value):
- self.count += value
-
- def finalize(self):
- return self.count
-
-con = sqlite3.connect(":memory:")
-con.create_aggregate("mysum", 1, MySum)
-cur = con.cursor()
-cur.execute("create table test(i)")
-cur.execute("insert into test(i) values (1)")
-cur.execute("insert into test(i) values (2)")
-cur.execute("select mysum(i) from test")
-print(cur.fetchone()[0])
-
-con.close()
diff --git a/Doc/includes/sqlite3/row_factory.py b/Doc/includes/sqlite3/row_factory.py
deleted file mode 100644
index 9de6e7b1b905..000000000000
--- a/Doc/includes/sqlite3/row_factory.py
+++ /dev/null
@@ -1,15 +0,0 @@
-import sqlite3
-
-def dict_factory(cursor, row):
- d = {}
- for idx, col in enumerate(cursor.description):
- d[col[0]] = row[idx]
- return d
-
-con = sqlite3.connect(":memory:")
-con.row_factory = dict_factory
-cur = con.cursor()
-cur.execute("select 1 as a")
-print(cur.fetchone()["a"])
-
-con.close()
diff --git a/Doc/includes/sqlite3/shortcut_methods.py b/Doc/includes/sqlite3/shortcut_methods.py
deleted file mode 100644
index 48ea6fad15a8..000000000000
--- a/Doc/includes/sqlite3/shortcut_methods.py
+++ /dev/null
@@ -1,24 +0,0 @@
-import sqlite3
-
-langs = [
- ("C++", 1985),
- ("Objective-C", 1984),
-]
-
-con = sqlite3.connect(":memory:")
-
-# Create the table
-con.execute("create table lang(name, first_appeared)")
-
-# Fill the table
-con.executemany("insert into lang(name, first_appeared) values (?, ?)", langs)
-
-# Print the table contents
-for row in con.execute("select name, first_appeared from lang"):
- print(row)
-
-print("I just deleted", con.execute("delete from lang").rowcount, "rows")
-
-# close is not a shortcut method and it's not called automatically,
-# so the connection object should be closed manually
-con.close()
diff --git a/Doc/includes/sqlite3/sumintwindow.py b/Doc/includes/sqlite3/sumintwindow.py
deleted file mode 100644
index 0e915d6cc6ae..000000000000
--- a/Doc/includes/sqlite3/sumintwindow.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# Example taken from https://www.sqlite.org/windowfunctions.html#udfwinfunc
-import sqlite3
-
-
-class WindowSumInt:
- def __init__(self):
- self.count = 0
-
- def step(self, value):
- """Adds a row to the current window."""
- self.count += value
-
- def value(self):
- """Returns the current value of the aggregate."""
- return self.count
-
- def inverse(self, value):
- """Removes a row from the current window."""
- self.count -= value
-
- def finalize(self):
- """Returns the final value of the aggregate.
-
- Any clean-up actions should be placed here.
- """
- return self.count
-
-
-con = sqlite3.connect(":memory:")
-cur = con.execute("create table test(x, y)")
-values = [
- ("a", 4),
- ("b", 5),
- ("c", 3),
- ("d", 8),
- ("e", 1),
-]
-cur.executemany("insert into test values(?, ?)", values)
-con.create_window_function("sumint", 1, WindowSumInt)
-cur.execute("""
- select x, sumint(y) over (
- order by x rows between 1 preceding and 1 following
- ) as sum_y
- from test order by x
-""")
-print(cur.fetchall())
diff --git a/Doc/includes/sqlite3/text_factory.py b/Doc/includes/sqlite3/text_factory.py
deleted file mode 100644
index c0d87cd55911..000000000000
--- a/Doc/includes/sqlite3/text_factory.py
+++ /dev/null
@@ -1,29 +0,0 @@
-import sqlite3
-
-con = sqlite3.connect(":memory:")
-cur = con.cursor()
-
-AUSTRIA = "Österreich"
-
-# by default, rows are returned as str
-cur.execute("select ?", (AUSTRIA,))
-row = cur.fetchone()
-assert row[0] == AUSTRIA
-
-# but we can make sqlite3 always return bytestrings ...
-con.text_factory = bytes
-cur.execute("select ?", (AUSTRIA,))
-row = cur.fetchone()
-assert type(row[0]) is bytes
-# the bytestrings will be encoded in UTF-8, unless you stored garbage in the
-# database ...
-assert row[0] == AUSTRIA.encode("utf-8")
-
-# we can also implement a custom text_factory ...
-# here we implement one that appends "foo" to all strings
-con.text_factory = lambda x: x.decode("utf-8") + "foo"
-cur.execute("select ?", ("bar",))
-row = cur.fetchone()
-assert row[0] == "barfoo"
-
-con.close()
diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst
index 58343b1a0f77..7ac7162c2527 100644
--- a/Doc/library/sqlite3.rst
+++ b/Doc/library/sqlite3.rst
@@ -601,7 +601,25 @@ Connection objects
Example:
- .. literalinclude:: ../includes/sqlite3/row_factory.py
+ .. testcode::
+
+ def dict_factory(cursor, row):
+ d = {}
+ for idx, col in enumerate(cursor.description):
+ d[col[0]] = row[idx]
+ return d
+
+ con = sqlite3.connect(":memory:")
+ con.row_factory = dict_factory
+ cur = con.execute("SELECT 1 AS a")
+ print(cur.fetchone()["a"])
+
+ con.close()
+
+ .. testoutput::
+ :hide:
+
+ 1
If returning a tuple doesn't suffice and you want name-based access to
columns, you should consider setting :attr:`row_factory` to the
@@ -622,7 +640,35 @@ Connection objects
Example:
- .. literalinclude:: ../includes/sqlite3/text_factory.py
+ .. testcode::
+
+ con = sqlite3.connect(":memory:")
+ cur = con.cursor()
+
+ AUSTRIA = "Österreich"
+
+ # by default, rows are returned as str
+ cur.execute("SELECT ?", (AUSTRIA,))
+ row = cur.fetchone()
+ assert row[0] == AUSTRIA
+
+ # but we can make sqlite3 always return bytestrings ...
+ con.text_factory = bytes
+ cur.execute("SELECT ?", (AUSTRIA,))
+ row = cur.fetchone()
+ assert type(row[0]) is bytes
+ # the bytestrings will be encoded in UTF-8, unless you stored garbage in the
+ # database ...
+ assert row[0] == AUSTRIA.encode("utf-8")
+
+ # we can also implement a custom text_factory ...
+ # here we implement one that appends "foo" to all strings
+ con.text_factory = lambda x: x.decode("utf-8") + "foo"
+ cur.execute("SELECT ?", ("bar",))
+ row = cur.fetchone()
+ assert row[0] == "barfoo"
+
+ con.close()
.. attribute:: total_changes
@@ -738,7 +784,16 @@ Connection objects
Example:
- .. literalinclude:: ../includes/sqlite3/md5func.py
+ .. doctest::
+
+ >>> import hashlib
+ >>> def md5sum(t):
+ ... return hashlib.md5(t).hexdigest()
+ >>> con = sqlite3.connect(":memory:")
+ >>> con.create_function("md5", 1, md5sum)
+ >>> for row in con.execute("SELECT md5(?)", (b"foo",)):
+ ... print(row)
+ ('acbd18db4cc2f85cedef654fccc4a4d8',)
.. method:: create_aggregate(name, /, n_arg, aggregate_class)
@@ -767,7 +822,32 @@ Connection objects
Example:
- .. literalinclude:: ../includes/sqlite3/mysumaggr.py
+ .. testcode::
+
+ class MySum:
+ def __init__(self):
+ self.count = 0
+
+ def step(self, value):
+ self.count += value
+
+ def finalize(self):
+ return self.count
+
+ con = sqlite3.connect(":memory:")
+ con.create_aggregate("mysum", 1, MySum)
+ cur = con.execute("CREATE TABLE test(i)")
+ cur.execute("INSERT INTO test(i) VALUES(1)")
+ cur.execute("INSERT INTO test(i) VALUES(2)")
+ cur.execute("SELECT mysum(i) FROM test")
+ print(cur.fetchone()[0])
+
+ con.close()
+
+ .. testoutput::
+ :hide:
+
+ 3
.. method:: create_window_function(name, num_params, aggregate_class, /)
@@ -805,8 +885,56 @@ Connection objects
Example:
- .. literalinclude:: ../includes/sqlite3/sumintwindow.py
+ .. testcode::
+
+ # Example taken from https://www.sqlite.org/windowfunctions.html#udfwinfunc
+ class WindowSumInt:
+ def __init__(self):
+ self.count = 0
+ def step(self, value):
+ """Add a row to the current window."""
+ self.count += value
+
+ def value(self):
+ """Return the current value of the aggregate."""
+ return self.count
+
+ def inverse(self, value):
+ """Remove a row from the current window."""
+ self.count -= value
+
+ def finalize(self):
+ """Return the final value of the aggregate.
+
+ Any clean-up actions should be placed here.
+ """
+ return self.count
+
+
+ con = sqlite3.connect(":memory:")
+ cur = con.execute("CREATE TABLE test(x, y)")
+ values = [
+ ("a", 4),
+ ("b", 5),
+ ("c", 3),
+ ("d", 8),
+ ("e", 1),
+ ]
+ cur.executemany("INSERT INTO test VALUES(?, ?)", values)
+ con.create_window_function("sumint", 1, WindowSumInt)
+ cur.execute("""
+ SELECT x, sumint(y) OVER (
+ ORDER BY x ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING
+ ) AS sum_y
+ FROM test ORDER BY x
+ """)
+ print(cur.fetchall())
+
+ .. testoutput::
+ :hide:
+
+ [('a', 9), ('b', 12), ('c', 16), ('d', 12), ('e', 9)]
.. method:: create_collation(name, callable)
@@ -820,7 +948,31 @@ Connection objects
The following example shows a reverse sorting collation:
- .. literalinclude:: ../includes/sqlite3/collation_reverse.py
+ .. testcode::
+
+ def collate_reverse(string1, string2):
+ if string1 == string2:
+ return 0
+ elif string1 < string2:
+ return 1
+ else:
+ return -1
+
+ con = sqlite3.connect(":memory:")
+ con.create_collation("reverse", collate_reverse)
+
+ cur = con.execute("CREATE TABLE test(x)")
+ cur.executemany("INSERT INTO test(x) VALUES(?)", [("a",), ("b",)])
+ cur.execute("SELECT x FROM test ORDER BY x COLLATE reverse")
+ for row in cur:
+ print(row)
+ con.close()
+
+ .. testoutput::
+ :hide:
+
+ ('b',)
+ ('a',)
Remove a collation function by setting *callable* to ``None``.
@@ -925,7 +1077,43 @@ Connection objects
.. versionchanged:: 3.10
Added the ``sqlite3.enable_load_extension`` auditing event.
- .. literalinclude:: ../includes/sqlite3/load_extension.py
+ .. testsetup:: sqlite3.loadext
+
+ import sqlite3
+ con = sqlite3.connect(":memory:")
+
+ .. testcode:: sqlite3.loadext
+ :skipif: True # not testable at the moment
+
+ con.enable_load_extension(True)
+
+ # Load the fulltext search extension
+ con.execute("select load_extension('./fts3.so')")
+
+ # alternatively you can load the extension using an API call:
+ # con.load_extension("./fts3.so")
+
+ # disable extension loading again
+ con.enable_load_extension(False)
+
+ # example from SQLite wiki
+ con.execute("CREATE VIRTUAL TABLE recipe USING fts3(name, ingredients)")
+ con.executescript("""
+ INSERT INTO recipe (name, ingredients) VALUES('broccoli stew', 'broccoli peppers cheese tomatoes');
+ INSERT INTO recipe (name, ingredients) VALUES('pumpkin stew', 'pumpkin onions garlic celery');
+ INSERT INTO recipe (name, ingredients) VALUES('broccoli pie', 'broccoli cheese onions flour');
+ INSERT INTO recipe (name, ingredients) VALUES('pumpkin pie', 'pumpkin sugar flour butter');
+ """)
+ for row in con.execute("SELECT rowid, name, ingredients FROM recipe WHERE name MATCH 'pie'"):
+ print(row)
+
+ con.close()
+
+ .. testoutput:: sqlite3.loadext
+ :hide:
+
+ (2, 'broccoli pie', 'broccoli cheese onions flour')
+ (3, 'pumpkin pie', 'pumpkin sugar flour butter')
.. method:: load_extension(path, /)
@@ -1400,7 +1588,30 @@ Blob objects
Use the :class:`Blob` as a :term:`context manager` to ensure that the blob
handle is closed after use.
- .. literalinclude:: ../includes/sqlite3/blob.py
+ .. testcode::
+
+ con = sqlite3.connect(":memory:")
+ con.execute("CREATE TABLE test(blob_col blob)")
+ con.execute("INSERT INTO test(blob_col) VALUES(zeroblob(13))")
+
+ # Write to our blob, using two write operations:
+ with con.blobopen("test", "blob_col", 1) as blob:
+ blob.write(b"hello, ")
+ blob.write(b"world.")
+ # Modify the first and last bytes of our blob
+ blob[0] = ord("H")
+ blob[-1] = ord("!")
+
+ # Read the contents of our blob
+ with con.blobopen("test", "blob_col", 1) as blob:
+ greeting = blob.read()
+
+ print(greeting) # outputs "b'Hello, world!'"
+
+ .. testoutput::
+ :hide:
+
+ b'Hello, world!'
.. method:: close()
@@ -1678,7 +1889,30 @@ placeholders (named style). For the qmark style, ``parameters`` must be a
keys for all named parameters. Any extra items are ignored. Here's an example of
both styles:
-.. literalinclude:: ../includes/sqlite3/execute_1.py
+.. testcode::
+
+ con = sqlite3.connect(":memory:")
+ cur = con.execute("CREATE TABLE lang(name, first_appeared)")
+
+ # This is the qmark style:
+ cur.execute("INSERT INTO lang VALUES(?, ?)", ("C", 1972))
+
+ # The qmark style used with executemany():
+ lang_list = [
+ ("Fortran", 1957),
+ ("Python", 1991),
+ ("Go", 2009),
+ ]
+ cur.executemany("INSERT INTO lang VALUES(?, ?)", lang_list)
+
+ # And this is the named style:
+ cur.execute("SELECT * FROM lang WHERE first_appeared = :year", {"year": 1972})
+ print(cur.fetchall())
+
+.. testoutput::
+ :hide:
+
+ [('C', 1972)]
.. _sqlite3-adapters:
@@ -1712,7 +1946,26 @@ This can be implemented by adding a ``__conform__(self, protocol)``
method which returns the adapted value.
The object passed to *protocol* will be of type :class:`PrepareProtocol`.
-.. literalinclude:: ../includes/sqlite3/adapter_point_1.py
+.. testcode::
+
+ class Point:
+ def __init__(self, x, y):
+ self.x, self.y = x, y
+
+ def __conform__(self, protocol):
+ if protocol is sqlite3.PrepareProtocol:
+ return f"{self.x};{self.y}"
+
+ con = sqlite3.connect(":memory:")
+ cur = con.cursor()
+
+ cur.execute("SELECT ?", (Point(4.0, -3.2),))
+ print(cur.fetchone()[0])
+
+.. testoutput::
+ :hide:
+
+ 4.0;-3.2
How to register adapter callables
@@ -1722,7 +1975,27 @@ The other possibility is to create a function that converts the Python object
to an SQLite-compatible type.
This function can then be registered using :func:`register_adapter`.
-.. literalinclude:: ../includes/sqlite3/adapter_point_2.py
+.. testcode::
+
+ class Point:
+ def __init__(self, x, y):
+ self.x, self.y = x, y
+
+ def adapt_point(point):
+ return f"{point.x};{point.y}"
+
+ sqlite3.register_adapter(Point, adapt_point)
+
+ con = sqlite3.connect(":memory:")
+ cur = con.cursor()
+
+ cur.execute("SELECT ?", (Point(1.0, 2.5),))
+ print(cur.fetchone()[0])
+
+.. testoutput::
+ :hide:
+
+ 1.0;2.5
.. _sqlite3-converters:
@@ -1764,7 +2037,50 @@ of :func:`connect`. There are three options:
The following example illustrates the implicit and explicit approaches:
-.. literalinclude:: ../includes/sqlite3/converter_point.py
+.. testcode::
+
+ class Point:
+ def __init__(self, x, y):
+ self.x, self.y = x, y
+
+ def __repr__(self):
+ return f"Point({self.x}, {self.y})"
+
+ def adapt_point(point):
+ return f"{point.x};{point.y}".encode("utf-8")
+
+ def convert_point(s):
+ x, y = list(map(float, s.split(b";")))
+ return Point(x, y)
+
+ # Register the adapter and converter
+ sqlite3.register_adapter(Point, adapt_point)
+ sqlite3.register_converter("point", convert_point)
+
+ # 1) Parse using declared types
+ p = Point(4.0, -3.2)
+ con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_DECLTYPES)
+ cur = con.execute("CREATE TABLE test(p point)")
+
+ cur.execute("INSERT INTO test(p) VALUES(?)", (p,))
+ cur.execute("SELECT p FROM test")
+ print("with declared types:", cur.fetchone()[0])
+ cur.close()
+ con.close()
+
+ # 2) Parse using column names
+ con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_COLNAMES)
+ cur = con.execute("CREATE TABLE test(p)")
+
+ cur.execute("INSERT INTO test(p) VALUES(?)", (p,))
+ cur.execute('SELECT p AS "p [point]" FROM test')
+ print("with column names:", cur.fetchone()[0])
+
+.. testoutput::
+ :hide:
+
+ with declared types: Point(4.0, -3.2)
+ with column names: Point(4.0, -3.2)
.. _sqlite3-adapter-converter-recipes:
@@ -1826,7 +2142,33 @@ objects are created implicitly and these shortcut methods return the cursor
objects. This way, you can execute a ``SELECT`` statement and iterate over it
directly using only a single call on the :class:`Connection` object.
-.. literalinclude:: ../includes/sqlite3/shortcut_methods.py
+.. testcode::
+
+ # Create and fill the table.
+ con = sqlite3.connect(":memory:")
+ con.execute("CREATE TABLE lang(name, first_appeared)")
+ data = [
+ ("C++", 1985),
+ ("Objective-C", 1984),
+ ]
+ con.executemany("INSERT INTO lang(name, first_appeared) VALUES(?, ?)", data)
+
+ # Print the table contents
+ for row in con.execute("SELECT name, first_appeared FROM lang"):
+ print(row)
+
+ print("I just deleted", con.execute("DELETE FROM lang").rowcount, "rows")
+
+ # close() is not a shortcut method and it's not called automatically;
+ # the connection object should be closed manually
+ con.close()
+
+.. testoutput::
+ :hide:
+
+ ('C++', 1985)
+ ('Objective-C', 1984)
+ I just deleted 2 rows
.. _sqlite3-connection-context-manager:
@@ -1851,7 +2193,31 @@ the context manager is a no-op.
The context manager neither implicitly opens a new transaction
nor closes the connection.
-.. literalinclude:: ../includes/sqlite3/ctx_manager.py
+.. testcode::
+
+ con = sqlite3.connect(":memory:")
+ con.execute("CREATE TABLE lang(id INTEGER PRIMARY KEY, name VARCHAR UNIQUE)")
+
+ # Successful, con.commit() is called automatically afterwards
+ with con:
+ con.execute("INSERT INTO lang(name) VALUES(?)", ("Python",))
+
+ # con.rollback() is called after the with block finishes with an exception,
+ # the exception is still raised and must be caught
+ try:
+ with con:
+ con.execute("INSERT INTO lang(name) VALUES(?)", ("Python",))
+ except sqlite3.IntegrityError:
+ print("couldn't add Python twice")
+
+ # Connection object used as context manager only commits or rollbacks transactions,
+ # so the connection object should be closed manually
+ con.close()
+
+.. testoutput::
+ :hide:
+
+ couldn't add Python twice
.. _sqlite3-uri-tricks:
1
0
https://github.com/python/cpython/commit/8ba22b90cafdf83d26318905a021311c69…
commit: 8ba22b90cafdf83d26318905a021311c6932d2c0
branch: main
author: Dennis Sweeney <36520290+sweeneyde(a)users.noreply.github.com>
committer: sweeneyde <36520290+sweeneyde(a)users.noreply.github.com>
date: 2022-08-30T21:39:51-04:00
summary:
gh-95865: Speed up urllib.parse.quote_from_bytes() (GH-95872)
files:
A Misc/NEWS.d/next/Library/2022-08-11-03-16-48.gh-issue-95865.0IOkFP.rst
M Lib/urllib/parse.py
diff --git a/Lib/urllib/parse.py b/Lib/urllib/parse.py
index fd6d9f44c626..f25c770068bd 100644
--- a/Lib/urllib/parse.py
+++ b/Lib/urllib/parse.py
@@ -906,7 +906,7 @@ def quote_from_bytes(bs, safe='/'):
if not bs.rstrip(_ALWAYS_SAFE_BYTES + safe):
return bs.decode()
quoter = _byte_quoter_factory(safe)
- return ''.join([quoter(char) for char in bs])
+ return ''.join(map(quoter, bs))
def urlencode(query, doseq=False, safe='', encoding=None, errors=None,
quote_via=quote_plus):
diff --git a/Misc/NEWS.d/next/Library/2022-08-11-03-16-48.gh-issue-95865.0IOkFP.rst b/Misc/NEWS.d/next/Library/2022-08-11-03-16-48.gh-issue-95865.0IOkFP.rst
new file mode 100644
index 000000000000..aa7c73ff1a35
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-08-11-03-16-48.gh-issue-95865.0IOkFP.rst
@@ -0,0 +1 @@
+Speed up :func:`urllib.parse.quote_from_bytes` by replacing a list comprehension with ``map()``.
1
0
https://github.com/python/cpython/commit/88671a9d6916229badc8b97a358a0f596f…
commit: 88671a9d6916229badc8b97a358a0f596f5aa0a1
branch: main
author: Brandt Bucher <brandtbucher(a)microsoft.com>
committer: brandtbucher <brandtbucher(a)gmail.com>
date: 2022-08-30T15:45:24-07:00
summary:
Remove the binary_subscr_dict_error label (GH-96443)
files:
M Python/ceval.c
diff --git a/Python/ceval.c b/Python/ceval.c
index b3a0a3640eb..c61ccd7dfc6 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -1621,7 +1621,10 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
PyObject *sub = TOP();
PyObject *res = PyDict_GetItemWithError(dict, sub);
if (res == NULL) {
- goto binary_subscr_dict_error;
+ if (!_PyErr_Occurred(tstate)) {
+ _PyErr_SetKeyError(sub);
+ }
+ goto error;
}
Py_INCREF(res);
STACK_SHRINK(1);
@@ -5193,16 +5196,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
DISPATCH_GOTO();
}
-binary_subscr_dict_error:
- {
- PyObject *sub = POP();
- if (!_PyErr_Occurred(tstate)) {
- _PyErr_SetKeyError(sub);
- }
- Py_DECREF(sub);
- goto error;
- }
-
unbound_local_error:
{
format_exc_check_arg(tstate, PyExc_UnboundLocalError,
1
0
https://github.com/python/cpython/commit/02dbb362d3fb7d82af9dce7c8caac08fe0…
commit: 02dbb362d3fb7d82af9dce7c8caac08fe0d8efdb
branch: main
author: Filip Łajszczak <filip.lajszczak(a)gmail.com>
committer: rhettinger <rhettinger(a)users.noreply.github.com>
date: 2022-08-30T17:43:23-05:00
summary:
gh-96408: Test set operation on items dict view. (GH-96438)
files:
M Lib/test/test_dictviews.py
diff --git a/Lib/test/test_dictviews.py b/Lib/test/test_dictviews.py
index be271bebaaf..7c48d800cd8 100644
--- a/Lib/test/test_dictviews.py
+++ b/Lib/test/test_dictviews.py
@@ -170,6 +170,10 @@ def test_items_set_operations(self):
{('a', 1), ('b', 2)})
self.assertEqual(d1.items() & set(d2.items()), {('b', 2)})
self.assertEqual(d1.items() & set(d3.items()), set())
+ self.assertEqual(d1.items() & (("a", 1), ("b", 2)),
+ {('a', 1), ('b', 2)})
+ self.assertEqual(d1.items() & (("a", 2), ("b", 2)), {('b', 2)})
+ self.assertEqual(d1.items() & (("d", 4), ("e", 5)), set())
self.assertEqual(d1.items() | d1.items(),
{('a', 1), ('b', 2)})
@@ -183,12 +187,23 @@ def test_items_set_operations(self):
{('a', 1), ('a', 2), ('b', 2)})
self.assertEqual(d1.items() | set(d3.items()),
{('a', 1), ('b', 2), ('d', 4), ('e', 5)})
+ self.assertEqual(d1.items() | (('a', 1), ('b', 2)),
+ {('a', 1), ('b', 2)})
+ self.assertEqual(d1.items() | (('a', 2), ('b', 2)),
+ {('a', 1), ('a', 2), ('b', 2)})
+ self.assertEqual(d1.items() | (('d', 4), ('e', 5)),
+ {('a', 1), ('b', 2), ('d', 4), ('e', 5)})
self.assertEqual(d1.items() ^ d1.items(), set())
self.assertEqual(d1.items() ^ d2.items(),
{('a', 1), ('a', 2)})
self.assertEqual(d1.items() ^ d3.items(),
{('a', 1), ('b', 2), ('d', 4), ('e', 5)})
+ self.assertEqual(d1.items() ^ (('a', 1), ('b', 2)), set())
+ self.assertEqual(d1.items() ^ (("a", 2), ("b", 2)),
+ {('a', 1), ('a', 2)})
+ self.assertEqual(d1.items() ^ (("d", 4), ("e", 5)),
+ {('a', 1), ('b', 2), ('d', 4), ('e', 5)})
self.assertEqual(d1.items() - d1.items(), set())
self.assertEqual(d1.items() - d2.items(), {('a', 1)})
@@ -196,6 +211,9 @@ def test_items_set_operations(self):
self.assertEqual(d1.items() - set(d1.items()), set())
self.assertEqual(d1.items() - set(d2.items()), {('a', 1)})
self.assertEqual(d1.items() - set(d3.items()), {('a', 1), ('b', 2)})
+ self.assertEqual(d1.items() - (('a', 1), ('b', 2)), set())
+ self.assertEqual(d1.items() - (("a", 2), ("b", 2)), {('a', 1)})
+ self.assertEqual(d1.items() - (("d", 4), ("e", 5)), {('a', 1), ('b', 2)})
self.assertFalse(d1.items().isdisjoint(d1.items()))
self.assertFalse(d1.items().isdisjoint(d2.items()))
1
0
30 Aug '22
https://github.com/python/cpython/commit/895c7a440168769fe96154c3476b7917a5…
commit: 895c7a440168769fe96154c3476b7917a5874bcc
branch: 3.11
author: Miss Islington (bot) <31488909+miss-islington(a)users.noreply.github.com>
committer: miss-islington <31488909+miss-islington(a)users.noreply.github.com>
date: 2022-08-30T14:06:11-07:00
summary:
Docs: normalise sqlite3 placeholder how-to heading (GH-96413)
(cherry picked from commit 7b01ce7953c0e24aa7aeaf207216fc9e7aefd18a)
Co-authored-by: Erlend E. Aasland <erlend.aasland(a)protonmail.com>
files:
M Doc/library/sqlite3.rst
diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst
index 21ad3540ccb..2c58ef71c33 100644
--- a/Doc/library/sqlite3.rst
+++ b/Doc/library/sqlite3.rst
@@ -1615,8 +1615,8 @@ How-to guides
.. _sqlite3-placeholders:
-Using placeholders to bind values in SQL queries
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+How to use placeholders to bind values in SQL queries
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
SQL operations usually need to use values from Python variables. However,
beware of using Python's string operations to assemble queries, as they
1
0