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
64 changes: 63 additions & 1 deletion Doc/library/dbm.rst
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,69 @@ This module can be used with the "classic" NDBM interface or the

.. data:: library

Name of the NDBM implementation library used.
Name of the NDBM implementation library used:
``'GNU gdbm'``, ``'Berkeley DB'`` or ``'ndbm'``.

.. versionchanged:: next
The value is ``'ndbm'`` for a classic NDBM library.
It was ``'GNU gdbm'`` before.


.. data:: GDBM_VERSION_INFO
gdbm_version_info
gdbm_version

Information about the GDBM library in use,
with the same meaning as the constants of the same names in :mod:`dbm.gnu`.
Only available if :const:`library` is ``'GNU gdbm'``.

.. versionadded:: next


.. data:: BDB_VERSION

The version string of the Berkeley DB library that was used for building
the module, like ``'Berkeley DB 5.3.28: (September 9, 2013)'``.
This may be different from the Berkeley DB library actually used at runtime,
which is available as :const:`bdb_version`.
Only available if :const:`library` is ``'Berkeley DB'``.

.. versionadded:: next


.. data:: bdb_version

The version string of the Berkeley DB library actually loaded by the
interpreter.
Only available if :const:`library` is ``'Berkeley DB'``.

.. versionadded:: next


.. data:: BDB_VERSION_INFO

A named tuple containing the three components of the Berkeley DB 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.ndbm.BDB_VERSION_INFO[0]`` is equivalent to
``dbm.ndbm.BDB_VERSION_INFO.major`` and so on.
This may be different from the Berkeley DB library actually used at runtime,
which is available as :const:`bdb_version_info`.
Only available if :const:`library` is ``'Berkeley DB'``.

.. versionadded:: next


.. data:: bdb_version_info

A named tuple containing the version of the Berkeley DB library
actually loaded by the interpreter,
with the same fields as :const:`BDB_VERSION_INFO`.
Only available if :const:`library` is ``'Berkeley DB'``.

.. versionadded:: next


.. function:: open(filename, flag="r", mode=0o666, /)
Expand Down
14 changes: 14 additions & 0 deletions Doc/whatsnew/3.16.rst
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,20 @@ concurrent.futures
(Contributed by xzmeng and Serhiy Storchaka in :gh:`108518`.)


dbm.ndbm
--------

* Added constants which provide information about the version of the
underlying library in use: :const:`~dbm.ndbm.GDBM_VERSION_INFO`,
:const:`~dbm.ndbm.gdbm_version_info` and :const:`~dbm.ndbm.gdbm_version`
if it is GDBM, or :const:`~dbm.ndbm.BDB_VERSION`,
:const:`~dbm.ndbm.bdb_version`, :const:`~dbm.ndbm.BDB_VERSION_INFO` and
:const:`~dbm.ndbm.bdb_version_info` if it is Berkeley DB.
:const:`dbm.ndbm.library` is now ``'ndbm'`` instead of ``'GNU gdbm'``
for a classic NDBM library.
(Contributed by Serhiy Storchaka in :gh:`157480`.)


difflib
-------

Expand Down
12 changes: 12 additions & 0 deletions Lib/test/pythoninfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,17 @@ def collect_cc(info_add):
info_add('CC.version', text)


def collect_ndbm(info_add):
try:
import _dbm
except ImportError:
return

attributes = ('library', 'GDBM_VERSION_INFO', 'gdbm_version',
'BDB_VERSION', 'bdb_version')
copy_attributes(info_add, _dbm, 'ndbm.%s', attributes)


def collect_gdbm(info_add):
try:
from _gdbm import _GDBM_VERSION
Expand Down Expand Up @@ -1356,6 +1367,7 @@ def collect_info(info):
collect_expat,
collect_fips,
collect_gdb,
collect_ndbm,
collect_gdbm,
collect_get_config,
collect_locale,
Expand Down
48 changes: 48 additions & 0 deletions Lib/test/test_dbm_ndbm.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from test import support
from test.support import import_helper
from test.support import os_helper
import_helper.import_module("dbm.ndbm") #skip if not supported
Expand All @@ -17,6 +18,53 @@ def tearDown(self):
for suffix in ['', '.pag', '.dir', '.db']:
os_helper.unlink(self.filename + suffix)

def _test_version_info(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)

def test_library_version(self):
library = dbm.ndbm.library
if support.verbose:
print(f'library = {library!r}', flush=True)
self.assertIsInstance(library, str)
if library == 'GNU gdbm':
prefix = 'GDBM'
elif library == 'Berkeley DB':
prefix = 'BDB'
else:
self.assertEqual(library, 'ndbm')
self.assertNotHasAttr(dbm.ndbm, 'GDBM_VERSION_INFO')
self.assertNotHasAttr(dbm.ndbm, 'BDB_VERSION_INFO')
return
V = getattr(dbm.ndbm, f'{prefix}_VERSION_INFO')
v = getattr(dbm.ndbm, f'{prefix.lower()}_version_info')
version = getattr(dbm.ndbm, f'{prefix.lower()}_version')
if support.verbose:
print(f'{prefix}_VERSION_INFO = {V}', flush=True)
print(f'{prefix.lower()}_version_info = {v}', flush=True)
print(f'{prefix.lower()}_version = {version!r}', flush=True)
self._test_version_info(V)
self._test_version_info(v)
self.assertEqual(V[0], v[0])
self.assertIsInstance(version, str)
if library == 'GNU gdbm':
self.assertStartsWith(version, 'GDBM version %d.%d' % v[:2])
else:
self.assertIsInstance(dbm.ndbm.BDB_VERSION, str)
self.assertIn('%d.%d.%d' % v[:3], version)

def test_keys(self):
self.d = dbm.ndbm.open(self.filename, 'c')
self.assertEqual(self.d.keys(), [])
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Add constants in :mod:`dbm.ndbm` which provide information about the version
of the underlying library in use: :const:`~dbm.ndbm.GDBM_VERSION_INFO`,
:const:`~dbm.ndbm.gdbm_version_info` and :const:`~dbm.ndbm.gdbm_version` if
it is GDBM, or :const:`~dbm.ndbm.BDB_VERSION`, :const:`~dbm.ndbm.bdb_version`,
:const:`~dbm.ndbm.BDB_VERSION_INFO` and :const:`~dbm.ndbm.bdb_version_info` if
it is Berkeley DB.
:const:`dbm.ndbm.library` is now ``'ndbm'`` instead of ``'GNU gdbm'`` for a
classic NDBM library.
138 changes: 138 additions & 0 deletions Modules/_dbmmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,12 @@
static const char which_dbm[] = "GNU gdbm";
#elif defined(USE_NDBM)
#include <ndbm.h>
#ifdef _GDBM_H_
/* ndbm.h is the GDBM compatibility interface. */
static const char which_dbm[] = "GNU gdbm";
#else
static const char which_dbm[] = "ndbm";
#endif
#elif defined(USE_BERKDB)
#ifndef DB_DBM_HSEARCH
#define DB_DBM_HSEARCH 1
Expand Down Expand Up @@ -628,6 +633,136 @@ static PyMethodDef dbmmodule_methods[] = {
{ 0, 0 },
};

#if defined(GDBM_VERSION_MAJOR) || defined(DB_VERSION_MAJOR)
static PyStructSequence_Field version_info_fields[] = {
{"major", "Major release number"},
{"minor", "Minor release number"},
{"patch", "Patch release number"},
{0}
};

static PyObject *
make_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;
}
#endif

#if defined(GDBM_VERSION_MAJOR)
PyDoc_STRVAR(gdbm_version_info__doc__,
"_dbm.gdbm_version_info\n\
\n\
GDBM version information as a named tuple.");

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

static int
add_version_constants(PyObject *module)
{
if (PyModule_AddStringConstant(module, "gdbm_version", gdbm_version) < 0) {
return -1;
}
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_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_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);
return 0;
}
#elif defined(DB_VERSION_MAJOR)
PyDoc_STRVAR(bdb_version_info__doc__,
"_dbm.bdb_version_info\n\
\n\
Berkeley DB version information as a named tuple.");

static PyStructSequence_Desc bdb_version_info_desc = {
"_dbm.bdb_version_info", /* name */
bdb_version_info__doc__, /* doc */
version_info_fields, /* fields */
3
};

static int
add_version_constants(PyObject *module)
{
int major, minor, patch;
const char *version = db_version(&major, &minor, &patch);
if (PyModule_AddStringConstant(module, "BDB_VERSION", DB_VERSION_STRING) < 0) {
return -1;
}
if (PyModule_AddStringConstant(module, "bdb_version", version) < 0) {
return -1;
}
PyTypeObject *version_type;
version_type = PyStructSequence_NewType(&bdb_version_info_desc);
if (version_type == NULL) {
return -1;
}
if (PyModule_Add(module, "BDB_VERSION_INFO",
make_version_info(version_type, DB_VERSION_MAJOR,
DB_VERSION_MINOR, DB_VERSION_PATCH)) < 0)
{
Py_DECREF(version_type);
return -1;
}
if (PyModule_Add(module, "bdb_version_info",
make_version_info(version_type, major, minor, patch)) < 0)
{
Py_DECREF(version_type);
return -1;
}
Py_DECREF(version_type);
return 0;
}
#else
static int
add_version_constants(PyObject *module)
{
return 0;
}
#endif

static int
_dbm_exec(PyObject *module)
{
Expand All @@ -644,6 +779,9 @@ _dbm_exec(PyObject *module)
if (PyModule_AddStringConstant(module, "library", which_dbm) < 0) {
return -1;
}
if (add_version_constants(module) < 0) {
return -1;
}
if (PyModule_AddType(module, (PyTypeObject *)state->dbm_error) < 0) {
return -1;
}
Expand Down
Loading
Loading