diff --git a/CHANGELOG.md b/CHANGELOG.md index f4acb27e4..e2066dbc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,20 @@ See also https://github.com/neo4j/neo4j-python-driver/wiki for a full changelog. - Remove `ExperimentalWarning` and turn the few left instances of it into `PreviewWarning`. - Deprecate importing `PreviewWarning` from `neo4j`. Import it from `neo4j.warnings` instead. +- Make undocumented internal constants private: + - `neo4j.api` + - `DRIVER_BOLT` + - `DRIVER_NEO4J` + - `SECURITY_TYPE_NOT_SECURE` + - `SECURITY_TYPE_SECURE` + - `SECURITY_TYPE_SELF_SIGNED_CERTIFICATE` + - `neo4j.exceptions` + - `CLASSIFICATION_CLIENT` + - `CLASSIFICATION_DATABASE` + - `CLASSIFICATION_TRANSIENT` + - `ERROR_REWRITE_MAP` + - `client_errors` + - `transient_errors` ## Version 5.28 diff --git a/src/neo4j/_api.py b/src/neo4j/_api.py index c87e7424e..81da85123 100644 --- a/src/neo4j/_api.py +++ b/src/neo4j/_api.py @@ -25,6 +25,11 @@ __all__ = [ + "DRIVER_BOLT", + "DRIVER_NEO4J", + "SECURITY_TYPE_NOT_SECURE", + "SECURITY_TYPE_SECURE", + "SECURITY_TYPE_SELF_SIGNED_CERTIFICATE", "NotificationCategory", "NotificationClassification", "NotificationDisabledCategory", @@ -36,6 +41,16 @@ ] +DRIVER_BOLT: te.Final[str] = "DRIVER_BOLT" +DRIVER_NEO4J: te.Final[str] = "DRIVER_NEO4J" + +SECURITY_TYPE_NOT_SECURE: te.Final[str] = "SECURITY_TYPE_NOT_SECURE" +SECURITY_TYPE_SELF_SIGNED_CERTIFICATE: te.Final[str] = ( + "SECURITY_TYPE_SELF_SIGNED_CERTIFICATE" +) +SECURITY_TYPE_SECURE: te.Final[str] = "SECURITY_TYPE_SECURE" + + class NotificationMinimumSeverity(str, Enum): """ Filter notifications returned by the server by minimum severity. diff --git a/src/neo4j/_async/driver.py b/src/neo4j/_async/driver.py index 4c0aabcbd..886cfc4fa 100644 --- a/src/neo4j/_async/driver.py +++ b/src/neo4j/_async/driver.py @@ -31,8 +31,12 @@ from .._addressing import Address from .._api import ( + DRIVER_BOLT, + DRIVER_NEO4J, NotificationMinimumSeverity, RoutingControl, + SECURITY_TYPE_SECURE, + SECURITY_TYPE_SELF_SIGNED_CERTIFICATE, TelemetryAPI, ) from .._async_compat.util import AsyncUtil @@ -60,13 +64,9 @@ Auth, BookmarkManager, Bookmarks, - DRIVER_BOLT, - DRIVER_NEO4J, parse_neo4j_uri, parse_routing_context, READ_ACCESS, - SECURITY_TYPE_SECURE, - SECURITY_TYPE_SELF_SIGNED_CERTIFICATE, ServerInfo, TRUST_ALL_CERTIFICATES, TRUST_SYSTEM_CA_SIGNED_CERTIFICATES, diff --git a/src/neo4j/_sync/driver.py b/src/neo4j/_sync/driver.py index 42231309f..4ab2962e3 100644 --- a/src/neo4j/_sync/driver.py +++ b/src/neo4j/_sync/driver.py @@ -31,8 +31,12 @@ from .._addressing import Address from .._api import ( + DRIVER_BOLT, + DRIVER_NEO4J, NotificationMinimumSeverity, RoutingControl, + SECURITY_TYPE_SECURE, + SECURITY_TYPE_SELF_SIGNED_CERTIFICATE, TelemetryAPI, ) from .._async_compat.util import Util @@ -59,13 +63,9 @@ Auth, BookmarkManager, Bookmarks, - DRIVER_BOLT, - DRIVER_NEO4J, parse_neo4j_uri, parse_routing_context, READ_ACCESS, - SECURITY_TYPE_SECURE, - SECURITY_TYPE_SELF_SIGNED_CERTIFICATE, ServerInfo, TRUST_ALL_CERTIFICATES, TRUST_SYSTEM_CA_SIGNED_CERTIFICATES, diff --git a/src/neo4j/api.py b/src/neo4j/api.py index 3636b1cab..ec5c06730 100644 --- a/src/neo4j/api.py +++ b/src/neo4j/api.py @@ -25,32 +25,27 @@ urlparse, ) - -if t.TYPE_CHECKING: - from typing_extensions import deprecated -else: - from ._warnings import deprecated - +from . import _api from .exceptions import ConfigurationError if t.TYPE_CHECKING: import typing_extensions as te - from typing_extensions import Protocol as _Protocol + from typing_extensions import ( + deprecated, + Protocol as _Protocol, + ) from ._addressing import Address else: + from ._warnings import deprecated + _Protocol = object __all__ = [ "DEFAULT_DATABASE", - "DRIVER_BOLT", - "DRIVER_NEO4J", "READ_ACCESS", - "SECURITY_TYPE_NOT_SECURE", - "SECURITY_TYPE_SECURE", - "SECURITY_TYPE_SELF_SIGNED_CERTIFICATE", "SYSTEM_DATABASE", "TRUST_ALL_CERTIFICATES", "TRUST_SYSTEM_CA_SIGNED_CERTIFICATES", @@ -81,17 +76,6 @@ READ_ACCESS: te.Final[str] = "READ" WRITE_ACCESS: te.Final[str] = "WRITE" -# TODO: 6.0 - make these 2 constants private -DRIVER_BOLT: te.Final[str] = "DRIVER_BOLT" -DRIVER_NEO4J: te.Final[str] = "DRIVER_NEO4J" - -# TODO: 6.0 - make these 3 constants private -SECURITY_TYPE_NOT_SECURE: te.Final[str] = "SECURITY_TYPE_NOT_SECURE" -SECURITY_TYPE_SELF_SIGNED_CERTIFICATE: te.Final[str] = ( - "SECURITY_TYPE_SELF_SIGNED_CERTIFICATE" -) -SECURITY_TYPE_SECURE: te.Final[str] = "SECURITY_TYPE_SECURE" - URI_SCHEME_BOLT: te.Final[str] = "bolt" URI_SCHEME_BOLT_SELF_SIGNED_CERTIFICATE: te.Final[str] = "bolt+ssc" URI_SCHEME_BOLT_SECURE: te.Final[str] = "bolt+s" @@ -477,23 +461,23 @@ def parse_neo4j_uri(uri): f"Use {URI_SCHEME_NEO4J!r}" ) elif parsed.scheme == URI_SCHEME_BOLT: - driver_type = DRIVER_BOLT - security_type = SECURITY_TYPE_NOT_SECURE + driver_type = _api.DRIVER_BOLT + security_type = _api.SECURITY_TYPE_NOT_SECURE elif parsed.scheme == URI_SCHEME_BOLT_SELF_SIGNED_CERTIFICATE: - driver_type = DRIVER_BOLT - security_type = SECURITY_TYPE_SELF_SIGNED_CERTIFICATE + driver_type = _api.DRIVER_BOLT + security_type = _api.SECURITY_TYPE_SELF_SIGNED_CERTIFICATE elif parsed.scheme == URI_SCHEME_BOLT_SECURE: - driver_type = DRIVER_BOLT - security_type = SECURITY_TYPE_SECURE + driver_type = _api.DRIVER_BOLT + security_type = _api.SECURITY_TYPE_SECURE elif parsed.scheme == URI_SCHEME_NEO4J: - driver_type = DRIVER_NEO4J - security_type = SECURITY_TYPE_NOT_SECURE + driver_type = _api.DRIVER_NEO4J + security_type = _api.SECURITY_TYPE_NOT_SECURE elif parsed.scheme == URI_SCHEME_NEO4J_SELF_SIGNED_CERTIFICATE: - driver_type = DRIVER_NEO4J - security_type = SECURITY_TYPE_SELF_SIGNED_CERTIFICATE + driver_type = _api.DRIVER_NEO4J + security_type = _api.SECURITY_TYPE_SELF_SIGNED_CERTIFICATE elif parsed.scheme == URI_SCHEME_NEO4J_SECURE: - driver_type = DRIVER_NEO4J - security_type = SECURITY_TYPE_SECURE + driver_type = _api.DRIVER_NEO4J + security_type = _api.SECURITY_TYPE_SECURE else: supported_schemes = [ URI_SCHEME_BOLT, diff --git a/src/neo4j/exceptions.py b/src/neo4j/exceptions.py index ae7950b1f..7580e988a 100644 --- a/src/neo4j/exceptions.py +++ b/src/neo4j/exceptions.py @@ -75,46 +75,6 @@ from ._warnings import deprecated as _deprecated -__all__ = [ - "AuthConfigurationError", - "AuthError", - "BrokenRecordError", - "CertificateConfigurationError", - "ClientError", - "ConfigurationError", - "ConnectionAcquisitionTimeoutError", - "ConnectionPoolError", - "ConstraintError", - "CypherSyntaxError", - "CypherTypeError", - "DatabaseError", - "DatabaseUnavailable", - "DriverError", - "Forbidden", - "ForbiddenOnReadOnlyDatabase", - "GqlError", - "GqlErrorClassification", - "IncompleteCommit", - "Neo4jError", - "NotALeader", - "ReadServiceUnavailable", - "ResultConsumedError", - "ResultError", - "ResultFailedError", - "ResultNotSingleError", - "RoutingServiceUnavailable", - "ServiceUnavailable", - "SessionError", - "SessionExpired", - "TokenExpired", - "TransactionError", - "TransactionNestingError", - "TransientError", - "UnsupportedServerProduct", - "WriteServiceUnavailable", -] - - if t.TYPE_CHECKING: from collections.abc import Mapping @@ -145,16 +105,14 @@ __all__ = [ - "CLASSIFICATION_CLIENT", # TODO: 6.0 - make constant private - "CLASSIFICATION_DATABASE", # TODO: 6.0 - make constant private - "CLASSIFICATION_TRANSIENT", # TODO: 6.0 - make constant private - "ERROR_REWRITE_MAP", # TODO: 6.0 - make constant private "AuthConfigurationError", "AuthError", "BrokenRecordError", "CertificateConfigurationError", "ClientError", "ConfigurationError", + "ConnectionAcquisitionTimeoutError", + "ConnectionPoolError", "ConstraintError", "CypherSyntaxError", "CypherTypeError", @@ -163,6 +121,8 @@ "DriverError", "Forbidden", "ForbiddenOnReadOnlyDatabase", + "GqlError", + "GqlErrorClassification", "IncompleteCommit", "Neo4jError", "NotALeader", @@ -184,30 +144,30 @@ ] -CLASSIFICATION_CLIENT: te.Final[str] = "ClientError" -CLASSIFICATION_TRANSIENT: te.Final[str] = "TransientError" -CLASSIFICATION_DATABASE: te.Final[str] = "DatabaseError" +_CLASSIFICATION_CLIENT: te.Final[str] = "ClientError" +_CLASSIFICATION_TRANSIENT: te.Final[str] = "TransientError" +_CLASSIFICATION_DATABASE: te.Final[str] = "DatabaseError" -ERROR_REWRITE_MAP: dict[str, tuple[str, str | None]] = { +_ERROR_REWRITE_MAP: dict[str, tuple[str, str | None]] = { # This error can be retried ed. The driver just needs to re-authenticate # with the same credentials. "Neo.ClientError.Security.AuthorizationExpired": ( - CLASSIFICATION_TRANSIENT, + _CLASSIFICATION_TRANSIENT, None, ), # In 5.0, this error has been re-classified as ClientError. # For backwards compatibility with Neo4j 4.4 and earlier, we re-map it in # the driver, too. "Neo.TransientError.Transaction.Terminated": ( - CLASSIFICATION_CLIENT, + _CLASSIFICATION_CLIENT, "Neo.ClientError.Transaction.Terminated", ), # In 5.0, this error has been re-classified as ClientError. # For backwards compatibility with Neo4j 4.4 and earlier, we re-map it in # the driver, too. "Neo.TransientError.Transaction.LockClientStopped": ( - CLASSIFICATION_CLIENT, + _CLASSIFICATION_CLIENT, "Neo.ClientError.Transaction.LockClientStopped", ), } @@ -638,11 +598,11 @@ def _basic_hydrate(cls, *, neo4j_code: str, message: str) -> Neo4jError: try: _, classification, category, title = neo4j_code.split(".") except ValueError: - classification = CLASSIFICATION_DATABASE + classification = _CLASSIFICATION_DATABASE category = "General" title = "UnknownError" else: - classification_override, code_override = ERROR_REWRITE_MAP.get( + classification_override, code_override = _ERROR_REWRITE_MAP.get( neo4j_code, (None, None) ) if classification_override is not None: @@ -665,19 +625,19 @@ def _basic_hydrate(cls, *, neo4j_code: str, message: str) -> Neo4jError: @classmethod def _extract_error_class(cls, classification, code) -> type[Neo4jError]: - if classification == CLASSIFICATION_CLIENT: + if classification == _CLASSIFICATION_CLIENT: try: - return client_errors[code] + return _client_errors[code] except KeyError: return ClientError - elif classification == CLASSIFICATION_TRANSIENT: + elif classification == _CLASSIFICATION_TRANSIENT: try: - return transient_errors[code] + return _transient_errors[code] except KeyError: return TransientError - elif classification == CLASSIFICATION_DATABASE: + elif classification == _CLASSIFICATION_DATABASE: return DatabaseError else: @@ -958,8 +918,7 @@ class ForbiddenOnReadOnlyDatabase(TransientError): pass -# TODO: 6.0 - Make map private -client_errors: dict[str, type[Neo4jError]] = { +_client_errors: dict[str, type[Neo4jError]] = { # ConstraintError "Neo.ClientError.Schema.ConstraintValidationFailed": ConstraintError, "Neo.ClientError.Schema.ConstraintViolation": ConstraintError, @@ -988,8 +947,7 @@ class ForbiddenOnReadOnlyDatabase(TransientError): "Neo.ClientError.Cluster.NotALeader": NotALeader, } -# TODO: 6.0 - Make map private -transient_errors: dict[str, type[Neo4jError]] = { +_transient_errors: dict[str, type[Neo4jError]] = { # DatabaseUnavailableError "Neo.TransientError.General.DatabaseUnavailable": DatabaseUnavailable } diff --git a/tests/unit/common/test_api.py b/tests/unit/common/test_api.py index 46f7e9116..e059a3513 100644 --- a/tests/unit/common/test_api.py +++ b/tests/unit/common/test_api.py @@ -20,6 +20,7 @@ import pytest +import neo4j._api import neo4j.api from neo4j.addressing import Address from neo4j.exceptions import ConfigurationError @@ -172,38 +173,38 @@ def test_serverinfo_with_metadata( [ ( "bolt://localhost:7676", - neo4j.api.DRIVER_BOLT, - neo4j.api.SECURITY_TYPE_NOT_SECURE, + neo4j._api.DRIVER_BOLT, + neo4j._api.SECURITY_TYPE_NOT_SECURE, None, ), ( "bolt+ssc://localhost:7676", - neo4j.api.DRIVER_BOLT, - neo4j.api.SECURITY_TYPE_SELF_SIGNED_CERTIFICATE, + neo4j._api.DRIVER_BOLT, + neo4j._api.SECURITY_TYPE_SELF_SIGNED_CERTIFICATE, None, ), ( "bolt+s://localhost:7676", - neo4j.api.DRIVER_BOLT, - neo4j.api.SECURITY_TYPE_SECURE, + neo4j._api.DRIVER_BOLT, + neo4j._api.SECURITY_TYPE_SECURE, None, ), ( "neo4j://localhost:7676", - neo4j.api.DRIVER_NEO4J, - neo4j.api.SECURITY_TYPE_NOT_SECURE, + neo4j._api.DRIVER_NEO4J, + neo4j._api.SECURITY_TYPE_NOT_SECURE, None, ), ( "neo4j+ssc://localhost:7676", - neo4j.api.DRIVER_NEO4J, - neo4j.api.SECURITY_TYPE_SELF_SIGNED_CERTIFICATE, + neo4j._api.DRIVER_NEO4J, + neo4j._api.SECURITY_TYPE_SELF_SIGNED_CERTIFICATE, None, ), ( "neo4j+s://localhost:7676", - neo4j.api.DRIVER_NEO4J, - neo4j.api.SECURITY_TYPE_SECURE, + neo4j._api.DRIVER_NEO4J, + neo4j._api.SECURITY_TYPE_SECURE, None, ), ("undefined://localhost:7676", None, None, ConfigurationError), @@ -211,8 +212,8 @@ def test_serverinfo_with_metadata( ("://localhost:7676", None, None, ConfigurationError), ( "bolt+routing://localhost:7676", - neo4j.api.DRIVER_NEO4J, - neo4j.api.SECURITY_TYPE_NOT_SECURE, + neo4j._api.DRIVER_NEO4J, + neo4j._api.SECURITY_TYPE_NOT_SECURE, ConfigurationError, ), ("bolt://username@localhost:7676", None, None, ConfigurationError), diff --git a/tests/unit/common/test_exceptions.py b/tests/unit/common/test_exceptions.py index a405dbc88..ec4c727e5 100644 --- a/tests/unit/common/test_exceptions.py +++ b/tests/unit/common/test_exceptions.py @@ -30,9 +30,9 @@ ) from neo4j._sync.io import Bolt from neo4j.exceptions import ( - CLASSIFICATION_CLIENT, - CLASSIFICATION_DATABASE, - CLASSIFICATION_TRANSIENT, + _CLASSIFICATION_CLIENT, + _CLASSIFICATION_DATABASE, + _CLASSIFICATION_TRANSIENT, ClientError, DatabaseError, GqlError, @@ -191,7 +191,7 @@ def test_neo4jerror_hydrate_with_no_args(): error = Neo4jError._hydrate_neo4j() assert isinstance(error, DatabaseError) - assert error.classification == CLASSIFICATION_DATABASE + assert error.classification == _CLASSIFICATION_DATABASE assert error.category == "General" assert error.title == "UnknownError" assert error.metadata == {} @@ -206,7 +206,7 @@ def test_neo4jerror_hydrate_with_message_and_code_rubbish(): ) assert isinstance(error, DatabaseError) - assert error.classification == CLASSIFICATION_DATABASE + assert error.classification == _CLASSIFICATION_DATABASE assert error.category == "General" assert error.title == "UnknownError" assert error.metadata == {} @@ -222,7 +222,7 @@ def test_neo4jerror_hydrate_with_message_and_code_database(): ) assert isinstance(error, DatabaseError) - assert error.classification == CLASSIFICATION_DATABASE + assert error.classification == _CLASSIFICATION_DATABASE assert error.category == "General" assert error.title == "UnknownError" assert error.metadata == {} @@ -238,28 +238,28 @@ def test_neo4jerror_hydrate_with_message_and_code_transient(): ) assert isinstance(error, TransientError) - assert error.classification == CLASSIFICATION_TRANSIENT + assert error.classification == _CLASSIFICATION_TRANSIENT assert error.category == "General" assert error.title == "TestError" assert error.metadata == {} assert error.message == "Test error message" - assert error.code == f"Neo.{CLASSIFICATION_TRANSIENT}.General.TestError" + assert error.code == f"Neo.{_CLASSIFICATION_TRANSIENT}.General.TestError" _assert_default_gql_error_attrs_from_neo4j_error(error) def test_neo4jerror_hydrate_with_message_and_code_client(): error = Neo4jError._hydrate_neo4j( message="Test error message", - code=f"Neo.{CLASSIFICATION_CLIENT}.General.TestError", + code=f"Neo.{_CLASSIFICATION_CLIENT}.General.TestError", ) assert isinstance(error, ClientError) - assert error.classification == CLASSIFICATION_CLIENT + assert error.classification == _CLASSIFICATION_CLIENT assert error.category == "General" assert error.title == "TestError" assert error.metadata == {} assert error.message == "Test error message" - assert error.code == f"Neo.{CLASSIFICATION_CLIENT}.General.TestError" + assert error.code == f"Neo.{_CLASSIFICATION_CLIENT}.General.TestError" _assert_default_gql_error_attrs_from_neo4j_error(error)