Skip to content

Commit e2fa92d

Browse files
author
Zhen Li
committed
Merge branch '1.1' of https://github.com/neo4j/neo4j-python-driver into 1.1
2 parents fe5fc71 + 099ff77 commit e2fa92d

20 files changed

+595
-402
lines changed

.gitmodules

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
[submodule "neokit"]
2-
path = neokit
3-
url = https://github.com/nigelsmall/neokit.git
2+
path = neokit
3+
url = https://github.com/neo-technology/neokit.git

CONTRIBUTING.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Contributing to the Neo4j Ecosystem
2+
3+
At [Neo Technology](http://neo4j.com/), we develop our software in the open at GitHub.
4+
This provides transparency for you, our users, and allows you to fork the software to make your own additions and enhancements.
5+
We also provide areas specifically for community contributions, in particular the [neo4j-contrib](https://github.com/neo4j-contrib) space.
6+
7+
There's an active [mailing list](https://groups.google.com/forum/#!forum/neo4j) and [Slack channel](http://neo4j.com/slack) where we work directly with the community.
8+
If you're not already a member, sign up!
9+
10+
We love our community and wouldn't be where we are without you.
11+
12+
13+
## Need to raise an issue?
14+
15+
Where you raise an issue depends largely on the nature of the problem.
16+
17+
Firstly, if you are an Enterprise customer, you might want to head over to our [Customer Support Portal](http://support.neo4j.com/).
18+
19+
There are plenty of public channels available too, though.
20+
If you simply want to get started or have a question on how to use a particular feature, drop a line to the [mailing list](https://groups.google.com/forum/#!forum/neo4j), ask a question in [Slack](http://neo4j.com/slack), or [tweet](https://twitter.com/neo4j) us.
21+
If you think you might have hit a bug in our software (it happens occasionally!) or you have specific feature request then use the issue feature on the relevant GitHub repository.
22+
Check first though as someone else may have already raised something similar.
23+
24+
[StackOverflow](http://stackoverflow.com/questions/tagged/neo4j) also hosts a ton of questions and might already have a discussion around your problem.
25+
Make sure you have a look there too.
26+
27+
Include as much information as you can in any request you make:
28+
29+
- Which versions of our products are you using?
30+
- Which language (and which version of that language) are you developing with?
31+
- What operating system are you on?
32+
- Are you working with a cluster or on a single machine?
33+
- What code are you running?
34+
- What errors are you seeing?
35+
- What solutions have you tried already?
36+
37+
38+
## Want to contribute?
39+
40+
If you want to contribute a pull request, we have a little bit of process you'll need to follow:
41+
42+
- Do all your work in a personal fork of the original repository
43+
- [Rebase](https://github.com/edx/edx-platform/wiki/How-to-Rebase-a-Pull-Request), don't merge (we prefer to keep our history clean)
44+
- Create a branch (with a useful name) for your contribution
45+
- Make sure you're familiar with the appropriate coding style (this varies by language so ask if you're in doubt)
46+
- Include unit tests if appropriate (obviously not necessary for documentation changes)
47+
- Take a moment to read and sign our [CLA](http://neo4j.com/developer/cla)
48+
49+
We can't guarantee that we'll accept pull requests and may ask you to make some changes before they go in.
50+
Occasionally, we might also have logistical, commercial, or legal reasons why we can't accept your work but we'll try to find an alternative way for you to contribute in that case.
51+
Remember that many community members have become regular contributors and some are now even Neo employees!
52+
53+
54+
## Got an idea for a new project?
55+
56+
If you have an idea for a new tool or library, start by talking to other people in the community.
57+
Chances are that someone has a similar idea or may have already started working on it.
58+
The best software comes from getting like minds together to solve a problem.
59+
And we'll do our best to help you promote and co-ordinate your Neo ecosystem projects.
60+
61+
62+
## Further reading
63+
64+
If you want to find out more about how you can contribute, head over to our website for [more information](http://neo4j.com/developer/contributing-code/).
65+
File renamed without changes.

examples/test_examples.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
# (* "good reason" is defined as knowing what you are doing)
3333

3434

35-
auth_token = basic_auth("neo4j", "password")
35+
auth_token = basic_auth("neo4j", "neo4j")
3636

3737

3838
class FreshDatabaseTestCase(ServerTestCase):
@@ -48,7 +48,7 @@ class MinimalWorkingExampleTestCase(FreshDatabaseTestCase):
4848

4949
def test_minimal_working_example(self):
5050
# tag::minimal-example[]
51-
driver = GraphDatabase.driver("bolt://localhost", auth=basic_auth("neo4j", "password"))
51+
driver = GraphDatabase.driver("bolt://localhost", auth=basic_auth("neo4j", "neo4j"))
5252
session = driver.session()
5353

5454
session.run("CREATE (a:Person {name:'Arthur', title:'King'})")
@@ -65,33 +65,33 @@ class ExamplesTestCase(FreshDatabaseTestCase):
6565

6666
def test_construct_driver(self):
6767
# tag::construct-driver[]
68-
driver = GraphDatabase.driver("bolt://localhost", auth=basic_auth("neo4j", "password"))
68+
driver = GraphDatabase.driver("bolt://localhost", auth=basic_auth("neo4j", "neo4j"))
6969
# end::construct-driver[]
7070
return driver
7171

7272
def test_configuration(self):
7373
# tag::configuration[]
74-
driver = GraphDatabase.driver("bolt://localhost", auth=basic_auth("neo4j", "password"), max_pool_size=10)
74+
driver = GraphDatabase.driver("bolt://localhost", auth=basic_auth("neo4j", "neo4j"), max_pool_size=10)
7575
# end::configuration[]
7676
return driver
7777

7878
@skipUnless(SSL_AVAILABLE, "Bolt over TLS is not supported by this version of Python")
7979
def test_tls_require_encryption(self):
8080
# tag::tls-require-encryption[]
81-
driver = GraphDatabase.driver("bolt://localhost", auth=basic_auth("neo4j", "password"), encrypted=True)
81+
driver = GraphDatabase.driver("bolt://localhost", auth=basic_auth("neo4j", "neo4j"), encrypted=True)
8282
# end::tls-require-encryption[]
8383

8484
@skipUnless(SSL_AVAILABLE, "Bolt over TLS is not supported by this version of Python")
8585
def test_tls_trust_on_first_use(self):
8686
# tag::tls-trust-on-first-use[]
87-
driver = GraphDatabase.driver("bolt://localhost", auth=basic_auth("neo4j", "password"), encrypted=True, trust=TRUST_ON_FIRST_USE)
87+
driver = GraphDatabase.driver("bolt://localhost", auth=basic_auth("neo4j", "neo4j"), encrypted=True, trust=TRUST_ON_FIRST_USE)
8888
# end::tls-trust-on-first-use[]
8989
assert driver
9090

9191
@skip("testing verified certificates not yet supported ")
9292
def test_tls_signed(self):
9393
# tag::tls-signed[]
94-
driver = GraphDatabase.driver("bolt://localhost", auth=basic_auth("neo4j", "password"), encrypted=True, trust=TRUST_SIGNED_CERTIFICATES)
94+
driver = GraphDatabase.driver("bolt://localhost", auth=basic_auth("neo4j", "neo4j"), encrypted=True, trust=TRUST_SIGNED_CERTIFICATES)
9595
# end::tls-signed[]
9696
assert driver
9797

makedocs.sh

Lines changed: 0 additions & 5 deletions
This file was deleted.

neo4j/.DS_Store

-6 KB
Binary file not shown.

neo4j/__init__.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,12 @@
2020

2121

2222
from .meta import version as __version__
23+
24+
# Export current (v1) API. This should be updated to export the latest
25+
# version of the API when a new one is added. This gives the option to
26+
# `import neo4j.vX` for a specific version or `import neo4j` for the
27+
# latest.
28+
from .v1.constants import *
29+
from .v1.exceptions import *
30+
from .v1.session import *
31+
from .v1.types import *

neo4j/util.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,8 @@
1919
# limitations under the License.
2020

2121

22-
from __future__ import unicode_literals
23-
2422
import logging
25-
from argparse import ArgumentParser
26-
from json import loads as json_loads
27-
from sys import stdout, stderr
28-
29-
from .v1.session import GraphDatabase, CypherError
23+
from sys import stdout
3024

3125

3226
class ColourFormatter(logging.Formatter):
@@ -50,7 +44,7 @@ def format(self, record):
5044

5145

5246
class Watcher(object):
53-
""" Log watcher for debug output.
47+
""" Log watcher for monitoring driver and protocol activity.
5448
"""
5549

5650
handlers = {}
@@ -74,3 +68,16 @@ def stop(self):
7468
self.logger.removeHandler(self.handlers[self.logger_name])
7569
except KeyError:
7670
pass
71+
72+
73+
def watch(logger_name, level=logging.INFO, out=stdout):
74+
""" Quick wrapper for using the Watcher.
75+
76+
:param logger_name: name of logger to watch
77+
:param level: minimum log level to show (default INFO)
78+
:param out: where to send output (default stdout)
79+
:return: Watcher instance
80+
"""
81+
watcher = Watcher(logger_name)
82+
watcher.watch(level, out)
83+
return watcher

neo4j/v1/__init__.py

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

21-
from .connection import ProtocolError
2221
from .constants import *
22+
from .exceptions import *
2323
from .session import *
2424
from .types import *

neo4j/v1/connection.py renamed to neo4j/v1/bolt.py

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@
1919
# limitations under the License.
2020

2121

22+
"""
23+
This module contains the low-level functionality required for speaking
24+
Bolt. It is not intended to be used directly by driver users. Instead,
25+
the `session` module provides the main user-facing abstractions.
26+
"""
27+
28+
2229
from __future__ import division
2330

2431
from base64 import b64encode
@@ -31,18 +38,16 @@
3138
from socket import create_connection, SHUT_RDWR, error as SocketError
3239
from struct import pack as struct_pack, unpack as struct_unpack, unpack_from as struct_unpack_from
3340

34-
import errno
35-
36-
from .constants import DEFAULT_PORT, DEFAULT_USER_AGENT, KNOWN_HOSTS, MAGIC_PREAMBLE, \
37-
TRUST_DEFAULT, TRUST_ON_FIRST_USE
41+
from .constants import DEFAULT_USER_AGENT, KNOWN_HOSTS, MAGIC_PREAMBLE, TRUST_DEFAULT, TRUST_ON_FIRST_USE
3842
from .compat import hex2
3943
from .exceptions import ProtocolError
4044
from .packstream import Packer, Unpacker
4145
from .ssl_compat import SSL_AVAILABLE, HAS_SNI, SSLError
4246

4347

4448
# Signature bytes for each message type
45-
INIT = b"\x01" # 0000 0001 // INIT <user_agent>
49+
INIT = b"\x01" # 0000 0001 // INIT <user_agent> <auth>
50+
ACK_FAILURE = b"\x0E" # 0000 1110 // ACK_FAILURE
4651
RESET = b"\x0F" # 0000 1111 // RESET
4752
RUN = b"\x10" # 0001 0000 // RUN <statement> <parameters>
4853
DISCARD_ALL = b"\x2F" # 0010 1111 // DISCARD *
@@ -57,6 +62,7 @@
5762

5863
message_names = {
5964
INIT: "INIT",
65+
ACK_FAILURE: "ACK_FAILURE",
6066
RESET: "RESET",
6167
RUN: "RUN",
6268
DISCARD_ALL: "DISCARD_ALL",
@@ -224,7 +230,7 @@ def __init__(self, sock, **config):
224230
self.der_encoded_server_certificate = config.get("der_encoded_server_certificate")
225231

226232
def on_failure(metadata):
227-
raise ProtocolError(metadata.get("message", "Inititalisation failed"))
233+
raise ProtocolError(metadata.get("message", "INIT failed"))
228234

229235
response = Response(self)
230236
response.on_failure = on_failure
@@ -237,6 +243,13 @@ def on_failure(metadata):
237243
def __del__(self):
238244
self.close()
239245

246+
@property
247+
def healthy(self):
248+
""" Return ``True`` if this connection is healthy, ``False`` if
249+
unhealthy and ``None`` if closed.
250+
"""
251+
return None if self.closed else not self.defunct
252+
240253
def append(self, signature, fields=(), response=None):
241254
""" Add a message to the outgoing queue.
242255
@@ -253,6 +266,23 @@ def append(self, signature, fields=(), response=None):
253266
self.channel.flush(end_of_message=True)
254267
self.responses.append(response)
255268

269+
def acknowledge_failure(self):
270+
""" Add an ACK_FAILURE message to the outgoing queue, send
271+
it and consume all remaining messages.
272+
"""
273+
response = Response(self)
274+
275+
def on_failure(metadata):
276+
raise ProtocolError("ACK_FAILURE failed")
277+
278+
response.on_failure = on_failure
279+
280+
self.append(ACK_FAILURE, response=response)
281+
self.send()
282+
fetch = self.fetch
283+
while not response.complete:
284+
fetch()
285+
256286
def reset(self):
257287
""" Add a RESET message to the outgoing queue, send
258288
it and consume all remaining messages.
@@ -304,7 +334,7 @@ def fetch(self):
304334
response.complete = True
305335
self.responses.popleft()
306336
if signature == FAILURE:
307-
self.reset()
337+
self.acknowledge_failure()
308338
handler_name = "on_%s" % message_names[signature].lower()
309339
try:
310340
handler = getattr(response, handler_name)
@@ -314,6 +344,12 @@ def fetch(self):
314344
handler(*fields)
315345
raw.close()
316346

347+
def fetch_all(self):
348+
while self.responses:
349+
response = self.responses[0]
350+
while not response.complete:
351+
self.fetch()
352+
317353
def close(self):
318354
""" Close the connection.
319355
"""
@@ -370,26 +406,26 @@ def match_or_trust(self, host, der_encoded_certificate):
370406
return True
371407

372408

373-
def connect(host, port=None, ssl_context=None, **config):
409+
def connect(host_port, ssl_context=None, **config):
374410
""" Connect and perform a handshake and return a valid Connection object, assuming
375411
a protocol version can be agreed.
376412
"""
377413

378414
# Establish a connection to the host and port specified
379415
# Catches refused connections see:
380416
# https://docs.python.org/2/library/errno.html
381-
port = port or DEFAULT_PORT
382-
if __debug__: log_info("~~ [CONNECT] %s %d", host, port)
417+
if __debug__: log_info("~~ [CONNECT] %s", host_port)
383418
try:
384-
s = create_connection((host, port))
419+
s = create_connection(host_port)
385420
except SocketError as error:
386421
if error.errno == 111 or error.errno == 61:
387-
raise ProtocolError("Unable to connect to %s on port %d - is the server running?" % (host, port))
422+
raise ProtocolError("Unable to connect to %s on port %d - is the server running?" % host_port)
388423
else:
389424
raise
390425

391426
# Secure the connection if an SSL context has been provided
392427
if ssl_context and SSL_AVAILABLE:
428+
host, port = host_port
393429
if __debug__: log_info("~~ [SECURE] %s", host)
394430
try:
395431
s = ssl_context.wrap_socket(s, server_hostname=host if HAS_SNI else None)

0 commit comments

Comments
 (0)