miurahr / py7zr

7zip in python3 with ZStandard, PPMd, LZMA2, LZMA1, Delta, BCJ, BZip2, and Deflate compressions, and AES encryption.

Home Page:https://pypi.org/project/py7zr/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

IndexError / Worker initialization sets wrong last_file_index

baterflyrity opened this issue · 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 you are welcome raising pull-request to fix the issue.
I want to see reproducible test case and the proposal fix it.

@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.