From 51b4087e87364db898b82b548ce8b8cfafdee1a3 Mon Sep 17 00:00:00 2001 From: Tom Hammel Date: Wed, 19 Nov 2025 13:53:24 +0100 Subject: [PATCH 1/6] adapter.aasx: Add `rename_file` to `DictSupplementaryFileContainer` Before: There was no supported way to rename supplementary files in `DictSupplementaryFileContainer`. Workflow had to manipulate internals or create duplicates, making file handling error-prone. Now: A public rename operation preserves content de-duplication and resolves name conflicts, simplifying AASX read/write flows and reducing inconsistent mappings. --- sdk/basyx/aas/adapter/aasx.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/sdk/basyx/aas/adapter/aasx.py b/sdk/basyx/aas/adapter/aasx.py index 8bb5958f..9d633aa3 100644 --- a/sdk/basyx/aas/adapter/aasx.py +++ b/sdk/basyx/aas/adapter/aasx.py @@ -824,15 +824,24 @@ def add_file(self, name: str, file: IO[bytes], content_type: str) -> str: if hash not in self._store: self._store[hash] = data self._store_refcount[hash] = 0 - name_map_data = (hash, content_type) + return self._assign_unique_name(name, hash, content_type) + + def rename_file(self, old_name: str, new_name: str) -> str: + if old_name not in self._name_map: + raise KeyError(f"File with name {old_name} not found in SupplementaryFileContainer.") + if new_name == old_name: + return new_name + old_hash, old_ct = self._name_map[old_name] + return self._assign_unique_name(new_name, old_hash, old_ct) + + def _assign_unique_name(self, name: str, sha: bytes, content_type: str) -> str: new_name = name i = 1 while True: if new_name not in self._name_map: - self._name_map[new_name] = name_map_data - self._store_refcount[hash] += 1 + self._name_map[new_name] = (sha, content_type) return new_name - elif self._name_map[new_name] == name_map_data: + elif self._name_map[new_name] == (sha, content_type): return new_name new_name = self._append_counter(name, i) i += 1 From e2e154d164fb7997e761df8ef349b155463ae131 Mon Sep 17 00:00:00 2001 From: Tom Hammel Date: Tue, 9 Dec 2025 16:04:46 +0100 Subject: [PATCH 2/6] test_aasx: Add unit tests for rename_file method --- sdk/test/adapter/aasx/test_aasx.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/sdk/test/adapter/aasx/test_aasx.py b/sdk/test/adapter/aasx/test_aasx.py index a83c6018..2db6b2bb 100644 --- a/sdk/test/adapter/aasx/test_aasx.py +++ b/sdk/test/adapter/aasx/test_aasx.py @@ -36,6 +36,28 @@ def test_supplementary_file_container(self) -> None: self.assertNotEqual("/TestFile.pdf", new_name) self.assertIn(new_name, container) + # Rename file to a new unique name + renamed = container.rename_file(new_name, "/RenamedTestFile.pdf") + self.assertIn(renamed, container) + # Old name should no longer exist + self.assertNotIn(new_name, container) + self.assertEqual(renamed, "/RenamedTestFile.pdf") + + # Renaming to the same name should be no-op + renamed_same = container.rename_file(renamed, renamed) + self.assertEqual(renamed, renamed_same) + + # Renaming to an existing name should create a conflict + renamed_conflict = container.rename_file(renamed, "/TestFile.pdf") + self.assertNotEqual(renamed_conflict, "/TestFile.pdf") + self.assertIn(renamed_conflict, container) + + # Renaming a non-existing file should raise KeyError + with self.assertRaises(KeyError): + container.rename_file("/NonExistingFile.pdf", "/AnotherName.pdf") + + new_name = renamed_conflict + # Check metadata self.assertEqual("application/pdf", container.get_content_type("/TestFile.pdf")) self.assertEqual("b18229b24a4ee92c6c2b6bc6a8018563b17472f1150d35d5a5945afeb447ed44", From f9e130af5e986d6da9670f93f3bd4e49b726add2 Mon Sep 17 00:00:00 2001 From: Tom Hammel Date: Tue, 9 Dec 2025 16:05:15 +0100 Subject: [PATCH 3/6] Fix rename_file method to delete old name from name map --- sdk/basyx/aas/adapter/aasx.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sdk/basyx/aas/adapter/aasx.py b/sdk/basyx/aas/adapter/aasx.py index 9d633aa3..824504b2 100644 --- a/sdk/basyx/aas/adapter/aasx.py +++ b/sdk/basyx/aas/adapter/aasx.py @@ -831,8 +831,9 @@ def rename_file(self, old_name: str, new_name: str) -> str: raise KeyError(f"File with name {old_name} not found in SupplementaryFileContainer.") if new_name == old_name: return new_name - old_hash, old_ct = self._name_map[old_name] - return self._assign_unique_name(new_name, old_hash, old_ct) + file_hash, file_ct = self._name_map[old_name] + del self._name_map[old_name] + return self._assign_unique_name(new_name, file_hash, file_ct) def _assign_unique_name(self, name: str, sha: bytes, content_type: str) -> str: new_name = name From 89e4b6e9e70507c441d20e7f383f09644433c0ed Mon Sep 17 00:00:00 2001 From: Moritz Sommer Date: Sun, 8 Jun 2025 12:16:40 +0200 Subject: [PATCH 4/6] Small refactoring --- sdk/basyx/aas/adapter/aasx.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/basyx/aas/adapter/aasx.py b/sdk/basyx/aas/adapter/aasx.py index 824504b2..7ddf9131 100644 --- a/sdk/basyx/aas/adapter/aasx.py +++ b/sdk/basyx/aas/adapter/aasx.py @@ -831,9 +831,9 @@ def rename_file(self, old_name: str, new_name: str) -> str: raise KeyError(f"File with name {old_name} not found in SupplementaryFileContainer.") if new_name == old_name: return new_name - file_hash, file_ct = self._name_map[old_name] + file_hash, file_content_type = self._name_map[old_name] del self._name_map[old_name] - return self._assign_unique_name(new_name, file_hash, file_ct) + return self._assign_unique_name(new_name, file_hash, file_content_type) def _assign_unique_name(self, name: str, sha: bytes, content_type: str) -> str: new_name = name From e578c233aa6baf2c5922e1cc51da24fee1505728 Mon Sep 17 00:00:00 2001 From: zrgt Date: Wed, 10 Dec 2025 17:03:56 +0100 Subject: [PATCH 5/6] Small refactoring --- sdk/test/adapter/aasx/test_aasx.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/sdk/test/adapter/aasx/test_aasx.py b/sdk/test/adapter/aasx/test_aasx.py index 2db6b2bb..447c18ff 100644 --- a/sdk/test/adapter/aasx/test_aasx.py +++ b/sdk/test/adapter/aasx/test_aasx.py @@ -22,25 +22,26 @@ class TestAASXUtils(unittest.TestCase): def test_supplementary_file_container(self) -> None: container = aasx.DictSupplementaryFileContainer() with open(os.path.join(os.path.dirname(__file__), 'TestFile.pdf'), 'rb') as f: - new_name = container.add_file("/TestFile.pdf", f, "application/pdf") + saved_file_name = container.add_file("/TestFile.pdf", f, "application/pdf") # Name should not be modified, since there is no conflict - self.assertEqual("/TestFile.pdf", new_name) + self.assertEqual("/TestFile.pdf", saved_file_name) f.seek(0) container.add_file("/TestFile.pdf", f, "application/pdf") # Name should not be modified, since there is still no conflict - self.assertEqual("/TestFile.pdf", new_name) + self.assertEqual("/TestFile.pdf", saved_file_name) + # Add a file with the same name to create a conflict with open(__file__, 'rb') as f: - new_name = container.add_file("/TestFile.pdf", f, "application/pdf") + saved_file_name_2 = container.add_file("/TestFile.pdf", f, "application/pdf") # Now, we have a conflict - self.assertNotEqual("/TestFile.pdf", new_name) - self.assertIn(new_name, container) + self.assertNotEqual(saved_file_name, saved_file_name_2) + self.assertIn(saved_file_name_2, container) # Rename file to a new unique name - renamed = container.rename_file(new_name, "/RenamedTestFile.pdf") + renamed = container.rename_file(saved_file_name, "/RenamedTestFile.pdf") self.assertIn(renamed, container) # Old name should no longer exist - self.assertNotIn(new_name, container) + self.assertNotIn(saved_file_name, container) self.assertEqual(renamed, "/RenamedTestFile.pdf") # Renaming to the same name should be no-op From 09b22972fbb9d31a549152915e1582d4f22714d2 Mon Sep 17 00:00:00 2001 From: zrgt Date: Wed, 10 Dec 2025 17:16:51 +0100 Subject: [PATCH 6/6] Small refactoring --- sdk/test/adapter/aasx/test_aasx.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sdk/test/adapter/aasx/test_aasx.py b/sdk/test/adapter/aasx/test_aasx.py index 447c18ff..caf4e24c 100644 --- a/sdk/test/adapter/aasx/test_aasx.py +++ b/sdk/test/adapter/aasx/test_aasx.py @@ -26,11 +26,12 @@ def test_supplementary_file_container(self) -> None: # Name should not be modified, since there is no conflict self.assertEqual("/TestFile.pdf", saved_file_name) f.seek(0) - container.add_file("/TestFile.pdf", f, "application/pdf") + # Add the same file again with the same name + same_file_with_same_name = container.add_file("/TestFile.pdf", f, "application/pdf") # Name should not be modified, since there is still no conflict - self.assertEqual("/TestFile.pdf", saved_file_name) + self.assertEqual("/TestFile.pdf", same_file_with_same_name) - # Add a file with the same name to create a conflict + # Add other file with the same name to create a conflict with open(__file__, 'rb') as f: saved_file_name_2 = container.add_file("/TestFile.pdf", f, "application/pdf") # Now, we have a conflict @@ -38,10 +39,10 @@ def test_supplementary_file_container(self) -> None: self.assertIn(saved_file_name_2, container) # Rename file to a new unique name - renamed = container.rename_file(saved_file_name, "/RenamedTestFile.pdf") + renamed = container.rename_file(saved_file_name_2, "/RenamedTestFile.pdf") self.assertIn(renamed, container) # Old name should no longer exist - self.assertNotIn(saved_file_name, container) + self.assertNotIn(saved_file_name_2, container) self.assertEqual(renamed, "/RenamedTestFile.pdf") # Renaming to the same name should be no-op