diff -r 8676e436c0f0 Lib/sqlite3/dbapi2.py --- a/Lib/sqlite3/dbapi2.py Wed May 21 17:12:21 2014 +0300 +++ b/Lib/sqlite3/dbapi2.py Mon May 26 23:56:40 2014 +0300 @@ -22,6 +22,7 @@ import datetime import time +from collections.abc import Sequence from _sqlite3 import * @@ -50,6 +51,7 @@ sqlite_version_info = tuple([int(x) for x in sqlite_version.split(".")]) Binary = memoryview +Sequence.register(Row) def register_adapters_and_converters(): def adapt_date(val): diff -r 8676e436c0f0 Lib/sqlite3/test/factory.py --- a/Lib/sqlite3/test/factory.py Wed May 21 17:12:21 2014 +0300 +++ b/Lib/sqlite3/test/factory.py Mon May 26 23:56:40 2014 +0300 @@ -23,6 +23,7 @@ import unittest import sqlite3 as sqlite +from collections.abc import Sequence class MyConnection(sqlite.Connection): def __init__(self, *args, **kwargs): @@ -142,6 +143,21 @@ self.assertNotEqual(row_1, row_3) self.assertNotEqual(hash(row_1), hash(row_3)) + def CheckSqliteRowAsSequence(self): + """ Checks if the row object can act like a sequence """ + self.con.row_factory = sqlite.Row + row = self.con.execute("select 1 as a, 2 as b").fetchone() + + as_tuple = tuple(row) + self.assertEqual(list(reversed(row)), list(reversed(as_tuple))) + self.assertEqual(row[-1], 2) + self.assertEqual(row[-2], 1) + with self.assertRaises(IndexError): + row[-3] + with self.assertRaises(OverflowError): + row[2 ** 1000] + self.assertIsInstance(row, Sequence) + def tearDown(self): self.con.close() diff -r 8676e436c0f0 Modules/_sqlite/row.c --- a/Modules/_sqlite/row.c Wed May 21 17:12:21 2014 +0300 +++ b/Modules/_sqlite/row.c Mon May 26 23:56:40 2014 +0300 @@ -63,9 +63,17 @@ return 0; } +PyObject* pysqlite_row_item(pysqlite_Row* self, Py_ssize_t idx) +{ + PyObject* item = PyTuple_GetItem(self->data, idx); + Py_XINCREF(item); + return item; +} + PyObject* pysqlite_row_subscript(pysqlite_Row* self, PyObject* idx) { long _idx; + int long_overflow; char* key; Py_ssize_t nitems, i; char* compare_key; @@ -75,8 +83,15 @@ PyObject* item; - if (PyLong_Check(idx)) { - _idx = PyLong_AsLong(idx); + if (PyLong_Check(idx)) { + _idx = PyLong_AsLongAndOverflow(idx, &long_overflow); + if (long_overflow) { + PyErr_SetString(PyExc_OverflowError, + "Row index doesn't fit into a long."); + return NULL; + } + if (_idx < 0) + _idx += PyTuple_GET_SIZE(self->data); item = PyTuple_GetItem(self->data, _idx); Py_XINCREF(item); return item; @@ -198,6 +213,14 @@ /* mp_ass_subscript */ (objobjargproc)0, }; +static PySequenceMethods pysqlite_row_as_sequence = { + /* sq_length */ (lenfunc)pysqlite_row_length, + /* sq_concat */ 0, + /* sq_repeat */ 0, + /* sq_item */ (ssizeargfunc)pysqlite_row_item, +}; + + static PyMethodDef pysqlite_row_methods[] = { {"keys", (PyCFunction)pysqlite_row_keys, METH_NOARGS, PyDoc_STR("Returns the keys of the row.")}, @@ -251,5 +274,6 @@ { pysqlite_RowType.tp_new = PyType_GenericNew; pysqlite_RowType.tp_as_mapping = &pysqlite_row_as_mapping; + pysqlite_RowType.tp_as_sequence = &pysqlite_row_as_sequence; return PyType_Ready(&pysqlite_RowType); }