PYTHON-3193 - Add ResourceWarning for unclosed MongoClients in __del__ (#1833)
This commit is contained in:
parent
e683b81bf4
commit
2cca2d9e3d
@ -42,6 +42,16 @@ PyMongo 4.9 brings a number of improvements including:
|
||||
- Fixed a bug where PyMongo would raise ``InvalidBSON: date value out of range``
|
||||
when using :attr:`~bson.codec_options.DatetimeConversion.DATETIME_CLAMP` or
|
||||
:attr:`~bson.codec_options.DatetimeConversion.DATETIME_AUTO` with a non-UTC timezone.
|
||||
- Added a warning to unclosed MongoClient instances
|
||||
telling users to explicitly close clients when finished with them to avoid leaking resources.
|
||||
For example:
|
||||
|
||||
.. code-block::
|
||||
|
||||
sys:1: ResourceWarning: Unclosed MongoClient opened at:
|
||||
File "/Users/<user>/my_file.py", line 8, in <module>``
|
||||
client = MongoClient()
|
||||
Call MongoClient.close() to safely shut down your client and free up resources.
|
||||
- The default value for ``connect`` in ``MongoClient`` is changed to ``False`` when running on
|
||||
unction-as-a-service (FaaS) like AWS Lambda, Google Cloud Functions, and Microsoft Azure Functions.
|
||||
On some FaaS systems, there is a ``fork()`` operation at function
|
||||
|
||||
@ -34,6 +34,7 @@ from __future__ import annotations
|
||||
|
||||
import contextlib
|
||||
import os
|
||||
import warnings
|
||||
import weakref
|
||||
from collections import defaultdict
|
||||
from typing import (
|
||||
@ -871,6 +872,7 @@ class AsyncMongoClient(common.BaseObject, Generic[_DocumentType]):
|
||||
)
|
||||
|
||||
self._opened = False
|
||||
self._closed = False
|
||||
self._init_background()
|
||||
|
||||
if _IS_SYNC and connect:
|
||||
@ -1180,6 +1182,22 @@ class AsyncMongoClient(common.BaseObject, Generic[_DocumentType]):
|
||||
"""
|
||||
return database.AsyncDatabase(self, name)
|
||||
|
||||
def __del__(self) -> None:
|
||||
"""Check that this AsyncMongoClient has been closed and issue a warning if not."""
|
||||
try:
|
||||
if not self._closed:
|
||||
warnings.warn(
|
||||
(
|
||||
f"Unclosed {type(self).__name__} opened at:\n{self._topology_settings._stack}"
|
||||
f"Call {type(self).__name__}.close() to safely shut down your client and free up resources."
|
||||
),
|
||||
ResourceWarning,
|
||||
stacklevel=2,
|
||||
source=self,
|
||||
)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def _close_cursor_soon(
|
||||
self,
|
||||
cursor_id: int,
|
||||
@ -1547,6 +1565,7 @@ class AsyncMongoClient(common.BaseObject, Generic[_DocumentType]):
|
||||
if self._encrypter:
|
||||
# TODO: PYTHON-1921 Encrypted MongoClients cannot be re-opened.
|
||||
await self._encrypter.close()
|
||||
self._closed = True
|
||||
|
||||
if not _IS_SYNC:
|
||||
# Add support for contextlib.aclosing.
|
||||
|
||||
@ -82,7 +82,7 @@ class TopologySettings:
|
||||
self._topology_id = ObjectId()
|
||||
# Store the allocation traceback to catch unclosed clients in the
|
||||
# test suite.
|
||||
self._stack = "".join(traceback.format_stack())
|
||||
self._stack = "".join(traceback.format_stack()[:-2])
|
||||
|
||||
@property
|
||||
def seeds(self) -> Collection[tuple[str, int]]:
|
||||
|
||||
@ -34,6 +34,7 @@ from __future__ import annotations
|
||||
|
||||
import contextlib
|
||||
import os
|
||||
import warnings
|
||||
import weakref
|
||||
from collections import defaultdict
|
||||
from typing import (
|
||||
@ -871,6 +872,7 @@ class MongoClient(common.BaseObject, Generic[_DocumentType]):
|
||||
)
|
||||
|
||||
self._opened = False
|
||||
self._closed = False
|
||||
self._init_background()
|
||||
|
||||
if _IS_SYNC and connect:
|
||||
@ -1180,6 +1182,22 @@ class MongoClient(common.BaseObject, Generic[_DocumentType]):
|
||||
"""
|
||||
return database.Database(self, name)
|
||||
|
||||
def __del__(self) -> None:
|
||||
"""Check that this MongoClient has been closed and issue a warning if not."""
|
||||
try:
|
||||
if not self._closed:
|
||||
warnings.warn(
|
||||
(
|
||||
f"Unclosed {type(self).__name__} opened at:\n{self._topology_settings._stack}"
|
||||
f"Call {type(self).__name__}.close() to safely shut down your client and free up resources."
|
||||
),
|
||||
ResourceWarning,
|
||||
stacklevel=2,
|
||||
source=self,
|
||||
)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def _close_cursor_soon(
|
||||
self,
|
||||
cursor_id: int,
|
||||
@ -1543,6 +1561,7 @@ class MongoClient(common.BaseObject, Generic[_DocumentType]):
|
||||
if self._encrypter:
|
||||
# TODO: PYTHON-1921 Encrypted MongoClients cannot be re-opened.
|
||||
self._encrypter.close()
|
||||
self._closed = True
|
||||
|
||||
if not _IS_SYNC:
|
||||
# Add support for contextlib.closing.
|
||||
|
||||
@ -82,7 +82,7 @@ class TopologySettings:
|
||||
self._topology_id = ObjectId()
|
||||
# Store the allocation traceback to catch unclosed clients in the
|
||||
# test suite.
|
||||
self._stack = "".join(traceback.format_stack())
|
||||
self._stack = "".join(traceback.format_stack()[:-2])
|
||||
|
||||
@property
|
||||
def seeds(self) -> Collection[tuple[str, int]]:
|
||||
|
||||
@ -95,6 +95,9 @@ filterwarnings = [
|
||||
"module:please use dns.resolver.Resolver.resolve:DeprecationWarning",
|
||||
# https://github.com/dateutil/dateutil/issues/1314
|
||||
"module:datetime.datetime.utc:DeprecationWarning:dateutil",
|
||||
# TODO: Remove both of these in https://jira.mongodb.org/browse/PYTHON-4731
|
||||
"ignore:Unclosed AsyncMongoClient*",
|
||||
"ignore:Unclosed MongoClient*",
|
||||
]
|
||||
markers = [
|
||||
"auth_aws: tests that rely on pymongo-auth-aws",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user