py7zr icon indicating copy to clipboard operation
py7zr copied to clipboard

IndexError / Worker initialization sets wrong last_file_index

Open baterflyrity opened this issue 10 months ago • 2 comments

Describe the bug

During initialization Worker sets instance field self.last_file_index = len(self.files) which is totally wrong for non-empty list of files. Lately when working with archive this bug raises IndexError: list index out of range.

Such behavior is caused by side-effects while working with Worker hence on second and further initializations it becomes corrupted.

To Reproduce

import py7zr
with py7zr.SevenZipFile('test.7z', 'w') as archive:
	archive.writestr('1', '1.txt')
	archive.test()

Traceback (most recent call last): File "1.py", line 99, in with py7zr.SevenZipFile('test.7z', 'w') as archive: File "C:\Python311\Lib\site-packages\py7zr\py7zr.py", line 417, in exit self.close() File "C:\Python311\Lib\site-packages\py7zr\py7zr.py", line 1110, in close self._write_flush() File "C:\Python311\Lib\site-packages\py7zr\py7zr.py", line 697, in _write_flush self.worker.flush_archive(self.fp, folder) File "C:\Python311\Lib\site-packages\py7zr\py7zr.py", line 1512, in flush_archive if "maxsize" in self.header.files_info.files[self.last_file_index]: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^ IndexError: list index out of range

Expected behavior

image

Environment:

  • OS: Windows 10
  • Python 3.11.5
  • py7zr version: 0.20.6

Current workaround

Just create new wrapper per each archive operation:

import py7zr
with py7zr.SevenZipFile('test.7z', 'w') as archive:
	archive.writestr('1', '1.txt')
with py7zr.SevenZipFile('test.7z', 'r') as archive:
	archive.test()

Possible fixes

  1. Correctly initialize worker.
self.last_file_index = len(self.files) - 1
  1. Alternative bad solution is to prevent Worker reinitialization. On of many approaches:
class SevenZipFile:
	def __init__(self, ...):
		self._worker = None
		...

	@property
	def worker(self):
		if not self._worker:
			raise NotInitializedError(...)
		return self._worker
	
	@worker.setter
	def worker(self, value):
		if self._worker:
			raise AlreadyInitializedError(...)
		self._worker = value

baterflyrity avatar Sep 29 '23 21:09 baterflyrity

@baterflyrity you are welcome raising pull-request to fix the issue. I want to see reproducible test case and the proposal fix it.

miurahr avatar Sep 30 '23 13:09 miurahr

@baterflyrity you are welcome raising pull-request to fix the issue. I want to see reproducible test case and the proposal fix it.

Sure, I will try.

baterflyrity avatar Sep 30 '23 18:09 baterflyrity