Skip to content

Commit f296f9f

Browse files
TalLevAmiclaude
andcommitted
Fix sync for raw files whose display name has no extension
In dynamic folder mode, the display name of an uploaded raw file does not include the extension (`notes`), but the public ID does (`abc123.txt`). `_display_path` used the display name as is, so: - `sync --push` did not match local `notes.txt` with the remote asset and uploaded it again on every run; - `sync --pull` saved the file as `notes`, with no extension. For raw assets, add the public ID extension to the display name when the display name does not already end with it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 4e38035 commit f296f9f

2 files changed

Lines changed: 28 additions & 2 deletions

File tree

‎cloudinary_cli/utils/api_utils.py‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,13 @@ def _display_path(asset):
124124
if asset.get("display_name") is None:
125125
return ""
126126

127-
if asset["resource_type"] == "raw" or asset["type"] == 'fetch':
127+
if asset["resource_type"] == "raw":
128+
# The display name of a raw asset can omit the extension that the public ID keeps.
129+
normalized_display_name = asset["display_name"]
130+
extension = path.splitext(asset["public_id"])[1]
131+
if extension and not normalized_display_name.lower().endswith(extension.lower()):
132+
normalized_display_name += extension
133+
elif asset["type"] == 'fetch':
128134
normalized_display_name = asset["display_name"]
129135
else:
130136
normalized_display_name = ".".join(filter(None, [asset["display_name"], asset.get("format", None)]))

‎test/test_modules/test_cli_sync.py‎

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,27 @@
1010
from test.helper_test import unique_suffix, RESOURCES_DIR, TEST_FILES_DIR, delete_cld_folder_if_exists, retry_assertion, \
1111
get_request_url, get_params, URLLIB3_REQUEST
1212
from test.test_modules.test_cli_upload_dir import UPLOAD_MOCK_RESPONSE
13-
from cloudinary_cli.utils.api_utils import get_folder_mode
13+
from cloudinary_cli.utils.api_utils import get_folder_mode, _display_path
14+
15+
16+
class TestDisplayPath(unittest.TestCase):
17+
@staticmethod
18+
def _asset(resource_type, public_id, display_name, fmt=None):
19+
return {"resource_type": resource_type, "type": "upload", "public_id": public_id,
20+
"display_name": display_name, "format": fmt, "asset_folder": "folder"}
21+
22+
def test_image_display_path_adds_format(self):
23+
self.assertEqual("folder/red.png", _display_path(self._asset("image", "abc123", "red", "png")))
24+
25+
def test_raw_display_path_adds_missing_extension(self):
26+
self.assertEqual("folder/notes.txt", _display_path(self._asset("raw", "abc123.txt", "notes")))
27+
28+
def test_raw_display_path_keeps_existing_extension(self):
29+
self.assertEqual("folder/notes.txt", _display_path(self._asset("raw", "abc123.txt", "notes.txt")))
30+
self.assertEqual("folder/notes.TXT", _display_path(self._asset("raw", "abc123.txt", "notes.TXT")))
31+
32+
def test_raw_display_path_without_extension(self):
33+
self.assertEqual("folder/notes", _display_path(self._asset("raw", "abc123", "notes")))
1434

1535

1636
class TestCLISync(unittest.TestCase):

0 commit comments

Comments
 (0)