Skip to content

Commit 67432b5

Browse files
jkhsjdhjss-heppner
authored andcommitted
adapter.aasx: allow deleting files from SupplementaryFileContainer
`AbstractSupplementaryFileContainer` and `DictSupplementaryFileContainer` are extended by a `delete_file()` method, that allows deleting files from them. Since different files may have the same content, references to the files contents in `DictSupplementaryFileContainer._store` are tracked via `_store_refcount`. A files contents are only deleted from `_store`, if all filenames referring to these these contents are deleted, i.e. if the refcount reaches 0.
1 parent ecf4c13 commit 67432b5

File tree

2 files changed

+39
-0
lines changed

2 files changed

+39
-0
lines changed

basyx/aas/adapter/aasx.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -778,6 +778,13 @@ def write_file(self, name: str, file: IO[bytes]) -> None:
778778
"""
779779
pass # pragma: no cover
780780

781+
@abc.abstractmethod
782+
def delete_file(self, name: str) -> None:
783+
"""
784+
Deletes a file from this SupplementaryFileContainer given its name.
785+
"""
786+
pass # pragma: no cover
787+
781788
@abc.abstractmethod
782789
def __contains__(self, item: str) -> bool:
783790
"""
@@ -802,18 +809,23 @@ def __init__(self):
802809
self._store: Dict[bytes, bytes] = {}
803810
# Maps file names to (sha256, content_type)
804811
self._name_map: Dict[str, Tuple[bytes, str]] = {}
812+
# Tracks the number of references to _store keys,
813+
# i.e. the number of different filenames referring to the same file
814+
self._store_refcount: Dict[bytes, int] = {}
805815

806816
def add_file(self, name: str, file: IO[bytes], content_type: str) -> str:
807817
data = file.read()
808818
hash = hashlib.sha256(data).digest()
809819
if hash not in self._store:
810820
self._store[hash] = data
821+
self._store_refcount[hash] = 0
811822
name_map_data = (hash, content_type)
812823
new_name = name
813824
i = 1
814825
while True:
815826
if new_name not in self._name_map:
816827
self._name_map[new_name] = name_map_data
828+
self._store_refcount[hash] += 1
817829
return new_name
818830
elif self._name_map[new_name] == name_map_data:
819831
return new_name
@@ -839,6 +851,16 @@ def get_sha256(self, name: str) -> bytes:
839851
def write_file(self, name: str, file: IO[bytes]) -> None:
840852
file.write(self._store[self._name_map[name][0]])
841853

854+
def delete_file(self, name: str) -> None:
855+
# The number of different files with the same content are kept track of via _store_refcount.
856+
# The contents are only deleted, once the refcount reaches zero.
857+
hash: bytes = self._name_map[name][0]
858+
self._store_refcount[hash] -= 1
859+
if self._store_refcount[hash] == 0:
860+
del self._store[hash]
861+
del self._store_refcount[hash]
862+
del self._name_map[name]
863+
842864
def __contains__(self, item: object) -> bool:
843865
return item in self._name_map
844866

test/adapter/aasx/test_aasx.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,23 @@ def test_supplementary_file_container(self) -> None:
5454
container.write_file("/TestFile.pdf", file_content)
5555
self.assertEqual(hashlib.sha1(file_content.getvalue()).hexdigest(), "78450a66f59d74c073bf6858db340090ea72a8b1")
5656

57+
# Add same file again with different content_type to test reference counting
58+
with open(__file__, 'rb') as f:
59+
duplicate_file = container.add_file("/TestFile.pdf", f, "image/jpeg")
60+
self.assertIn(duplicate_file, container)
61+
62+
# Delete files
63+
container.delete_file(new_name)
64+
self.assertNotIn(new_name, container)
65+
# File should still be accessible
66+
container.write_file(duplicate_file, file_content)
67+
68+
container.delete_file(duplicate_file)
69+
self.assertNotIn(duplicate_file, container)
70+
# File should now not be accessible anymore
71+
with self.assertRaises(KeyError):
72+
container.write_file(duplicate_file, file_content)
73+
5774

5875
class AASXWriterTest(unittest.TestCase):
5976
def test_writing_reading_example_aas(self) -> None:

0 commit comments

Comments
 (0)