Skip to content

Commit 7155cc3

Browse files
gh-157470: Add structured version info for sqlite3
Add constants sqlite3.SQLITE_VERSION and sqlite3.SQLITE_VERSION_INFO which provide information about the version of the SQLite library that was used for building the module. Make sqlite3.sqlite_version_info a named tuple. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent a844747 commit 7155cc3

7 files changed

Lines changed: 149 additions & 6 deletions

File tree

Doc/library/sqlite3.rst

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -498,14 +498,40 @@ Module constants
498498

499499
The ``named`` DB-API parameter style is also supported.
500500

501+
.. data:: SQLITE_VERSION
502+
503+
The version string of the SQLite library that was used for building
504+
the module.
505+
This may be different from the SQLite library actually used at runtime,
506+
which is available as :const:`sqlite_version`.
507+
508+
.. versionadded:: next
509+
501510
.. data:: sqlite_version
502511

503512
Version number of the runtime SQLite library as a :class:`string <str>`.
504513

514+
.. data:: SQLITE_VERSION_INFO
515+
516+
A named tuple containing the three components of the SQLite library
517+
version that was used for building the module:
518+
*major*, *minor*, and *patch*.
519+
All values are integers.
520+
The components can also be accessed by name,
521+
so ``sqlite3.SQLITE_VERSION_INFO[0]`` is equivalent to
522+
``sqlite3.SQLITE_VERSION_INFO.major`` and so on.
523+
This may be different from the SQLite library actually used at runtime,
524+
which is available as :const:`sqlite_version_info`.
525+
526+
.. versionadded:: next
527+
505528
.. data:: sqlite_version_info
506529

507-
Version number of the runtime SQLite library as a :class:`tuple` of
508-
:class:`integers <int>`.
530+
A named tuple containing the version of the runtime SQLite library,
531+
with the same fields as :const:`SQLITE_VERSION_INFO`.
532+
533+
.. versionchanged:: next
534+
It is now a named tuple.
509535

510536
.. data:: SQLITE_KEYWORDS
511537

Doc/whatsnew/3.16.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,12 @@ sqlite3
558558
:exc:`SystemError` or :exc:`ValueError`.
559559
(Contributed by Jiseok CHOI in :gh:`150449`.)
560560

561+
* Added constants :const:`~sqlite3.SQLITE_VERSION` and
562+
:const:`~sqlite3.SQLITE_VERSION_INFO` which provide information
563+
about the version of the SQLite library that was used for building the module.
564+
:const:`~sqlite3.sqlite_version_info` is now a named tuple.
565+
(Contributed by Serhiy Storchaka in :gh:`157470`.)
566+
561567

562568
symtable
563569
--------

Lib/sqlite3/dbapi2.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,6 @@ def TimestampFromTicks(ticks):
4646
return Timestamp(*time.localtime(ticks)[:6])
4747

4848

49-
sqlite_version_info = tuple([int(x) for x in sqlite_version.split(".")])
50-
5149
Binary = memoryview
5250
collections.abc.Sequence.register(Row)
5351

Lib/test/pythoninfo.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -701,7 +701,7 @@ def collect_sqlite(info_add):
701701
except ImportError:
702702
return
703703

704-
attributes = ('sqlite_version',)
704+
attributes = ('SQLITE_VERSION', 'sqlite_version')
705705
copy_attributes(info_add, sqlite3, 'sqlite3.%s', attributes)
706706

707707

Lib/test/test_sqlite3/test_dbapi.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import urllib.parse
3232
import warnings
3333

34+
from test import support
3435
from test.support import (
3536
SHORT_TIMEOUT, check_disallow_instantiation, requires_subprocess
3637
)
@@ -88,6 +89,33 @@ def test_programming_error(self):
8889
def test_not_supported_error(self):
8990
self.assertIsSubclass(sqlite.NotSupportedError, sqlite.DatabaseError)
9091

92+
def _test_sqlite_version(self, v, string):
93+
self.assertIsInstance(v[:], tuple)
94+
self.assertEqual(len(v), 3)
95+
self.assertIsInstance(v[0], int)
96+
self.assertIsInstance(v[1], int)
97+
self.assertIsInstance(v[2], int)
98+
self.assertIsInstance(v.major, int)
99+
self.assertIsInstance(v.minor, int)
100+
self.assertIsInstance(v.patch, int)
101+
self.assertEqual(v[0], v.major)
102+
self.assertEqual(v[1], v.minor)
103+
self.assertEqual(v[2], v.patch)
104+
self.assertGreaterEqual(v.major, 3)
105+
self.assertGreaterEqual(v.minor, 0)
106+
self.assertGreaterEqual(v.patch, 0)
107+
self.assertEqual(string, '%d.%d.%d' % v)
108+
109+
def test_sqlite_version(self):
110+
if support.verbose:
111+
print(f'SQLITE_VERSION = {sqlite.SQLITE_VERSION}', flush=True)
112+
print(f'sqlite_version = {sqlite.sqlite_version}', flush=True)
113+
print(f'SQLITE_VERSION_INFO = {sqlite.SQLITE_VERSION_INFO}', flush=True)
114+
print(f'sqlite_version_info = {sqlite.sqlite_version_info}', flush=True)
115+
self._test_sqlite_version(sqlite.SQLITE_VERSION_INFO, sqlite.SQLITE_VERSION)
116+
self._test_sqlite_version(sqlite.sqlite_version_info, sqlite.sqlite_version)
117+
self.assertEqual(sqlite.SQLITE_VERSION_INFO[0], sqlite.sqlite_version_info[0])
118+
91119
def test_module_constants(self):
92120
consts = [
93121
"SQLITE_ABORT",
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Add constants :const:`sqlite3.SQLITE_VERSION` and
2+
:const:`sqlite3.SQLITE_VERSION_INFO` which provide information about
3+
the version of the SQLite library that was used for building the module.
4+
:const:`sqlite3.sqlite_version_info` is now a named tuple.

Modules/_sqlite/module.c

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,87 @@ add_keyword_tuple(PyObject *module)
439439
#endif
440440
}
441441

442+
PyDoc_STRVAR(sqlite_version_info__doc__,
443+
"_sqlite3.sqlite_version_info\n\
444+
\n\
445+
SQLite version information as a named tuple.");
446+
447+
static PyStructSequence_Field sqlite_version_info_fields[] = {
448+
{"major", "Major release number"},
449+
{"minor", "Minor release number"},
450+
{"patch", "Patch release number"},
451+
{0}
452+
};
453+
454+
static PyStructSequence_Desc sqlite_version_info_desc = {
455+
"_sqlite3.sqlite_version_info", /* name */
456+
sqlite_version_info__doc__, /* doc */
457+
sqlite_version_info_fields, /* fields */
458+
3
459+
};
460+
461+
static PyObject *
462+
make_sqlite_version_info(PyTypeObject *type, int number)
463+
{
464+
PyObject *version;
465+
int pos = 0;
466+
int major = number / 1000000;
467+
int minor = (number % 1000000) / 1000;
468+
int patch = number % 1000;
469+
470+
version = PyStructSequence_New(type);
471+
if (version == NULL) {
472+
return NULL;
473+
}
474+
475+
#define SetItem(VALUE) \
476+
PyStructSequence_SET_ITEM(version, pos++, VALUE); \
477+
if (PyErr_Occurred()) { \
478+
Py_DECREF(version); \
479+
return NULL; \
480+
}
481+
482+
SetItem(PyLong_FromLong(major))
483+
SetItem(PyLong_FromLong(minor))
484+
SetItem(PyLong_FromLong(patch))
485+
#undef SetItem
486+
487+
return version;
488+
}
489+
490+
static int
491+
add_version_constants(PyObject *module)
492+
{
493+
if (PyModule_AddStringMacro(module, SQLITE_VERSION) < 0) {
494+
return -1;
495+
}
496+
if (PyModule_AddStringConstant(module, "sqlite_version",
497+
sqlite3_libversion()) < 0)
498+
{
499+
return -1;
500+
}
501+
PyTypeObject *version_type;
502+
version_type = PyStructSequence_NewType(&sqlite_version_info_desc);
503+
if (version_type == NULL) {
504+
return -1;
505+
}
506+
if (PyModule_Add(module, "SQLITE_VERSION_INFO",
507+
make_sqlite_version_info(version_type, SQLITE_VERSION_NUMBER)) < 0)
508+
{
509+
Py_DECREF(version_type);
510+
return -1;
511+
}
512+
if (PyModule_Add(module, "sqlite_version_info",
513+
make_sqlite_version_info(version_type,
514+
sqlite3_libversion_number())) < 0)
515+
{
516+
Py_DECREF(version_type);
517+
return -1;
518+
}
519+
Py_DECREF(version_type);
520+
return 0;
521+
}
522+
442523
static int
443524
add_integer_constants(PyObject *module) {
444525
#define ADD_INT(ival) \
@@ -741,7 +822,7 @@ module_exec(PyObject *module)
741822
goto error;
742823
}
743824

744-
if (PyModule_AddStringConstant(module, "sqlite_version", sqlite3_libversion())) {
825+
if (add_version_constants(module) < 0) {
745826
goto error;
746827
}
747828

0 commit comments

Comments
 (0)