Skip to content

Commit 0da2579

Browse files
committed
Merge 1.0
1 parent a403b17 commit 0da2579

22 files changed

+680
-439
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

docs/source/index.rst

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,13 @@ Session API
2424
.. autoclass:: neo4j.v1.Record
2525
:members:
2626

27-
.. autofunction:: neo4j.v1.record
28-
29-
.. autoclass:: neo4j.v1.ResultCursor
27+
.. autoclass:: neo4j.v1.StatementResult
3028
:members:
3129

3230
.. autoclass:: neo4j.v1.ResultSummary
3331
:members:
3432

35-
.. autoclass:: neo4j.v1.StatementStatistics
33+
.. autoclass:: neo4j.v1.Counters
3634
:members:
3735

3836

example.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@
1818
# See the License for the specific language governing permissions and
1919
# limitations under the License.
2020

21-
from neo4j.v1.session import GraphDatabase
21+
from neo4j.v1.session import GraphDatabase, basic_auth
2222

23-
driver = GraphDatabase.driver("bolt://localhost")
23+
24+
driver = GraphDatabase.driver("bolt://localhost", auth=basic_auth("neo4j", "neo4j"))
2425
session = driver.session()
2526

2627
session.run("MERGE (a:Person {name:'Alice'})")

examples/test_examples.py

Lines changed: 78 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,26 @@
1919
# limitations under the License.
2020

2121

22-
from unittest import TestCase
22+
from unittest import skip
2323

24+
from neo4j.v1 import TRUST_ON_FIRST_USE, TRUST_SIGNED_CERTIFICATES
25+
from test.util import ServerTestCase
26+
27+
# Do not change the contents of this tagged section without good reason*
2428
# tag::minimal-example-import[]
25-
from neo4j.v1 import GraphDatabase
29+
from neo4j.v1 import GraphDatabase, basic_auth
2630
# end::minimal-example-import[]
31+
# (* "good reason" is defined as knowing what you are doing)
32+
33+
34+
auth_token = basic_auth("neo4j", "password")
2735

2836

29-
class FreshDatabaseTestCase(TestCase):
37+
class FreshDatabaseTestCase(ServerTestCase):
3038

3139
def setUp(self):
32-
session = GraphDatabase.driver("bolt://localhost").session()
40+
ServerTestCase.setUp(self)
41+
session = GraphDatabase.driver("bolt://localhost", auth=auth_token).session()
3342
session.run("MATCH (n) DETACH DELETE n")
3443
session.close()
3544

@@ -38,14 +47,14 @@ class MinimalWorkingExampleTestCase(FreshDatabaseTestCase):
3847

3948
def test_minimal_working_example(self):
4049
# tag::minimal-example[]
41-
driver = GraphDatabase.driver("bolt://localhost")
50+
driver = GraphDatabase.driver("bolt://localhost", auth=basic_auth("neo4j", "password"))
4251
session = driver.session()
4352

44-
session.run("CREATE (neo:Person {name:'Neo', age:23})")
53+
session.run("CREATE (a:Person {name:'Arthur', title:'King'})", )
4554

46-
cursor = session.run("MATCH (p:Person) WHERE p.name = 'Neo' RETURN p.age")
47-
while cursor.next():
48-
print("Neo is %d years old." % cursor["p.age"])
55+
result = session.run("MATCH (a:Person) WHERE a.name = 'Arthur' RETURN a.name AS name, a.title AS title")
56+
for record in result:
57+
print("%s %s" % (record["title"], record["name"]))
4958

5059
session.close()
5160
# end::minimal-example[]
@@ -55,7 +64,7 @@ class ExamplesTestCase(FreshDatabaseTestCase):
5564

5665
def test_construct_driver(self):
5766
# tag::construct-driver[]
58-
driver = GraphDatabase.driver("bolt://localhost")
67+
driver = GraphDatabase.driver("bolt://localhost", auth=basic_auth("neo4j", "password"))
5968
# end::construct-driver[]
6069
return driver
6170

@@ -67,124 +76,123 @@ def test_configuration(self):
6776

6877
def test_tls_require_encryption(self):
6978
# tag::tls-require-encryption[]
70-
# TODO: Unfortunately, this feature is not yet implemented for Python
71-
pass
79+
driver = GraphDatabase.driver("bolt://localhost", auth=basic_auth("neo4j", "password"), encrypted=True)
7280
# end::tls-require-encryption[]
7381

7482
def test_tls_trust_on_first_use(self):
7583
# tag::tls-trust-on-first-use[]
76-
# TODO: Unfortunately, this feature is not yet implemented for Python
77-
pass
84+
driver = GraphDatabase.driver("bolt://localhost", auth=basic_auth("neo4j", "password"), encrypted=True, trust=TRUST_ON_FIRST_USE)
7885
# end::tls-trust-on-first-use[]
86+
assert driver
7987

88+
@skip("testing verified certificates not yet supported ")
8089
def test_tls_signed(self):
8190
# tag::tls-signed[]
82-
# TODO: Unfortunately, this feature is not yet implemented for Python
83-
pass
91+
driver = GraphDatabase.driver("bolt://localhost", auth=basic_auth("neo4j", "password"), encrypted=True, trust=TRUST_SIGNED_CERTIFICATES)
8492
# end::tls-signed[]
93+
assert driver
8594

8695
def test_statement(self):
87-
driver = GraphDatabase.driver("bolt://localhost")
96+
driver = GraphDatabase.driver("bolt://localhost", auth=auth_token)
8897
session = driver.session()
8998
# tag::statement[]
90-
session.run("CREATE (person:Person {name: {name}})", {"name": "Neo"}).close()
99+
result = session.run("CREATE (person:Person {name: {name}})", {"name": "Arthur"})
91100
# end::statement[]
101+
result.consume()
92102
session.close()
93103

94104
def test_statement_without_parameters(self):
95-
driver = GraphDatabase.driver("bolt://localhost")
105+
driver = GraphDatabase.driver("bolt://localhost", auth=auth_token)
96106
session = driver.session()
97107
# tag::statement-without-parameters[]
98-
session.run("CREATE (person:Person {name: 'Neo'})").close()
108+
result = session.run("CREATE (person:Person {name: 'Arthur'})")
99109
# end::statement-without-parameters[]
110+
result.consume()
100111
session.close()
101112

102-
def test_result_cursor(self):
103-
driver = GraphDatabase.driver("bolt://localhost")
113+
def test_result_traversal(self):
114+
driver = GraphDatabase.driver("bolt://localhost", auth=auth_token)
104115
session = driver.session()
105-
# tag::result-cursor[]
106-
search_term = "hammer"
107-
cursor = session.run("MATCH (tool:Tool) WHERE tool.name CONTAINS {term} "
108-
"RETURN tool.name", {"term": search_term})
109-
print("List of tools called %r:" % search_term)
110-
while cursor.next():
111-
print(cursor["tool.name"])
112-
# end::result-cursor[]
116+
# tag::result-traversal[]
117+
search_term = "sword"
118+
result = session.run("MATCH (weapon:Weapon) WHERE weapon.name CONTAINS {term} "
119+
"RETURN weapon.name", {"term": search_term})
120+
print("List of weapons called %r:" % search_term)
121+
for record in result:
122+
print(record["weapon.name"])
123+
# end::result-traversal[]
113124
session.close()
114125

115-
def test_cursor_nesting(self):
116-
driver = GraphDatabase.driver("bolt://localhost")
126+
def test_result_retention(self):
127+
driver = GraphDatabase.driver("bolt://localhost", auth=auth_token)
128+
# tag::retain-result[]
117129
session = driver.session()
118-
# tag::retain-result-query[]
119-
cursor = session.run("MATCH (person:Person) WHERE person.dept = {dept} "
120-
"RETURN id(person) AS minion", {"dept": "IT"})
121-
while cursor.next():
122-
session.run("MATCH (person) WHERE id(person) = {id} "
123-
"MATCH (boss:Person) WHERE boss.name = {boss} "
124-
"CREATE (person)-[:REPORTS_TO]->(boss)", {"id": cursor["minion"], "boss": "Bob"})
125-
# end::retain-result-query[]
130+
result = session.run("MATCH (knight:Person:Knight) WHERE knight.castle = {castle} "
131+
"RETURN knight.name AS name", {"castle": "Camelot"})
132+
retained_result = list(result)
126133
session.close()
134+
for record in retained_result:
135+
print("%s is a knight of Camelot" % record["name"])
136+
# end::retain-result[]
137+
assert isinstance(retained_result, list)
127138

128-
def test_result_retention(self):
129-
driver = GraphDatabase.driver("bolt://localhost")
139+
def test_nested_statements(self):
140+
driver = GraphDatabase.driver("bolt://localhost", auth=auth_token)
130141
session = driver.session()
131-
# tag::retain-result-process[]
132-
cursor = session.run("MATCH (person:Person) WHERE person.dept = {dept} "
133-
"RETURN id(person) AS minion", {"dept": "IT"})
134-
minion_records = list(cursor.stream())
135-
136-
for record in minion_records:
137-
session.run("MATCH (person) WHERE id(person) = {id} "
138-
"MATCH (boss:Person) WHERE boss.name = {boss} "
139-
"CREATE (person)-[:REPORTS_TO]->(boss)", {"id": record["minion"], "boss": "Bob"})
140-
# end::retain-result-process[]
142+
# tag::nested-statements[]
143+
result = session.run("MATCH (knight:Person:Knight) WHERE knight.castle = {castle} "
144+
"RETURN id(knight) AS knight_id", {"castle": "Camelot"})
145+
for record in result:
146+
session.run("MATCH (knight) WHERE id(knight) = {id} "
147+
"MATCH (king:Person) WHERE king.name = {king} "
148+
"CREATE (knight)-[:DEFENDS]->(king)", {"id": record["knight_id"], "king": "Arthur"})
149+
# end::nested-statements[]
141150
session.close()
142151

143152
def test_transaction_commit(self):
144-
driver = GraphDatabase.driver("bolt://localhost")
153+
driver = GraphDatabase.driver("bolt://localhost", auth=auth_token)
145154
session = driver.session()
146155
# tag::transaction-commit[]
147156
tx = session.begin_transaction()
148-
tx.run("CREATE (p:Person {name: 'The One'})")
157+
tx.run("CREATE (:Person {name: 'Guinevere'})")
149158
tx.commit()
150159
# end::transaction-commit[]
151-
cursor = session.run("MATCH (p:Person {name: 'The One'}) RETURN count(p)")
152-
assert cursor.next()
153-
assert cursor["count(p)"] == 1
154-
assert cursor.at_end()
160+
result = session.run("MATCH (p:Person {name: 'Guinevere'}) RETURN count(p)")
161+
record = next(result)
162+
assert record["count(p)"] == 1
155163
session.close()
156164

157165
def test_transaction_rollback(self):
158-
driver = GraphDatabase.driver("bolt://localhost")
166+
driver = GraphDatabase.driver("bolt://localhost", auth=auth_token)
159167
session = driver.session()
160168
# tag::transaction-rollback[]
161169
tx = session.begin_transaction()
162-
tx.run("CREATE (p:Person {name: 'The One'})")
170+
tx.run("CREATE (:Person {name: 'Merlin'})")
163171
tx.rollback()
164172
# end::transaction-rollback[]
165-
cursor = session.run("MATCH (p:Person {name: 'The One'}) RETURN count(p)")
166-
assert cursor.next()
167-
assert cursor["count(p)"] == 0
168-
assert cursor.at_end()
173+
result = session.run("MATCH (p:Person {name: 'Merlin'}) RETURN count(p)")
174+
record = next(result)
175+
assert record["count(p)"] == 0
169176
session.close()
170177

171178
def test_result_summary_query_profile(self):
172-
driver = GraphDatabase.driver("bolt://localhost")
179+
driver = GraphDatabase.driver("bolt://localhost", auth=auth_token)
173180
session = driver.session()
174181
# tag::result-summary-query-profile[]
175-
cursor = session.run("PROFILE MATCH (p:Person {name: {name}}) "
176-
"RETURN id(p)", {"name": "The One"})
177-
summary = cursor.summarize()
182+
result = session.run("PROFILE MATCH (p:Person {name: {name}}) "
183+
"RETURN id(p)", {"name": "Arthur"})
184+
summary = result.consume()
178185
print(summary.statement_type)
179186
print(summary.profile)
180187
# end::result-summary-query-profile[]
181188
session.close()
182189

183190
def test_result_summary_notifications(self):
184-
driver = GraphDatabase.driver("bolt://localhost")
191+
driver = GraphDatabase.driver("bolt://localhost", auth=auth_token)
185192
session = driver.session()
186193
# tag::result-summary-notifications[]
187-
summary = session.run("EXPLAIN MATCH (a), (b) RETURN a,b").summarize()
194+
result = session.run("EXPLAIN MATCH (king), (queen) RETURN king, queen")
195+
summary = result.consume()
188196
for notification in summary.notifications:
189197
print(notification)
190198
# end::result-summary-notifications[]

neo4j/__main__.py

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,21 @@
2727
from sys import stdout, stderr
2828

2929
from .util import Watcher
30-
from .v1.session import GraphDatabase, CypherError
30+
from .v1.session import GraphDatabase, CypherError, basic_auth
3131

3232

3333
def main():
3434
parser = ArgumentParser(description="Execute one or more Cypher statements using Bolt.")
3535
parser.add_argument("statement", nargs="+")
36-
parser.add_argument("-u", "--url", default="bolt://localhost", metavar="CONNECTION_URL")
36+
parser.add_argument("-k", "--keys", action="store_true")
37+
parser.add_argument("-P", "--password")
3738
parser.add_argument("-p", "--parameter", action="append", metavar="NAME=VALUE")
3839
parser.add_argument("-q", "--quiet", action="store_true")
39-
parser.add_argument("-s", "--secure", action="store_true")
40+
parser.add_argument("-U", "--user", default="neo4j")
41+
parser.add_argument("-u", "--url", default="bolt://localhost", metavar="CONNECTION_URL")
4042
parser.add_argument("-v", "--verbose", action="count")
4143
parser.add_argument("-x", "--times", type=int, default=1)
42-
parser.add_argument("-z", "--summarize", action="store_true")
44+
parser.add_argument("-z", "--summary", action="store_true")
4345
args = parser.parse_args()
4446

4547
if args.verbose:
@@ -57,30 +59,26 @@ def main():
5759
except ValueError:
5860
parameters[name] = value
5961

60-
driver = GraphDatabase.driver(args.url, secure=args.secure)
62+
driver = GraphDatabase.driver(args.url, auth=basic_auth(args.user, args.password))
6163
session = driver.session()
6264
for _ in range(args.times):
6365
for statement in args.statement:
6466
try:
65-
cursor = session.run(statement, parameters)
67+
result = session.run(statement, parameters)
6668
except CypherError as error:
6769
stderr.write("%s: %s\r\n" % (error.code, error.message))
6870
else:
6971
if not args.quiet:
70-
has_results = False
71-
for i, record in enumerate(cursor.stream()):
72-
has_results = True
73-
if i == 0:
74-
stdout.write("%s\r\n" % "\t".join(record.keys()))
75-
stdout.write("%s\r\n" % "\t".join(map(repr, record)))
76-
if has_results:
77-
stdout.write("\r\n")
78-
if args.summarize:
79-
summary = cursor.summarize()
72+
if args.keys:
73+
stdout.write("%s\r\n" % "\t".join(result.keys()))
74+
for i, record in enumerate(result):
75+
stdout.write("%s\r\n" % "\t".join(map(repr, record.values())))
76+
if args.summary:
77+
summary = result.summary
8078
stdout.write("Statement : %r\r\n" % summary.statement)
8179
stdout.write("Parameters : %r\r\n" % summary.parameters)
8280
stdout.write("Statement Type : %r\r\n" % summary.statement_type)
83-
stdout.write("Statistics : %r\r\n" % summary.statistics)
81+
stdout.write("Counters : %r\r\n" % summary.counters)
8482
stdout.write("\r\n")
8583
session.close()
8684

neo4j/meta.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,4 @@
1919
# limitations under the License.
2020

2121

22-
version = "1.0.0b2"
22+
version = "1.0.0b3"

neo4j/v1/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@
1818
# See the License for the specific language governing permissions and
1919
# limitations under the License.
2020

21+
from .constants import *
2122
from .session import *
22-
from .typesystem import *
23+
from .types import *

0 commit comments

Comments
 (0)