diff --git a/trx/tests/test_memmap.py b/trx/tests/test_memmap.py index 0d9488f..21451a4 100644 --- a/trx/tests/test_memmap.py +++ b/trx/tests/test_memmap.py @@ -398,6 +398,26 @@ def test_trxfile_close(): pass +@pytest.mark.parametrize("path", [("small.trx")]) +def test_close_releases_mmap_from_zip(path): + """close() must release mmap handles even when loaded via load_from_zip().""" + path = os.path.join(get_home(), "memmap_test_data", path) + trx = tmm.load_from_zip(path) + + assert trx._uncompressed_folder_handle is None + + mmap_obj = trx.streamlines._data._mmap + assert mmap_obj is not None, "expected a live mmap before close()" + assert not mmap_obj.closed, "mmap should be open before close()" + + trx.close() + + assert mmap_obj.closed, ( + "mmap is still open after close() — the mmap teardown was skipped " + "because _uncompressed_folder_handle was None" + ) + + # Endianness tests for cross-platform compatibility (Issue #83) @pytest.mark.parametrize( "dtype_input,expected_byteorder", diff --git a/trx/trx_file_memmap.py b/trx/trx_file_memmap.py index d6aa485..1556f59 100644 --- a/trx/trx_file_memmap.py +++ b/trx/trx_file_memmap.py @@ -2260,23 +2260,22 @@ def close(self) -> None: None Releases file handles and removes temporary storage. """ - if self._uncompressed_folder_handle is not None: - close_or_delete_mmap(self.streamlines) + close_or_delete_mmap(self.streamlines) - # # Close or delete attributes in dictionaries - for key in self.data_per_vertex: - close_or_delete_mmap(self.data_per_vertex[key]) + for key in self.data_per_vertex: + close_or_delete_mmap(self.data_per_vertex[key]) - for key in self.data_per_streamline: - close_or_delete_mmap(self.data_per_streamline[key]) + for key in self.data_per_streamline: + close_or_delete_mmap(self.data_per_streamline[key]) - for key in self.groups: - close_or_delete_mmap(self.groups[key]) + for key in self.groups: + close_or_delete_mmap(self.groups[key]) - for key in self.data_per_group: - for dpg in self.data_per_group[key]: - close_or_delete_mmap(self.data_per_group[key][dpg]) + for key in self.data_per_group: + for dpg in self.data_per_group[key]: + close_or_delete_mmap(self.data_per_group[key][dpg]) + if self._uncompressed_folder_handle is not None: try: self._uncompressed_folder_handle.cleanup() except PermissionError: