Skip to content

Commit c496fd0

Browse files
gh-157478: Add structured version info for dbm.gnu
Add constants dbm.gnu.GDBM_VERSION_INFO, dbm.gnu.gdbm_version_info and dbm.gnu.gdbm_version, which provide information about the version of the GDBM library in use. The private constant _gdbm._GDBM_VERSION has been removed. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent a844747 commit c496fd0

6 files changed

Lines changed: 158 additions & 14 deletions

File tree

Doc/library/dbm.rst

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,38 @@ functionality like crash tolerance.
253253
A string of characters the *flag* parameter of :meth:`~dbm.gnu.open` supports.
254254

255255

256+
.. data:: GDBM_VERSION_INFO
257+
258+
A named tuple containing the three components of the GDBM library
259+
version that was used for building the module:
260+
*major*, *minor*, and *patch*.
261+
All values are integers.
262+
The components can also be accessed by name,
263+
so ``dbm.gnu.GDBM_VERSION_INFO[0]`` is equivalent to
264+
``dbm.gnu.GDBM_VERSION_INFO.major`` and so on.
265+
This may be different from the GDBM library actually used at runtime,
266+
which is available as :const:`gdbm_version_info`.
267+
268+
.. versionadded:: next
269+
270+
271+
.. data:: gdbm_version_info
272+
273+
A named tuple containing the version of the GDBM library
274+
actually loaded by the interpreter,
275+
with the same fields as :const:`GDBM_VERSION_INFO`.
276+
277+
.. versionadded:: next
278+
279+
280+
.. data:: gdbm_version
281+
282+
The release string of the GDBM library actually loaded by the interpreter,
283+
like ``'GDBM version 1.26. 30/07/2025'``.
284+
285+
.. versionadded:: next
286+
287+
256288
.. function:: open(filename, flag="r", mode=0o666, /)
257289

258290
Open a GDBM database and return a :class:`!gdbm` object.

Doc/whatsnew/3.16.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,15 @@ concurrent.futures
340340
(Contributed by xzmeng and Serhiy Storchaka in :gh:`108518`.)
341341

342342

343+
dbm.gnu
344+
-------
345+
346+
* Added constants :const:`~dbm.gnu.GDBM_VERSION_INFO`,
347+
:const:`~dbm.gnu.gdbm_version_info` and :const:`~dbm.gnu.gdbm_version`,
348+
which provide information about the version of the GDBM library in use.
349+
(Contributed by Serhiy Storchaka in :gh:`157478`.)
350+
351+
343352
difflib
344353
-------
345354

Lib/test/pythoninfo.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -937,11 +937,12 @@ def collect_cc(info_add):
937937

938938
def collect_gdbm(info_add):
939939
try:
940-
from _gdbm import _GDBM_VERSION
940+
import _gdbm
941941
except ImportError:
942942
return
943943

944-
info_add('gdbm.GDBM_VERSION', '.'.join(map(str, _GDBM_VERSION)))
944+
attributes = ('GDBM_VERSION_INFO', 'gdbm_version')
945+
copy_attributes(info_add, _gdbm, 'gdbm.%s', attributes)
945946

946947

947948
def collect_get_config(info_add):

Lib/test/test_dbm_gnu.py

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,7 @@ class TestGdbm(unittest.TestCase):
1313
@staticmethod
1414
def setUpClass():
1515
if support.verbose:
16-
try:
17-
from _gdbm import _GDBM_VERSION as version
18-
except ImportError:
19-
pass
20-
else:
21-
print(f"gdbm version: {version}")
16+
print(f"gdbm version: {gdbm.gdbm_version}")
2217

2318
def setUp(self):
2419
self.g = None
@@ -29,6 +24,35 @@ def tearDown(self):
2924
unlink(filename)
3025

3126
@cpython_only
27+
def _test_gdbm_version(self, v):
28+
self.assertIsInstance(v[:], tuple)
29+
self.assertEqual(len(v), 3)
30+
self.assertIsInstance(v[0], int)
31+
self.assertIsInstance(v[1], int)
32+
self.assertIsInstance(v[2], int)
33+
self.assertIsInstance(v.major, int)
34+
self.assertIsInstance(v.minor, int)
35+
self.assertIsInstance(v.patch, int)
36+
self.assertEqual(v[0], v.major)
37+
self.assertEqual(v[1], v.minor)
38+
self.assertEqual(v[2], v.patch)
39+
self.assertGreaterEqual(v.major, 1)
40+
self.assertGreaterEqual(v.minor, 0)
41+
self.assertGreaterEqual(v.patch, 0)
42+
43+
@unittest.skipUnless(hasattr(gdbm, 'GDBM_VERSION_INFO'),
44+
'requires gdbm >= 1.9')
45+
def test_gdbm_version(self):
46+
if support.verbose:
47+
print(f'GDBM_VERSION_INFO = {gdbm.GDBM_VERSION_INFO}', flush=True)
48+
print(f'gdbm_version_info = {gdbm.gdbm_version_info}', flush=True)
49+
self._test_gdbm_version(gdbm.GDBM_VERSION_INFO)
50+
self._test_gdbm_version(gdbm.gdbm_version_info)
51+
self.assertEqual(gdbm.GDBM_VERSION_INFO[0], gdbm.gdbm_version_info[0])
52+
v = gdbm.gdbm_version_info
53+
self.assertIsInstance(gdbm.gdbm_version, str)
54+
self.assertStartsWith(gdbm.gdbm_version, 'GDBM version %d.%d' % v[:2])
55+
3256
def test_disallow_instantiation(self):
3357
# Ensure that the type disallows instantiation (bpo-43916)
3458
self.g = gdbm.open(filename, 'c')
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Add constants :const:`dbm.gnu.GDBM_VERSION_INFO`,
2+
:const:`dbm.gnu.gdbm_version_info` and :const:`dbm.gnu.gdbm_version`, which
3+
provide information about the version of the GDBM library in use.
4+
The private constant ``_gdbm._GDBM_VERSION`` has been removed.

Modules/_gdbmmodule.c

Lines changed: 80 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -855,6 +855,85 @@ static PyMethodDef _gdbm_module_methods[] = {
855855
{ 0, 0 },
856856
};
857857

858+
PyDoc_STRVAR(gdbm_version_info__doc__,
859+
"_gdbm.gdbm_version_info\n\
860+
\n\
861+
GDBM version information as a named tuple.");
862+
863+
static PyStructSequence_Field gdbm_version_info_fields[] = {
864+
{"major", "Major release number"},
865+
{"minor", "Minor release number"},
866+
{"patch", "Patch release number"},
867+
{0}
868+
};
869+
870+
static PyStructSequence_Desc gdbm_version_info_desc = {
871+
"_gdbm.gdbm_version_info", /* name */
872+
gdbm_version_info__doc__, /* doc */
873+
gdbm_version_info_fields, /* fields */
874+
3
875+
};
876+
877+
static PyObject *
878+
make_gdbm_version_info(PyTypeObject *type, int major, int minor, int patch)
879+
{
880+
PyObject *version;
881+
int pos = 0;
882+
883+
version = PyStructSequence_New(type);
884+
if (version == NULL) {
885+
return NULL;
886+
}
887+
888+
#define SetItem(VALUE) \
889+
PyStructSequence_SET_ITEM(version, pos++, VALUE); \
890+
if (PyErr_Occurred()) { \
891+
Py_DECREF(version); \
892+
return NULL; \
893+
}
894+
895+
SetItem(PyLong_FromLong(major))
896+
SetItem(PyLong_FromLong(minor))
897+
SetItem(PyLong_FromLong(patch))
898+
#undef SetItem
899+
900+
return version;
901+
}
902+
903+
static int
904+
add_version_constants(PyObject *module)
905+
{
906+
if (PyModule_AddStringConstant(module, "gdbm_version", gdbm_version) < 0) {
907+
return -1;
908+
}
909+
#if defined(GDBM_VERSION_MAJOR) && defined(GDBM_VERSION_MINOR) && \
910+
defined(GDBM_VERSION_PATCH)
911+
PyTypeObject *version_type;
912+
version_type = PyStructSequence_NewType(&gdbm_version_info_desc);
913+
if (version_type == NULL) {
914+
return -1;
915+
}
916+
if (PyModule_Add(module, "GDBM_VERSION_INFO",
917+
make_gdbm_version_info(version_type, GDBM_VERSION_MAJOR,
918+
GDBM_VERSION_MINOR,
919+
GDBM_VERSION_PATCH)) < 0)
920+
{
921+
Py_DECREF(version_type);
922+
return -1;
923+
}
924+
if (PyModule_Add(module, "gdbm_version_info",
925+
make_gdbm_version_info(version_type, gdbm_version_number[0],
926+
gdbm_version_number[1],
927+
gdbm_version_number[2])) < 0)
928+
{
929+
Py_DECREF(version_type);
930+
return -1;
931+
}
932+
Py_DECREF(version_type);
933+
#endif
934+
return 0;
935+
}
936+
858937
static int
859938
_gdbm_exec(PyObject *module)
860939
{
@@ -876,14 +955,9 @@ _gdbm_exec(PyObject *module)
876955
return -1;
877956
}
878957

879-
#if defined(GDBM_VERSION_MAJOR) && defined(GDBM_VERSION_MINOR) && \
880-
defined(GDBM_VERSION_PATCH)
881-
PyObject *obj = Py_BuildValue("iii", GDBM_VERSION_MAJOR,
882-
GDBM_VERSION_MINOR, GDBM_VERSION_PATCH);
883-
if (PyModule_Add(module, "_GDBM_VERSION", obj) < 0) {
958+
if (add_version_constants(module) < 0) {
884959
return -1;
885960
}
886-
#endif
887961
return 0;
888962
}
889963

0 commit comments

Comments
 (0)