Skip to content

Commit cd4b1db

Browse files
committed
PYTHON-4946 - GridFS spec: Add performant 'rename all revisions by filename' feature - rename_by_name
1 parent f77e1ac commit cd4b1db

File tree

6 files changed

+383
-2
lines changed

6 files changed

+383
-2
lines changed

doc/changelog.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ PyMongo 4.12 brings a number of changes including:
99
- Support for configuring DEK cache lifetime via the ``key_expiration_ms`` argument to
1010
:class:`~pymongo.encryption_options.AutoEncryptionOpts`.
1111
- Support for $lookup in CSFLE and QE supported on MongoDB 8.1+.
12+
- Added :meth:`gridfs.asynchronous.grid_file.AsyncGridFSBucket.rename_by_name` and :meth:`gridfs.grid_file.GridFSBucket.rename_by_name`
13+
for more performant renaming of file revisions.
1214

1315
Issues Resolved
1416
...............

gridfs/asynchronous/grid_file.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1021,6 +1021,36 @@ async def rename(
10211021
"matched file_id %i" % (new_filename, file_id)
10221022
)
10231023

1024+
async def rename_by_name(
1025+
self, filename: str, new_filename: str, session: Optional[AsyncClientSession] = None
1026+
) -> None:
1027+
"""Renames the stored file with the specified filename.
1028+
1029+
For example::
1030+
1031+
my_db = AsyncMongoClient().test
1032+
fs = AsyncGridFSBucket(my_db)
1033+
await fs.upload_from_stream("test_file", "data I want to store!")
1034+
await fs.rename_by_name("test_file", "new_test_name")
1035+
1036+
Raises :exc:`~gridfs.errors.NoFile` if no file with file_id exists.
1037+
1038+
:param filename: The filename of the file to be renamed.
1039+
:param new_filename: The new name of the file.
1040+
:param session: a
1041+
:class:`~pymongo.client_session.AsyncClientSession`
1042+
1043+
.. versionadded:: 4.12
1044+
"""
1045+
_disallow_transactions(session)
1046+
result = await self._files.update_many(
1047+
{"filename": filename}, {"$set": {"filename": new_filename}}, session=session
1048+
)
1049+
if not result.matched_count:
1050+
raise NoFile(
1051+
f"no files could be renamed {new_filename} because none matched filename {filename}"
1052+
)
1053+
10241054

10251055
class AsyncGridIn:
10261056
"""Class to write data to GridFS."""

gridfs/synchronous/grid_file.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,6 +1015,36 @@ def rename(
10151015
"matched file_id %i" % (new_filename, file_id)
10161016
)
10171017

1018+
def rename_by_name(
1019+
self, filename: str, new_filename: str, session: Optional[ClientSession] = None
1020+
) -> None:
1021+
"""Renames the stored file with the specified filename.
1022+
1023+
For example::
1024+
1025+
my_db = MongoClient().test
1026+
fs = GridFSBucket(my_db)
1027+
fs.upload_from_stream("test_file", "data I want to store!")
1028+
fs.rename_by_name("test_file", "new_test_name")
1029+
1030+
Raises :exc:`~gridfs.errors.NoFile` if no file with file_id exists.
1031+
1032+
:param filename: The filename of the file to be renamed.
1033+
:param new_filename: The new name of the file.
1034+
:param session: a
1035+
:class:`~pymongo.client_session.ClientSession`
1036+
1037+
.. versionadded:: 4.12
1038+
"""
1039+
_disallow_transactions(session)
1040+
result = self._files.update_many(
1041+
{"filename": filename}, {"$set": {"filename": new_filename}}, session=session
1042+
)
1043+
if not result.matched_count:
1044+
raise NoFile(
1045+
f"no files could be renamed {new_filename} because none matched filename {filename}"
1046+
)
1047+
10181048

10191049
class GridIn:
10201050
"""Class to write data to GridFS."""

test/asynchronous/unified_format.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
from bson import SON, json_util
6767
from bson.codec_options import DEFAULT_CODEC_OPTIONS
6868
from bson.objectid import ObjectId
69-
from gridfs import AsyncGridFSBucket, GridOut
69+
from gridfs import AsyncGridFSBucket, GridOut, NoFile
7070
from pymongo import ASCENDING, AsyncMongoClient, CursorType, _csot
7171
from pymongo.asynchronous.change_stream import AsyncChangeStream
7272
from pymongo.asynchronous.client_session import AsyncClientSession, TransactionOptions, _TxnState
@@ -630,6 +630,9 @@ def process_error(self, exception, spec):
630630
self.assertNotIsInstance(error, NotPrimaryError)
631631
elif isinstance(error, (InvalidOperation, ConfigurationError, EncryptionError)):
632632
pass
633+
# gridfs NoFile errors are considered client errors.
634+
elif isinstance(error, NoFile):
635+
pass
633636
else:
634637
self.assertNotIsInstance(error, PyMongoError)
635638

0 commit comments

Comments
 (0)