Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ 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 .
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resources . -> resources.

Let's include an example of the warning here too.


Issues Resolved
...............
Expand Down
19 changes: 19 additions & 0 deletions pymongo/asynchronous/mongo_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@

import contextlib
import os
import warnings
import weakref
from collections import defaultdict
from typing import (
Expand Down Expand Up @@ -864,6 +865,7 @@ def __init__(
)

self._opened = False
self._closed = False
self._init_background()

if _IS_SYNC and connect:
Expand Down Expand Up @@ -1173,6 +1175,22 @@ def __getitem__(self, name: str) -> database.AsyncDatabase[_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,
Expand Down Expand Up @@ -1540,6 +1558,7 @@ async def close(self) -> None:
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.
Expand Down
2 changes: 1 addition & 1 deletion pymongo/asynchronous/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def __init__(
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]]:
Expand Down
19 changes: 19 additions & 0 deletions pymongo/synchronous/mongo_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@

import contextlib
import os
import warnings
import weakref
from collections import defaultdict
from typing import (
Expand Down Expand Up @@ -863,6 +864,7 @@ def __init__(
)

self._opened = False
self._closed = False
self._init_background()

if _IS_SYNC and connect:
Expand Down Expand Up @@ -1172,6 +1174,22 @@ def __getitem__(self, name: str) -> database.Database[_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,
Expand Down Expand Up @@ -1535,6 +1553,7 @@ def close(self) -> None:
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.
Expand Down
2 changes: 1 addition & 1 deletion pymongo/synchronous/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def __init__(
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]]:
Expand Down
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Loading