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

AttributeError when adding empty string with writestr()

kokumura opened this issue · comments

Describe the bug

SevenZipFile.writestr() raises AttributeError: 'NoneType' object has no attribute 'files' under the following conditions:

  1. The data argument value is an empty string
  2. It is the first write** mthod call for the archive file

Related issue

#398

To Reproduce

Here is a minimal code to reproduce the issue.

import py7zr

with py7zr.SevenZipFile(
    file="test.7z",
    mode="w",
) as archive:

    archive.writestr(
        data="",
        arcname="empty.txt",
    )

Run the code above, the following error occurs.

Traceback (most recent call last):
  File "test-py7zr.py", line 8, in <module>
    archive.writestr(
  File "/path-to-site-packages/py7zr/py7zr.py", line 1093, in writestr
    return self._writestr(data, arcname)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/path-to-site-packages/py7zr/py7zr.py", line 1099, in _writestr
    self._writef(io.BytesIO(data.encode("UTF-8")), arcname)
  File "/path-to-site-packages/py7zr/py7zr.py", line 1086, in _writef
    self.header.files_info.files.append(file_info)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'files'

Expected behavior
An empty file is created in the archive file with no errors.

Environment (please complete the following information):

  • OS: macOS
  • Python: CPython 3.11.5
  • py7zr version: v0.20.7

Test data(please attach in the report):

See thre reproduction code above.

Additional context

I found that SevenZipFile._writef() does not call self.header.initialize() when size == 0
(self.header.files_info is None until self.header.initialize() is called.)

py7zr/py7zr/py7zr.py

Lines 1067 to 1078 in 30e4184

if size > 0:
folder = self.header.initialize()
file_info = self._make_file_info_from_name(bio, size, arcname)
self.header.files_info.files.append(file_info)
self.header.files_info.emptyfiles.append(file_info["emptystream"])
self.files.append(file_info)
self.worker.archive(self.fp, self.files, folder, deref=False)
else:
file_info = self._make_file_info_from_name(bio, size, arcname)
self.header.files_info.files.append(file_info)
self.header.files_info.emptyfiles.append(file_info["emptystream"])
self.files.append(file_info)

For a workaround, explicitly calling header.initialize() before writestr() solved my problem.
(Update) I noticed this workaround is not right. Do not try this.
As reported in #398, writing empty file with writestr() seems not working at all.

I found that #398 covers this problem in more detail.
Please close this issue as duplication, sorry.

@kokumura when you have more time to investigate, it is a chance to dig and fix the long pending problem, flaged help wanted, and join to contributors list with Pull-Reuqest. As always it is welcome.