-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for custom archive formats
- Loading branch information
Showing
10 changed files
with
125 additions
and
80 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
from __future__ import annotations | ||
|
||
import posixpath | ||
from collections.abc import Callable, Iterable, Iterator, Mapping, Set | ||
from contextlib import AbstractContextManager, contextmanager | ||
from pathlib import Path | ||
from typing import NamedTuple, Protocol | ||
from zipfile import ZipFile | ||
|
||
|
||
class ArchiveOpener(Protocol): # pragma: no cover | ||
def __call__(self, path: Path) -> AbstractContextManager[Archive]: | ||
... | ||
|
||
|
||
class Archive(NamedTuple): | ||
top_level_folders: Set[str] | ||
extract: Callable[[Path], None] | ||
|
||
|
||
def find_addon_zip_tocs(names: Iterable[str]): | ||
"Find top-level folders in a list of zip member paths." | ||
for name in names: | ||
if name.count(posixpath.sep) == 1: | ||
head, tail = posixpath.split(name) | ||
if tail.startswith(head) and tail[-4:].lower() == '.toc': | ||
yield (name, head) | ||
|
||
|
||
def make_zip_member_filter_fn(base_dirs: Set[str]): | ||
"Filter out items which are not sub-paths of top-level folders in a zip." | ||
|
||
def is_subpath(name: str): | ||
head, sep, _ = name.partition(posixpath.sep) | ||
return head in base_dirs if sep else False | ||
|
||
return is_subpath | ||
|
||
|
||
@contextmanager | ||
def open_zip_archive(path: Path) -> Iterator[Archive]: | ||
with ZipFile(path) as archive: | ||
names = archive.namelist() | ||
top_level_folders = {h for _, h in find_addon_zip_tocs(names)} | ||
|
||
def extract(parent: Path) -> None: | ||
should_extract = make_zip_member_filter_fn(top_level_folders) | ||
archive.extractall(parent, members=(n for n in names if should_extract(n))) | ||
|
||
yield Archive(top_level_folders, extract) | ||
|
||
|
||
ARCHIVE_OPENERS: Mapping[str, ArchiveOpener] = { | ||
'application/zip': open_zip_archive, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
from __future__ import annotations | ||
|
||
from itertools import product | ||
|
||
import pytest | ||
|
||
from instawow.archive import find_addon_zip_tocs, make_zip_member_filter_fn | ||
|
||
|
||
def test_find_addon_zip_tocs_can_find_explicit_dirs(): | ||
assert {h for _, h in find_addon_zip_tocs(['b/', 'b/b.toc'])} == {'b'} | ||
|
||
|
||
def test_find_addon_zip_tocs_can_find_implicit_dirs(): | ||
assert {h for _, h in find_addon_zip_tocs(['b/b.toc'])} == {'b'} | ||
|
||
|
||
def test_find_addon_zip_tocs_discards_tocless_paths(): | ||
assert {h for _, h in find_addon_zip_tocs(['a', 'b/b.toc', 'c/'])} == {'b'} | ||
|
||
|
||
def test_find_addon_zip_tocs_discards_mismatched_tocs(): | ||
assert not {h for _, h in find_addon_zip_tocs(['a', 'a/b.toc'])} | ||
|
||
|
||
def test_find_addon_zip_tocs_accepts_multitoc(): | ||
assert {h for _, h in find_addon_zip_tocs(['a', 'a/a_mainline.toc'])} == {'a'} | ||
|
||
|
||
@pytest.mark.parametrize('ext', product('Tt', 'Oo', 'Cc')) | ||
def test_find_addon_zip_tocs_toc_is_case_insensitive(ext: tuple[str, ...]): | ||
assert {h for _, h in find_addon_zip_tocs([f'a/a.{"".join(ext)}'])} == {'a'} | ||
|
||
|
||
def test_make_zip_member_filter_fn_discards_names_with_prefix_not_in_dirs(): | ||
is_member = make_zip_member_filter_fn({'b'}) | ||
assert list(filter(is_member, ['a/', 'b/', 'aa/', 'bb/', 'b/c', 'a/d'])) == ['b/', 'b/c'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters