Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions Doc/library/dbm.rst
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,38 @@ functionality like crash tolerance.
A string of characters the *flag* parameter of :meth:`~dbm.gnu.open` supports.


.. data:: GDBM_VERSION_INFO

A named tuple containing the three components of the GDBM library
version that was used for building the module:
*major*, *minor*, and *patch*.
All values are integers.
The components can also be accessed by name,
so ``dbm.gnu.GDBM_VERSION_INFO[0]`` is equivalent to
``dbm.gnu.GDBM_VERSION_INFO.major`` and so on.
This may be different from the GDBM library actually used at runtime,
which is available as :const:`gdbm_version_info`.

.. versionadded:: next


.. data:: gdbm_version_info

A named tuple containing the version of the GDBM library
actually loaded by the interpreter,
with the same fields as :const:`GDBM_VERSION_INFO`.

.. versionadded:: next


.. data:: gdbm_version

The release string of the GDBM library actually loaded by the interpreter,
like ``'GDBM version 1.26. 30/07/2025'``.

.. versionadded:: next


.. function:: open(filename, flag="r", mode=0o666, /)

Open a GDBM database and return a :class:`!gdbm` object.
Expand Down
9 changes: 9 additions & 0 deletions Doc/whatsnew/3.16.rst
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,15 @@ concurrent.futures
(Contributed by xzmeng and Serhiy Storchaka in :gh:`108518`.)


dbm.gnu
-------

* Added constants :const:`~dbm.gnu.GDBM_VERSION_INFO`,
:const:`~dbm.gnu.gdbm_version_info` and :const:`~dbm.gnu.gdbm_version`,
which provide information about the version of the GDBM library in use.
(Contributed by Serhiy Storchaka in :gh:`157478`.)


difflib
-------

Expand Down
5 changes: 3 additions & 2 deletions Lib/test/pythoninfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -937,11 +937,12 @@ def collect_cc(info_add):

def collect_gdbm(info_add):
try:
from _gdbm import _GDBM_VERSION
import _gdbm
except ImportError:
return

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


def collect_get_config(info_add):
Expand Down
36 changes: 30 additions & 6 deletions Lib/test/test_dbm_gnu.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,7 @@ class TestGdbm(unittest.TestCase):
@staticmethod
def setUpClass():
if support.verbose:
try:
from _gdbm import _GDBM_VERSION as version
except ImportError:
pass
else:
print(f"gdbm version: {version}")
print(f"gdbm version: {gdbm.gdbm_version}")

def setUp(self):
self.g = None
Expand All @@ -29,6 +24,35 @@ def tearDown(self):
unlink(filename)

@cpython_only
def _test_gdbm_version(self, v):
self.assertIsInstance(v[:], tuple)
self.assertEqual(len(v), 3)
self.assertIsInstance(v[0], int)
self.assertIsInstance(v[1], int)
self.assertIsInstance(v[2], int)
self.assertIsInstance(v.major, int)
self.assertIsInstance(v.minor, int)
self.assertIsInstance(v.patch, int)
self.assertEqual(v[0], v.major)
self.assertEqual(v[1], v.minor)
self.assertEqual(v[2], v.patch)
self.assertGreaterEqual(v.major, 1)
self.assertGreaterEqual(v.minor, 0)
self.assertGreaterEqual(v.patch, 0)

@unittest.skipUnless(hasattr(gdbm, 'GDBM_VERSION_INFO'),
'requires gdbm >= 1.9')
def test_gdbm_version(self):
if support.verbose:
print(f'GDBM_VERSION_INFO = {gdbm.GDBM_VERSION_INFO}', flush=True)
print(f'gdbm_version_info = {gdbm.gdbm_version_info}', flush=True)
self._test_gdbm_version(gdbm.GDBM_VERSION_INFO)
self._test_gdbm_version(gdbm.gdbm_version_info)
self.assertEqual(gdbm.GDBM_VERSION_INFO[0], gdbm.gdbm_version_info[0])
v = gdbm.gdbm_version_info
self.assertIsInstance(gdbm.gdbm_version, str)
self.assertStartsWith(gdbm.gdbm_version, 'GDBM version %d.%d' % v[:2])

def test_disallow_instantiation(self):
# Ensure that the type disallows instantiation (bpo-43916)
self.g = gdbm.open(filename, 'c')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Add constants :const:`dbm.gnu.GDBM_VERSION_INFO`,
:const:`dbm.gnu.gdbm_version_info` and :const:`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.
86 changes: 80 additions & 6 deletions Modules/_gdbmmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -855,6 +855,85 @@ static PyMethodDef _gdbm_module_methods[] = {
{ 0, 0 },
};

PyDoc_STRVAR(gdbm_version_info__doc__,
"_gdbm.gdbm_version_info\n\
\n\
GDBM version information as a named tuple.");

static PyStructSequence_Field gdbm_version_info_fields[] = {
{"major", "Major release number"},
{"minor", "Minor release number"},
{"patch", "Patch release number"},
{0}
};

static PyStructSequence_Desc gdbm_version_info_desc = {
"_gdbm.gdbm_version_info", /* name */
gdbm_version_info__doc__, /* doc */
gdbm_version_info_fields, /* fields */
3
};

static PyObject *
make_gdbm_version_info(PyTypeObject *type, int major, int minor, int patch)
{
PyObject *version;
int pos = 0;

version = PyStructSequence_New(type);
if (version == NULL) {
return NULL;
}

#define SetItem(VALUE) \
PyStructSequence_SET_ITEM(version, pos++, VALUE); \
if (PyErr_Occurred()) { \
Py_DECREF(version); \
return NULL; \
}

SetItem(PyLong_FromLong(major))
SetItem(PyLong_FromLong(minor))
SetItem(PyLong_FromLong(patch))
#undef SetItem

return version;
}

static int
add_version_constants(PyObject *module)
{
if (PyModule_AddStringConstant(module, "gdbm_version", gdbm_version) < 0) {
return -1;
}
#if defined(GDBM_VERSION_MAJOR) && defined(GDBM_VERSION_MINOR) && \
defined(GDBM_VERSION_PATCH)
PyTypeObject *version_type;
version_type = PyStructSequence_NewType(&gdbm_version_info_desc);
if (version_type == NULL) {
return -1;
}
if (PyModule_Add(module, "GDBM_VERSION_INFO",
make_gdbm_version_info(version_type, GDBM_VERSION_MAJOR,
GDBM_VERSION_MINOR,
GDBM_VERSION_PATCH)) < 0)
{
Py_DECREF(version_type);
return -1;
}
if (PyModule_Add(module, "gdbm_version_info",
make_gdbm_version_info(version_type, gdbm_version_number[0],
gdbm_version_number[1],
gdbm_version_number[2])) < 0)
{
Py_DECREF(version_type);
return -1;
}
Py_DECREF(version_type);
#endif
return 0;
}

static int
_gdbm_exec(PyObject *module)
{
Expand All @@ -876,14 +955,9 @@ _gdbm_exec(PyObject *module)
return -1;
}

#if defined(GDBM_VERSION_MAJOR) && defined(GDBM_VERSION_MINOR) && \
defined(GDBM_VERSION_PATCH)
PyObject *obj = Py_BuildValue("iii", GDBM_VERSION_MAJOR,
GDBM_VERSION_MINOR, GDBM_VERSION_PATCH);
if (PyModule_Add(module, "_GDBM_VERSION", obj) < 0) {
if (add_version_constants(module) < 0) {
return -1;
}
#endif
return 0;
}

Expand Down
Loading