Skip to content

Commit

Permalink
Issue #10203: sqlite3.Row now truly supports sequence protocol. In pa…
Browse files Browse the repository at this point in the history
…rticulr

it supports reverse() and negative indices.  Original patch by Claudiu Popa.
  • Loading branch information
serhiy-storchaka committed May 28, 2014
1 parent d8a58a6 commit 98db768
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 5 deletions.
2 changes: 2 additions & 0 deletions Lib/sqlite3/dbapi2.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import datetime
import time
import collections.abc

from _sqlite3 import *

Expand Down Expand Up @@ -50,6 +51,7 @@ def TimestampFromTicks(ticks):
sqlite_version_info = tuple([int(x) for x in sqlite_version.split(".")])

Binary = memoryview
collections.abc.Sequence.register(Row)

def register_adapters_and_converters():
def adapt_date(val):
Expand Down
26 changes: 23 additions & 3 deletions Lib/sqlite3/test/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

import unittest
import sqlite3 as sqlite
from collections.abc import Sequence

class MyConnection(sqlite.Connection):
def __init__(self, *args, **kwargs):
Expand Down Expand Up @@ -96,9 +97,19 @@ def CheckSqliteRowIndex(self):
self.assertEqual(col1, 1, "by name: wrong result for column 'A'")
self.assertEqual(col2, 2, "by name: wrong result for column 'B'")

col1, col2 = row[0], row[1]
self.assertEqual(col1, 1, "by index: wrong result for column 0")
self.assertEqual(col2, 2, "by index: wrong result for column 1")
self.assertEqual(row[0], 1, "by index: wrong result for column 0")
self.assertEqual(row[1], 2, "by index: wrong result for column 1")
self.assertEqual(row[-1], 2, "by index: wrong result for column -1")
self.assertEqual(row[-2], 1, "by index: wrong result for column -2")

with self.assertRaises(IndexError):
row['c']
with self.assertRaises(IndexError):
row[2]
with self.assertRaises(IndexError):
row[-3]
with self.assertRaises(IndexError):
row[2**1000]

def CheckSqliteRowIter(self):
"""Checks if the row object is iterable"""
Expand Down Expand Up @@ -142,6 +153,15 @@ def CheckSqliteRowHashCmp(self):
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.assertIsInstance(row, Sequence)

def tearDown(self):
self.con.close()

Expand Down
3 changes: 3 additions & 0 deletions Misc/NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ Core and Builtins
Library
-------

- Issue #10203: sqlite3.Row now truly supports sequence protocol. In particulr
it supports reverse() and negative indices. Original patch by Claudiu Popa.

- Issue #18807: If copying (no symlinks) specified for a venv, then the python
interpreter aliases (python, python3) are now created by copying rather than
symlinking.
Expand Down
24 changes: 22 additions & 2 deletions Modules/_sqlite/row.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,16 @@ int pysqlite_row_init(pysqlite_Row* self, PyObject* args, PyObject* kwargs)
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;
Py_ssize_t _idx;
char* key;
Py_ssize_t nitems, i;
char* compare_key;
Expand All @@ -76,7 +83,11 @@ PyObject* pysqlite_row_subscript(pysqlite_Row* self, PyObject* idx)
PyObject* item;

if (PyLong_Check(idx)) {
_idx = PyLong_AsLong(idx);
_idx = PyNumber_AsSsize_t(idx, PyExc_IndexError);
if (_idx == -1 && PyErr_Occurred())
return NULL;
if (_idx < 0)
_idx += PyTuple_GET_SIZE(self->data);
item = PyTuple_GetItem(self->data, _idx);
Py_XINCREF(item);
return item;
Expand Down Expand Up @@ -198,6 +209,14 @@ PyMappingMethods pysqlite_row_as_mapping = {
/* 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.")},
Expand Down Expand Up @@ -251,5 +270,6 @@ extern int pysqlite_row_setup_types(void)
{
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);
}

0 comments on commit 98db768

Please sign in to comment.