diff --git a/Lib/test/test_sqlite3/test_factory.py b/Lib/test/test_sqlite3/test_factory.py index adc15485e28580c..d28754ff14729cc 100644 --- a/Lib/test/test_sqlite3/test_factory.py +++ b/Lib/test/test_sqlite3/test_factory.py @@ -248,6 +248,17 @@ def test_sqlite_row_hash_cmp(self): self.assertEqual(hash(row_1), hash(row_2)) + def test_sqlite_row_hash_unhashable(self): + # An unhashable value must raise TypeError, not SystemError. + sqlite.register_converter("LST", lambda b: [1, 2, 3]) + self.addCleanup(sqlite.converters.pop, "LST", None) + with memory_database(detect_types=sqlite.PARSE_DECLTYPES) as con: + con.row_factory = sqlite.Row + con.execute("create table t(x LST)") + con.execute("insert into t values(?)", (b"x",)) + row = con.execute("select x from t").fetchone() + self.assertRaisesRegex(TypeError, "unhashable", hash, row) + def test_sqlite_row_as_sequence(self): # Checks if the row object can act like a sequence. row = self.con.execute("select 1 as a, 2 as b").fetchone() diff --git a/Misc/NEWS.d/next/Library/2026-07-14-19-45-21.gh-issue-153695.UYdZJZ.rst b/Misc/NEWS.d/next/Library/2026-07-14-19-45-21.gh-issue-153695.UYdZJZ.rst new file mode 100644 index 000000000000000..43d7feebf97d6f2 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-07-14-19-45-21.gh-issue-153695.UYdZJZ.rst @@ -0,0 +1,2 @@ +Hashing a :class:`sqlite3.Row` that contains an unhashable value now raises +:exc:`TypeError` instead of :exc:`SystemError`. Patch by tonghuaroot. diff --git a/Modules/_sqlite/row.c b/Modules/_sqlite/row.c index 3427fae373d4fc8..7e428bd487dfa58 100644 --- a/Modules/_sqlite/row.c +++ b/Modules/_sqlite/row.c @@ -220,7 +220,15 @@ static PyObject* pysqlite_iter(pysqlite_Row* self) static Py_hash_t pysqlite_row_hash(pysqlite_Row *self) { - return PyObject_Hash(self->description) ^ PyObject_Hash(self->data); + Py_hash_t hash_description = PyObject_Hash(self->description); + if (hash_description == -1) { + return -1; + } + Py_hash_t hash_data = PyObject_Hash(self->data); + if (hash_data == -1) { + return -1; + } + return hash_description ^ hash_data; } static PyObject* pysqlite_row_richcompare(pysqlite_Row *self, PyObject *_other, int opid)