Skip to content

Commit 3d2eb62

Browse files
authored
Fix suppression of warnings for internal API usage (#961)
Using `warnings.catch_warning` for suppressing warnings when using APIs internally that are preview/experimental/deprecated is a bad idea because the `warnings` module stores the configuration globally per module. This is not thread-safe. Therefore, the code was restructured to not rely of the `warnings` module to suppress those warnings. Instead, warning-free internal APIs are being used.
1 parent 32d1ac4 commit 3d2eb62

File tree

10 files changed

+165
-251
lines changed

10 files changed

+165
-251
lines changed

src/neo4j/_async/auth_management.py

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222

2323

2424
import typing as t
25-
import warnings
2625
from logging import getLogger
2726

2827
from .._async_compat.concurrency import AsyncLock
@@ -31,10 +30,7 @@
3130
expiring_auth_has_expired,
3231
ExpiringAuth,
3332
)
34-
from .._meta import (
35-
preview,
36-
PreviewWarning,
37-
)
33+
from .._meta import preview
3834

3935
# work around for https://github.com/sphinx-doc/sphinx/pull/10880
4036
# make sure TAuth is resolved in the docs, else they're pretty useless
@@ -215,11 +211,9 @@ async def auth_provider():
215211
handled_codes = frozenset(("Neo.ClientError.Security.Unauthorized",))
216212

217213
async def wrapped_provider() -> ExpiringAuth:
218-
with warnings.catch_warnings():
219-
warnings.filterwarnings("ignore",
220-
message=r"^Auth managers\b.*",
221-
category=PreviewWarning)
222-
return ExpiringAuth(await provider())
214+
return ExpiringAuth._without_warning( # type: ignore
215+
await provider()
216+
)
223217

224218
return AsyncNeo4jAuthTokenManager(wrapped_provider, handled_codes)
225219

src/neo4j/_async/driver.py

Lines changed: 61 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020

2121
import asyncio
2222
import typing as t
23-
import warnings
2423

2524

2625
if t.TYPE_CHECKING:
@@ -47,7 +46,6 @@
4746
experimental_warn,
4847
preview,
4948
preview_warn,
50-
PreviewWarning,
5149
unclosed_resource_warn,
5250
)
5351
from .._work import EagerResult
@@ -196,12 +194,7 @@ def driver(
196194
driver_type, security_type, parsed = parse_neo4j_uri(uri)
197195

198196
if not isinstance(auth, AsyncAuthManager):
199-
with warnings.catch_warnings():
200-
warnings.filterwarnings(
201-
"ignore", message=r".*\bAuth managers\b.*",
202-
category=PreviewWarning
203-
)
204-
auth = AsyncAuthManagers.static(auth)
197+
auth = AsyncAuthManagers.static._without_warning(auth)
205198
else:
206199
preview_warn("Auth managers are a preview feature.",
207200
stack_level=2)
@@ -501,13 +494,6 @@ def encrypted(self) -> bool:
501494
"""Indicate whether the driver was configured to use encryption."""
502495
return bool(self._pool.pool_config.encrypted)
503496

504-
def _prepare_session_config(self, **config):
505-
if "auth" in config:
506-
preview_warn("User switching is a preview feature.",
507-
stack_level=3)
508-
_normalize_notifications_config(config)
509-
return config
510-
511497
if t.TYPE_CHECKING:
512498

513499
def session(
@@ -549,7 +535,25 @@ def session(self, **config) -> AsyncSession:
549535
550536
:returns: new :class:`neo4j.AsyncSession` object
551537
"""
552-
raise NotImplementedError
538+
session_config = self._read_session_config(config)
539+
return self._session(session_config)
540+
541+
def _session(self, session_config) -> AsyncSession:
542+
return AsyncSession(self._pool, session_config)
543+
544+
def _read_session_config(self, config_kwargs, preview_check=True):
545+
config = self._prepare_session_config(preview_check, config_kwargs)
546+
session_config = SessionConfig(self._default_workspace_config,
547+
config)
548+
return session_config
549+
550+
@classmethod
551+
def _prepare_session_config(cls, preview_check, config_kwargs):
552+
if preview_check and "auth" in config_kwargs:
553+
preview_warn("User switching is a preview feature.",
554+
stack_level=5)
555+
_normalize_notifications_config(config_kwargs)
556+
return config_kwargs
553557

554558
async def close(self) -> None:
555559
""" Shut down, closing any open connections in the pool.
@@ -844,14 +848,16 @@ async def example(driver: neo4j.AsyncDriver) -> neo4j.Record::
844848
bookmark_manager_ = self._query_bookmark_manager
845849
assert bookmark_manager_ is not _default
846850

847-
with warnings.catch_warnings():
848-
warnings.filterwarnings("ignore",
849-
message=r"^User switching\b.*",
850-
category=PreviewWarning)
851-
session = self.session(database=database_,
852-
impersonated_user=impersonated_user_,
853-
bookmark_manager=bookmark_manager_,
854-
auth=auth_)
851+
session_config = self._read_session_config(
852+
{
853+
"database": database_,
854+
"impersonated_user": impersonated_user_,
855+
"bookmark_manager": bookmark_manager_,
856+
"auth": auth_,
857+
},
858+
preview_check=False
859+
)
860+
session = self._session(session_config)
855861
async with session:
856862
if routing_ == RoutingControl.WRITE:
857863
executor = session.execute_write
@@ -963,7 +969,8 @@ async def verify_connectivity(self, **config) -> None:
963969
"changed or removed in any future version without prior "
964970
"notice."
965971
)
966-
await self._get_server_info()
972+
session_config = self._read_session_config(config)
973+
await self._get_server_info(session_config)
967974

968975
if t.TYPE_CHECKING:
969976

@@ -1034,7 +1041,8 @@ async def get_server_info(self, **config) -> ServerInfo:
10341041
"changed or removed in any future version without prior "
10351042
"notice."
10361043
)
1037-
return await self._get_server_info()
1044+
session_config = self._read_session_config(config)
1045+
return await self._get_server_info(session_config)
10381046

10391047
async def supports_multi_db(self) -> bool:
10401048
""" Check if the server or cluster supports multi-databases.
@@ -1049,7 +1057,8 @@ async def supports_multi_db(self) -> bool:
10491057
won't throw a :exc:`ConfigurationError` when trying to use this
10501058
driver feature.
10511059
"""
1052-
async with self.session() as session:
1060+
session_config = self._read_session_config({}, preview_check=False)
1061+
async with self._session(session_config) as session:
10531062
await session._connect(READ_ACCESS)
10541063
assert session._connection
10551064
return session._connection.supports_multiple_databases
@@ -1130,30 +1139,24 @@ async def verify_authentication(
11301139
"changed or removed in any future version without prior "
11311140
"notice."
11321141
)
1133-
config["auth"] = auth
11341142
if "database" not in config:
11351143
config["database"] = "system"
1136-
try:
1137-
with warnings.catch_warnings():
1138-
warnings.filterwarnings(
1139-
"ignore", message=r"^User switching\b.*",
1140-
category=PreviewWarning
1141-
)
1142-
session = self.session(**config)
1143-
async with session as session:
1144+
session_config = self._read_session_config(config)
1145+
session_config = SessionConfig(session_config, {"auth": auth})
1146+
async with self._session(session_config) as session:
1147+
try:
11441148
await session._verify_authentication()
1145-
except Neo4jError as exc:
1146-
if exc.code in (
1147-
"Neo.ClientError.Security.CredentialsExpired",
1148-
"Neo.ClientError.Security.Forbidden",
1149-
"Neo.ClientError.Security.TokenExpired",
1150-
"Neo.ClientError.Security.Unauthorized",
1151-
):
1152-
return False
1153-
raise
1149+
except Neo4jError as exc:
1150+
if exc.code in (
1151+
"Neo.ClientError.Security.CredentialsExpired",
1152+
"Neo.ClientError.Security.Forbidden",
1153+
"Neo.ClientError.Security.TokenExpired",
1154+
"Neo.ClientError.Security.Unauthorized",
1155+
):
1156+
return False
1157+
raise
11541158
return True
11551159

1156-
11571160
async def supports_session_auth(self) -> bool:
11581161
"""Check if the remote supports connection re-authentication.
11591162
@@ -1170,13 +1173,14 @@ async def supports_session_auth(self) -> bool:
11701173
11711174
.. versionadded:: 5.8
11721175
"""
1173-
async with self.session() as session:
1176+
session_config = self._read_session_config({}, preview_check=False)
1177+
async with self._session(session_config) as session:
11741178
await session._connect(READ_ACCESS)
11751179
assert session._connection
11761180
return session._connection.supports_re_auth
11771181

1178-
async def _get_server_info(self, **config) -> ServerInfo:
1179-
async with self.session(**config) as session:
1182+
async def _get_server_info(self, session_config) -> ServerInfo:
1183+
async with self._session(session_config) as session:
11801184
return await session._get_server_info()
11811185

11821186

@@ -1225,21 +1229,6 @@ def __init__(self, pool, default_workspace_config):
12251229
AsyncDriver.__init__(self, pool, default_workspace_config)
12261230
self._default_workspace_config = default_workspace_config
12271231

1228-
if not t.TYPE_CHECKING:
1229-
1230-
def session(self, **config) -> AsyncSession:
1231-
"""
1232-
:param config: The values that can be specified are found in
1233-
:class: `neo4j.SessionConfig`
1234-
1235-
:returns:
1236-
:rtype: :class: `neo4j.AsyncSession`
1237-
"""
1238-
config = self._prepare_session_config(**config)
1239-
session_config = SessionConfig(self._default_workspace_config,
1240-
config)
1241-
return AsyncSession(self._pool, session_config)
1242-
12431232

12441233
class AsyncNeo4jDriver(_Routing, AsyncDriver):
12451234
""":class:`.AsyncNeo4jDriver` is instantiated for ``neo4j`` URIs. The
@@ -1264,23 +1253,15 @@ def __init__(self, pool, default_workspace_config):
12641253
_Routing.__init__(self, [pool.address])
12651254
AsyncDriver.__init__(self, pool, default_workspace_config)
12661255

1267-
if not t.TYPE_CHECKING:
1268-
1269-
def session(self, **config) -> AsyncSession:
1270-
config = self._prepare_session_config(**config)
1271-
session_config = SessionConfig(self._default_workspace_config,
1272-
config)
1273-
return AsyncSession(self._pool, session_config)
1274-
12751256

1276-
def _normalize_notifications_config(config):
1277-
if config.get("notifications_disabled_categories") is not None:
1278-
config["notifications_disabled_categories"] = [
1257+
def _normalize_notifications_config(config_kwargs):
1258+
if config_kwargs.get("notifications_disabled_categories") is not None:
1259+
config_kwargs["notifications_disabled_categories"] = [
12791260
getattr(e, "value", e)
1280-
for e in config["notifications_disabled_categories"]
1261+
for e in config_kwargs["notifications_disabled_categories"]
12811262
]
1282-
if config.get("notifications_min_severity") is not None:
1283-
config["notifications_min_severity"] = getattr(
1284-
config["notifications_min_severity"], "value",
1285-
config["notifications_min_severity"]
1263+
if config_kwargs.get("notifications_min_severity") is not None:
1264+
config_kwargs["notifications_min_severity"] = getattr(
1265+
config_kwargs["notifications_min_severity"], "value",
1266+
config_kwargs["notifications_min_severity"]
12861267
)

src/neo4j/_async/work/session.py

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,14 @@
2020

2121
import asyncio
2222
import typing as t
23-
import warnings
2423
from logging import getLogger
2524
from random import random
2625
from time import perf_counter
2726

2827
from ..._async_compat import async_sleep
2928
from ..._async_compat.util import AsyncUtil
3029
from ..._conf import SessionConfig
31-
from ..._meta import (
32-
deprecated,
33-
PreviewWarning,
34-
)
30+
from ..._meta import deprecated
3531
from ..._util import ContextBool
3632
from ..._work import Query
3733
from ...api import (
@@ -108,14 +104,9 @@ class AsyncSession(AsyncWorkspace):
108104
def __init__(self, pool, session_config):
109105
assert isinstance(session_config, SessionConfig)
110106
if session_config.auth is not None:
111-
with warnings.catch_warnings():
112-
warnings.filterwarnings(
113-
"ignore", message=r".*\bAuth managers\b.*",
114-
category=PreviewWarning
115-
)
116-
session_config.auth = AsyncAuthManagers.static(
117-
session_config.auth
118-
)
107+
session_config.auth = AsyncAuthManagers.static._without_warning(
108+
session_config.auth
109+
)
119110
super().__init__(pool, session_config)
120111
self._config = session_config
121112
self._initialize_bookmarks(session_config.bookmarks)

src/neo4j/_auth_management.py

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,9 @@
2424
import abc
2525
import time
2626
import typing as t
27-
import warnings
2827
from dataclasses import dataclass
2928

30-
from ._meta import (
31-
preview,
32-
PreviewWarning,
33-
)
29+
from ._meta import preview
3430
from .api import _TAuth
3531
from .exceptions import Neo4jError
3632

@@ -89,11 +85,9 @@ def expires_in(self, seconds: float) -> "ExpiringAuth":
8985
9086
.. versionadded:: 5.9
9187
"""
92-
with warnings.catch_warnings():
93-
warnings.filterwarnings("ignore",
94-
message=r"^Auth managers\b.*",
95-
category=PreviewWarning)
96-
return ExpiringAuth(self.auth, time.time() + seconds)
88+
return ExpiringAuth._without_warning( # type: ignore
89+
self.auth, time.time() + seconds
90+
)
9791

9892

9993
def expiring_auth_has_expired(auth: ExpiringAuth) -> bool:

0 commit comments

Comments
 (0)