Skip to content

Commit 0fedf78

Browse files
gh-131323: Add comprehensive key-value type documentation to dbm module (#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 <pieter.eendebak@gmail.com> --------- Co-authored-by: Pieter Eendebak <pieter.eendebak@gmail.com>
1 parent 18f3a71 commit 0fedf78

3 files changed

Lines changed: 59 additions & 3 deletions

File tree

‎Doc/library/dbm.rst‎

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ The :meth:`!setdefault` method requires two arguments.
100100

101101
Key and values are always stored as :class:`bytes`. This means that when
102102
strings are used they are implicitly converted to the default encoding before
103-
being stored.
103+
being stored. For detailed information about accepted types, see
104+
:ref:`dbm-key-value-types`.
104105

105106
These objects also support being used in a :keyword:`with` statement, which
106107
will automatically close them when done.
@@ -121,6 +122,47 @@ will automatically close them when done.
121122
:meth:`!clear` methods are now available for all :mod:`!dbm` backends.
122123

123124

125+
.. _dbm-key-value-types:
126+
127+
Key and value types
128+
-------------------
129+
130+
The accepted types for keys and values vary by backend. Keys and values are
131+
handled the same way, except where noted below:
132+
133+
* **Traditional backends:**
134+
135+
* :mod:`dbm.gnu` and :mod:`dbm.ndbm`: Accept :class:`str` and :class:`bytes`
136+
objects.
137+
* :mod:`dbm.dumb`: Accepts :class:`str` and :class:`bytes` objects;
138+
:class:`bytearray` is acceptable as a value, but not as a key.
139+
140+
* **SQLite backend** (:mod:`dbm.sqlite3`): Accepts :class:`str`, :class:`bytes`,
141+
:class:`int`, :class:`float`, :class:`bool`, :class:`bytearray`,
142+
:class:`memoryview`, and :class:`array.array` objects.
143+
144+
**Storage format:**
145+
146+
All keys and values are stored as :class:`bytes` objects in the database.
147+
When retrieving, you'll always get bytes back, regardless of the original
148+
type stored.
149+
150+
**Type conversion examples:**
151+
152+
* ``db['key'] = 'string'`` stored as ``b'string'``
153+
* ``db['key'] = bytearray(b'data')`` stored as ``b'data'``
154+
(:mod:`dbm.dumb` and :mod:`dbm.sqlite3` only)
155+
* ``db['key'] = 42`` stored as ``b'42'`` (:mod:`dbm.sqlite3` only)
156+
* ``db['key'] = 3.14`` stored as ``b'3.14'`` (:mod:`dbm.sqlite3` only)
157+
* ``db['key'] = True`` stored as ``b'1'`` (:mod:`dbm.sqlite3` only)
158+
* ``db['key'] = False`` stored as ``b'0'`` (:mod:`dbm.sqlite3` only)
159+
* ``db['key'] = memoryview(b'data')`` stored as ``b'data'``
160+
(:mod:`dbm.sqlite3` only)
161+
* ``db['key'] = array.array('i', [1, 2, 3])`` stored as bytes (for example, on
162+
little-endian: ``b'\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00'``)
163+
(:mod:`dbm.sqlite3` only)
164+
165+
124166
The following example records some hostnames and a corresponding title, and
125167
then prints out the contents of the database::
126168

@@ -142,8 +184,9 @@ then prints out the contents of the database::
142184
# Often-used methods of the dict interface work too.
143185
print(db.get('python.org', b'not present'))
144186

145-
# Storing a non-string key or value will raise an exception (most
146-
# likely a TypeError).
187+
# Storing a non-string key or value behavior depends on the backend:
188+
# - Traditional backends (ndbm, gnu, dumb) will raise a TypeError
189+
# - The sqlite3 backend accepts it and converts to bytes
147190
db['www.yahoo.com'] = 4
148191

149192
# db is automatically closed when leaving the with statement.

‎Lib/test/test_dbm_dumb.py‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class DumbDBMTestCase(unittest.TestCase):
3131
b'd': b'way',
3232
b'f': b'Guido',
3333
b'g': b'intended',
34+
b'h': bytearray(b'bytearray_value'),
3435
'\u00fc'.encode('utf-8') : b'!',
3536
}
3637

@@ -378,6 +379,14 @@ def test_nonascii_filename(self):
378379
self.assertTrue(b'key' in db)
379380
self.assertEqual(db[b'key'], b'value')
380381

382+
def test_bytearray(self):
383+
self.init_db()
384+
with contextlib.closing(dumbdbm.open(_fname, 'r')) as f:
385+
self.assertEqual(f[b'h'], b'bytearray_value')
386+
with contextlib.closing(dumbdbm.open(_fname)) as f:
387+
with self.assertRaises(TypeError):
388+
f[bytearray(b'bytearray_key')] = b'value'
389+
381390
def test_open_with_pathlib_path(self):
382391
dumbdbm.open(os_helper.FakePath(_fname), "c").close()
383392

‎Lib/test/test_dbm_sqlite3.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,10 @@ class DataTypes(_SQLiteDbmTests):
267267
(3.14, b"3.14"),
268268
("string", b"string"),
269269
(b"bytes", b"bytes"),
270+
(True, b"1"),
271+
(False, b"0"),
272+
(bytearray(b"bytearray"), b"bytearray"),
273+
(memoryview(b"memoryview"), b"memoryview"),
270274
)
271275

272276
def setUp(self):

0 commit comments

Comments
 (0)