From 48d986bc50b3b6072e4f903def2bce9694fc89b8 Mon Sep 17 00:00:00 2001 From: Furkan Onder Date: Sun, 18 Oct 2026 04:39:40 +0900 Subject: [PATCH] gh-131323: Add comprehensive key-value type documentation to dbm module (GH-137972) * Add comprehensive key-value type documentation to dbm module * Clarify dbm type support and extend type coverage tests * Re-clarify traditional dbm backend types and drop dumb bytearray test * Clarify dbm docs for sqlite3 array storage bytes example * Clarify dbm.dumb bytearray handling and cover with test * docs: apply devguide style guide to dbm key and value types * Correct the claim that dbm keys and values are handled identically Co-authored-by: Pieter Eendebak --------- (cherry picked from commit 0fedf7852c24727b5a9fa7f2014c18cbaa50cdb9) Co-authored-by: Furkan Onder Co-authored-by: Pieter Eendebak --- Doc/library/dbm.rst | 49 +++++++++++++++++++++++++++++++++--- Lib/test/test_dbm_dumb.py | 9 +++++++ Lib/test/test_dbm_sqlite3.py | 4 +++ 3 files changed, 59 insertions(+), 3 deletions(-) diff --git a/Doc/library/dbm.rst b/Doc/library/dbm.rst index c736ee0d705acc4..cd2d3f6b7c22d8e 100644 --- a/Doc/library/dbm.rst +++ b/Doc/library/dbm.rst @@ -94,7 +94,8 @@ The :meth:`!setdefault` method requires two arguments. Key and values are always stored as :class:`bytes`. This means that when strings are used they are implicitly converted to the default encoding before -being stored. +being stored. For detailed information about accepted types, see +:ref:`dbm-key-value-types`. These objects also support being used in a :keyword:`with` statement, which will automatically close them when done. @@ -115,6 +116,47 @@ will automatically close them when done. :meth:`!clear` methods are now available for all :mod:`!dbm` backends. +.. _dbm-key-value-types: + +Key and value types +------------------- + +The accepted types for keys and values vary by backend. Keys and values are +handled the same way, except where noted below: + +* **Traditional backends:** + + * :mod:`dbm.gnu` and :mod:`dbm.ndbm`: Accept :class:`str` and :class:`bytes` + objects. + * :mod:`dbm.dumb`: Accepts :class:`str` and :class:`bytes` objects; + :class:`bytearray` is acceptable as a value, but not as a key. + +* **SQLite backend** (:mod:`dbm.sqlite3`): Accepts :class:`str`, :class:`bytes`, + :class:`int`, :class:`float`, :class:`bool`, :class:`bytearray`, + :class:`memoryview`, and :class:`array.array` objects. + +**Storage format:** + +All keys and values are stored as :class:`bytes` objects in the database. +When retrieving, you'll always get bytes back, regardless of the original +type stored. + +**Type conversion examples:** + +* ``db['key'] = 'string'`` stored as ``b'string'`` +* ``db['key'] = bytearray(b'data')`` stored as ``b'data'`` + (:mod:`dbm.dumb` and :mod:`dbm.sqlite3` only) +* ``db['key'] = 42`` stored as ``b'42'`` (:mod:`dbm.sqlite3` only) +* ``db['key'] = 3.14`` stored as ``b'3.14'`` (:mod:`dbm.sqlite3` only) +* ``db['key'] = True`` stored as ``b'1'`` (:mod:`dbm.sqlite3` only) +* ``db['key'] = False`` stored as ``b'0'`` (:mod:`dbm.sqlite3` only) +* ``db['key'] = memoryview(b'data')`` stored as ``b'data'`` + (:mod:`dbm.sqlite3` only) +* ``db['key'] = array.array('i', [1, 2, 3])`` stored as bytes (for example, on + little-endian: ``b'\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00'``) + (:mod:`dbm.sqlite3` only) + + The following example records some hostnames and a corresponding title, and then prints out the contents of the database:: @@ -136,8 +178,9 @@ then prints out the contents of the database:: # Often-used methods of the dict interface work too. print(db.get('python.org', b'not present')) - # Storing a non-string key or value will raise an exception (most - # likely a TypeError). + # Storing a non-string key or value behavior depends on the backend: + # - Traditional backends (ndbm, gnu, dumb) will raise a TypeError + # - The sqlite3 backend accepts it and converts to bytes db['www.yahoo.com'] = 4 # db is automatically closed when leaving the with statement. diff --git a/Lib/test/test_dbm_dumb.py b/Lib/test/test_dbm_dumb.py index 672f9092207cf62..c24c9c3817a1e73 100644 --- a/Lib/test/test_dbm_dumb.py +++ b/Lib/test/test_dbm_dumb.py @@ -31,6 +31,7 @@ class DumbDBMTestCase(unittest.TestCase): b'd': b'way', b'f': b'Guido', b'g': b'intended', + b'h': bytearray(b'bytearray_value'), '\u00fc'.encode('utf-8') : b'!', } @@ -368,6 +369,14 @@ def test_nonascii_filename(self): self.assertTrue(b'key' in db) self.assertEqual(db[b'key'], b'value') + def test_bytearray(self): + self.init_db() + with contextlib.closing(dumbdbm.open(_fname, 'r')) as f: + self.assertEqual(f[b'h'], b'bytearray_value') + with contextlib.closing(dumbdbm.open(_fname)) as f: + with self.assertRaises(TypeError): + f[bytearray(b'bytearray_key')] = b'value' + def test_open_with_pathlib_path(self): dumbdbm.open(os_helper.FakePath(_fname), "c").close() diff --git a/Lib/test/test_dbm_sqlite3.py b/Lib/test/test_dbm_sqlite3.py index f367a98865d4aad..0e87bb1c6e8dbf8 100644 --- a/Lib/test/test_dbm_sqlite3.py +++ b/Lib/test/test_dbm_sqlite3.py @@ -267,6 +267,10 @@ class DataTypes(_SQLiteDbmTests): (3.14, b"3.14"), ("string", b"string"), (b"bytes", b"bytes"), + (True, b"1"), + (False, b"0"), + (bytearray(b"bytearray"), b"bytearray"), + (memoryview(b"memoryview"), b"memoryview"), ) def setUp(self):