Skip to content

Commit 0f5abe6

Browse files
gh-157478: Add structured version info for dbm.gnu (GH-157479)
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 90d4002 commit 0f5abe6

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
@@ -347,6 +347,15 @@ concurrent.futures
347347
(Contributed by xzmeng and Serhiy Storchaka in :gh:`108518`.)
348348

349349

350+
dbm.gnu
351+
-------
352+
353+
* Added constants :const:`~dbm.gnu.GDBM_VERSION_INFO`,
354+
:const:`~dbm.gnu.gdbm_version_info` and :const:`~dbm.gnu.gdbm_version`,
355+
which provide information about the version of the GDBM library in use.
356+
(Contributed by Serhiy Storchaka in :gh:`157478`.)
357+
358+
350359
difflib
351360
-------
352361

‎Lib/test/pythoninfo.py‎

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

961961
def collect_gdbm(info_add):
962962
try:
963-
from _gdbm import _GDBM_VERSION
963+
import _gdbm
964964
except ImportError:
965965
return
966966

967-
info_add('gdbm.GDBM_VERSION', '.'.join(map(str, _GDBM_VERSION)))
967+
attributes = ('GDBM_VERSION_INFO', 'gdbm_version')
968+
copy_attributes(info_add, _gdbm, 'gdbm.%s', attributes)
968969

969970

970971
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
@@ -862,6 +862,85 @@ static PyMethodDef _gdbm_module_methods[] = {
862862
{ 0, 0 },
863863
};
864864

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

886-
#if defined(GDBM_VERSION_MAJOR) && defined(GDBM_VERSION_MINOR) && \
887-
defined(GDBM_VERSION_PATCH)
888-
PyObject *obj = Py_BuildValue("iii", GDBM_VERSION_MAJOR,
889-
GDBM_VERSION_MINOR, GDBM_VERSION_PATCH);
890-
if (PyModule_Add(module, "_GDBM_VERSION", obj) < 0) {
965+
if (add_version_constants(module) < 0) {
891966
return -1;
892967
}
893-
#endif
894968
return 0;
895969
}
896970

0 commit comments

Comments
 (0)