Skip to content

Commit fdd0701

Browse files
4.0 exception hierarchy (#350)
* Changed CypherError to Neo4jError * Removed unused error. * Neo4jAvailabilityError renamed to BoltNeo4jAvailabilityError * Removed NotALeader and use NotALeaderError * Removed ForbiddenOnReadOnlyDatabase Using ForbiddenOnReadOnlyDatabaseError instead * Divided Driver API Errors and Connector API Errors The Driver API Errors can be found in exceptions.py The Connector API Errors can be found in errors.py * Added DriverError Group SessionExpired(DriverError) TransactionError(DriverError) ServiceUnavailable(DriverError) * SecurityError is replaced by BoltSecurityError Driver API ClientErrors does not inherit SecurityError and SecurityError is removed from the Driver API Errors. * IncompleteCommitError is replaced with BoltIncompleteCommitError * ProtocolError is replaced with BoltProtocolError * Removed BoltNeo4jAvailabilityError Added ServiceUnavailable errors that replace BoltNeo4jAvailabilityError errors. from neo4j.exceptions import ( RoutingServiceUnavailable, ReadServiceUnavailable, WriteServiceUnavailable, ) * Added docs info about internal driver errors It's important to leave a note in here as a catch-all. If users see an internal error, in particular a protocol error, they should open an issue. * Renamed errors.py to _exceptions.py The BoltError are internal errors that the user should never experience. * Changed NotALeaderError to NotALeader To align with the naming of the status code Neo.ClientError.Cluster.NotALeader * Renamed DatabaseUnavailableError to DatabaseUnavailable This is to align with the naming of status code: Neo.TransientError.General.DatabaseUnavailable * Renamed ForbiddenOnReadOnlyDatabaseError to ForbiddenOnReadOnlyDatabase This is to align with the status code: Neo.ClientError.General.ForbiddenOnReadOnlyDatabase
1 parent 2f5d58c commit fdd0701

File tree

17 files changed

+202
-191
lines changed

17 files changed

+202
-191
lines changed

docs/source/errors.rst

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,16 @@ Connectivity errors
1111
Raised when a database server or service is not available.
1212
This may be due to incorrect configuration or could indicate a runtime failure of a database service that the driver is unable to route around.
1313

14-
.. class:: neo4j.exceptions.SecurityError
1514

16-
Raised when a security issue occurs, generally around TLS or authentication.
17-
18-
19-
Cypher execution errors
15+
Neo4j execution errors
2016
=======================
2117

22-
.. class:: neo4j.exceptions.CypherError
18+
.. class:: neo4j.exceptions.Neo4jError
2319

2420
Raised when the Cypher engine returns an error to the client.
25-
There are many possible types of Cypher error, each identified by a unique `status code <https://neo4j.com/docs/developer-manual/current/reference/status-codes/>`_.
21+
There are many possible types of Cypher error, each identified by a unique `status code <https://neo4j.com/docs/status-codes/current/>`_.
2622

27-
The three classifications of status code are supported by the three subclasses of :class:`.CypherError`, listed below:
23+
The three classifications of status code are supported by the three subclasses of :class:`.Neo4jError`, listed below:
2824

2925
.. autoclass:: neo4j.exceptions.ClientError
3026

@@ -33,11 +29,18 @@ Cypher execution errors
3329
.. autoclass:: neo4j.exceptions.TransientError
3430

3531

36-
Low-level errors
37-
================
32+
Internal Driver Errors
33+
=======================
34+
35+
If users see an internal error, in particular a protocol error (BoltError*), they should open an issue on github.
36+
37+
https://github.com/neo4j/neo4j-python-driver/issues
38+
39+
Please provide details about your running environment,
3840

39-
.. class:: neo4j.exceptions.ProtocolError
41+
Operating System:
42+
Python Version:
43+
Python Driver Version:
44+
Neo4j Version:
4045

41-
Raised when an unexpected or unsupported protocol event occurs.
42-
This error generally indicates a fault with the driver or server software.
43-
If you receive this error, please raise a GitHub issue or a support ticket.
46+
the code block with a description that produced the error and the error message.

neo4j/errors.py renamed to neo4j/_exceptions.py

Lines changed: 8 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -168,53 +168,14 @@ def transaction(self):
168168
return None
169169

170170

171-
class ClientError(BoltFailure):
171+
class BoltIncompleteCommitError(BoltError):
172+
""" Raised when a disconnection occurs while still waiting for a commit
173+
response. For non-idempotent write transactions, this leaves the data
174+
in an unknown state with regard to whether the transaction completed
175+
successfully or not.
172176
"""
173-
"""
174-
175-
transient = False
176-
177-
178-
class NotALeader(ClientError):
179-
"""
180-
"""
181-
182-
code = "Neo.ClientError.Cluster.NotALeader"
183177

184178

185-
class ForbiddenOnReadOnlyDatabase(ClientError):
186-
"""
187-
"""
188-
189-
code = "Neo.ClientError.General.ForbiddenOnReadOnlyDatabase"
190-
191-
192-
class DatabaseError(BoltFailure):
193-
"""
194-
"""
195-
196-
transient = False
197-
198-
199-
class TransientError(BoltFailure):
200-
"""
201-
"""
202-
203-
transient = True
204-
205-
206-
class Neo4jError(Exception):
207-
""" Base class for all Neo4j service exceptions.
208-
"""
209-
210-
211-
class Neo4jAvailabilityError(Neo4jError):
212-
""" Raised when a Neo4j service is partially or completely
213-
unavailable.
214-
"""
215-
216-
217-
class Neo4jCompatibilityError(Neo4jError):
218-
""" Raised when expected functionality is not supported or
219-
available.
220-
"""
179+
class BoltProtocolError(BoltError):
180+
""" Raised when an unexpected or unsupported protocol event occurs.
181+
"""

neo4j/aio/__init__.py

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,17 @@
6262
from neo4j.addressing import Address
6363
from neo4j.aio._collections import WaitingList
6464
from neo4j.aio._mixins import Addressable, Breakable
65-
from neo4j.errors import (
65+
from neo4j._exceptions import (
6666
BoltError,
6767
BoltConnectionError,
6868
BoltSecurityError,
6969
BoltConnectionBroken,
7070
BoltHandshakeError,
71-
Neo4jAvailabilityError,
71+
)
72+
from neo4j.exceptions import (
73+
RoutingServiceUnavailable,
74+
ReadServiceUnavailable,
75+
WriteServiceUnavailable,
7276
)
7377
from neo4j.api import Version
7478
from neo4j.conf import PoolConfig
@@ -398,7 +402,7 @@ async def get_routing_table(self, context=None):
398402
:return: a new RoutingTable instance or None if the given router is
399403
currently unable to provide routing information
400404
:raise ServiceUnavailable: if no writers are available
401-
:raise ProtocolError: if the routing information received is unusable
405+
:raise BoltProtocolError: if the routing information received is unusable
402406
"""
403407

404408

@@ -817,7 +821,7 @@ async def _get_routing_table(self):
817821

818822
# None of the routers have been successful, so just fail
819823
log.error("Unable to retrieve routing information")
820-
raise Neo4jAvailabilityError("Unable to retrieve routing information")
824+
raise RoutingServiceUnavailable("Unable to retrieve routing information")
821825

822826
async def _ensure_routing_table_is_fresh(self, readonly=False):
823827
""" Update the routing table if stale.
@@ -855,8 +859,10 @@ async def _select_pool(self, readonly=False):
855859
for pool in pools:
856860
pools_by_usage.setdefault(pool.in_use, []).append(pool)
857861
if not pools_by_usage:
858-
raise Neo4jAvailabilityError("No {} service currently "
859-
"available".format("read" if readonly else "write"))
862+
if readonly:
863+
raise ReadServiceUnavailable("No read service currently available")
864+
else:
865+
raise WriteServiceUnavailable("No write service currently available")
860866
return choice(pools_by_usage[min(pools_by_usage)])
861867

862868
async def acquire(self, *, readonly=False, force_reset=False):
@@ -878,10 +884,7 @@ async def acquire(self, *, readonly=False, force_reset=False):
878884
# readonly, then intercept NotALeader and
879885
# ForbiddenOnReadOnlyDatabase errors to
880886
# invalidate the routing table.
881-
from neo4j.errors import (
882-
NotALeader,
883-
ForbiddenOnReadOnlyDatabase,
884-
)
887+
from neo4j.exceptions import ForbiddenOnReadOnlyDatabaseError, NotALeaderError
885888

886889
def handler(failure):
887890
""" Invalidate the routing table before raising the failure.
@@ -890,8 +893,8 @@ def handler(failure):
890893
self._routing_table.ttl = 0
891894
raise failure
892895

893-
cx.set_failure_handler(NotALeader, handler)
894-
cx.set_failure_handler(ForbiddenOnReadOnlyDatabase, handler)
896+
cx.set_failure_handler(NotALeaderError, handler)
897+
cx.set_failure_handler(ForbiddenOnReadOnlyDatabaseError, handler)
895898
return cx
896899

897900
async def release(self, connection, *, force_reset=False):
@@ -905,12 +908,9 @@ async def release(self, connection, *, force_reset=False):
905908
pass
906909
else:
907910
# Unhook any custom error handling and exit.
908-
from neo4j.errors import (
909-
NotALeader,
910-
ForbiddenOnReadOnlyDatabase,
911-
)
912-
connection.del_failure_handler(NotALeader)
913-
connection.del_failure_handler(ForbiddenOnReadOnlyDatabase)
911+
from neo4j.exceptions import ForbiddenOnReadOnlyDatabaseError, NotALeaderError
912+
connection.del_failure_handler(NotALeaderError)
913+
connection.del_failure_handler(ForbiddenOnReadOnlyDatabaseError)
914914
break
915915
else:
916916
raise ValueError("Connection does not belong to this pool")

neo4j/aio/_bolt3.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,15 @@
2828
from neo4j.aio._mixins import Addressable
2929
from neo4j.api import Bookmark, Version
3030
from neo4j.data import Record
31-
from neo4j.errors import (
31+
from neo4j._exceptions import (
3232
BoltError,
3333
BoltFailure,
3434
BoltConnectionBroken,
3535
BoltConnectionClosed,
3636
BoltTransactionError,
3737
BoltRoutingError,
3838
)
39+
from neo4j.exceptions import Neo4jError
3940
from neo4j.packstream import PackStream, Structure
4041
from neo4j.routing import RoutingTable
4142

@@ -612,7 +613,9 @@ def fail(self, failure):
612613
# TODO: fix "requires two params, only one was given" error
613614
handler(failure)
614615
else:
615-
raise failure
616+
# TODO: fix correct error logic after error and exception refactoring
617+
# raise failure # This is a BoltFailure
618+
raise Neo4jError.hydrate(message=str(failure), code=failure.code)
616619

617620
def get_failure_handler(self, cls):
618621
return self._failure_handlers.get(cls)

0 commit comments

Comments
 (0)