Skip to content
42 changes: 39 additions & 3 deletions Doc/library/dbm.rst
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,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.
Expand All @@ -121,6 +122,40 @@ 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 identically:

* **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 (e.g. 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::

Expand All @@ -142,8 +177,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.
Expand Down
9 changes: 9 additions & 0 deletions Lib/test/test_dbm_dumb.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'!',
}

Expand Down Expand Up @@ -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()

Expand Down
4 changes: 4 additions & 0 deletions Lib/test/test_dbm_sqlite3.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
Loading