diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 042685bc2..fd2808ea1 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -46,7 +46,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@76621b61decf072c1cee8dd1ce2d2a82d33c17ed # v3 + uses: github/codeql-action/init@51f77329afa6477de8c49fc9c7046c15b9a4e79d # v3 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} @@ -63,6 +63,6 @@ jobs: pip install -e . - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@76621b61decf072c1cee8dd1ce2d2a82d33c17ed # v3 + uses: github/codeql-action/analyze@51f77329afa6477de8c49fc9c7046c15b9a4e79d # v3 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/dist.yml b/.github/workflows/dist.yml index 586f86282..14c253fe7 100644 --- a/.github/workflows/dist.yml +++ b/.github/workflows/dist.yml @@ -142,7 +142,7 @@ jobs: name: Download Wheels steps: - name: Download all workflow run artifacts - uses: actions/download-artifact@v5 + uses: actions/download-artifact@v4 - name: Flatten directory working-directory: . run: | diff --git a/.github/workflows/release-python.yml b/.github/workflows/release-python.yml index 0c10fa2e9..9cce310d9 100644 --- a/.github/workflows/release-python.yml +++ b/.github/workflows/release-python.yml @@ -76,7 +76,7 @@ jobs: id-token: write steps: - name: Download all the dists - uses: actions/download-artifact@v5 + uses: actions/download-artifact@v4 with: name: all-dist-${{ github.run_id }} path: dist/ diff --git a/.github/workflows/test-python.yml b/.github/workflows/test-python.yml index 9a2f2e42b..b7b8fb506 100644 --- a/.github/workflows/test-python.yml +++ b/.github/workflows/test-python.yml @@ -195,7 +195,7 @@ jobs: timeout-minutes: 20 steps: - name: Download sdist - uses: actions/download-artifact@v5 + uses: actions/download-artifact@v4 - name: Unpack SDist shell: bash run: | diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index 14bf81a08..8a2bccf93 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -18,4 +18,4 @@ jobs: with: persist-credentials: false - name: Run zizmor 🌈 - uses: zizmorcore/zizmor-action@c17832b972c15fd5f3d5065a7e16ad761a0a10d2 + uses: zizmorcore/zizmor-action@383d31df2eb66a2f42db98c9654bdc73231f3e3a diff --git a/pymongo/asynchronous/collection.py b/pymongo/asynchronous/collection.py index e07c8615d..741c11e55 100644 --- a/pymongo/asynchronous/collection.py +++ b/pymongo/asynchronous/collection.py @@ -1776,6 +1776,15 @@ class AsyncCollection(common.BaseObject, Generic[_DocumentType]): improper type. Returns an instance of :class:`~pymongo.asynchronous.cursor.AsyncCursor` corresponding to this query. + Cursors are closed automatically when they are exhausted (the last batch of data is retrieved from the database). + If a cursor is not exhausted, it will be closed automatically upon garbage collection, which leaves resources open but unused for a potentially long period of time. + To avoid this, best practice is to call :meth:`AsyncCursor.close` when the cursor is no longer needed, + or use the cursor in a with statement:: + + async with collection.find() as cursor: + async for doc in cursor: + print(doc) + The :meth:`find` method obeys the :attr:`read_preference` of this :class:`AsyncCollection`. @@ -2503,6 +2512,15 @@ class AsyncCollection(common.BaseObject, Generic[_DocumentType]): ... SON([('v', 2), ('key', SON([('_id', 1)])), ('name', '_id_')]) + Cursors are closed automatically when they are exhausted (the last batch of data is retrieved from the database). + If a cursor is not exhausted, it will be closed automatically upon garbage collection, which leaves resources open but unused for a potentially long period of time. + To avoid this, best practice is to call :meth:`AsyncCursor.close` when the cursor is no longer needed, + or use the cursor in a with statement:: + + async with await collection.list_indexes() as cursor: + async for index in cursor: + print(index) + :param session: a :class:`~pymongo.asynchronous.client_session.AsyncClientSession`. :param comment: A user-provided comment to attach to this @@ -2620,6 +2638,15 @@ class AsyncCollection(common.BaseObject, Generic[_DocumentType]): ) -> AsyncCommandCursor[Mapping[str, Any]]: """Return a cursor over search indexes for the current collection. + Cursors are closed automatically when they are exhausted (the last batch of data is retrieved from the database). + If a cursor is not exhausted, it will be closed automatically upon garbage collection, which leaves resources open but unused for a potentially long period of time. + To avoid this, best practice is to call :meth:`AsyncCursor.close` when the cursor is no longer needed, + or use the cursor in a with statement:: + + async with await collection.list_search_indexes() as cursor: + async for index in cursor: + print(index) + :param name: If given, the name of the index to search for. Only indexes with matching index names will be returned. If not given, all search indexes for the current collection @@ -2922,6 +2949,15 @@ class AsyncCollection(common.BaseObject, Generic[_DocumentType]): .. note:: The :attr:`~pymongo.asynchronous.collection.AsyncCollection.write_concern` of this collection is automatically applied to this operation. + Cursors are closed automatically when they are exhausted (the last batch of data is retrieved from the database). + If a cursor is not exhausted, it will be closed automatically upon garbage collection, which leaves resources open but unused for a potentially long period of time. + To avoid this, best practice is to call :meth:`AsyncCursor.close` when the cursor is no longer needed, + or use the cursor in a with statement:: + + async with await collection.aggregate() as cursor: + async for operation in cursor: + print(operation) + :param pipeline: a list of aggregation pipeline stages :param session: a :class:`~pymongo.asynchronous.client_session.AsyncClientSession`. diff --git a/pymongo/asynchronous/database.py b/pymongo/asynchronous/database.py index 5a1dcfe36..f70c2b403 100644 --- a/pymongo/asynchronous/database.py +++ b/pymongo/asynchronous/database.py @@ -643,8 +643,8 @@ class AsyncDatabase(common.BaseObject, Generic[_DocumentType]): .. code-block:: python # Lists all operations currently running on the server. - with client.admin.aggregate([{"$currentOp": {}}]) as cursor: - for operation in cursor: + async with await client.admin.aggregate([{"$currentOp": {}}]) as cursor: + async for operation in cursor: print(operation) The :meth:`aggregate` method obeys the :attr:`read_preference` of this @@ -652,6 +652,11 @@ class AsyncDatabase(common.BaseObject, Generic[_DocumentType]): which case :attr:`~pymongo.read_preferences.ReadPreference.PRIMARY` is used. + Cursors are closed automatically when they are exhausted (the last batch of data is retrieved from the database). + If a cursor is not exhausted, it will be closed automatically upon garbage collection, which leaves resources open but unused for a potentially long period of time. + To avoid this, best practice is to call :meth:`AsyncCursor.close` when the cursor is no longer needed, + or use the cursor in a with statement. + .. note:: This method does not support the 'explain' option. Please use :meth:`~pymongo.asynchronous.database.AsyncDatabase.command` instead. @@ -1154,6 +1159,15 @@ class AsyncDatabase(common.BaseObject, Generic[_DocumentType]): ) -> AsyncCommandCursor[MutableMapping[str, Any]]: """Get a cursor over the collections of this database. + Cursors are closed automatically when they are exhausted (the last batch of data is retrieved from the database). + If a cursor is not exhausted, it will be closed automatically upon garbage collection, which leaves resources open but unused for a potentially long period of time. + To avoid this, best practice is to call :meth:`AsyncCursor.close` when the cursor is no longer needed, + or use the cursor in a with statement:: + + async with await database.list_collections() as cursor: + async for collection in cursor: + print(collection) + :param session: a :class:`~pymongo.asynchronous.client_session.AsyncClientSession`. :param filter: A query document to filter the list of diff --git a/pymongo/asynchronous/mongo_client.py b/pymongo/asynchronous/mongo_client.py index 8ca7bca43..b61664779 100644 --- a/pymongo/asynchronous/mongo_client.py +++ b/pymongo/asynchronous/mongo_client.py @@ -202,6 +202,12 @@ class AsyncMongoClient(common.BaseObject, Generic[_DocumentType]): exception (recognizing that the operation failed) and then continue to execute. + Best practice is to call :meth:`AsyncMongoClient.close` when the client is no longer needed, + or use the client in a with statement:: + + async with AsyncMongoClient(url) as client: + # Use client here. + The `host` parameter can be a full `mongodb URI `_, in addition to a simple hostname. It can also be a list of hostnames but no more @@ -2345,6 +2351,15 @@ class AsyncMongoClient(common.BaseObject, Generic[_DocumentType]): ) -> AsyncCommandCursor[dict[str, Any]]: """Get a cursor over the databases of the connected server. + Cursors are closed automatically when they are exhausted (the last batch of data is retrieved from the database). + If a cursor is not exhausted, it will be closed automatically upon garbage collection, which leaves resources open but unused for a potentially long period of time. + To avoid this, best practice is to call :meth:`AsyncCursor.close` when the cursor is no longer needed, + or use the cursor in a with statement:: + + async with await client.list_databases() as cursor: + async for database in cursor: + print(database) + :param session: a :class:`~pymongo.asynchronous.client_session.AsyncClientSession`. :param comment: A user-provided comment to attach to this diff --git a/pymongo/synchronous/collection.py b/pymongo/synchronous/collection.py index f37c32e93..9f32deb76 100644 --- a/pymongo/synchronous/collection.py +++ b/pymongo/synchronous/collection.py @@ -1775,6 +1775,15 @@ class Collection(common.BaseObject, Generic[_DocumentType]): improper type. Returns an instance of :class:`~pymongo.cursor.Cursor` corresponding to this query. + Cursors are closed automatically when they are exhausted (the last batch of data is retrieved from the database). + If a cursor is not exhausted, it will be closed automatically upon garbage collection, which leaves resources open but unused for a potentially long period of time. + To avoid this, best practice is to call :meth:`Cursor.close` when the cursor is no longer needed, + or use the cursor in a with statement:: + + with collection.find() as cursor: + for doc in cursor: + print(doc) + The :meth:`find` method obeys the :attr:`read_preference` of this :class:`Collection`. @@ -2500,6 +2509,15 @@ class Collection(common.BaseObject, Generic[_DocumentType]): ... SON([('v', 2), ('key', SON([('_id', 1)])), ('name', '_id_')]) + Cursors are closed automatically when they are exhausted (the last batch of data is retrieved from the database). + If a cursor is not exhausted, it will be closed automatically upon garbage collection, which leaves resources open but unused for a potentially long period of time. + To avoid this, best practice is to call :meth:`Cursor.close` when the cursor is no longer needed, + or use the cursor in a with statement:: + + with collection.list_indexes() as cursor: + for index in cursor: + print(index) + :param session: a :class:`~pymongo.client_session.ClientSession`. :param comment: A user-provided comment to attach to this @@ -2617,6 +2635,15 @@ class Collection(common.BaseObject, Generic[_DocumentType]): ) -> CommandCursor[Mapping[str, Any]]: """Return a cursor over search indexes for the current collection. + Cursors are closed automatically when they are exhausted (the last batch of data is retrieved from the database). + If a cursor is not exhausted, it will be closed automatically upon garbage collection, which leaves resources open but unused for a potentially long period of time. + To avoid this, best practice is to call :meth:`Cursor.close` when the cursor is no longer needed, + or use the cursor in a with statement:: + + with collection.list_search_indexes() as cursor: + for index in cursor: + print(index) + :param name: If given, the name of the index to search for. Only indexes with matching index names will be returned. If not given, all search indexes for the current collection @@ -2915,6 +2942,15 @@ class Collection(common.BaseObject, Generic[_DocumentType]): .. note:: The :attr:`~pymongo.collection.Collection.write_concern` of this collection is automatically applied to this operation. + Cursors are closed automatically when they are exhausted (the last batch of data is retrieved from the database). + If a cursor is not exhausted, it will be closed automatically upon garbage collection, which leaves resources open but unused for a potentially long period of time. + To avoid this, best practice is to call :meth:`Cursor.close` when the cursor is no longer needed, + or use the cursor in a with statement:: + + with collection.aggregate() as cursor: + for operation in cursor: + print(operation) + :param pipeline: a list of aggregation pipeline stages :param session: a :class:`~pymongo.client_session.ClientSession`. diff --git a/pymongo/synchronous/database.py b/pymongo/synchronous/database.py index 2874e1c8d..e30f97817 100644 --- a/pymongo/synchronous/database.py +++ b/pymongo/synchronous/database.py @@ -652,6 +652,11 @@ class Database(common.BaseObject, Generic[_DocumentType]): which case :attr:`~pymongo.read_preferences.ReadPreference.PRIMARY` is used. + Cursors are closed automatically when they are exhausted (the last batch of data is retrieved from the database). + If a cursor is not exhausted, it will be closed automatically upon garbage collection, which leaves resources open but unused for a potentially long period of time. + To avoid this, best practice is to call :meth:`Cursor.close` when the cursor is no longer needed, + or use the cursor in a with statement. + .. note:: This method does not support the 'explain' option. Please use :meth:`~pymongo.database.Database.command` instead. @@ -1148,6 +1153,15 @@ class Database(common.BaseObject, Generic[_DocumentType]): ) -> CommandCursor[MutableMapping[str, Any]]: """Get a cursor over the collections of this database. + Cursors are closed automatically when they are exhausted (the last batch of data is retrieved from the database). + If a cursor is not exhausted, it will be closed automatically upon garbage collection, which leaves resources open but unused for a potentially long period of time. + To avoid this, best practice is to call :meth:`Cursor.close` when the cursor is no longer needed, + or use the cursor in a with statement:: + + with database.list_collections() as cursor: + for collection in cursor: + print(collection) + :param session: a :class:`~pymongo.client_session.ClientSession`. :param filter: A query document to filter the list of diff --git a/pymongo/synchronous/mongo_client.py b/pymongo/synchronous/mongo_client.py index f44368ec5..ef0663584 100644 --- a/pymongo/synchronous/mongo_client.py +++ b/pymongo/synchronous/mongo_client.py @@ -199,6 +199,12 @@ class MongoClient(common.BaseObject, Generic[_DocumentType]): exception (recognizing that the operation failed) and then continue to execute. + Best practice is to call :meth:`MongoClient.close` when the client is no longer needed, + or use the client in a with statement:: + + with MongoClient(url) as client: + # Use client here. + The `host` parameter can be a full `mongodb URI `_, in addition to a simple hostname. It can also be a list of hostnames but no more @@ -2335,6 +2341,15 @@ class MongoClient(common.BaseObject, Generic[_DocumentType]): ) -> CommandCursor[dict[str, Any]]: """Get a cursor over the databases of the connected server. + Cursors are closed automatically when they are exhausted (the last batch of data is retrieved from the database). + If a cursor is not exhausted, it will be closed automatically upon garbage collection, which leaves resources open but unused for a potentially long period of time. + To avoid this, best practice is to call :meth:`Cursor.close` when the cursor is no longer needed, + or use the cursor in a with statement:: + + with client.list_databases() as cursor: + for database in cursor: + print(database) + :param session: a :class:`~pymongo.client_session.ClientSession`. :param comment: A user-provided comment to attach to this