Skip to content

Commit b233d95

Browse files
authored
Make Cursor an iterator conforming to DB-API 2.0 (#785)
1 parent 55cc53f commit b233d95

2 files changed

Lines changed: 31 additions & 18 deletions

File tree

src/MySQLdb/cursors.py

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
from ._exceptions import ProgrammingError
1010

11-
1211
#: Regular expression for ``Cursor.executemany```.
1312
#: executemany only supports simple bulk insert.
1413
#: You can use it to load large dataset.
@@ -329,7 +328,13 @@ def _fetch_row(self, size=1):
329328
return self._result.fetch_row(size, self._fetch_type)
330329

331330
def __iter__(self):
332-
return iter(self.fetchone, None)
331+
return self
332+
333+
def __next__(self):
334+
row = self.fetchone()
335+
if row is None:
336+
raise StopIteration
337+
return row
333338

334339
def __getattr__(self, name):
335340
# DB-API 2.0 optional extension says these errors can be accessed
@@ -419,11 +424,6 @@ def scroll(self, value, mode="relative"):
419424
raise IndexError("out of range")
420425
self.rownumber = r
421426

422-
def __iter__(self):
423-
self._check_executed()
424-
result = self.rownumber and self._rows[self.rownumber :] or self._rows
425-
return iter(result)
426-
427427

428428
class CursorUseResultMixIn:
429429
"""This is a MixIn class which causes the result set to be stored
@@ -464,17 +464,6 @@ def fetchall(self):
464464
self.rownumber = self.rownumber + len(r)
465465
return r
466466

467-
def __iter__(self):
468-
return self
469-
470-
def next(self):
471-
row = self.fetchone()
472-
if row is None:
473-
raise StopIteration
474-
return row
475-
476-
__next__ = next
477-
478467

479468
class CursorTupleRowsMixIn:
480469
"""This is a MixIn class that causes all rows to be returned as tuples,

tests/test_cursor.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,3 +284,27 @@ def test_sscursor_warning_count():
284284
rows = cursor.fetchmany(2)
285285
assert len(rows) == 1
286286
assert cursor.warning_count == 1
287+
288+
289+
@pytest.mark.parametrize("Cursor", [MySQLdb.cursors.Cursor, MySQLdb.cursors.SSCursor])
290+
def test_cursor_is_iterator(Cursor):
291+
conn = connect()
292+
cursor = conn.cursor(Cursor)
293+
294+
cursor.execute("DROP TABLE IF EXISTS test_cursor_is_iterator")
295+
cursor.execute(
296+
"CREATE TABLE test_cursor_is_iterator (id INT PRIMARY KEY, name VARCHAR(20))"
297+
)
298+
_tables.append("test_cursor_is_iterator")
299+
cursor.executemany(
300+
"INSERT INTO test_cursor_is_iterator (id, name) VALUES (%s, %s)",
301+
[(1, "a"), (2, "b"), (3, "c")],
302+
)
303+
304+
cursor.execute("SELECT name FROM test_cursor_is_iterator ORDER BY id")
305+
assert iter(cursor) is cursor
306+
assert next(cursor) == ("a",)
307+
assert next(cursor) == ("b",)
308+
assert next(cursor) == ("c",)
309+
with pytest.raises(StopIteration):
310+
next(cursor)

0 commit comments

Comments
 (0)