Skip to content

Commit a3f3ec5

Browse files
authored
PYTHON-4946 - Add GridFSBucket.rename_by_name (#2219)
1 parent 8675a16 commit a3f3ec5

File tree

10 files changed

+413
-2
lines changed

10 files changed

+413
-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 a file with multiple revisions.
1214
- Added :meth:`gridfs.asynchronous.grid_file.AsyncGridFSBucket.delete_by_name` and :meth:`gridfs.grid_file.GridFSBucket.delete_by_name`
1315
for more performant deletion of a file with multiple revisions.
1416
- AsyncMongoClient no longer performs DNS resolution for "mongodb+srv://" connection strings on creation.

gridfs/asynchronous/grid_file.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1050,6 +1050,35 @@ async def rename(
10501050
"matched file_id %i" % (new_filename, file_id)
10511051
)
10521052

1053+
async def rename_by_name(
1054+
self, filename: str, new_filename: str, session: Optional[AsyncClientSession] = None
1055+
) -> None:
1056+
"""Renames the stored file with the specified filename.
1057+
1058+
For example::
1059+
1060+
my_db = AsyncMongoClient().test
1061+
fs = AsyncGridFSBucket(my_db)
1062+
await fs.upload_from_stream("test_file", "data I want to store!")
1063+
await fs.rename_by_name("test_file", "new_test_name")
1064+
1065+
Raises :exc:`~gridfs.errors.NoFile` if no file with the given filename exists.
1066+
1067+
:param filename: The filename of the file to be renamed.
1068+
:param new_filename: The new name of the file.
1069+
:param session: a :class:`~pymongo.client_session.AsyncClientSession`
1070+
1071+
.. versionadded:: 4.12
1072+
"""
1073+
_disallow_transactions(session)
1074+
result = await self._files.update_many(
1075+
{"filename": filename}, {"$set": {"filename": new_filename}}, session=session
1076+
)
1077+
if not result.matched_count:
1078+
raise NoFile(
1079+
f"no files could be renamed {new_filename!r} because none matched filename {filename!r}"
1080+
)
1081+
10531082

10541083
class AsyncGridIn:
10551084
"""Class to write data to GridFS."""

gridfs/synchronous/grid_file.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,6 +1042,35 @@ def rename(
10421042
"matched file_id %i" % (new_filename, file_id)
10431043
)
10441044

1045+
def rename_by_name(
1046+
self, filename: str, new_filename: str, session: Optional[ClientSession] = None
1047+
) -> None:
1048+
"""Renames the stored file with the specified filename.
1049+
1050+
For example::
1051+
1052+
my_db = MongoClient().test
1053+
fs = GridFSBucket(my_db)
1054+
fs.upload_from_stream("test_file", "data I want to store!")
1055+
fs.rename_by_name("test_file", "new_test_name")
1056+
1057+
Raises :exc:`~gridfs.errors.NoFile` if no file with the given filename exists.
1058+
1059+
:param filename: The filename of the file to be renamed.
1060+
:param new_filename: The new name of the file.
1061+
:param session: a :class:`~pymongo.client_session.ClientSession`
1062+
1063+
.. versionadded:: 4.12
1064+
"""
1065+
_disallow_transactions(session)
1066+
result = self._files.update_many(
1067+
{"filename": filename}, {"$set": {"filename": new_filename}}, session=session
1068+
)
1069+
if not result.matched_count:
1070+
raise NoFile(
1071+
f"no files could be renamed {new_filename!r} because none matched filename {filename!r}"
1072+
)
1073+
10451074

10461075
class GridIn:
10471076
"""Class to write data to GridFS."""

test/asynchronous/test_gridfs_bucket.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,19 @@ async def test_rename(self):
450450
b"testing", await (await self.fs.open_download_stream_by_name("second_name")).read()
451451
)
452452

453+
async def test_rename_by_name(self):
454+
_id = await self.fs.upload_from_stream("first_name", b"testing")
455+
self.assertEqual(
456+
b"testing", await (await self.fs.open_download_stream_by_name("first_name")).read()
457+
)
458+
459+
await self.fs.rename_by_name("first_name", "second_name")
460+
with self.assertRaises(NoFile):
461+
await self.fs.open_download_stream_by_name("first_name")
462+
self.assertEqual(
463+
b"testing", await (await self.fs.open_download_stream_by_name("second_name")).read()
464+
)
465+
453466
@patch("gridfs.asynchronous.grid_file._UPLOAD_BUFFER_SIZE", 5)
454467
async def test_abort(self):
455468
gin = self.fs.open_upload_stream("test_filename", chunk_size_bytes=5)

test/asynchronous/test_session.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,7 @@ async def find(session=None):
541541
(bucket.download_to_stream_by_name, ["f", sio], {}),
542542
(find, [], {}),
543543
(bucket.rename, [1, "f2"], {}),
544+
(bucket.rename_by_name, ["f2", "f3"], {}),
544545
# Delete both files so _test_ops can run these operations twice.
545546
(bucket.delete, [1], {}),
546547
(bucket.delete_by_name, ["f"], {}),

test/asynchronous/test_transactions.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,14 @@ async def gridfs_open_upload_stream(*args, **kwargs):
295295
"new-name",
296296
),
297297
),
298-
(bucket.delete_by_name, ("new-name",)),
298+
(
299+
bucket.rename_by_name,
300+
(
301+
"new-name",
302+
"new-name2",
303+
),
304+
),
305+
(bucket.delete_by_name, ("new-name2",)),
299306
]
300307

301308
async with client.start_session() as s, await s.start_transaction():

0 commit comments

Comments
 (0)