Skip to content

Commit 638c05f

Browse files
authored
Mitigate errors during resource warning on interpreter shutdown (#1037)
1 parent b7272af commit 638c05f

File tree

5 files changed

+63
-42
lines changed

5 files changed

+63
-42
lines changed

src/neo4j/_async/driver.py

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -503,20 +503,27 @@ async def __aenter__(self) -> AsyncDriver:
503503
async def __aexit__(self, exc_type, exc_value, traceback):
504504
await self.close()
505505

506-
def __del__(self):
506+
# Copy globals as function locals to make sure that they are available
507+
# during Python shutdown when the Pool is destroyed.
508+
def __del__(
509+
self, _unclosed_resource_warn=unclosed_resource_warn,
510+
_is_async_code=AsyncUtil.is_async_code,
511+
_deprecation_warn=deprecation_warn,
512+
):
507513
if not self._closed:
508-
unclosed_resource_warn(self)
514+
_unclosed_resource_warn(self)
509515
# TODO: 6.0 - remove this
516+
if _is_async_code:
517+
return
510518
if not self._closed:
511-
if not AsyncUtil.is_async_code:
512-
deprecation_warn(
513-
"Relying on AsyncDriver's destructor to close the session "
514-
"is deprecated. Please make sure to close the session. "
515-
"Use it as a context (`with` statement) or make sure to "
516-
"call `.close()` explicitly. Future versions of the "
517-
"driver will not close drivers automatically."
518-
)
519-
self.close()
519+
_deprecation_warn(
520+
"Relying on AsyncDriver's destructor to close the session "
521+
"is deprecated. Please make sure to close the session. "
522+
"Use it as a context (`with` statement) or make sure to "
523+
"call `.close()` explicitly. Future versions of the "
524+
"driver will not close drivers automatically."
525+
)
526+
self.close()
520527

521528
def _check_state(self):
522529
if self._closed:

src/neo4j/_async/work/workspace.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,15 +59,19 @@ def __init__(self, pool, config):
5959
self._closed = False
6060
super().__init__()
6161

62-
def __del__(self):
62+
def __del__(
63+
self, _unclosed_resource_warn=unclosed_resource_warn,
64+
_is_async_code=AsyncUtil.is_async_code,
65+
_deprecation_warn=deprecation_warn,
66+
):
6367
if self._closed:
6468
return
65-
unclosed_resource_warn(self)
69+
_unclosed_resource_warn(self)
6670
# TODO: 6.0 - remove this
67-
if asyncio.iscoroutinefunction(self.close):
71+
if _is_async_code:
6872
return
6973
try:
70-
deprecation_warn(
74+
_deprecation_warn(
7175
"Relying on AsyncSession's destructor to close the session "
7276
"is deprecated. Please make sure to close the session. Use it "
7377
"as a context (`with` statement) or make sure to call "

src/neo4j/_meta.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import asyncio
2020
import platform
2121
import sys
22-
import tracemalloc
2322
import typing as t
2423
from functools import wraps
2524
from inspect import isclass
@@ -91,8 +90,11 @@ def copy_signature(_: _FuncT) -> t.Callable[[t.Callable], _FuncT]:
9190
return _id
9291

9392

94-
def deprecation_warn(message, stack_level=1):
95-
warn(message, category=DeprecationWarning, stacklevel=stack_level + 1)
93+
94+
# Copy globals as function locals to make sure that they are available
95+
# during Python shutdown when the Pool is destroyed.
96+
def deprecation_warn(message, stack_level=1, _warn=warn):
97+
_warn(message, category=DeprecationWarning, stacklevel=stack_level + 1)
9698

9799

98100
def deprecated(message: str) -> t.Callable[[_FuncT], _FuncT]:
@@ -224,12 +226,9 @@ def inner(*args, **kwargs):
224226
return decorator
225227

226228

227-
def unclosed_resource_warn(obj):
228-
msg = f"Unclosed {obj!r}."
229-
trace = tracemalloc.get_object_traceback(obj)
230-
if trace:
231-
msg += "\nObject allocated at (most recent call last):\n"
232-
msg += "\n".join(trace.format())
233-
else:
234-
msg += "\nEnable tracemalloc to get the object allocation traceback."
235-
warn(msg, ResourceWarning, stacklevel=2, source=obj)
229+
# Copy globals as function locals to make sure that they are available
230+
# during Python shutdown when the Pool is destroyed.
231+
def unclosed_resource_warn(obj, _warn=warn):
232+
cls_name = obj.__class__.__name__
233+
msg = f"unclosed {cls_name}: {obj!r}."
234+
_warn(msg, ResourceWarning, stacklevel=2, source=obj)

src/neo4j/_sync/driver.py

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -502,20 +502,27 @@ def __enter__(self) -> Driver:
502502
def __exit__(self, exc_type, exc_value, traceback):
503503
self.close()
504504

505-
def __del__(self):
505+
# Copy globals as function locals to make sure that they are available
506+
# during Python shutdown when the Pool is destroyed.
507+
def __del__(
508+
self, _unclosed_resource_warn=unclosed_resource_warn,
509+
_is_async_code=Util.is_async_code,
510+
_deprecation_warn=deprecation_warn,
511+
):
506512
if not self._closed:
507-
unclosed_resource_warn(self)
513+
_unclosed_resource_warn(self)
508514
# TODO: 6.0 - remove this
515+
if _is_async_code:
516+
return
509517
if not self._closed:
510-
if not Util.is_async_code:
511-
deprecation_warn(
512-
"Relying on Driver's destructor to close the session "
513-
"is deprecated. Please make sure to close the session. "
514-
"Use it as a context (`with` statement) or make sure to "
515-
"call `.close()` explicitly. Future versions of the "
516-
"driver will not close drivers automatically."
517-
)
518-
self.close()
518+
_deprecation_warn(
519+
"Relying on Driver's destructor to close the session "
520+
"is deprecated. Please make sure to close the session. "
521+
"Use it as a context (`with` statement) or make sure to "
522+
"call `.close()` explicitly. Future versions of the "
523+
"driver will not close drivers automatically."
524+
)
525+
self.close()
519526

520527
def _check_state(self):
521528
if self._closed:

src/neo4j/_sync/work/workspace.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,15 +59,19 @@ def __init__(self, pool, config):
5959
self._closed = False
6060
super().__init__()
6161

62-
def __del__(self):
62+
def __del__(
63+
self, _unclosed_resource_warn=unclosed_resource_warn,
64+
_is_async_code=Util.is_async_code,
65+
_deprecation_warn=deprecation_warn,
66+
):
6367
if self._closed:
6468
return
65-
unclosed_resource_warn(self)
69+
_unclosed_resource_warn(self)
6670
# TODO: 6.0 - remove this
67-
if asyncio.iscoroutinefunction(self.close):
71+
if _is_async_code:
6872
return
6973
try:
70-
deprecation_warn(
74+
_deprecation_warn(
7175
"Relying on Session's destructor to close the session "
7276
"is deprecated. Please make sure to close the session. Use it "
7377
"as a context (`with` statement) or make sure to call "

0 commit comments

Comments
 (0)