Skip to content

Commit 39acd5c

Browse files
committed
Make cursor iterable
1 parent 27c36d5 commit 39acd5c

File tree

6 files changed

+105
-160
lines changed

6 files changed

+105
-160
lines changed

README.rst

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@ Example Usage
2828
driver = GraphDatabase.driver("bolt://localhost")
2929
session = driver.session()
3030
session.run("CREATE (a:Person {name:'Bob'})")
31-
cursor = session.run("MATCH (a:Person) RETURN a.name AS name")
32-
while cursor.next()
33-
print(cursor["name"])
34-
cursor.close()
31+
result = session.run("MATCH (a:Person) RETURN a.name AS name")
32+
for record in result:
33+
print(record["name"])
34+
result.close()
3535
session.close()
3636
3737

examples/test_examples.py

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ def test_minimal_working_example(self):
5353
session.run("CREATE (a:Person {name:'Arthur', title:'King'})", )
5454

5555
result = session.run("MATCH (a:Person) WHERE a.name = 'Arthur' RETURN a.name AS name, a.title AS title")
56-
while result.next():
57-
print("%s %s" % (result["title"], result["name"]))
56+
for record in result:
57+
print("%s %s" % (record["title"], record["name"]))
5858

5959
session.close()
6060
# end::minimal-example[]
@@ -116,8 +116,8 @@ def test_result_cursor(self):
116116
result = session.run("MATCH (tool:Tool) WHERE tool.name CONTAINS {term} "
117117
"RETURN tool.name", {"term": search_term})
118118
print("List of tools called %r:" % search_term)
119-
while result.next():
120-
print(result["tool.name"])
119+
for record in result:
120+
print(record["tool.name"])
121121
# end::result-cursor[]
122122
session.close()
123123

@@ -127,10 +127,10 @@ def test_cursor_nesting(self):
127127
# tag::retain-result-query[]
128128
result = session.run("MATCH (knight:Person:Knight) WHERE knight.castle = {castle} "
129129
"RETURN id(knight) AS knight_id", {"castle": "Camelot"})
130-
while result.next():
130+
for record in result:
131131
session.run("MATCH (knight) WHERE id(knight) = {id} "
132132
"MATCH (king:Person) WHERE king.name = {king} "
133-
"CREATE (knight)-[:DEFENDS]->(king)", {"id": result["knight_id"], "king": "Arthur"})
133+
"CREATE (knight)-[:DEFENDS]->(king)", {"id": record["knight_id"], "king": "Arthur"})
134134
# end::retain-result-query[]
135135
session.close()
136136

@@ -140,9 +140,8 @@ def test_result_retention(self):
140140
# tag::retain-result-process[]
141141
result = session.run("MATCH (knight:Person:Knight) WHERE knight.castle = {castle} "
142142
"RETURN id(knight) AS knight_id", {"castle": "Camelot"})
143-
id_records = list(result.stream())
144-
145-
for record in id_records:
143+
retained_result = list(result)
144+
for record in retained_result:
146145
session.run("MATCH (knight) WHERE id(knight) = {id} "
147146
"MATCH (king:Person) WHERE king.name = {king} "
148147
"CREATE (knight)-[:DEFENDS]->(king)", {"id": record["knight_id"], "king": "Arthur"})
@@ -158,7 +157,7 @@ def test_transaction_commit(self):
158157
tx.commit()
159158
# end::transaction-commit[]
160159
result = session.run("MATCH (p:Person {name: 'Guinevere'}) RETURN count(p)")
161-
assert result.next()
160+
next(result)
162161
assert result["count(p)"] == 1
163162
assert result.at_end
164163
session.close()
@@ -172,7 +171,7 @@ def test_transaction_rollback(self):
172171
tx.rollback()
173172
# end::transaction-rollback[]
174173
result = session.run("MATCH (p:Person {name: 'Merlin'}) RETURN count(p)")
175-
assert result.next()
174+
next(result)
176175
assert result["count(p)"] == 0
177176
assert result.at_end
178177
session.close()
@@ -183,8 +182,7 @@ def test_result_summary_query_profile(self):
183182
# tag::result-summary-query-profile[]
184183
result = session.run("PROFILE MATCH (p:Person {name: {name}}) "
185184
"RETURN id(p)", {"name": "Arthur"})
186-
while result.next():
187-
pass # skip the records to get to the summary
185+
list(result) # skip the records to get to the summary
188186
print(result.summary.statement_type)
189187
print(result.summary.profile)
190188
# end::result-summary-query-profile[]
@@ -195,8 +193,7 @@ def test_result_summary_notifications(self):
195193
session = driver.session()
196194
# tag::result-summary-notifications[]
197195
result = session.run("EXPLAIN MATCH (king), (queen) RETURN king, queen")
198-
while result.next():
199-
pass # skip the records to get to the summary
196+
list(result) # skip the records to get to the summary
200197
for notification in result.summary.notifications:
201198
print(notification)
202199
# end::result-summary-notifications[]

neo4j/__main__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,21 +62,21 @@ def main():
6262
for _ in range(args.times):
6363
for statement in args.statement:
6464
try:
65-
cursor = session.run(statement, parameters)
65+
result = session.run(statement, parameters)
6666
except CypherError as error:
6767
stderr.write("%s: %s\r\n" % (error.code, error.message))
6868
else:
6969
if not args.quiet:
7070
has_results = False
71-
for i, record in enumerate(cursor.stream()):
71+
for i, record in enumerate(result):
7272
has_results = True
7373
if i == 0:
7474
stdout.write("%s\r\n" % "\t".join(record.keys()))
7575
stdout.write("%s\r\n" % "\t".join(map(repr, record)))
7676
if has_results:
7777
stdout.write("\r\n")
7878
if args.summary:
79-
summary = cursor.summary
79+
summary = result.summary
8080
stdout.write("Statement : %r\r\n" % summary.statement)
8181
stdout.write("Parameters : %r\r\n" % summary.parameters)
8282
stdout.write("Statement Type : %r\r\n" % summary.statement_type)

neo4j/v1/session.py

Lines changed: 21 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,27 @@ def __init__(self, connection, statement, parameters):
170170
self._summary = None
171171
self._consumed = False
172172

173+
def __next__(self):
174+
if self._record_buffer:
175+
values = self._record_buffer.popleft()
176+
self._position += 1
177+
self._current = Record(self.keys(), tuple(map(hydrated, values)))
178+
return self._current
179+
elif self._consumed:
180+
raise StopIteration()
181+
else:
182+
self._connection.fetch()
183+
return self.__next__()
184+
185+
def __iter__(self):
186+
return self
187+
188+
def __getitem__(self, item):
189+
current = self._current
190+
if current is None:
191+
raise TypeError("No current record")
192+
return current[item]
193+
173194
def is_open(self):
174195
""" Return ``True`` if this cursor is still open, ``False`` otherwise.
175196
"""
@@ -183,26 +204,6 @@ def close(self):
183204
self._consume()
184205
self._connection = None
185206

186-
def next(self):
187-
""" Advance to the next record, if available, and return a boolean
188-
to indicate whether or not the cursor has moved.
189-
"""
190-
if self._record_buffer:
191-
values = self._record_buffer.popleft()
192-
self._current = Record(self.keys(), tuple(map(hydrated, values)))
193-
self._position += 1
194-
return True
195-
elif self._consumed:
196-
return False
197-
else:
198-
self._connection.fetch()
199-
return self.next()
200-
201-
def record(self):
202-
""" Return the current record.
203-
"""
204-
return self._current
205-
206207
@property
207208
def position(self):
208209
""" Return the current cursor position.
@@ -222,18 +223,6 @@ def at_end(self):
222223
self._connection.fetch()
223224
return self.at_end
224225

225-
def stream(self):
226-
""" Yield all subsequent records.
227-
"""
228-
while self.next():
229-
yield self.record()
230-
231-
def __getitem__(self, item):
232-
current = self._current
233-
if current is None:
234-
raise TypeError("No current record")
235-
return current[item]
236-
237226
def keys(self):
238227
""" Return the keys for the records.
239228
"""
@@ -686,20 +675,3 @@ def __eq__(self, other):
686675

687676
def __ne__(self, other):
688677
return not self.__eq__(other)
689-
690-
691-
def record(obj):
692-
""" Obtain an immutable record for the given object
693-
(either by calling obj.__record__() or by copying out the record data)
694-
"""
695-
try:
696-
return obj.__record__()
697-
except AttributeError:
698-
keys = obj.keys()
699-
values = []
700-
for key in keys:
701-
values.append(obj[key])
702-
return Record(keys, values)
703-
704-
705-

test/tck/tck_util.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,16 @@
2828

2929
def send_string(text):
3030
session = driver.session()
31-
cursor = session.run(text)
31+
result = session.run(text)
3232
session.close()
33-
return list(cursor.stream())
33+
return list(result)
3434

3535

3636
def send_parameters(statement, parameters):
3737
session = driver.session()
38-
cursor = session.run(statement, parameters)
38+
result = session.run(statement, parameters)
3939
session.close()
40-
return list(cursor.stream())
40+
return list(result)
4141

4242

4343
try:

0 commit comments

Comments
 (0)