Skip to content

Commit 33c3560

Browse files
committed
Added single
1 parent 18dd645 commit 33c3560

File tree

3 files changed

+43
-4
lines changed

3 files changed

+43
-4
lines changed

neo4j/v1/exceptions.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@ class ProtocolError(Exception):
2323
""" Raised when an unexpected or unsupported protocol event occurs.
2424
"""
2525

26-
pass
27-
2826

2927
class CypherError(Exception):
3028
""" Raised when the Cypher engine returns an error to the client.
@@ -38,3 +36,8 @@ def __init__(self, data):
3836
for key, value in data.items():
3937
if not key.startswith("_"):
4038
setattr(self, key, value)
39+
40+
41+
class ResultError(Exception):
42+
""" Raised when an error occurs while consuming a result.
43+
"""

neo4j/v1/session.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class which can be used to obtain `Driver` instances that are used for
3333
from .compat import integer, string, urlparse
3434
from .connection import connect, Response, RUN, PULL_ALL
3535
from .constants import ENCRYPTED_DEFAULT, TRUST_DEFAULT, TRUST_SIGNED_CERTIFICATES
36-
from .exceptions import CypherError
36+
from .exceptions import CypherError, ResultError
3737
from .ssl_compat import SSL_AVAILABLE, SSLContext, PROTOCOL_SSLv23, OP_NO_SSLv2, CERT_REQUIRED
3838
from .types import hydrated
3939

@@ -256,6 +256,18 @@ def consume(self):
256256
self.connection = None
257257
return self._summary
258258

259+
def single(self):
260+
""" Return the next record, failing if none or more than one remain.
261+
"""
262+
records = list(self)
263+
num_records = len(records)
264+
if num_records == 0:
265+
raise ResultError("No records found in stream")
266+
elif num_records != 1:
267+
raise ResultError("Multiple records found in stream")
268+
else:
269+
return records[0]
270+
259271

260272
class ResultSummary(object):
261273
""" A summary of execution returned with a :class:`.StatementResult` object.

test/test_session.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
from mock import patch
2727

2828
from neo4j.v1.constants import TRUST_ON_FIRST_USE
29-
from neo4j.v1.exceptions import CypherError
29+
from neo4j.v1.exceptions import CypherError, ResultError
3030
from neo4j.v1.session import GraphDatabase, basic_auth, Record, SSL_AVAILABLE
3131
from neo4j.v1.types import Node, Relationship, Path
3232

@@ -575,3 +575,27 @@ def test_can_consume_result_after_session_with_error(self):
575575
tx.commit()
576576
session.close()
577577
assert [record[0] for record in result] == [1, 2, 3]
578+
579+
def test_single_with_exactly_one_record(self):
580+
session = self.driver.session()
581+
result = session.run("UNWIND range(1, 1) AS n RETURN n")
582+
record = result.single()
583+
assert list(record.values()) == [1]
584+
585+
def test_single_with_no_records(self):
586+
session = self.driver.session()
587+
result = session.run("CREATE ()")
588+
with self.assertRaises(ResultError):
589+
_ = result.single()
590+
591+
def test_single_with_multiple_records(self):
592+
session = self.driver.session()
593+
result = session.run("UNWIND range(1, 3) AS n RETURN n")
594+
with self.assertRaises(ResultError):
595+
_ = result.single()
596+
597+
def test_single_consumes_entire_result(self):
598+
session = self.driver.session()
599+
result = session.run("UNWIND range(1, 1) AS n RETURN n")
600+
_ = result.single()
601+
assert result._consumed

0 commit comments

Comments
 (0)