Skip to content

Commit

Permalink
fix: append mode on non-existent files (#604)
Browse files Browse the repository at this point in the history
* fix: append mode on non-existent files

* fix: lint and format

* fix: copy zipfile

* fix: tests

* fix: remove one redundant isinstance check

* fix: refactor
  • Loading branch information
Ravencentric authored Aug 8, 2024
1 parent 3704630 commit a055656
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 30 deletions.
70 changes: 40 additions & 30 deletions py7zr/py7zr.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,34 +347,35 @@ def __init__(
self._block_size = blocksize
else:
self._block_size = get_default_blocksize()

# https://github.com/python/cpython/blob/b5e142ba7c2063efe9bb8065c3b0bad33e2a9afa/Lib/zipfile/__init__.py#L1350
# Check if we were passed a file-like object or not
if isinstance(file, os.PathLike):
file = os.fspath(file)
if isinstance(file, str):
self._filePassed: bool = False
self.filename: str = file
if mode == "r":
self.fp = open(file, "rb")
elif mode == "w":
self.fp = open(file, "w+b")
elif mode == "x":
self.fp = open(file, "x+b")
elif mode == "a":
self.fp = open(file, "r+b")
else:
raise ValueError("File open error.")
self.mode = mode
elif isinstance(file, pathlib.Path):
# No, it's a filename
self._filePassed = False
self.filename = str(file)
if mode == "r":
self.fp = file.open(mode="rb") # noqa # typeshed issue: 2911
elif mode == "w":
self.fp = file.open(mode="w+b") # noqa
elif mode == "x":
self.fp = file.open(mode="x+b") # noqa
elif mode == "a":
self.fp = file.open(mode="r+b") # noqa
else:
raise ValueError("File open error.")
self.filename = file
modeDict = {
"r": "rb",
"w": "w+b",
"x": "x+b",
"a": "r+b",
"r+b": "w+b",
"w+b": "wb",
"x+b": "xb",
}
filemode = modeDict[mode]

while True:
try:
self.fp = open(file, filemode) # type: ignore
except OSError:
if filemode in modeDict:
filemode = modeDict[filemode]
continue
raise
break
self.mode = mode
elif isinstance(file, multivolumefile.MultiVolume):
self._filePassed = True
Expand All @@ -401,8 +402,13 @@ def __init__(
elif mode == "x":
self._prepare_write(filters, password)
elif mode == "a":
self._real_get_contents(password)
self._prepare_append(filters, password)
try:
# Append if it's an existing 7zip file
self._real_get_contents(password)
self._prepare_append(filters, password)
except Bad7zFile:
# Not an existing 7zip file, write instead
self._prepare_write(filters, password)
else:
raise ValueError("Mode must be 'r', 'w', 'x', or 'a'") # never come here
except Exception as e:
Expand Down Expand Up @@ -776,9 +782,13 @@ def set_encrypted_header(self, mode: bool) -> None:

@staticmethod
def _check_7zfile(fp: Union[BinaryIO, io.BufferedReader, io.IOBase]) -> bool:
result = MAGIC_7Z == fp.read(len(MAGIC_7Z))[: len(MAGIC_7Z)]
fp.seek(-len(MAGIC_7Z), 1)
return result
try:
result = MAGIC_7Z == fp.read(len(MAGIC_7Z))[: len(MAGIC_7Z)]
fp.seek(-len(MAGIC_7Z), 1)
return result
except OSError:
# A new empty file raises OSError
return False

def _get_method_names(self) -> List[str]:
try:
Expand Down
7 changes: 7 additions & 0 deletions tests/test_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ def test_basic_append_mode(tmp_path):
archive.write(os.path.join(testdata_path, "test1.txt"), "test1.txt")


@pytest.mark.api
def test_basic_append_mode_on_non_existent_file(tmp_path):
target = tmp_path.joinpath("test_non_existent_file.7z")
with py7zr.SevenZipFile(target, mode="a") as archive:
archive.write(os.path.join(testdata_path, "test1.txt"), "test1.txt")


@pytest.mark.api
def test_basic_wrong_option_value(tmp_path):
with pytest.raises(ValueError):
Expand Down

0 comments on commit a055656

Please sign in to comment.