Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Unreleased
- Added `**kwargs` to `AzureBlobFileSystem.exists()`
- Populate `AzureBlobFile.version_id` on write when `version_aware` is enabled.
- Fixed issue where unawaitable Credential types were incorrectly awaited (#431)
- Fixed a bug where moving files does not delete them from the original location (#255)

2026.2.0
--------
Expand Down
27 changes: 14 additions & 13 deletions adlfs/spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -1340,21 +1340,19 @@ async def _separate_directory_markers_for_non_empty_directories(
A directory marker is an empty blob who's name is the path of the directory.
"""
unique_sorted_file_paths = sorted(set(file_paths)) # Remove duplicates and sort
directory_markers = []
files = [
unique_sorted_file_paths[-1]
] # The last file lexographically cannot be a directory marker for a non-empty directory.
prefix_set = set()
for fp in unique_sorted_file_paths:
parts = fp.split("/")
for i in range(1, len(parts)):
prefix_set.add("/".join(parts[:i]))

for file, next_file in zip(
unique_sorted_file_paths, unique_sorted_file_paths[1:]
):
# /path/to/directory -- directory marker
# /path/to/directory/file -- file in directory
# /path/to/directory2/file -- file in different directory
if next_file.startswith(file + "/"):
directory_markers.append(file)
directory_markers = []
files = []
for fp in unique_sorted_file_paths:
if fp in prefix_set:
directory_markers.append(fp)
else:
files.append(file)
files.append(fp)

return files, directory_markers

Expand Down Expand Up @@ -1794,6 +1792,9 @@ async def _cp_file(self, path1, path2, **kwargs):
container1, blob1, version_id = self.split_path(path1, delimiter="/")
container2, blob2, _ = self.split_path(path2, delimiter="/")

if version_id is None and await self._isdir(path1):
return

cc1 = self.service_client.get_container_client(container1)
blobclient1 = cc1.get_blob_client(blob=blob1)
if container1 == container2:
Expand Down
54 changes: 54 additions & 0 deletions adlfs/tests/test_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -2511,3 +2511,57 @@ class TestCloseCredential:
async def test_close_credential(self, credential):
file_obj = SimpleNamespace(credential=credential)
await close_credential(file_obj)


def test_mv_single_file(storage):
fs = AzureBlobFileSystem(
account_name=storage.account_name, connection_string=CONN_STR
)
fs.mkdir("mvcontainer")

with fs.open("mvcontainer/srcdir/file.txt", "wb") as f:
f.write(b"hello")

fs.mv("mvcontainer/srcdir/file.txt", "mvcontainer/dstdir/")

assert fs.exists("mvcontainer/dstdir/file.txt")
assert not fs.exists("mvcontainer/srcdir/file.txt")
assert fs.cat_file("mvcontainer/dstdir/file.txt") == b"hello"

fs.rm("mvcontainer", recursive=True)


def test_mv_directory(storage):
fs = AzureBlobFileSystem(
account_name=storage.account_name, connection_string=CONN_STR
)
fs.mkdir("mvcontainer")

with fs.open("mvcontainer/srcdir/file.txt", "wb") as f:
f.write(b"hello")

fs.mv("mvcontainer/srcdir", "mvcontainer/dstdir/", recursive=True)

assert fs.exists("mvcontainer/dstdir/srcdir/file.txt")
assert not fs.exists("mvcontainer/srcdir/")
assert fs.cat_file("mvcontainer/dstdir/srcdir/file.txt") == b"hello"

fs.rm("mvcontainer", recursive=True)


def test_mv_nested_directories(storage):
fs = AzureBlobFileSystem(
account_name=storage.account_name, connection_string=CONN_STR
)
fs.mkdir("mvcontainer")

with fs.open("mvcontainer/a/b/c/file.txt", "wb") as f:
f.write(b"content")

fs.mv("mvcontainer/a/b/c", "mvcontainer/a/c", recursive=True)

assert fs.exists("mvcontainer/a/c/file.txt")
assert not fs.exists("mvcontainer/a/b/c")
assert fs.cat_file("mvcontainer/a/c/file.txt") == b"content"

fs.rm("mvcontainer", recursive=True)
Loading