From a04db098b8ebce3223e89ca0f73961be5c3bfaff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?tonghuaroot=20=28=E7=AB=A5=E8=AF=9D=29?= Date: Wed, 15 Jul 2026 06:23:58 +0800 Subject: [PATCH] gh-153695: Fix sqlite3.Row.__hash__ with an unhashable value (#153696) (cherry picked from commit b704b647b2295924ebaf8d20ddf88925095039bb) --- Lib/test/test_sqlite3/test_factory.py | 11 +++++++++++ .../2026-07-14-19-45-21.gh-issue-153695.UYdZJZ.rst | 2 ++ Modules/_sqlite/row.c | 10 +++++++++- 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2026-07-14-19-45-21.gh-issue-153695.UYdZJZ.rst diff --git a/Lib/test/test_sqlite3/test_factory.py b/Lib/test/test_sqlite3/test_factory.py index adc15485e28580..d28754ff14729c 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 00000000000000..43d7feebf97d6f --- /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 3427fae373d4fc..7e428bd487dfa5 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)